1 Pattern matching

1.1 Detect reduced Riccati ode \(y'=a x^n + b y^2\)
1.2 Detect general Riccati ode \(y'=f_0(x) + f_1(x) y(x)+ f_2(x) y^2\)
1.3 Detect ode of form \(y'=(a + b x + c y(x))^n\)
1.4 Detect first order linear ode of form \(A(t) y'(t)+ B(t) y(t)=f(t)\)
1.5 Detect second order linear ode of form \(A(t) y''(t)+ B(t) y'(t) + C(t) y(t)=f(t)\)
1.6 pattern example 1
1.7 pattern example 2
1.8 pattern example 3
1.9 pattern example 4
1.10 pattern example 5
1.11 pattern example 6
1.12 pattern example 7
1.13 pattern example 8
1.14 pattern example 9
1.15 pattern example 10
1.16 pattern example 11
1.17 pattern example 12
1.18 pattern example 13
1.19 pattern example 14
1.20 pattern example 15
1.21 pattern example 16
1.22 pattern example 17
1.23 pattern example 18
1.24 pattern example 19

This section shows some examples using Maple to detect some patterns. In all of these the main maple function used is patmatch. Some example also show the Mathematica command to compare results with.

Many of these can be done in Maple without using patmatch command, and even using shorter code. But these examples are meant to show how to use patmatch.

1.1 Detect reduced Riccati ode \(y'=a x^n + b y^2\)

This detects Reduced Riccati ode \(y'=a x^n + b y^2\) where \(a,n,b\) are scalars and \(y(x)\) is the dependent function. The input to the proc is the ode and the dependent function \(y(x)\). The parsing function will return \(a,b,n\) if successful match is found, or FAIL if ode does not match the expected pattern.

reduced_riccati_parse:=proc(ode::`=`,func::function(name),$) 
local RHS; 
local y:=op(0,func); 
local x:=op(1,func); 
local la,a,b,n; 
 
   try 
      RHS:=timelimit(30,[solve(ode,diff(y(x),x))]); 
      if nops(RHS)<>1 then RETURN(FAIL); fi; 
      RHS:=expand(RHS[1]); 
   catch: 
      RETURN(FAIL); 
   end try; 
 
   if patmatch(RHS,a::anything*x^(n::anything)+b::anything*y(x)^2,'la') then 
      assign(la); 
      if has(a,x) or has(a,y(x)) then RETURN(FAIL); fi; 
      if has(n,x) or has(n,y(x)) then RETURN(FAIL); fi; 
      if has(b,x) or has(b,y(x)) then RETURN(FAIL); fi; 
      RETURN(a,b,n); 
   else 
      RETURN(FAIL); 
   fi; 
 
end proc:
                                                                                    
                                                                                    
 

Example usage

ode:=diff(y(x),x)=x-1+y(x)^2: 
reduced_riccati_parse(ode,y(x)); 
DEtools:-odeadvisor(ode); 
                              FAIL 
                           [_Riccati] 
 
ode:=diff(y(x),x)=x-1+y(x)^2: 
reduced_riccati_parse(ode,y(x)); 
DEtools:-odeadvisor(ode); 
                              FAIL 
                           [_Riccati] 
 
ode:=diff(y(x),x)=-3*x^2+y(x)^2: 
reduced_riccati_parse(ode,y(x)); 
DEtools:-odeadvisor(ode); 
 
                            -3, 1, 2 
                     [[_Riccati, _special]] 
 
ode:=diff(y(x),x)=x^(-4)+y(x)^2: 
reduced_riccati_parse(ode,y(x)); 
DEtools:-odeadvisor(ode); 
 
                            1, 1, -4 
               [_rational, [_Riccati, _special]] 
 
ode:=diff(y(x),x)=sin(x)*x^(-4)+y(x)^2: 
reduced_riccati_parse(ode,y(x)); 
DEtools:-odeadvisor(ode); 
 
                              FAIL 
                           [_Riccati] 
 
ode:=diff(y(x),x$2)=x^(-4)+y(x)^2: 
reduced_riccati_parse(ode,y(x)); 
DEtools:-odeadvisor(ode); 
 
                              FAIL 
            [[_2nd_order, _with_linear_symmetries]] 
 
ode:=diff(y(x),x)-x^(-4)-9*y(x)^2=0: 
reduced_riccati_parse(ode,y(x)); 
DEtools:-odeadvisor(ode); 
 
                            1, 9, -4 
               [_rational, [_Riccati, _special]] 
 
ode:=diff(y(x),x)=1/x+x*y(x)^2: 
reduced_riccati_parse(ode,y(x)); 
DEtools:-odeadvisor(ode); 
 
                              FAIL 
                     [_rational, _Riccati] 
 
ode:=diff(y(x),x)=a*1/x+b*y(x)^2: 
reduced_riccati_parse(ode,y(x)); 
DEtools:-odeadvisor(ode); 
 
                            a, b, -1 
               [_rational, [_Riccati, _special]] 
 
ode:=diff(y(x),x)=a*x^n+b*y(x)^2: 
reduced_riccati_parse(ode,y(x)); 
DEtools:-odeadvisor(ode); 
                            a, b, n 
                     [[_Riccati, _special]] 
 
ode:=diff(y(x),x)=a*x^n+y(x)+b*y(x)^2: 
reduced_riccati_parse(ode,y(x)); 
DEtools:-odeadvisor(ode); 
                              FAIL 
                           [_Riccati] 
 
ode:=diff(y(x),x)=y(x)*x^n+b*y(x)^2: 
reduced_riccati_parse(ode,y(x)); 
DEtools:-odeadvisor(ode); 
                              FAIL 
                          [_Bernoulli]
 

1.2 Detect general Riccati ode \(y'=f_0(x) + f_1(x) y(x)+ f_2(x) y^2\)

This detects the general Riccati ode \(y'=f_0(x) + f_1(x) y(x)+ f_2(x) y^2\)

The input to the proc is the ode and the dependent function \(y(x)\). The parsing function will return \(f_0,f_1,f_2\) if successful match is found, or FAIL if ode does not match the expected pattern.

\(f_0,f_2\) can not be zero (i.e. missing), but \(f_1\) could be missing.

In the case \(f_1=0\), either \(f_0\) or \(f_2\) or both must be functions of \(x\). Also in the case of \(f_1=0\), if it is reduced Riccati, then it return FAIL.

In the case \(f_1\) is present and also depends on \(x\), then now \(f_0,f_2\) are both allowed not to be functions of \(x\). But if \(f_1\) present and also does not depend on \(x\), then now at least one of \(f_0,f_2\) must be function of \(x\).

general_riccati_parse:=proc(ode::`=`,func::function(name),$) 
local RHS; 
local y:=op(0,func); 
local x:=op(1,func); 
local stat; 
local la,f0,f1,f2; 
 
   stat:= reduced_riccati_parse(ode,func); 
   if stat<>FAIL then #this is reduced riccati, hence not general 
      RETURN(FAIL); 
   fi; 
 
   #now check if general riccati 
   try 
      RHS:=timelimit(30,[solve(ode,diff(y(x),x))]); 
      if nops(RHS)<>1 then RETURN(FAIL); fi; 
      RHS:=expand(RHS[1]); 
      RHS:=collect(RHS,[y(x),y(x)^2]);#must collect to insure the right form 
   catch: 
      RETURN(FAIL); 
   end try; 
 
   if patmatch(RHS,f0::anything+f1::anything*y(x)+f2::anything*y(x)^2,'la') then 
      assign(la); 
      if f0=0 or f2=0 then RETURN(FAIL); fi; 
      if has(f0,y(x)) or has(f1,y(x)) or has(f2,y(x)) then RETURN(FAIL); fi; 
 
      if has(f1,x) then 
         RETURN(f0,f1,f2); 
      else 
         if not has(f0,x) and not has(f2,x) then 
            RETURN(FAIL); 
         else 
            RETURN(f0,f1,f2); 
         fi; 
      fi; 
   else #check for f1 missing 
      if patmatch(RHS,f0::anything+f2::anything*y(x)^2,'la') then 
         assign(la); 
         if has(f0,y(x)) or has(f2,y(x)) then RETURN(FAIL); fi; 
         if not has(f0,x) and not has(f2,x) then RETURN(FAIL); fi; 
         RETURN(f0,0,f2); 
      else 
         RETURN(FAIL); 
      fi; 
   fi; 
 
end proc:
 

Example usage

ode:=diff(y(x),x)=f0(x)+f1*y(x)+f2*y(x)^2: 
general_riccati_parse(ode,y(x)); 
DETools:-odeadvisor(ode); 
 
                         f0(x), f1, f2 
                           [_Riccati] 
 
ode:=diff(y(x),x)=f0+f1(x)*y(x)+f2*y(x)^2: 
general_riccati_parse(ode,y(x)); 
DETools:-odeadvisor(ode); 
 
                         f0, f1(x), f2 
                           [_Riccati] 
 
ode:=diff(y(x),x)=f0(x)+f1(x)*y(x)+f2*y(x)^2: 
general_riccati_parse(ode,y(x)); 
DETools:-odeadvisor(ode); 
 
                        f0(x), f1(x), f2 
                           [_Riccati] 
 
ode:=diff(y(x),x)=f0(x)+f1(x)*y(x)+f2(x)*y(x)^2: 
general_riccati_parse(ode,y(x)); 
DETools:-odeadvisor(ode); 
 
                      f0(x), f1(x), f2(x) 
                           [_Riccati] 
 
ode:=diff(y(x),x)=f0(x)+f2(x)*y(x)^2: 
general_riccati_parse(ode,y(x)); 
DETools:-odeadvisor(ode); 
 
                        f0(x), 0, f2(x) 
                           [_Riccati] 
 
ode:=diff(y(x),x)=x+f2(x)*y(x)^2: 
general_riccati_parse(ode,y(x)); 
DETools:-odeadvisor(ode); 
 
                          x, 0, f2(x) 
                           [_Riccati] 
 
ode:=diff(y(x),x)=x+f2*y(x)^2: 
general_riccati_parse(ode,y(x)); 
DETools:-odeadvisor(ode); 
 
                              FAIL 
                     [[_Riccati, _special]] 
 
ode:=diff(y(x),x)=1/x+y(x)+y(x)^2: 
general_riccati_parse(ode,y(x)); 
DETools:-odeadvisor(ode); 
 
                            1 
                            -, 1, 1 
                            x 
                     [_rational, _Riccati] 
 
ode:=diff(y(x),x)=1/x+y(x)+x*y(x)^2: 
general_riccati_parse(ode,y(x)); 
DETools:-odeadvisor(ode); 
 
                            1 
                            -, 1, x 
                            x 
                     [_rational, _Riccati] 
 
ode:=diff(y(x),x)=x*y(x)+c*y(x)^2: 
general_riccati_parse(ode,y(x)); 
DETools:-odeadvisor(ode); 
 
                              FAIL 
                          [_Bernoulli] 
 
ode:=diff(y(x),x)=x-1+y(x)^2: 
general_riccati_parse(ode,y(x)); 
DETools:-odeadvisor(ode); 
 
                          -1 + x, 0, 1 
                           [_Riccati] 
 
ode:=diff(y(x),x)=(3+9*x+8*y(x))^(2): #this onde needs the collect 
general_riccati_parse(ode,y(x)); 
DETools:-odeadvisor(ode); 
 
              81*x^2 + 54*x + 9, 144*x + 48, 64 
              [[_homogeneous, class C], _Riccati]
 

1.3 Detect ode of form \(y'=(a + b x + c y(x))^n\)

Where \(n\) can not be \(1\). \(a\) can be missing but not \(b,c\). And \(a,b,c\) are scalars.

The parsing routing returns either FAIL or \(a,b,c,b\) if pattern is matched.

homog_c_parse:=proc(ode::`=`,func::function(name),$) 
 
local RHS; 
local y:=op(0,func); 
local x:=op(1,func); 
 
local la,a,b,c,n,p,the_power; 
 
   try 
      RHS:=timelimit(30,[solve(ode,diff(y(x),x))]); 
      if nops(RHS)<>1 then RETURN(FAIL); fi; 
      RHS:=simplify(RHS[1]); 
   catch: 
      RETURN(FAIL); 
   end try; 
 
   if patmatch(RHS,(p::anything)^(the_power::anything),'la') then 
      assign(la); 
      if the_power=1 then 
         RETURN(FAIL); 
      fi; 
      if patmatch(p,a::anything+b::anything*x+c::anything*y(x),'la') then 
         assign(la); 
         if has(a,x) or has(a,y(x)) then RETURN(FAIL); fi; 
         if has(b,x) or has(b,y(x)) then RETURN(FAIL); fi; 
         if has(c,x) or has(c,y(x)) then RETURN(FAIL); fi; 
         RETURN(a,b,c,the_power); 
      else 
         if patmatch(p,b::anything*x+c::anything*y(x),'la') then 
            assign(la); 
            if has(b,x) or has(b,y(x)) then RETURN(FAIL); fi; 
            if has(c,x) or has(c,y(x)) then RETURN(FAIL); fi; 
            RETURN(0,b,c,the_power); 
         else 
            RETURN(FAIL); 
         fi; 
      fi; 
   else 
      RETURN(FAIL); 
   fi; 
end proc:
 

Examples usages

ode:=diff(y(x),x)=(3+9*x+8*y(x))^(1/2): 
homog_c_parse(ode,y(x)); 
DEtools:-odeadvisor(ode); 
                                    1 
                           3, 9, 8, - 
                                    2 
             [[_homogeneous, class C], _dAlembert] 
 
ode:=diff(y(x),x)=(3+9*x+8*y(x))^(2): 
homog_c_parse(ode,y(x)); 
DEtools:-odeadvisor(ode); 
 
                           3, 9, 8, 2 
              [[_homogeneous, class C], _Riccati] 
 
ode:=(x+y(x))*diff(y(x),x)=1: 
homog_c_parse(ode,y(x)); 
DEtools:-odeadvisor(ode); 
                          0, 1, 1, -1 
     [[_homogeneous, class C], [_Abel, 2nd type, class C], _dAlembert] 
 
ode:=diff(y(x),x)=sqrt(x+y(x)+1): 
homog_c_parse(ode,y(x)); 
DEtools:-odeadvisor(ode); 
                                    1 
                           1, 1, 1, - 
                                    2 
             [[_homogeneous, class C], _dAlembert] 
 
ode:=diff(y(x),x)=1/(x+y(x)+1): 
homog_c_parse(ode,y(x)); 
DEtools:-odeadvisor(ode); 
 
                          1, 1, 1, -1 
    [[_homogeneous, class C], [_Abel, 2nd type, class C], _dAlembert] 
 
ode:=diff(y(x),x)=1/(x+y(x)+sin(x)): 
homog_c_parse(ode,y(x)); 
DEtools:-odeadvisor(ode); 
 
                              FAIL 
                  [[_Abel, 2nd type, class C]] 
 
ode:=diff(y(x),x)=(x+y(x)^2)^(2/3): 
homog_c_parse(ode,y(x)); 
DEtools:-odeadvisor(ode); 
                              FAIL 
                          [y=_G(x,y')]
 

1.4 Detect first order linear ode of form \(A(t) y'(t)+ B(t) y(t)=f(t)\)

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

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

restart; 
linear_first_order_parse:=proc(ode::`=`,func::function(name),$) 
local y:=op(0,func); 
local x:=op(1,func); 
local LHS; 
local la,A,B,F; 
 
   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 
 
   #must collect on y and y' to get the pattern to work OK for all cases 
   LHS:= collect(LHS,diff(y(x),x)); 
   LHS:= collect(LHS,y(x)); 
 
   if patmatch(LHS,A::anything*diff(y(x),x)+B::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(F,y(x)) then RETURN(FAIL); fi; 
      RETURN(A,B,F); 
   else 
      if patmatch(LHS,A::anything*diff(y(x),x)+F::anything,'la') then 
         assign(la); 
         if has(A,y(x)) then RETURN(FAIL); fi; 
         B:=0; 
         if has(F,y(x)) then RETURN(FAIL); fi; 
         RETURN(A,B,F); 
      else 
         RETURN(FAIL); 
      fi; 
   fi; 
end proc:
 

Notice that we had to check for the general case \(A y'+ B y + F\) first and if that fails then check for the specific case \(A y' + F \). This is because if \(y\) term is missing then Maple will not match the pattern. i.e. it will not give \(B=0\). So have to make separate pattern matching for the case with missing \(y\) in the ode.

For the case \(A y' + F\) Maple will match \(F=0\) if this term is missing. So we do not need to make more specialized pattern for just \(A y'\).

This is typical. We start with the most general pattern and if that fail, we go to more specialized ones.

Examples usages

ode:=1/diff(y(t),t)+t=0; 
linear_first_order_parse(ode,y(t)); 
DEtools:-odeadvisor(ode); 
                            t, 0, 1 
 
                         [_quadrature] 
 
 
ode:=1/diff(y(t),t)+t=y(t); 
linear_first_order_parse(ode,y(t)); 
DEtools:-odeadvisor(ode); 
                              FAIL 
[[_homogeneous, class C], [_Abel, 2nd type, class C], _dAlembert] 
 
 
 
ode:=t*diff(y(t),t)+5=0; 
linear_first_order_parse(ode,y(t)); 
DEtools:-odeadvisor(ode); 
                            t, 0, 5 
 
                         [_quadrature] 
 
ode:=t^2*diff(y(t),t)+t*y(t)=sin(t); 
linear_first_order_parse(ode,y(t)); 
DEtools:-odeadvisor(ode); 
 
                          2 
                         t , t, -sin(t) 
 
                           [_linear] 
 
ode:=y(t)*diff(y(t),t)+t*y(t)=sin(t); 
linear_first_order_parse(ode,y(t)); 
DEtools:-odeadvisor(ode); 
 
                              FAIL 
 
                  [[_Abel, 2nd type, class A]] 
 
ode:=sin(t)*diff(y(t),t)-99*ln(t)*y(t)=sin(t)+y(t); 
linear_first_order_parse(ode,y(t)); 
DEtools:-odeadvisor(ode); 
 
                 sin(t), -99 ln(t) - 1, -sin(t) 
 
                           [_linear] 
 
ode:=sin(t)*diff(y(t),t)-99*ln(t)*y(t)=sin(t)+9*diff(y(t),t)/t+ln(t); 
linear_first_order_parse(ode,y(t)); 
DEtools:-odeadvisor(ode); 
 
                      9 
             sin(t) - -, -99 ln(t), -ln(t) - sin(t) 
                      t 
 
                           [_linear] 
 
ode:=diff(y(t),t)-y(t)^2=sin(t); 
linear_first_order_parse(ode,y(t)); 
DEtools:-odeadvisor(ode); 
 
                              FAIL 
 
                           [_Riccati]
 

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 becuase 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.

1.6 pattern example 1

Given list \(3,4,x,x^2,x^3\), find those elements of form \(x^n\) where \(n\) is anything other than \(1\).

In Maple

L:=[3,4,x,x^2,x^3]; 
map(X->`if`(patmatch(X,x^n::nonunit(anything)),X,NULL),L) 
 
     [x^2, x^3]
 

The above can also be written in the long form

restart; 
 
L:=[3,4,x,x^2,x^3]; 
 
map(proc(X) 
    if patmatch(X,x^n::nonunit(anything)) then 
       X; 
    else 
      NULL; 
    fi; 
    end proc,L) 
 
 
     [x^2, x^3]
 

In Mathematica

Cases[{3, 4, x, x^2, x^3}, x^_] 
 
          {x^2, x^3}
                                                                                    
                                                                                    
 

1.7 pattern example 2

Given list \(3,4,x,x^2,x^3\), find those elements of form \(x^n\) where \(n\) is now allowed to be 1. In Maple

L:=[3,4,x,x^2,x^3]; 
map(X->`if`(patmatch(X,x^n::anything),X,NULL),L) 
 
     [x, x^2, x^3]
 

In Mathematica

Cases[{3, 4, x, x^2, x^3}, x^_.] 
 
          {x,x^2,x^3}
 

1.8 pattern example 3

Given list \(3,4,x,x^2,x^3\), return list of exponents of \(x\), other than \(1\).

In Maple

L:=[3,4,x,x^2,x^3]; 
f:=proc(X::anything,x::symbol) 
   local la,n; 
   if patmatch(X,x^n::nonunit(anything),'la') then 
       eval(n,la); 
    else 
       NULL; 
    fi; 
end proc; 
 
map(X->f(X,x),L) 
 
   [2, 3]
 

Another option is to use inlined if, like this

restart; 
L:=[3,4,x,x^2,x^3]; 
map(proc(X) local la,n; 
 
    if  patmatch(X,x^n::nonunit(anything),'la') then 
        eval(n,la); 
    else 
        NULL; 
    fi; 
    end proc, L); 
 
 
    [2, 3]
 

In Mathematica

Cases[{3, 4, x, x^2, x^3}, x^n_ -> n] 
         {2, 3}
 

1.9 pattern example 4

Gives list {f(a,a),f(a,b),f(c,d),f(b,b)} return {g(a),f(a,b),f(c,d),g(b)} where only function that takes two arguments which are the same is replaced by \(g(x)\) where \(x\) here is the argument in the original list.

In Maple

restart; 
L:={f(a,a),f(a,b),f(c,d),f(b,b)}; 
map(X->[unassign('x'),`if`(patmatch(X,f(x::anything,x::anything),'la'),[assign(la),g(x)][],X)][],L) 
 
                 {f(a, b), f(c, d), g(a), g(b)}
 

Or using the long form of map, which is more clear, even though the code is longer

restart; 
L:={f(a,a),f(a,b),f(c,d),f(b,b)}; 
 
map(proc(X) local la; 
    if  patmatch(X,f('x'::anything,'x'::anything),'la') then 
        g(eval('x',la)); 
    else 
        X; 
    fi; 
    end proc, L 
); 
 
         {f(a, b), f(c, d), g(a), g(b)}
 

In Mathematica

L = {f[a, a], f[a, b], f[c, d], f[b, b]} 
L /. f[x_, x_] -> g[x] 
 
       {g[a], f[a, b], f[c, d], g[b]}
 

1.10 pattern example 5

Given f(a)+f(b) transform it using pattern f(x::anything)->x^2 to obtain a^2+b^2

In Maple

expr:=f(a)+f(b); 
map( proc(X) local la,x; 
     if patmatch(X,f(x::anything),'la') then 
        eval(x,la)^2; 
     else 
        NULL; 
     fi; 
     end proc, expr); 
 
 
           a^2 + b^2
 

In Mathematica

f[a] + f[b] /. f[x_] -> x^2 
 
    a^2+b^2
 

1.11 pattern example 6

Given [1, x, x^2, x^3] write pattern to change all x^n::anything to r(n)

In Maple

L:=[1, x, x^2, x^3]; 
map( proc(X) local la,n; 
     if patmatch(X,x^n::'nonunit'(anything),'la') then 
        r(eval(n,la)); 
     else 
        X; 
     fi; 
     end proc, L); 
 
 
         [1, x, r(2), r(3)]
 

In Mathematica

{1, x, x^2, x^3} /. x^n_ -> r[n] 
 
   {1, x, r[2], r[3]}
 

1.12 pattern example 7

Given f(a + b) + f(a + c) + f(b + d) apply transformation f(a + x_) + f(c + y_) -> p(x, y)

Here to do not use map, since we want to apply pattern on the whole expression. In Maple

restart; 
expr:=f(a+b)+f(a+c)+f(b+d): 
 
if patmatch(expr,f(a+x::anything)+f(c+y::anything)+z::anything,'la') then 
   p(eval(x,la),eval(y,la))+eval(z,la); 
else 
   expr; 
fi; 
 
        p(b, a) + f(b + d)
 

In Mathematica

f[a+b]+f[a+c]+f[b+d]/. f[a+x_]+f[c+y_]->p[x,y] 
 
     f[b+d]+p[b,a]
 

1.13 pattern example 8

Write pattern to change f(a^b) to p(a^b,b)

In Maple

restart; 
expr:=f(a^b); 
 
if patmatch(expr,f( (a::anything)^b::anything ),'la') then 
   print(la); 
   p( eval(a,la)^eval(b,la) , eval(b,la) ); 
else 
   expr; 
fi; 
 
 
     p(a^b, b)
 

In Mathematica

f[a^b] /. f[x : _^n_] -> p[x, n] 
 
     p[a^b,b]
 

1.14 pattern example 9

Write pattern to select from list [3,-4,5,-2] only the numbers that are negative.

In Maple, we use the form patmatch(X, conditional(patttern,condition))

restart; 
L:=[3,-4,5,-2]; 
map( proc(X) local la; 
     if patmatch(X, conditional(n::integer,n<0),'la') then 
        eval(n,la); 
     else 
        NULL; 
     fi; 
     end proc, L); 
 
 
    [-4, -2]
 

In Mathematica

Cases[{3, -4, 5, -2}, x_ /; x < 0] 
 
    {-4,-2}
 

1.15 pattern example 10

Write pattern to select from list [z(1, 1), z(-1, 1), z(-2, 2)] only those with first argument which is negative.

In Maple, we use the form patmatch(X, conditional(patttern,condition))

restart; 
L:=[z(1, 1), z(-1, 1), z(-2, 2)]; 
map( proc(X) local la,a,b; 
     if patmatch(X, conditional(z(a::integer,b::integer),a<0),'la') then 
        z(eval(a,la),eval(b,la)); 
     else 
        NULL; 
     fi; 
     end proc, L 
    ); 
 
 
         [z(-1, 1), z(-2, 2)]
 

In Mathematica

Cases[{z[1, 1], z[-1, 1], z[-2, 2]}, z[x_ /; x < 0, y_]] 
 
    {z[-1,1],z[-2,2]}
 

1.16 pattern example 11

Write pattern to select from list 1+a,2+a,-3+a] only those that have negative number added to \(a\) and output \(p(x)\) where \(x\) is that number.

In Maple, we use the form patmatch(X, conditional(patttern,condition))

L:=[1+a,2+a,-3+a]; 
map( proc(X) local la,x; 
     if patmatch(X, conditional(a+x::integer,x<0),'la') then 
        p(eval(x,la)); 
     else 
        X; 
     fi; 
     end proc, L 
    ); 
 
 
       [1 + a, 2 + a, p(-3)]
 

In Mathematica

{1+a,2+a,-3+a}/. (x_/;x<0)+a->p[x] 
 
   {1 + a, 2 + a, p[-3]}
 

1.17 pattern example 12

Use pattern to square only numbers in a list.

L:=[2.3,4,7/8,a,b]; 
map( proc(X) local la,x; 
     if patmatch(X, x::numeric,'la') then 
        eval(x,la)^2; 
     else 
        X; 
     fi; 
     end proc, L 
    ); 
 
       [5.29, 16, 49/64, a, b]
 

In Mathematica

{2.3, 4, 7/8, a, b} /. (x_ /; NumberQ[x]) -> x^2 
 
   {5.29,16,49/64,a,b}
 

1.18 pattern example 13

Use pattern to convert p(4.5) + p(3/2) + p(u) to 22.5 + p(u) by squaring and adding arguments of those function which has its argument numeric.

p:=x->`if`(x::numeric,x^2,x); 
p(4.5) + p(3/2) + p(u); 
 
 
      22.50000000 + u
 

In Mathematica

p[x_?NumberQ] := x^2 
p[4.5] + p[3/2] + p[u] 
 
   22.5 + p[u]
 

1.19 pattern example 14

Write pattern to pick all elements from list which are not integers.

L:=[a, b, 0, 1, 2, x, y]: 
map( proc(X) local la,x; 
     if patmatch(X, conditional( x::anything, not _type(x,integer) ),'la') then 
        eval(x,la); 
     else 
        NULL; 
     fi; 
     end proc, L 
    ); 
 
     [a, b, x, y]
 

This can also be done without pattern matching like this

select(not type,L,integer) 
 
      [a, b, x, y]
 

In Mathematica

Cases[{a, b, 0, 1, 2, x, y}, Except[_Integer]] 
 
   {a, b, x, y}
 

1.20 pattern example 15

Replace variable \(x\) in expression with a value \(1\).

L:=[x, x^2,y,z, sin(x), exp( tan(x) )]: 
 
f:=proc(X::anything,x::symbol) 
    local la,y; 
 
    if patmatch(X, conditional( y::anything, _has(y,x) ),'la') then 
        eval(y,la); 
        eval(%,x=1); 
     else 
        X; 
     fi; 
end proc; 
 
map(X->f(X,x),L); 
 
 
     [1, 1, y, z, sin(1), exp(tan(1))]
 

Actually, we do not need patmatch for this, but the above is just an exercise. This can be done in one line in Maple as follows

L:=[x, x^2,y,z, sin(x), exp( tan(x) )]: 
map(X->eval(X,x=1),L) 
 
   [1, 1, y, z, sin(1), exp(tan(1))]
 

In Mathematica

L={x, x^2,y,z, Sin[x], Exp[Tan[x]]} 
L/.x->1 
 
   {1,1,y,z,Sin[1],Exp[Tan[1]]}
 

1.21 pattern example 16

Replace variable \(x\) in expression with a list.

L:=[x, x^2,y,z]: 
 
f:=proc(X::anything,x::symbol) 
    local la,y; 
 
    if patmatch(X, conditional( y::anything, _has(y,x) ),'la') then 
        eval(y,la); 
        eval(%,x={a,b}); 
     else 
        X; 
     fi; 
end proc; 
 
map(X->f(X,x),L); 
 
 
     [{a, b}, {a, b}^2, y, z]
 

Actually, we do not need patmatch for this, but the above is just an exercise. This can be done in one line in Maple as follows

L:=[x, x^2,y,z]: 
map(X->eval(X,x={a,b}),L) 
 
   [{a, b}, {a, b}^2, y, z]
 

In Mathematica

{x, x^2, y, z} /. x -> {a, b} 
 
   {{a, b}, {a^2, b^2}, y, z}
 

1.22 pattern example 17

Replace \(sin\) by \(cos\)

f:=proc(X::anything) 
    local la,x; 
 
    if patmatch(X, conditional( x::anything, _has(x,sin) ),'la') then 
        eval(x,la); 
        eval(%,sin=cos); 
     else 
        X; 
     fi; 
end proc; 
 
L:=[sin(x), cos(x)+sin(2*x),tan(x)]: 
map(X->f(X), L); 
 
 
    [cos(x), cos(x) + cos(2*x), tan(x)]
 

Actually, we do not need patmatch for this, but the above is just an exercise. This can be done in one line in Maple as follows

L:=[sin(x), cos(x)+sin(2*x),tan(x)]: 
map(X->eval(X,sin=cos), L) 
 
 
    [cos(x), cos(x) + cos(2*x), tan(x)]
 

In Mathematica

{Sin[x], Cos[x] + Sin[2*x], Tan[x]} /. Sin -> Cos 
 
   {Cos[x], Cos[x] + Cos[2 x], Tan[x]}
 

1.23 pattern example 18

Use pattern to replace \(x^n\) by \(f(n)\)

restart; 
expr:=1 + x^2 + x^4; 
 
F:=proc(X::anything,x::symbol) 
    local la,y,n; 
    if patmatch(X, (y::anything)^(n::'nonunit'(anything)) ,'la') then 
        if eval(y,la)=1 then #bug in maple? 
           X; 
        else 
           f(eval(n,la)); 
        fi; 
     else 
        X; 
     fi; 
end proc; 
 
map(X->F(X,x), expr); 
 
 
    f(4) + f(2) + 1
                                                                                    
                                                                                    
 

In Mathematica

1 + x^2 + x^4 /. x^p_ :> f[p] 
 
   1 + f[2] + f[4]
 

1.24 pattern example 19

replaces a whole expression by default. In Maple, this is easier to do it without pattern, like this

restart; 
expr:=f(1, 2, z); 
e:=map(X->`if`(X::integer,"int",X),[op(expr)]); 
f(op(e)) 
 
    f("int", "int", z)
 

In Mathematica, a pattern works easier

Replace[f[1, 2, z], _Integer :> "int", {1}] 
 
   f["int", "int", z]
 

The main issue is that it is hard to match a squence in Maple. For example, in Mathematica this works

f[1, 2, z] /. f[x___] :> x 
 
        Sequence[1, 2, z]
 

But in Maple

expr:=f(1, 2, z); 
patmatch(expr, f(x::anything)) 
 
      false
 

So pattern matching does work to match sequence of arguments.