1.5 Detect second order linear ode of form \(A(t) y''(t)+ B(t) y'(t) + C(t) y(t)=f(t)\)

And also find the values of \(A,B,C,f\) in the above. This can be used to parse second order ode after checking it is linear ode (linear in \(y'',y',y\)).

The parsing routing returns either FAIL or \(A,B,C,f\) if pattern is matched.

linear_second_order_parse:=proc(ode::`=`,func::function(name),$) 
local y:=op(0,func); 
local x:=op(1,func); 
local LHS; 
local la,A,B,C,F; 
 
   #DEBUG(); 
   LHS:= lhs(ode)-rhs(ode); 
   LHS:= numer(normal(LHS)); #this is needed to normalize the ode 
         #for example 1/y'' + x = 0 will becomes 1+x y''  = 0, else 
         #the pattern will fail 
   LHS:= collect(LHS,diff(y(x),x$2)); 
   LHS:= collect(LHS,diff(y(x),x)); 
   LHS:= collect(LHS,y(x)); 
 
   if patmatch(LHS,A::anything*diff(y(x),x$2) 
                   +B::anything*diff(y(x),x) 
                   +C::anything*y(x)+F::anything,'la') then 
      assign(la); 
      if has(A,y(x)) then RETURN(FAIL); fi; 
      if has(B,y(x)) then RETURN(FAIL); fi; 
      if has(C,y(x)) then RETURN(FAIL); fi; 
      if has(F,y(x)) then RETURN(FAIL); fi; 
      RETURN(A,B,C,F); 
   elif patmatch(LHS,A::anything*diff(y(x),x$2) 
                   +C::anything*y(x)+F::anything,'la') then 
         assign(la); 
         if has(A,y(x)) then RETURN(FAIL); fi; 
         B:=0; 
         if has(C,y(x)) then RETURN(FAIL); fi; 
         if has(F,y(x)) then RETURN(FAIL); fi; 
         RETURN(A,B,C,F); 
   elif patmatch(LHS,A::anything*diff(y(x),x$2) 
                   +B::anything*diff(y(x),x)+F::anything,'la') then 
         assign(la); 
         if has(A,y(x)) then RETURN(FAIL); fi; 
         if has(B,y(x)) then RETURN(FAIL); fi; 
         C:=0; 
         if has(F,y(x)) then RETURN(FAIL); fi; 
         RETURN(A,B,C,F); 
    elif 
         patmatch(LHS,A::anything*diff(y(x),x$2)+F::anything,'la') then 
         assign(la); 
         if has(A,y(x)) then RETURN(FAIL); fi; 
         B:=0; 
         C:=0; 
         if has(F,y(x)) then RETURN(FAIL); fi; 
         RETURN(A,B,C,F); 
     else 
         RETURN(FAIL); 
     fi; 
end proc:
 

We see we had to check for all possible patterns. \(A y''+B y'+C y+f\) and \(A y''+B y'+f\) and \(A y''+C y+f\) and \(y''+f\). This is because the pattern for say \(B y'\) will not return \(B=0\) if the term \(B y'\) is missing. In this case there are 4 possible pattern to check for for second order linear ode.

It is possible to do this parsing without using pattern ofcourse (see my ode section below, using the command DEtools:-convertAlg(ode,y(x)); will do it. But the point here is to do this using patterns.

For expression of many terms, many patterns are need to cover all possible inputs. This is because there is no option to tell Maple to match  A::anything*y(x) and give \(A=0\) when the whole term is missing. If there was such an option, it will make life much simpler.

Examples usages

ode:=t/diff(y(t),t$2)+t=0: 
linear_second_order_parse(ode,y(t)); 
DEtools:-odeadvisor(ode); 
 
                           t, 0, 0, t 
                  [[_2nd_order, _quadrature]] 
 
 
ode:=t*diff(y(t),t$2)+5=0: 
linear_second_order_parse(ode,y(t)); 
DEtools:-odeadvisor(ode); 
                           t, 0, 0, 5 
                  [[_2nd_order, _quadrature]] 
 
ode:=t*diff(y(t),t$2)+diff(y(t),t)+5*t=0: 
linear_second_order_parse(ode,y(t)); 
DEtools:-odeadvisor(ode); 
                          t, 1, 0, 5 t 
 
                   [[_2nd_order, _missing_y]] 
 
ode:=t*diff(y(t),t$2)+99*y(t)=0: 
linear_second_order_parse(ode,y(t)); 
DEtools:-odeadvisor(ode); 
                          t, 0, 99, 0 
 
                      [[_Emden, _Fowler]] 
 
ode:=t*diff(y(t),t$2)+99*y(t)=sin(t)*y(t)+diff(y(t),t): 
linear_second_order_parse(ode,y(t)); 
DEtools:-odeadvisor(ode); 
                     t, -1, -sin(t) + 99, 0 
 
            [[_2nd_order, _with_linear_symmetries]] 
 
ode:=y(t)*diff(y(t),t$2)+99*y(t)=sin(t)*y(t)+diff(y(t),t): 
linear_second_order_parse(ode,y(t)); 
DEtools:-odeadvisor(ode); 
                              FAIL 
 
             [[_2nd_order, _reducible, _mu_x_y1], 
 
ode:=diff(y(t),t)*diff(y(t),t$2)+99*y(t)=sin(t)*y(t): 
linear_second_order_parse(ode,y(t)); 
DEtools:-odeadvisor(ode); 
                              FAIL 
                             [NONE]
 

Using Mathematica pattern the above is a little simpler, since there one can use default zero if term is missing, so no need to check for all possible patterns. Here is an example

rule = (aa : a_. y''[x] : 0) + (bb : b_. y'[x] : 0) + (cc : c_. y[x] :0) + any_. 
           :> {Plus@a, Plus@b, Plus@c, any}; 
 
x^2*y''[x] + x*y'[x] + x*y[x] - 3*x /. rule
 

Gives {x^2, x, x, -3 x} and if a term is missing, same pattern still works

x^2*y''[x] - 3*x /. rule
 

Gives {x^2, 0, 0, -3 x} so we did not have to make pattern for possible missing terms. I was not able to do the same using Maple’s pattern match as it has no default option. In this example, we see that Mathematica’s pattern matching is better. If I find a way to do the same in Maple I will update this.