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.
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
.
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]
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]
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')]
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}
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}
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}
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]}
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
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]}
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]
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]
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}
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]}
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]}
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}
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]
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}
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]]}
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}
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]}
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]
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.