In Maple 2021, it is now possible to use object:-method(arg) notation. This makes is easier to use OOP in maple. To do this, use _self as follows
restart; module person() option object; local name::string:=""; local age::integer:=0; export get_name::static:=proc(_self,$) return _self:-name; end proc; export set_name::static:=proc(_self,name::string,$) _self:-name:=name; end proc; export get_age::static:=proc(_self,$) return _self:-age; end proc; export set_age::static:=proc(_self,age::integer,$) _self:-age:=age; end proc; end module;
And now make an object and use it as follows
o:=Object(person) o := Object<<1846759887808>> o:-get_name(); "" o:-get_age(); 0 o:-set_name("joe doe"); o:-get_name(); "joe doe"
Add ModuleCopy proc in the class. This will automatically be called to initialize the object.
Here is an example
restart; module ODE() option object; local ode:=NULL; local y::symbol; local x::symbol; local sol; export ModuleCopy::static := proc( _self::ODE, proto::ODE, ode, func, $ ) print("Initilizing object with with args: ", [args]); _self:-ode:= ode; _self:-y:=op(0,func); _self:-x:=op(1,func); end proc; export dsolve::static:=proc(_self,$) _self:-sol := :-dsolve(ode,y(x)); end proc; export get_sol::static:=proc(_self,$) return sol; end proc; end module;
And now make an object and use it as follows
o:=Object(ODE, diff(y(x),x)+y(x)=sin(x), y(x)); o:-dsolve(): o:-get_sol(); #y(x) = -1/2*cos(x) + 1/2*sin(x) + exp(-x)*_C1
So a constructor just makes it easier to initialize the object without having to make a number of set() calls to initialize each memeber data.
This is done using overload with diﬀerent ModuleCopy proc in the class.
Here is an example. Lets make a constructor that takes an ode and initial conditions, and one that only takes an ode with no initial conditions.
restart; module ODE() option object; local ode:=NULL; local y::symbol; local x::symbol; local ic:=NULL; local sol; export ModuleCopy::static:= overload( [ proc( _self::ODE, proto::ODE, ode, func, $ ) option overload; _self:-ode:= ode; _self:-y:=op(0,func); _self:-x:=op(1,func); end proc, proc( _self::ODE, proto::ODE, ode, func, ic, $ ) option overload; _self:-ode:= ode; _self:-y:=op(0,func); _self:-x:=op(1,func); _self:-ic :=ic; end proc ] ); export dsolve::static:=proc(_self,$) if evalb(ic=NULL) then sol := :-dsolve(ode,y(x)); else sol := :-dsolve([ode,ic],y(x)); fi; end proc; export get_sol::static:=proc(_self,$) return sol; end proc; end module;
And now use it as follows
o:=Object(ODE, diff(y(x),x)+y(x)=sin(x), y(x), y(0)=0); o:-dsolve(): o:-get_sol(); #y(x) = -1/2*cos(x) + 1/2*sin(x) + 1/2*exp(-x) o:=Object(ODE, diff(y(x),x)+y(x)=sin(x), y(x)); o:-dsolve(): o:-get_sol(); #y(x) = -1/2*cos(x) + 1/2*sin(x) + exp(-x)*_C1
In the child class you want to extend from the parent class, add option object(ParentName);
Here is an example
restart; module ODE() option object; local ode; export set_ode::static:=proc(_self,ode,$) _self:-ode :=ode; end proc; export get_ode::static:=proc(_self,$) return _self:-ode; end proc; end module; #create class/module which extends the above module second_order_ode() option object(ODE); export get_ode_order::static:=proc(_self,$) return 2; end proc; end module;
In the above second_order_ode inherts all local variables and functions in the ODE class and adds new proc. Use as follows
o:=Object(second_order_ode); #create an object instance o:-set_ode(diff(y(x),x)=sin(x)); o:-get_ode(); o:-get_ode_order();
Note that the child class can not have its own variable with the same name as the parent class. This is limitation. in C++ for example, local variables in extended class overrides the same named variable in the parent class.
Even if the variable have diﬀerent type, Maple will not allow overriding. For example, this will fail
restart; module ODE() option object; local ode; local id::integer; export set_ode::static:=proc(_self,ode,$) print("Enter ode::set_ode"); _self:-ode :=ode; end proc; export get_ode::static:=proc(_self,$) return _self:-ode; end proc; end module; module second_order_ode() option object(ODE); local id::string; export get_ode_order::static:=proc(_self,$) return 2; end proc; end module; Error, (in second_order_ode) local `id` is declared more than once
There might be a way to handle this, i.e. to somehow exlicitly tell Maple to override parant class proc or variable name in the child. I do not know now. The above is using Maple 2021.1
This is called polymorphism in OOP. This is a base class animal_class which has make_sound method. This method acts just as a place holder (interface), which the extending class must extends (override) with an actual implementation.
The class is extended to make cat class and implementation is made.
module animal_class() option object; export make_sound::static:=proc(_self,$) error("Not implemented, must be overriden"); end proc; end module; %--------------- module cat_class() option object(animal_class); make_sound::static:=proc(_self,$) #note. DO NOT USE export print("mewooo"); end proc; end module;
And now
o:=Object(animal_class); o:-make_sound(); Error, (in anonymous procedure) Not implemented, must be overriden
The above is by design. As the animal_class is meant to be extended to be usable.
my_cat:=Object(cat_class); my_cat:-make_sound(); "mewooo"
So the base class can have number of methods, which are all meant to be be have its implementation provided by an extending class. Each class which extends the base class must provide implementation.
Once a base class is extended, all methods in the base class become part of the extending class. So to call a base class method just use same way as if calling any other method in the extending class itself.
Here is an example.
module person() option object; local age::integer:=100; export ModuleCopy::static:= proc(_self,proto::person, age::integer,$) _self:-age := age; end proc; local base_class_method::static:=proc(_self,$) print("In base class method..."); end proc; end module; #---- extend the above class module young_person() option object(person); export process::static:=proc(_self,$) print("In young_person process"); _self:-base_class_method(); end proc; end module;
Here is an example of usage
o:=Object(young_person,20); o:-process() "In young_person process" "In base class method..."
The above in Maple 2023.1
A Maple Object can be used a record type in other languags, such as Ada or Pascal. This example shows how to deﬁne a local type inside a proc and use it as record.
restart; foo:=proc(the_name::string,the_age::integer)::person_type; local module person_type() #this acts as a record type option object; export the_name::string; export the_age::integer; end module; local person::person_type:=Object(person_type); person:-the_name:=the_name; person:-the_age:=the_age; return person; end proc; o:=foo("joe doe",100); o:-the_name; "joe doe" o:-the_age; 100
In the above person is local variable of type person_type. In the above example, the local variable was returned back to user. But this is just an example. One can declare such variables and just use them internally inside the proc only. This method helps one organize related variables into one record/struct like type. The type can also be made global if needed.
Suppose we have list L1 of objects and we want to copy this list to another list, say L2. If we just do L2 = L1 then this will not make an actual copy as any changes to L1 are still reﬂected in L2. The above only copies the reference to the same list.
To make a physical copy, need to use the copy command as follows
restart; module person_type() option object; export the_name::string:="doe joe"; export the_age::integer:=100; end module; L1:=[]; for N from 1 to 5 do o:=Object(person_type); o:-the_name:=convert(N,string); L1:= [ op(L1), o]; od: L2:=map(Z->copy(Z),L1):
Now making any changes to L1 will not aﬀect L2. If we just did L2 = L1 then both will share same content which is not what we wanted.
This is a basic example of using OOP in Maple to implement an ode solver. There is a base module called ode_base_class (I will be using class instead of module, as this is more common in OOP)
This program will for now support ﬁrst order and second order ode’s only.
The base ode class will contain the basic operations and data which is common to both ﬁrst order and second order ode’s.
Next, we will make a ﬁrst order ode class, and second order ode class. Both of these will extend the base ode class.
Next, we will have more ode classes. For example, for ﬁrst order ode, there will be linear ﬁrst order ode class, and separable ﬁrst order ode class, and Bernoulli ode class and so on. Each one these classess will extend the ﬁrst order ode class which in turn extends the base ode class.
Same for second order ode’s. There will be second order constant coeﬃcients ode class, and second order variable coeﬃcient ode class and so on. Each one of these classes will extend the second order ode class which in turn extends the base ode class.
Let base_ode_class be the base of an ode class which will contain all the neccessary methods that are generic and applicable to an ode of any order and type.
These can be the ode expression itself, its order and any other data and operations which applicable to any ode type.
Now we want to create a ﬁrst order class. This will have its own operations and private variables that are speciﬁc and make sense only to any ﬁrst order ode.
Then these will be the ﬁrst order separable ode class, which has methods that implement solving the ode using separable method and has other methods which makes sense only for ﬁrst order separable ode. The following diagram is partial illustration of the is-A relation among possible classes.
First we deﬁne the base ode class and deﬁne private variables and method that are common to any ode type.
restart; module base_ode_class() option object; local the_ode; local the_order::integer; export get_ode::static:=proc(_self,$) RETURN(_self:-the_ode); end proc; export get_order::static:=proc(_self,$) RETURN(_self:-the_order); end proc; end module;
Note that the base ode class does not have constructor. Since it is meant to be extended only.
The following is the ﬁrst order ode class.
module first_order_ode_class() option object(base_ode_class); local initial_conditions; local solution_found; export get_IC::static:=proc(_self,$) RETURN(_self:-initial_conditions); end proc; export get_solution::static:=proc(_self,$) RETURN(_self:-solution_found); end proc; export verify_solution::static:=proc(_self,$)::truefalse; #code to verify if the solution found is valid or not #using odetest() end proc; end module;
The following is the ﬁrst order separable ode class which extends the above ﬁrst order ode class.
module first_order_separable_ode_class() option object(first_order_ode_class); local f,g; #ode of form y'=f(x)*g(y) export ModuleCopy::static:= proc(_self,proto::first_order_separable_ode_class, ode, IC,$) _self:-the_ode := ode; _self:-initial_conditions := IC; end proc; export dsolve::static:=proc(_self,$) #solve the ode for now we will use Maple but in my code #I have my own solver ofcourse. _self:-solution_found:= :-dsolve([_self:-the_ode, _self:-initial_conditions]); end proc; end module;
In the above, when we create a instance of first_order_separable_ode_class then it now have the whole chain of classes into one. i.e. ﬁrst order separable class extending the ﬁrst order class which in turn extends the base ode class. For example
o:=Object(first_order_separable_ode_class,diff(y(x),x)=3*sin(x)*y(x),y(0)=1) o:-dsolve(); o:-get_solution() #y(x) = exp(3)*exp(-3*cos(x)) o:-get_ode() #diff(y(x), x) = 3*sin(x)*y(x)
The above calls will all work, even though the ﬁrst order separable class has no method in it called get_ode but it extends a class which does, hence it works as it.
Now we will do the same for second order ode’s.
Advantage of this design, is that methods in base classes that are being extended can be reused by any class which extends them. Only methods that applies and speciﬁc to the lower level classes need to be implemented.
As we add more speciﬁc solvers, we just have to extend the base classes and the new solvers just need to implement its own speciﬁc dsolve and any speciﬁc methods and data that it needs itself.
Ofcourse in practice the above design is not complete as is. The user should not have to specify which class to instantiate, as user does not care what the ode type or class it is. They just want to to do
o:=Object(ode_class,diff(y(x),x)=3*sin(x)*y(x),y(0)=1) o:-dsolve(); o:-get_solution()
To solve this problem we have to make a factory method which is called to make the correct instance of the class and return that to the user. The factory method ﬁgures out the type of ode and it creates the correct instance of the correct class and returns that. So the call will become
o := make_ode_object( diff(y(x),x)=3*sin(x)*y(x), y(0)=1) o:-dsolve(); o:-get_solution()
The function make_ode_object above is the main interface the user will call to make an ode object.
This will be explained next with examples. One possibility is to make the factory function a global function or better, a public method in a utility module. For now, it is given here as stadalone function for illustration. The user calls this method to make an object of the correct instance of the ode. Here is complete implementation of all the above including the factory method.
#factory method. Makes objects for users make_ode_object:=proc(ode::`=`,func::function(name)) local x,y,the_order; y:=op(0,func); x:=op(1,func); the_order := PDEtools:-difforder(ode,x); if the_order=1 then RETURN(first_order_ode_class:-make_ode_object(ode,func)); elif the_order=2 then #RETURN(second_order_ode_class:-make_ode_object(ode,func)); #implement later NULL; else error "Only first and second order ode's are currently supported"; fi; end proc: ######################### module base_ode_class() option object; local the_ode; local the_order::integer; #methods bound to the object export get_ode::static:=proc(_self,$) RETURN(_self:-the_ode); end proc; export get_order::static:=proc(_self,$) RETURN(_self:-the_order); end proc; end module: ################## module first_order_ode_class() option object(base_ode_class); local initial_conditions; local solution_found; #public factory method not bound to the object. export make_ode_object:=proc(ode::`=`,func::function(name)) local x,y,ode_type::string; y:=op(0,func); x:=op(1,func); ode_type:="separable"; #code here which determined first order ode type if ode_type="separable" then RETURN( Object(first_order_separable_ode_class,ode,func)); elif ode_type="linear" then RETURN( Object(first_order_linear_ode_class,ode,func)); fi; #more ode types added here end proc; #methods bound to the object export get_IC::static:=proc(_self,$) RETURN(_self:-initial_conditions); end proc; export get_solution::static:=proc(_self,$) RETURN(_self:-solution_found); end proc; export verify_solution::static:=proc(_self,$)::truefalse; #code to verify if the solution found is valid or not #using odetest() end proc; end module: ################## module first_order_separable_ode_class() option object(first_order_ode_class); local f,g; #ode of form y'=f(x)*g(y) export ModuleCopy::static:= proc(_self,proto::first_order_separable_ode_class,ode,func::function(name),$) _self:-the_ode := ode; end proc; export dsolve::static:=proc(_self,$) #print("Enter first_order_separable_ode_class:-dsolve"); #solve the ode for now we will use Maple but in my code #I have my own solver ofcourse. _self:-solution_found:= :-dsolve(_self:-the_ode); NULL; end proc; end module:
It is used as follows
o:=make_ode_object(diff(y(x),x)=sin(x)*y(x),y(x)); o:-dsolve(); o:-get_solution(); y(x) = c__1 exp(-cos(x))
Here, I will start making a complete small OOP ode solver in Maple.
At each step more classes are added and enhanced until we get a fully working small ode solver based on OOP design that solves a ﬁrst and second order ode, this is to show how it all works. Another solvers can be added later by simply extending the base class.
The base class is called Base_ode_class. There will be Second_order_ode_class and First_order_ode_class and these classes extend the base ode class. We can later add higher_order_ode_class.
Next, there are diﬀerent classes which extend these. There is First_order_linear_ode_class and First_order_separable_ode_class and so on, and these extend the First_order_ode_class.
For example, if a user wanted to solve a ﬁrst order ode which happend to be say separable, then object of class First_order_separable_ode_class will be created and used.
Since the user does not know and should not know what object to create, then the factory class will be used. The factory class is what the user initially calls to make the ode object.
It is the factory class which determines which type of class to instantiate based on the ode given.
The factory class is singleton (standard module in Maple, not of type object), which has the make_ode method which is called by the user. This method parses the ode and determines its order and then based on the order determine which subclass to use, and then instantiate this and returns the correct object to the user to use.
This object will have the dsolve method and other methods the user can use on the object.
The make_ode method in the factory module accepts only the ode itself the function such as \(y(x)\). A typical use is given below
ode := ODE_factoy_class:-make_ode( diff(y(x),x)=sin(x), y(x) ); ode:-set_IC(....); ode:-set_hint("the hint"); ..... ode:-dsolve(); #solves the ode ode:-is_solved(); #checks if ode was successfully solved ode:-verify_sol(); #verifies the solution using maple odetest() ode:-is_sol_verified(); #asks if solution is verified ode:-get_number_of_sol(); #returns number of solutions, 1 or 2 etc... ode:-get_sol(); #returns the solutions found in list #and more method ...
Examples at the end will show how all the above works on actual odes’s.
The initial call to make an ode does not have initial conditions, or hint and any other parameters. This is so to keep the call simple. As the factory method only makes the concrete ode object.
Additional methods are then used to add more information if needed by using the returned object itself, such as initial conditions, and hint and so on before calling the dsolve method on the object.
Here is a very basic setup which include the base ode class and extended to ﬁrst order two subclasses for now.
restart; ODE_factory_class :=module() #notice, normal module. No option object. export make_ode:=proc(ode::`=`,func::function(name),$) local dep_variables_found::list,item; local y::symbol; local x::symbol; local ode_order::integer; if nops(func)<>1 then error("Parsing error, dependent variable must contain one argument, found ", func); fi; y:=op(0,func); x:=op(1,func); if not has(ode,y) then error ("Supplied ode ",ode," has no ",y); fi; if not has(ode,x) then error ("Supplied ode ",ode," has no ",x); fi; if not has(ode,func) then error ("Supplied ode ",ode," has no ",func); fi; ode_order := PDEtools:-difforder(ode,x); #this will check that the dependent variable will show with #SAME argument in the ode. i.e. if y(x) and y(t) show up in same ode, it #will throw exception, which is what we want. try dep_variables_found := PDEtools:-Library:-GetDepVars([y],ode); catch: error lastexception; end try; #now go over dep_variables_found and check the independent #variable is same as x i.e. ode can be y'(z)+y(z)=0 but function is y(x). for item in dep_variables_found do if not type(item,function) then error("Parsing error. Expected ",func," found ",item," in ode"); else if op(1,item) <> x then error("Parsing error. Expected ",func," found ",item," in ode"); fi; fi; od; #now go over all indents in ode and check that y only shows as y(x) and not as just y #as the PDEtools:-Library:-GetDepVars([_self:-y],ode) code above does not detect this. #i.e. it does not check y'(x)+y=0 if numelems(indets(ode,identical(y))) > 0 then error("Parsing error, Can not have ",y," with no argument inside ",ode); fi; if ode_order=1 then RETURN(make_first_order_ode(ode,y,x)); elif ode_order=2 then RETURN(make_second_order_ode(ode,y,x)); else RETURN(make_higher_order_ode(ode,y,x)); fi; end proc; local make_first_order_ode:=proc(ode::`=`,y::symbol,x::symbol) #decide on what specific type the ode is, and make instant of it if first_order_ode_quadrature_class:-is_quadrature(ode,y,x) then RETURN(Object(first_order_ode_quadrature_class,ode,y,x)); elif first_order_ode_linear_class:-is_linear(ode,y,x) then RETURN(Object(first_order_ode_linear_class,ode,y,x)); fi; #and so on end proc; local make_second_order_ode:=proc(ode::`=`,y::symbol,x::symbol) #decide on what specific type the ode is, and make instant of it #same as for first order end proc; end module; #------------------------- module solution_class() option object; local the_solution; local is_verified_solution::truefalse:=false; local is_implicit_solution::truefalse:=false; export ModuleCopy::static:= proc(_self,proto::solution_class,the_solution::`=`,is_implicit_solution::truefalse,$) _self:-the_solution:=the_solution; _self:-is_implicit_solution:=is_implicit_solution; end proc; export get_solution::static:=proc(_self,$) RETURN(_self:-the_solution); end proc; export is_verified::static:=proc(_self,$) RETURN(_self:-is_verified_solution); end proc; export is_implicit::static:=proc(_self,$) RETURN(_self:-is_implicit_solution); end proc; export is_explicit::static:=proc(_self,$) RETURN(not(_self:-is_implicit_solution)); end proc; export verify_solution::static:= overload( [ proc(_self, ode::`=`,$) option overload; local stat; stat:= odetest(_self:-the_solution,ode); if stat=0 then _self:-is_verified_solution:=true; else if simplify(stat)=0 then _self:-is_verified_solution:=true; else _self:-is_verified_solution:=false; fi; fi; end, proc(_self, ode::`=`,IC::list,$) option overload; local stat; stat:= odetest([_self:-the_solution,IC],ode); if stat=[0,0] then _self:-is_verified_solution:=true; else if simplify(stat)=[0,0] then _self:-is_verified_solution:=true; else _self:-is_verified_solution:=false; fi; fi; end ]); end module: #------------------------- module ODE_base_class() option object; local y::symbol; local x::symbol; local func::function(name); #y(x) local ode::`=`; local ode_order::posint; local IC::list:=[]; local parsed_IC::list:=[]; local the_hint::string:=""; local solutions_found::list(solution_class):=[]; #exported getters methods export get_ode::static:=proc(_self,$) RETURN(_self:-ode); end proc; export get_x::static:=proc(_self,$) RETURN(_self:-x); end proc; export get_y::static:=proc(_self,$) RETURN(_self:-y); end proc; export get_ode_order::static:=proc(_self,$) RETURN(_self:-ode_order); end proc; export get_IC::static:=proc(_self,$) RETURN(_self:-IC); end proc; export get_parsed_IC::static:=proc(_self,$) RETURN(_self:-parsed_IC); end proc; export get_sol::static:=proc(_self,$) local L:=Array(1..0): local sol; for sol in _self:-solutions_found do L ,= sol:-get_solution(); od; RETURN(convert(L,list)); end proc; #exported setters methods export set_hint::static:=proc(_self,hint::string,$) #add code to check if hint is valid _self:-the_hint:=hint; end proc; end module; #------------------------- module first_order_ode_quadrature_class() option object(ODE_base_class); local f,g; #ode of form y'=f(x)*g(y) #this method is not an object method. It is part of the module but does #not have _self. It is called by the factory class to find if the ode #is of this type first export is_quadrature:=proc(ode::`=`,y::symbol,x::symbol)::truefalse; RETURN(true); #for now end proc; export ModuleCopy::static:= proc(_self,proto::first_order_ode_quadrature_class,ode::`=`,y::symbol,x::symbol,$) _self:-ode := ode; _self:-y := y; _self:-x := x; _self:-func := _self:-y(_self:-x); _self:-ode_order :=1; end proc; export dsolve::static:=proc(_self,$) local sol,o; #print("Enter first_order_ode_quadrature_class:-dsolve"); #solve the ode for now we will use Maple but in my code #I have my own solver ofcourse. sol:= :-dsolve(_self:-ode,_self:-func); o:=Object(solution_class,sol,false); _self:-solutions_found:= [o]; NULL; end proc; end module: #------------------------- module first_order_ode_linear_class() option object(ODE_base_class); local f,g; #ode of form y'=f(x)*g(y) #this method is not an object method. It is part of the module but does #not have _self. It is called by the factory class to find if the ode #is of this type first export is_linear:=proc(ode::`=`,y::symbol,x::symbol)::truefalse; RETURN(true); #for now end proc; export ModuleCopy::static:= proc(_self,proto::first_order_ode_linear_class,ode::`=`,y::symbol,x::symbol,$) _self:-ode := ode; _self:-y := y; _self:-x := x; _self:-func := _self:-y(_self:-x); _self:-ode_order :=1; end proc; export dsolve::static:=proc(_self,$) local sol,o; sol:= :-dsolve(_self:-ode,_self:-func); o:=Object(solution_class,sol,false); _self:-solutions_found[1]:= [o]: end proc: end module:
Example usage is
o:=ODE_factory_class:-make_ode(diff(y(x),x)=x,y(x)) o:-get_ode() d --- y(x) = x dx o:-dsolve(); o:-get_sol() [ 1 2 ] [y(x) = - x + c__1] [ 2 ]
To ﬁnd what methods dsolve uses do
`dsolve/methods`[1] [quadrature, linear, Bernoulli, separable, inverse_linear, homogeneous, Chini, lin_sym, exact, Abel, pot_sym]
The above gives methods for ﬁrst order ODEs. More are given by
`dsolve/methods`[1,'semiclass'] [Riccati, inverse_Riccati, equivalent_to_Abel, linearizable, linearizable_by_differentiation] `dsolve/methods`[1,'high_degree'] [WeierstrassP, WeierstrassPPrime, JacobiSN, linearizable_by_differentiation, missing, dAlembert, homogeneous_B, sym_implicit] `dsolve/methods`[1,"development"] [linearizable_by_differentiation, linearizable, con_sym, WeierstrassP, WeierstrassPPrime, equivalent_to_Abel, Abel_AIR, special, Riccati_symmetries] `dsolve/methods`[1,extra] [inverse_Riccati, Abel_AIL, `sym_pat/[F(x)*G(y),0]`, `sym_pat/[F(x),G(x)]`, `sym_pat/[F(x),G(y)]`, `sym_pat/[F(x)+G(y),0]`, `sym_pat/[F(x),G(x)*y+H(x)]`, sym_pat, exp_sym]
For example given a ﬁrst order ode, we can ask dsolve to solve it using one of these methods as follows
restart; ode:=diff(y(x),x)=sqrt( sqrt( y(x) )* sin(x))/sqrt(y(x)) dsolve(ode,y(x),[`exact`]) 4/5*y(x)^(5/4) + Intat(-sqrt(sqrt(y(x))*sin(_a))/y(x)^(1/4), _a = x) + c__1 = 0
We can ask it to use symmetry with speciﬁc pattern as follows
restart; ode:=diff(y(x),x)=sqrt( sqrt( y(x) )* sin(x))/sqrt(y(x)) dsolve(ode,y(x),[`sym_pat/[F(x),G(x)*y+H(x)]`]) c__1 + 4/5*y(x)^(3/2)*sqrt(sin(x))/sqrt(sqrt(y(x))*sin(x)) - Int(sqrt(sin(x)), x) = 0 #or using short cut, same thing can be done as follows dsolve(ode,y(x),Lie) c__1 + 4/5*y(x)^(3/2)*sqrt(sin(x))/sqrt(sqrt(y(x))*sin(x)) - Int(sqrt(sin(x)), x) = 0 #another short cut for Lie is dsolve(ode,[`sym_pat`]) c__1 + 4/5*y(x)^(3/2)*sqrt(sin(x))/sqrt(sqrt(y(x))*sin(x)) - Int(sqrt(sin(x)), x) = 0
To ﬁnd all methods, and for higher order ode, we ﬁrst use indices as follows
indices(`dsolve/methods`) [high, linear_nonhomogeneous], [1], [1, high_degree], [3, linear_homogeneous], [2, "linear_homogeneous all methods"], [2, "linear_homogeneous other"], [2, linear_homogeneous], [2, "special_functions"], [3, linear_nonhomogeneous], [2, "linear_homogeneous in Normal Form"], [2, "development"], [2, "hypergeometric"], [1, extra], [high, linear_homogeneous], [high, nonlinear], [1, "special"], [1, "development"], [2, nonlinear], [high, "development"], [1, semiclass], [3, nonlinear], [2, linear_nonhomogeneous], [2, "linear_homogeneous as given"], [3, "development"]
Using the above, to ﬁnd all methods for second order linear_homogeneous ode, the command is
`dsolve/methods`[2, "linear_homogeneous all methods"] [quadrature, const_coeffs, Euler, linear_1, `linear/missing_y`, Kovacic, special_functions, to_const_coeffs, exact_linear, sym_1, Mathieu, MeijerG, Heun, HeunG, HeunC, HeunB, HeunD, HeunT, mu_xy, equivalent_to_Bessel, to_Riccati, Bessel, elliptic, Legendre, Whittaker, Kummer, cylindrical, hypergeometric, hypergeom1, hypergeom2, Riemann, RNF, hypergeometricsols, rationalize_lode, with_periodic_functions] `dsolve/methods`[2, "linear_homogeneous other"] [exact_linear, sym_1, to_const_coeffs, mu_xy, equivalent_to_Bessel, to_Riccati, with_periodic_functions]
For example, given the ode \(y''+3 y'+ y=0\), we can now do
dsolve(ode,y(x)) y(x) = c__1*exp(1/2*(sqrt(5) - 3)*x) + c__2*exp(-1/2*(3 + sqrt(5))*x) dsolve(ode,y(x),[`const_coeffs`]) y(x) = c__1*exp(1/2*(sqrt(5) - 3)*x) + c__2*exp(-1/2*(3 + sqrt(5))*x) dsolve(ode,y(x),[`Kovacic`]) y(x) = c__1*exp(1/2*(sqrt(5) - 3)*x) + c__2*exp(-1/2*(3 + sqrt(5))*x)
Not all methods ofcourse will work, as it depends on the ode type.
This function below lists all methods
ind:=indices(`dsolve/methods`); for item in ind do cat("`dsolve/methods`",String(item)); eval(parse(%)) od;
Which gives
"`dsolve/methods`[high, linear_nonhomogeneous]" [quadrature, fully_exact_linear, linear_nonhomogeneous_[0,1], exact_linear_nonhomogeneous, linear, exp_reduce] "`dsolve/methods`[1]" [quadrature, linear, Bernoulli, separable, inverse_linear, homogeneous, Chini, lin_sym, exact, Abel, pot_sym] "`dsolve/methods`[1, high_degree]" [WeierstrassP, WeierstrassPPrime, JacobiSN, linearizable_by_differentiation, missing, dAlembert, homogeneous_B, sym_implicit] "`dsolve/methods`[3, linear_homogeneous]" [quadrature, const_coeffs, Euler, fully_exact_linear, to_const_coeffs, linear, exp_reduce, exact_linear, with_periodic_functions] "`dsolve/methods`[2, "linear_homogeneous all methods"]" [quadrature, const_coeffs, Euler, linear_1, linear/missing_y, Kovacic, special_functions, to_const_coeffs, exact_linear, sym_1, Mathieu, MeijerG, Heun, HeunG, HeunC, HeunB, HeunD, HeunT, mu_xy, equivalent_to_Bessel, to_Riccati, Bessel, elliptic, Legendre, Whittaker, Kummer, cylindrical, hypergeometric, hypergeom1, hypergeom2, Riemann, RNF, hypergeometricsols, rationalize_lode, with_periodic_functions] "`dsolve/methods`[2, "linear_homogeneous other"]" [exact_linear, sym_1, to_const_coeffs, mu_xy, equivalent_to_Bessel, to_Riccati, with_periodic_functions] "`dsolve/methods`[2, linear_homogeneous]" [linear_homogeneous] "`dsolve/methods`[2, "special_functions"]" [Bessel, elliptic, Legendre, Kummer, Whittaker, hypergeometric, Mathieu] "`dsolve/methods`[3, linear_nonhomogeneous]" [quadrature, fully_exact_linear, linear_nonhomogeneous_[0,1], exact_linear_nonhomogeneous, linear, exp_reduce] "`dsolve/methods`[2, "linear_homogeneous in Normal Form"]" [linear_1] "`dsolve/methods`[2, "development"]" [mu_xyp, mu_xyp2, mu_formal, mu_heuristic, exp_reduce, linear, Bessel2, Whittaker_old, nonlinear_homogeneous, exact_linear_nonhomogeneous, mu_y1, mu_x_y1, mu_y_y1, mu_poly_yn, exp_sym, sym_pat, sym_8] "`dsolve/methods`[2, "hypergeometric"]" [hypergeom1, hyper3] "`dsolve/methods`[1, extra]" [inverse_Riccati, Abel_AIL, sym_pat/[F(x)*G(y),0], sym_pat/[F(x),G(x)], sym_pat/[F(x),G(y)], sym_pat/[F(x)+G(y),0], sym_pat/[F(x),G(x)*y+H(x)], sym_pat, exp_sym] "`dsolve/methods`[high, linear_homogeneous]" [quadrature, const_coeffs, Euler, fully_exact_linear, to_const_coeffs, linear, exp_reduce, exact_linear, with_periodic_functions] "`dsolve/methods`[high, nonlinear]" [linearizable_by_differentiation, linearizable, reducible, exact_nonlinear, missing, mu_formal, lin_sym] "`dsolve/methods`[1, "special"]" [80, 81] "`dsolve/methods`[1, "development"]" [linearizable_by_differentiation, linearizable, con_sym, WeierstrassP, WeierstrassPPrime, equivalent_to_Abel, Abel_AIR, special, Riccati_symmetries] "`dsolve/methods`[2, nonlinear]" [Liouville, WeierstrassP, JacobiSN, linearizable, linearizable_by_differentiation, mu_xy_2, missing, mu_xyp2_dynamical_symmetries_fully_reducible, mu_xyp_singularcases, sym_1, exact_nonlinear, reducible, lin_sym, S-function, mu_xyp_generalcase, mu_xyp2_dynamical_symmetries_not_fully_reducible] "`dsolve/methods`[high, "development"]" [k25, RNF, mu_heuristic, MeijerG, nonlinear_homogeneous, mu_poly_yn, exp_sym] "`dsolve/methods`[1, semiclass]" [Riccati, inverse_Riccati, equivalent_to_Abel, linearizable, linearizable_by_differentiation] "`dsolve/methods`[3, nonlinear]" [linearizable_by_differentiation, linearizable, missing, exact_nonlinear, reducible, mu_formal, lin_sym] "`dsolve/methods`[2, linear_nonhomogeneous]" [quadrature, fully_exact_linear, linear_nonhomogeneous_[0,1], linear_nonhomogeneous_[0,F(x)], linear_nonhomogeneous] "`dsolve/methods`[2, "linear_homogeneous as given"]" [quadrature, const_coeffs, Euler, linear_1, linear/missing_y, Kovacic, RNF, special_functions, MeijerG, Heun, hypergeometricsols, rationalize_lode] "`dsolve/methods`[3, "development"]" [k25, RNF, mu_heuristic, linear_patterns, MeijerG, nonlinear_homogeneous, mu_y2, mu_poly_yn, exp_sym, pFq, 3F2, 2F2, 1F2, 0F2]
restart; ode:=diff(y(x),x)+y(x)^2*sin(x)-2*sin(x)/cos(x)^2 = 0; yp:=DETools:-particularsol(ode);
To step into the code, do
restart; ode:=diff(y(x),x)+y(x)^2*sin(x)-2*sin(x)/cos(x)^2 = 0; stopat(`DEtools/particularsol`); DETools:-particularsol(ode);
To print it do
print(`DEtools/particularsol`);
Use the output=basis option
ode:=diff(y(x),x$2)-x*diff(y(x),x)-x*y(x)=0; dsolve(ode,output=basis);
To solve \[ y''-3y'+2y=10 e^{5 x} \] with \(y(0)=1,y'(0)=5\) do
eq1:= diff(y(x),x$2)-3*diff(y(x),x)+2*y(x)=10*exp(5*x); dsolve({eq1,y(0)=1,D(y)(0)=5},y(x)); Methods for second order ODEs: Trying to isolate the derivative d^2y/dx^2... Successful isolation of d^2y/dx^2 --- Trying classification methods --- trying a quadrature trying high order exact linear fully integrable trying differential order: 2; linear nonhomogeneous with symmetry [0,1] trying a double symmetry of the form [xi=0, eta=F(x)] <- double symmetry of the form [xi=0, eta=F(x)] successful ....
The above can also be written using D@@ notation, like this
eq:= (D@@2)(y)(x) - 3*D(y)(x) +2*y(x) = 10*exp(5*x); IC := y(0)=1,D(y)(0)=5; dsolve({eq,IC},y(x));
use odetest and check if it gives zero.
eq1:= diff(diff(y(x),x),x)-3*diff(y(x),x)+2*y(x)=10*exp(5*x); ans:=dsolve({eq1,IC},y(x)); odetest(ans,eq1); 0
Maple can classify the ODE.
eq1:= diff(y(x),x$2)-3*diff(y(x),x)+2*y(x)=10*exp(5*x); R0 := DEtools['odeadvisor'](eq1,y(x)); R0 := [[_2nd_order, _with_linear_symmetries]]
To get help on this type of ODE, do
DEtools['odeadvisor'](eq1,'help');
Use with(DEtools);
restart; eq1:= diff(y(x),x$2)-3*diff(y(x),x)+2*y(x)=10*exp(5*x); DEtools[DEplot](eq1,y(x),x=-2..5, [ [y(0)=0, D(y)(0)=0]], y=-3..3,linecolor=red);
To get a better plot, change the stepsize and independent variable range
restart; eq1:= diff(y(x),x$2)-3*diff(y(x),x)+2*y(x)=10*exp(5*x); DEtools[DEplot](eq1,y(x),x=-1..1,[[y(0)=0,D(y)(0)=0]],y=-3..3,stepsize=0.001,linecolor=red);
From: Robert Israel (israel@math.ubc.ca) Subject: Re: given precision in Maple Newsgroups: comp.soft-sys.math.maple Date: 2003-07-16 20:19:06 PST Set Digits:= n and all calculations from this point will be done with n digits. Mathematical functions will be correct to n digits as well (to the extent this is practical). If you want high-accuracy numerical ODE solutions, on the other hand, it's not so simple. I think the best way is using the taylorseries method. For example, consider the problem y' = y^2, y(1) = 1, where the exact solution y = 1/(2-x) has y(1.9) = 10. > Digits:= 30: sol:= dsolve({D(y)(x)=y(x)^2, y(1) = 1}, y(x), numeric, method=taylorseries, abserr=1e-25): sol(1.9); [x = 1.9, y(x) = 9.99999999999999999999999797691] > 10 - eval(y(x),%); -23 0.202309 10 The other methods (in particular the default rkf45) do not give results anywhere near this good.
How to obtain the ODE in canonical coordinates \(S(R)\) for an ode using Lie symmetry?
Here is a function I wrote with the help of Maple docs, that does that. For now, I am using ﬁrst order ode. It takes the ode and the variables (coordinates) to use (\(R,S\)) is what normally used, and returns back the ODE in canonical coordinates. The ode should always be a quadrature for ﬁrst order.
get_ODE_in_canonical:=proc(ode::`=`,y::symbol,x::symbol,S::symbol,R::symbol) local infinitesimals,tr,itr,ODE; infinitesimals := [DEtools:-symgen(ode)]; if nops(infinitesimals)=0 then RETURN(FAIL); fi; infinitesimals:=infinitesimals[1]; tr := DEtools:-canoni(infinitesimals,y(x),S(R)); itr := op(1,[solve(tr,{x,y(x)})]); ODE:= PDEtools:-dchange(itr,ode,[R,S(R)],simplify); ODE:= op(solve(ODE,{diff(S(R),R)})); RETURN(ODE); end proc:
Example
ODE := diff(y(x),x) = 1/x^2+y(x); get_ODE_in_canonical(ODE,y,x,S,R) #diff(S(R), R) = 1/(exp(R)*R^2) ODE := diff(y(x),x) = x^2-2*x*y(x)+y(x)^2; get_ODE_in_canonical(ODE,y,x,S,R) #diff(S(R), R) = 1
The following function is small change of the above. It accepts your choice of \(\xi ,\eta \) to use instead of the ﬁrst one obtained by Maple. This makes it possible to experiment with diﬀerent inﬁnitesimals. Note that diﬀerent inﬁnitesimals will/can result in diﬀerent canonical ODE, but the ﬁnal solution for \(y(x)\) of course will always be the same after applying the transformation back to \(x,y\) coordinates.
restart; get_ODE_in_canonical_v2:=proc(ode::`=`,y::symbol,x::symbol,S::symbol,R::symbol,xi,eta) local infinitesimals:=[xi,eta],tr,itr,ODE; tr := DEtools:-canoni(infinitesimals,y(x),S(R)); itr := op(1,[solve(tr,{x,y(x)})]); ODE:= PDEtools:-dchange(itr,ode,[R,S(R)],simplify); ODE:= op(solve(ODE,{diff(S(R),R)})); RETURN(simplify(ODE,symbolic)); end proc:
And now
ODE:=diff(y(x),x) = x^2-2*x*y(x)+y(x)^2; get_ODE_in_canonical_v2(ODE,y,x,S,R,1,1) #diff(S(R), R) = 1/(R^2 - 1)
Here is an example.
ode:=diff(y(x),x)=-y(x)^2/(exp(x)-y(x)) DEtools:-symgen(ode,y(x),HINT=[c1*x+c2*y+c3,c4*x+c5*y+c6]); [_xi = 1, _eta = y]
Or
ode:=diff(y(x),x)=-y(x)^2/(exp(x)-y(x)) DEtools:-symgen(ode,y(x),HINT=[g(x),f(x)*y]); [_xi = 1, _eta = y]
So not use y(x) but only y in the hint.
The following function takes in a single ode and parses it to verify it is valid. It returns back 3 values. The dependent variable, the indepenent variable and the ode order.
#added 12/8/2022. interface(warnlevel=4); kernelopts('assertlevel'=2): parse_single_ode:=proc(ode::`=`)::symbol,symbol,integer; #parses single ode. returns back dep_var,indep_var,ode_order #throws exception when it detects parsing errors local func; local y,x; local dep_variables_found::list,item; local the_order; func:=PDEtools:-Library:-GetDepVars("not given",ode,onlydifferentiatedfunctions=true); if nops(func)=0 then error ("not differential equation ",ode); fi; func := func[1]; if nops(func)<>1 then error("Parsing error, dependent variable must contain one argument, found ", func); fi; y:=op(0,func); x:=op(1,func); #basic verification if not has(ode,y) then error ("Supplied ode ",ode," has no ",y); fi; if not has(ode,x) then error ("Supplied ode ",ode," has no ",x); fi; if not has(ode,func) then error ("Supplied ode ",ode," has no ",func); fi; the_order := PDEtools:-difforder(ode,x); #if the_order=0 then # error ("No derivative found in ",ode,". Input is not differential equation"); #fi; #note that the following call will also return y(x) if the input is not an ode #this will check that the dependent variable will show with SAME argument in the ode #i.e. if y(x) and y(t) show up in same ode, it will throw exception, which is what #we want. try dep_variables_found := PDEtools:-Library:-GetDepVars([y],ode); catch: error lastexception; end try; #now go over dep_variables_found and check the independent variable is same as x #i.e. ode can be y'(z)+y(z)=0 but function is y(x). for item in dep_variables_found do if not type(item,function) then error("Parsing error. Expected ",func," found ",item," in ode"); else if op(1,item) <> x then error("Parsing error. Expected ",func," found ",item," in ode"); fi; fi; od; #now go over all indents in ode and check that y only shows as y(x) and not as just y #as the PDEtools:-Library:-GetDepVars([_self:-y],ode) code above does not detect this. #i.e. it does not check y'(x)+y=0 if numelems(indets(ode,identical(y))) > 0 then error("Parsing error, Can not have ",y," with no argument inside ",ode); fi; return y,x,the_order; end proc:
To use do
y,x,the_order := parse_single_ode(diff(y(x),x)+y(x)=0);
An alternative to the above is to pass the dependent function itself as well as the ode. This is what I do myself in my ode solver. Like this
parse_single_ode:=proc(ode::`=`,func::function(name))::symbol,symbol,integer; #parses single ode. returns back dep_var,indep_var,ode_order #throws exception when it detects parsing errors local y,x; local dep_variables_found::list,item; local the_order; if nops(func)<>1 then error("Parsing error, dependent variable must contain one argument, found ", func); fi; y:=op(0,func); x:=op(1,func); #basic verification if not has(ode,y) then error ("Supplied ode ",ode," has no ",y); fi; if not has(ode,x) then error ("Supplied ode ",ode," has no ",x); fi; if not has(ode,func) then error ("Supplied ode ",ode," has no ",func); fi; the_order := PDEtools:-difforder(ode,x); #note that the following call will also return y(x) if the input is not an ode #this will check that the dependent variable will show with SAME argument in the ode #i.e. if y(x) and y(t) show up in same ode, it will throw exception, which is what #we want. try dep_variables_found := PDEtools:-Library:-GetDepVars([y],ode); #print("dep_variables_found=",dep_variables_found); catch: error lastexception; end try; #now go over dep_variables_found and check the independent variable is same as x #i.e. ode can be y'(z)+y(z)=0 but function is y(x). for item in dep_variables_found do if not type(item,function) then error("Parsing error. Expected ",func," found ",item," in ode"); else if op(1,item) <> x then error("Parsing error. Expected ",func," found ",item," in ode"); fi; fi; od; #now go over all indents in ode and check that y only shows as y(x) and not as just y #as the PDEtools:-Library:-GetDepVars([_self:-y],ode) code above does not detect this. #i.e. it does not check y'(x)+y=0 if numelems(indets(ode,identical(y))) > 0 then error("Parsing error, Can not have ",y," with no argument inside ",ode); fi; return y,x,the_order; end proc:
To use do the same as before, but need to add \(y(x)\) as second argument. Like this
y,x,the_order := parse_single_ode(diff(y(x),x)+y(x)=0, y(x));
Suppose to want to check if an ode has \(y'(x)\) in it. We can not write has(ode, diff(y(x),x)) and see if this gives true or not, because this will also match \(y''\) and \(y'''\) and any higher order.
One way is to ﬁrst convert the ode to D form and then use has on D(y)(x). This will not match \(y''\) anymore which is what we wanted, because \(y''\) becomes (D@@2)(y)(x) and so the check for ﬁrst order diﬀ will work as expected.
ode:=diff(y(x),x$2)+3*y(x)=0; ode:=convert(ode,D); has(ode,(D)(y)(x)) #false
Another example
ode:=diff(y(x),x$2)+1/diff(y(x),x)=0; ode:=convert(ode,D); has(ode,(D)(y)(x)) #true
The same thing if we wanted to check if the ode has \(y''\) or not.
ode:=diff(y(x),x$3)+diff(y(x),x)=0; ode:=convert(ode,D); has(ode,(D@@2)(y)(x)) #false
ode:=diff(y(x),x$3)+ x*diff(y(x),x)+sin(x)=0; if has(DEtools:-odeadvisor(ode,y(x),['linear']),_linear) then print("linear"); else print("not linear ode"); fi
The above prints "linear"
ode:=y(x)*diff(y(x),x$3)+ x*diff(y(x),x)+sin(x)=0; if has(DEtools:-odeadvisor(ode,y(x),['linear']),_linear) then print("linear"); else print("not linear ode"); fi
The above prints "not linear ode". This works for any ode order.
ode:=diff(y(x),x$3)+ x*diff(y(x),x)+sin(x)=0; PDEtools:-difforder(ode)
Gives \(3\).
Given a linear ode of the form \(A y''+B y' + C y= f(x)\) how to ﬁnd \(A,B,C,f(x)\) ?
ode:=diff(y(x),x$3)+ x*diff(y(x),x)+99*y(x)=sin(x); L:=DEtools:-convertAlg(ode,y(x)); #this only works on linear ode's
Gives L := [[99, x, 0, 1], sin(x)].
\(L\) is a list. The second entry is \(f(x)\) and the ﬁrst entry of \(L\) is a list which gives the coeﬃcents of the ode. Notice they are ordered from lowest order to highest order of the ode. Since this is third order ode, then there are \(4\) entries. The ﬁrst is the coeﬃcient of \(y(x)\), the second is the coeﬃcient of \(y'\) and the third is the coeﬃcient of \(y''\) and the fourth entry is the coeﬃcient of \(y'''\). Notice that coeﬃcient of \(y''\) is zero since \(y''\) is missing from the ode.
This function DEtools:-convertAlg only works on linear ode’s. Therefore we need to check if the ode is linear ﬁrst before using. How to check for linear ode is given above.
I need to ﬁnd the degree of the highest derivative in an ode.
For example, if the input is \[ x \left (\frac {d^{2}}{d x^{2}}y \left (x \right )\right )^{5}+x \left (\frac {d^{2}}{d x^{2}}y \left (x \right )\right )^{2}+\left (\frac {d}{d x}y \left (x \right )\right ) y \left (x \right )+\left (\frac {d^{3}}{d x^{3}}y \left (x \right )\right )^{4} \sin \left (x \right )+5 y \left (x \right )+\sin \left (x \right )+\frac {d^{6}}{d x^{6}}r \left (x \right )+\left (\frac {d^{3}}{d x^{3}}y \left (x \right )\right )^{4} \cos \left (x \right ) = 0 \]
Then the degree for highest derivative of \(y\) w.r.t. \(x\) is 4. For \[ y''(x)^2 + y'(x)+y(x)=0 \] It will be 2.
This function returns the order and degree of such term.
find_all_derivatives_of_specific_order:=proc(expr,y::symbol,x::symbol,N::posint)::set; local t1,t2; if not has(expr,diff(y(x),x$N)) then return {}; fi; t1 := identical(diff(y(x),x$N))^anything; t2 := identical(diff(y(x),x$N)); return indets[flat](expr,{t1,t2}); #MUST use flat end proc: get_order_and_degree_of_largest_derivative:=proc(expr,y::symbol,x::symbol)::integer,anything; local the_order,the_degree; local cand::set; local the_exponent; local item; if not has(expr,diff(y(x),x)) then return 0,0; fi; the_order := PDEtools:-difforder(expr,x); cand := find_all_derivatives_of_specific_order(expr,y,x,the_order); if nops(cand)=0 then the_degree := 0; else the_degree:=1; for item in cand do if type(item,`^`) then the_exponent := op(2,item); if type( the_exponent,symbol) or not type( the_exponent,numeric) then #assume larger the_degree :=the_exponent; else if type(the_degree,symbol) or not type( the_exponent,numeric) then next; else if op(2,item)>the_degree then the_degree := op(2,item); fi; fi; fi; else if type(the_degree,symbol) then next; else if the_degree=0 then the_degree := 1; fi; fi; fi; od; fi; return the_order,the_degree; end proc:
And now it can be called as follows
ode:=diff(y(x),x$2)+diff(y(x),x)+y(x)=0; get_order_and_degree_of_largest_derivative(lhs(ode),y,x) #2,1 ode:=diff(y(x),x$2)*diff(y(x),x$2)^4+diff(y(x),x)+y(x)=0; get_order_and_degree_of_largest_derivative(lhs(ode),y,x) #2,5 ode:=y(x)=0; get_order_and_degree_of_largest_derivative(lhs(ode),y,x) #0,0 ode:=diff(y(x),x)^n=0; get_order_and_degree_of_largest_derivative(lhs(ode),y,x) #1,n
I had a need to ﬁnd all derivatives of form diff(y( anything), anything) in an ode so to check that \(y\) argument is not diﬀerent among them.
For an example, given \[ a \left (\frac {d^{2}}{d x^{2}}y \left (x \right )\right ) \left (\frac {d^{3}}{d x^{3}}y \left (x \right )\right )-\sqrt {1+\left (\frac {d^{2}}{d x^{2}}y \left (x \right )\right )^{2}}+\frac {1}{\sin \left (\frac {d^{5}}{d x^{5}}y \left (x \right )\right )} = \frac {d}{d z}y \left (z \right )+{\mathrm e}^{y \left (x \right )+\frac {d}{d x}y \left (x \right )}+\frac {d}{d x}r \left (x \right ) \]
the result should be \[ \left [\begin {array}{c} \frac {d^{5}}{d x^{5}}y \left (x \right ) \\ \frac {d^{4}}{d x^{4}}y \left (x \right ) \\ \frac {d^{3}}{d x^{3}}y \left (x \right ) \\ \frac {d^{2}}{d x^{2}}y \left (x \right ) \\ \frac {d}{d x}y \left (x \right ) \\ \frac {d}{d z}y \left (z \right ) \end {array}\right ] \]
One issuse is how to check for diﬀ and also check that the dependent variable is \(y\) so as not to pick other dependent variables such as \(z\) in this example. This was done by converting diff to D otherwise it will not work.
restart; expr:=a*diff(y(x),x$2)*diff(y(x),x$3)-sqrt(1+ diff(y(x),x$2)^2)+1/sin(diff(y(x),x$5))=diff(y(z),z)+exp(y(x)+diff(y(x),x))+diff(r(x),x); #this finds all derivatives list_of_diffs:=indets(expr,'satisfies'(s->op(0,s)='diff' and op([0,1],convert(s,D))=y)); #This finds all dependent variables list_of_diffs:=convert(list_of_diffs,list); list_of_indep_variables := map(x->PDEtools:-Library:-GetIndepVars(x)[-1],list_of_diffs); #this converts it to set. If the ODE is valid, then the list_of_indep_variables should #have one entry $x$ in it and nothing else. list_of_indep_variables := convert(ListTools:-Flatten(list_of_indep_variables),set); if nops(list_of_indep_variables)>1 then error( cat("Only one independent variable expected in differential form, found ", convert(list_of_indep_variables,string)) ); fi; if list_of_indep_variables[1]<>x then error( cat("Independent variable expected in differential form not same as independent variable in function given ", convert(list_of_indep_variables,string)) ); fi;
Another option instead of doing all the above is to do this
expr:=a*diff(y(x),x$2)*diff(y(x),x$3)-sqrt(1+ diff(y(x),x$2)^2)+1/sin(diff(y(x),x$5))=diff(y(z),z)+exp(y(x)+diff(y(x),x))+diff(r(x),x); try PDEtools:-Library:-GetDepVars([y(x)],expr); catch error "functions with name [y] having different dependency: [[y(x), y(z)]]" end try;
The function PDEtools:-Library:-GetDepVars([y(x)],expr) checks that only \(y(x)\) dependency shows up. It throws an error otherwise. So if an error is thrown, then this means \(y\) shows up with diﬀerent independent variables.
Sometimes it is useful to invert an ode. i.e. make the independent variable the dependent variable, and the dependent variable the independent. For example, given \[ 1+\left (\frac {x}{y \left (x \right )}-\sin \left (y \left (x \right )\right )\right ) \left (\frac {d}{d x}y \left (x \right )\right ) = 0 \] We want the ode to become \[ -\sin \left (y \right ) y +y \left (\frac {d}{d y}x \left (y \right )\right )+x \left (y \right ) = 0 \]
This can be done as follows
restart; ode:=1+ (x/y(x)-sin(y(x) ))*diff(y(x),x)=0; tr:={x=u(t),y(x)=t}; ode:=PDEtools:-dchange(tr,ode); ode:=eval(ode,[t=y,u=x]); ode:=simplify(ode);
\[ \frac {-\sin \left (y \right ) y +y \left (\frac {d}{d y}x \left (y \right )\right )+x \left (y \right )}{y \left (\frac {d}{d y}x \left (y \right )\right )} = 0 \]
In this case, we can get rid of the denomator, but this is a manual step for now.
ode:=numer(lhs(ode))=0;
\[ -\sin \left (y \right ) y +y \left (\frac {d}{d y}x \left (y \right )\right )+x \left (y \right ) = 0 \]
The above can now be solved more easily for \(x(y)\) than solving the orignal ode for \(y(x)\).
For say Bessel ode of order zero:
eq:= x^2*diff(y(x),x$2)+x*diff(y(x),x)+x^2*y(x)=0; DEtools[indicialeq](eq,x,0,y(x)); #x^2 = 0
The third argument above is the singularity point of interest. So we have two roots, both zero. These are now used for ﬁnding the power series solution \(y(x)\) if needed.
Another example, is Bessel of order 1
eq:= x^2*diff(y(x),x$2)+x*diff(y(x),x)+(x^2-1)*y(x)=0; DEtools[indicialeq](eq,x,0,y(x)); #x^2-1 = 0
To write \(y'(x)=x\), one way is diff(y(x),x)=x and another is D(y)(x)=x. To write \(y''(x)=x\), one way is diff(y(x),x$2)=x and another is (D@@2)(y)(x)=x.
To convert from one form to another use convert(eq,diff) or convert(eq,D)
to solve \(\frac {\partial u(x,t)}{\partial t}=k \frac {\partial ^2 u(x,t)}{\partial x^2}\) with homogeneous dirichlet boundary conditions \(u(0,t)=0,u(L,t)=0\) the commands are
restart; pde:=diff(u(x,t),t)=k*diff(u(x,t),x$2); bc:=u(0,t)=0,u(L,t)=0; sol:=pdsolve([pde,bc]) assuming 0<L:
Which gives
\[ u \left ( x,t \right ) =\sum _{{\it \_Z1}=1}^{\infty }{\it \_C1} \left ( {\it \_Z1} \right ) \sin \left ( {\frac {\pi \,{\it \_Z1}\,x}{L}} \right ) {{\rm e}^{-{\frac {k{\pi }^{2}{{\it \_Z1}}^{2}t}{{L}^{2}}}}} \]
Which can be made more readable as follows
sol:=algsubs(_Z1=n,sol): sol:=algsubs(Pi*n/L=lambda(n),sol);
\[ u \left ( x,t \right ) =\sum _{n=1}^{\infty }{\it \_C1} \left ( n \right ) \sin \left ( x\lambda \left ( n \right ) \right ) {{\rm e}^{-kt \left ( \lambda \left ( n \right ) \right ) ^{2}}} \]
For homogeneous Neumann B.C., at \(x=0\), let \(\frac {\partial u}{\partial x}=0\) and at \(x=L\) let \(u(L,t)=0\), the solution it gives looks diﬀerent than my hand solution
restart; pde:=diff(u(x,t),t)=k*diff(u(x,t),x$2); bc:=D[1](u)(0,t)=0,u(L,t)=0; pdsolve([pde,bc]) assuming 0<L;
It gives
\[ u \left ( x,t \right ) ={\it \_C3}\,{\it \_C2}\, \left ( {{\rm e}^{1/4\,{\frac {2\,i\pi \,xL-k{\pi }^{2}t}{{L}^{2}}}}}+{{\rm e}^{-1/4\,{\frac {\pi \, \left ( 2\,ixL+k\pi \,t \right ) }{{L}^{2}}}}} \right ) \]
I need to look more into the above and see if this comes out to be the same as my hand solution.
Another example, with initial conditions now given
restart; pde:=diff(u(x,t),t)=k*diff(u(x,t),x$2); bc:=D[1](u)(0,t)=0,u(L,t)=0; ic:=u(x,0)=f(x); sol:=pdsolve([pde,bc,ic],u(x,t)) assuming 0<L; sol1:=algsubs(_Z2=n,sol);
The result is
\[ u \left ( x,t \right ) =\sum _{n=1}^{\infty } \left ( 2\,{\frac {1}{L}{{\rm e}^{-1/4\,{\frac {k{\pi }^{2}t \left ( 1+2\,n \right ) ^{2}}{{L}^{2}}}}}\cos \left ( 1/2\,{\frac {\pi \,x \left ( 1+2\,n \right ) }{L}} \right ) \int _{0}^{L}f \left ( x \right ) \cos \left ( 1/2\,{\frac {\pi \,x \left ( 1+2\,n \right ) }{L}} \right ) \,{\rm d}x} \right ) \]
Another example
restart; pde:=diff(u(x,t),t)=k*diff(u(x,t),x$2); bc:=D[1](u)(0,t)=0,u(L,t)=0; ic:=u(x,0)=3*sin(Pi*x/L)-sin(3*Pi*x/L); sol:=pdsolve([pde,bc,ic],u(x,t)) assuming 0<L; sol1:=algsubs(_Z2=n,sol);
\[ u \left ( x,t \right ) =\sum _{n=1}^{\infty }768\,{\frac {1}{\pi \, \left ( 16\,{n}^{4}+32\,{n}^{3}-136\,{n}^{2}-152\,n+105 \right ) }{{\rm e}^{-1/4\,{\frac {k{\pi }^{2}t \left ( 1+2\,n \right ) ^{2}}{{L}^{2}}}}}\cos \left ( 1/2\,{\frac {\pi \,x \left ( 1+2\,n \right ) }{L}} \right ) } \]
Another example
restart; pde:=diff(u(x,t),t)=k*diff(u(x,t),x$2); bc:=u(0,t)=0,u(L,t)=0; ic:=u(x,0)=3*sin(Pi*x/L)-sin(3*Pi*x/L); sol:=pdsolve([pde,bc,ic],u(x,t)) assuming 0<L;
\[ u \left ( x,t \right ) =\sin \left ( {\frac {\pi \,x}{L}} \right ) {{\rm e}^{-9\,{\frac {{\pi }^{2}kt}{{L}^{2}}}}} \left ( -2\,\cos \left ( 2\,{\frac {\pi \,x}{L}} \right ) +3\,{{\rm e}^{8\,{\frac {{\pi }^{2}kt}{{L}^{2}}}}}-1 \right ) \]
The above answer seems wrong. There is not even a summation in it. It is diﬀerent from my hand solution. Look more into it.
Add this
expr:=diff(y(x),x); Typesetting:-Settings(typesetprime=true, prime=x):
The above will display the expression as \(y'(x)\). To make it now show the \(x\) do
expr:=diff(y(x),x); Typesetting:-Settings(typesetprime=true, prime=x): Typesetting:-Suppress(y(x));
Now it will show the expression as just \(y'\). For all the above to work, make sure you have Typesetting level set to Extended in the GUI.
This is done inside Tools->Options->Display menu.
To clear all the above Typesetting, do restart or do Typesetting:-Unsuppress(y(x))
The Maple syntax for seeting initial and boundary conditions is very confusing, as compared to Mathematica, which seems to me to be simpler. So I wrote this to remind me of the syntax each time.
For PDE, assuming dependent variable is \(u(x,t)\) then
Conditions | Maple code |
\(u(0,t)=0\) | u(0,t)=0 |
\(\frac {\partial u}{\partial x}=0\) at \(x=0\) | D[1](u)(0,t)=0 |
\(\frac {\partial ^2 u}{\partial x^2}=0\) at \(x=0\) | D[1,1](u)(0,t)=0 |
\(\frac {\partial ^3 u}{\partial x^3}=0\) at \(x=0\) | D[1,1,1](u)(0,t)=0 |
\(\frac {\partial u}{\partial t}=0\) at \(t=0\) | D[2](u)(x,0)=0 |
\(\frac {\partial ^2 u}{\partial t^2}=0\) at \(t=0\) | D[2,2](u)(x,0)=0 |
\(\frac {\partial ^3 u}{\partial t^3}=0\) at \(t=0\) | D[2,2,2](u)(x,0)=0 |
Notice the syntax for the last one above. It is (D[1]@@2)(u)(0,t)=0 and not (D@@2)[1](u)(0,t)=0
For an ODE, assuming dependent variable is \(y(x)\) then the syntax is
Conditions | Maple code |
\(y(0)=0\) | y(0)=0 |
\(\frac {dy}{dx}=0\) at \(x=0\) | D(y)(0)=0 |
\(\frac {d^2 y}{d x^2}=0\) at \(x=0\) | (D@@2)(y)(0)=0 |
Use dsolve(ode,Lie)
To ﬁnd symmetries, do
DEtools:-symgen(ode,y(x),HINT=[c__1+c__2*x+c__3*y,c__4+c__5*x+c__6*y])
or just
DEtools:-symgen(ode,y(x))
To debug it do
stopat(`ODEtools/symgen`); before calling dsolve or DEtools:-symgen
given an ode
\[ y \left (x \right ) = \left (\frac {d}{d x}y \left (x \right )\right )^{3} y \left (x \right )^{2}+2 x \left (\frac {d}{d x}y \left (x \right )\right ) \]
do change of variable \(u(x)=y(x)^2\)
restart; ode:=y(x)=diff(y(x),x)^3*y(x)^2+2*x*diff(y(x),x); new_ode:=PDEtools:-dchange({y(x)=sqrt(u(x))},ode,{u});
\[ \sqrt {u \left (x \right )} = \frac {\left (\frac {d}{d x}u \left (x \right )\right )^{3}}{8 \sqrt {u \left (x \right )}}+\frac {x \left (\frac {d}{d x}u \left (x \right )\right )}{\sqrt {u \left (x \right )}} \]
given an ode
\[ \frac {d^{2}}{d t^{2}}y \left (t \right )+y \left (t \right ) = 2 t \]
do change of variable \(t=\tau + \pi \)
restart; ode:=diff(y(t),t$2)+y(t)=2*t; PDEtools:-dchange({t=tau+Pi},ode,known={t},unknown={tau},params=Pi)
\[ \frac {d^{2}}{d \tau ^{2}}y \left (\tau \right )+y \left (\tau \right ) = 2 \tau +2 \pi \]
it is important to use params=Pi. Watch what happens if we do not do that
restart; ode:=diff(y(t),t$2)+y(t)=2*t; PDEtools:-dchange({t=tau+Pi},ode,known={t},unknown={tau});
\[ \frac {d^{2}}{d \tau ^{2}}y \left (\tau , \pi \right )+y \left (\tau , \pi \right ) = 2 \tau +2 \pi \] Which is not what we want.
This veriﬁes solution given in https://math.stackexchange.com/questions/3477732/can-t-see-that-an-ode-is-equivalent-to-a-bessel-equation Where a change of variables on \[ \xi ^2 \frac {d^2\eta }{d\xi ^2} + \xi \frac {d\eta }{d\xi } - (\xi ^2+n^2)\eta =0 \] Was made using \[ \eta =\frac {y}{x^\alpha }, \quad \xi =\beta x^\gamma , \] To produce the ode \[ \frac {d^2y}{dx^2} - \frac {(2\alpha -1)}{x}\frac {dy}{dx} - (\beta ^2 \gamma ^2 x^{2\gamma -2}+\frac {n^2\gamma ^2-\alpha ^2}{x^2})y=0. \] In Maple the above is done using
restart; ode := zeta^2*diff(eta(zeta),zeta$2) + zeta*diff(eta(zeta),zeta) - (zeta^2 + n^2)*eta(zeta) = 0; the_tr:={zeta=beta*x^gamma,eta(zeta)=y(x)/x^alpha}; PDEtools:-dchange(the_tr,ode,{y(x),x},'known'={eta(zeta)},'uknown'={y(x)},'params'={alpha,beta,gamma}); simplify(%); numer(lhs(%))=0; simplify(numer(lhs(%))/(x^(1-alpha)))=0; numer(lhs(%))=0; collect(%,[y(x),diff(y(x),x),diff(y(x),x$2)]);
Which gives \[ \left (-\gamma ^{2} x^{-1+2 \gamma } \beta ^{2} x -\gamma ^{2} n^{2}+\alpha ^{2}\right ) y \left (x \right )+\left (-2 \alpha x +x \right ) \left (\frac {d}{d x}y \left (x \right )\right )+x^{2} \left (\frac {d^{2}}{d x^{2}}y \left (x \right )\right ) = 0 \]
Here is another example. Here to make change of variables to polar coordinates by making \(x=r \cos \theta \) and \(y=r \sin \theta \) The ode is \[ \frac {y-x y'}{\sqrt {1+(y')^2}}=x^2+y^2 \]
In Maple
restart; ode := (y(x)-x*diff(y(x),x))/sqrt(1+ diff(y(x),x)^2) = x^2+y(x)^2; the_tr:={x=r(t)*cos(t),y(x)=r(t)*sin(t)}; PDEtools:-dchange(the_tr,ode,{r(t),t},'known'={y(x)},'uknown'={r(t)});
Which gives
\[ \frac {r \left (t \right ) \sin \left (t \right )-\frac {r \left (t \right ) \cos \left (t \right ) \left (\left (\frac {d}{d t}r \left (t \right )\right ) \sin \left (t \right )+r \left (t \right ) \cos \left (t \right )\right )}{\left (\frac {d}{d t}r \left (t \right )\right ) \cos \left (t \right )-r \left (t \right ) \sin \left (t \right )}}{\sqrt {1+\frac {\left (\left (\frac {d}{d t}r \left (t \right )\right ) \sin \left (t \right )+r \left (t \right ) \cos \left (t \right )\right )^{2}}{\left (\left (\frac {d}{d t}r \left (t \right )\right ) \cos \left (t \right )-r \left (t \right ) \sin \left (t \right )\right )^{2}}}} = r \left (t \right )^{2} \left (\cos ^{2}\left (t \right )\right )+r \left (t \right )^{2} \left (\sin ^{2}\left (t \right )\right ) \]
Here is another example. Where we want to change \(R(r)\) to \(y(x)\) everywhere
\[ \frac {d^{2}}{d r^{2}}R \left (r \right )+\frac {d}{d r}R \left (r \right )+R \left (r \right ) = 0 \]
restart; ode:=diff(R(r),r$2)+diff(R(r),r)+R(r)=0; the_tr:={r=x,R(r)=y(x)}; PDEtools:-dchange(the_tr,ode,{y(x),x},'known'={R(r),r},'uknown'={y(x),x});
\[ \frac {d^{2}}{d x^{2}}y \left (x \right )+\frac {d}{d x}y \left (x \right )+y \! \left (x \right ) = 0 \]
The format of the transformation is old_independent_variable=new_independent_variable and old_dependent_variable=new_dependent_variable
Make a phase plot of \[ \frac {d^{2}}{d t^{2}}x \left (t \right )+\frac {\frac {d}{d t}x \left (t \right )}{2}+x \left (t \right ) = u \left (t \right ) \]
By plotting \(x(t)\) vs \(x'(t)\) without solving the ODE.
restart; alias(DS=DynamicSystems): ode := diff(x(t),t$2) +1/2*diff(x(t),t)+ x(t) = u(t); sys:=DS:-DiffEquation(ode,'outputvariable'=[x(t)],'inputvariable'=[u(t)]); sys0:=DS:-StateSpace(sys); eq1:=diff(x1(t),t)=sys0:-a[1,..].Vector([x1(t),x2(t)]); eq2:=diff(x2(t),t)=sys0:-a[2,..].Vector([x1(t),x2(t)]); DEtools:-DEplot([eq1,eq2],[x1(t),x2(t)],t=0..35,[[x1(0)=1,x2(0)=1]],x1=-2..2,x2=-2..2, numpoints=200, linecolor=black, axes=boxed);
restart; with(MmaTranslator); #load the package FromMma(`Integrate[Cos[x],x]`);
Or
restart; with(MmaTranslator); #load the package convert(`Integrate[Cos[x],x]`, FromMma);
f:=proc() eq:=x*diff(y(x),x)+y(x)=exp(2*x); dsolve(eq,y(x)); end proc;
Then used the command stopat(f); then called the procedure f(); and now the debugger comes up. Did step command and now it steps inside dsolve
Some examples
stopat(`ODEtools/symtest`); stopat(`ODEtools/test`); stopat(`ODEtools/normal/expanded`); stopat(`ODEtools/odepde`); DEtools:-symtest([-3,y],ode,y(x));
‘ODEtools/normal/expanded‘
For integration use
infolevel[`evalf/int`]:=5;infolevel[int]:=5;
Another option
restart; interface(verboseproc=3) #(try 2 also)
then print(procedure); or eval(procedure_name); for example
restart: interface(verboseproc=3): print(LinearAlgebra:-GramSchmidt); print(lcm);
Also can use showstat, in this case interface(verboseproc=3) is not needed. Also showstat gives line numbers and I think it is easier to read. Some examples
showstat(`odsolve/2nd_order`) showstat(`evalf/hypergeom`); showstat(`evalf/exp/general`); showstat(`evalf/Psi`); showstat(`evalf/int`); showstat(`dsolve/SERIES`); #these 3 shows the main 3 functions by each solver showstat(`odeadv/dAlembert`); #used by advisor showstat(`odsolve/dAlembert`); # main API. showstat(`odsolve/dAlembert/integrate`); #used to integrate the ode showstat(`ODEtools/odeadv`); showstat(DEtools:-odeadvisor); showstat(`dsolve/series/froben/inhom`) showstat(`dsolve/series/froben`)
To stop at anyone of these functions in debugger do
stopat(`dsolve/series/froben/inhom`) #code here, say dsolve command.
The above will stop in the debugger in the above function.
There is also a function by Joe Riel here here is the post by Joe Riel:
"A disadvantage of showstat, particularly if you want to cut and paste the output, is that it includes line numbers. Here is a simple procedure I threw together to remove the line numbers."
PrintProc := proc(p::name,lines::{posint,posint..posint}) local width; option `Copyright (C) 2004 by Joseph S. Riel. All rights reserved.`; description "Print like showstat, but without line numbers"; width := interface('screenwidth'=200); try printf("%s", StringTools:-RegSubs( "\n ...." = "\n" ,debugopts('procdump'= `if`(nargs=1,p,[args])))) catch "procedure name expected": error "%1 is not a procedure name",p finally interface('screenwidth'=width) end try; NULL end:
To print source code to ﬁle using the above, do the following
currentdir("C:\\data"); interface('prettyprint'=1): interface('verboseproc'=3): writeto("listing.txt") PrintProc('singular'); writeto('terminal'):
Now the output will show up in the ﬁle "listing.txt" and also no line wrapping. The above I found is the best solution so far to do this.
trace(foo); untrace(foo);
also see debug(foo);
Also
infolevel[all]:=5: printlevel:=10:
See http://www.mapleprimes.com/questions/35951-How-To-Debugtrace-Things-In-Maple
Also look at kernelopts(opaquemodules=true)
Here is a useful post by Carl Love from Maple prime forum that summarizes all of these
Here are four things that you can do to get more information. I have listed them in order by how structured the information is, with the most structured ﬁrst.
Set
infolevel[all]:= 5;
That will cause programs to print out additional information of the programmers’ choosing. You can use higher or lower numbers for more or less information. Most programs don’t use levels higher than 5.
Print the code of procedures with showstat:
showstat(int); showstat(sin); showstat(cos);
Trace the execution of particular procedures with trace:
trace(int); trace(sin);
Trace the execution of everything with printlevel:
printlevel:= 10000:
You can use higher or lower numbers for more or less information.
Some examples
interface(verboseproc=3); print(DEtools) print(`ODEtools/symgen`); print(`symgen/methods`); print(`symgen/do`);
To stop the debugger at symgen do
stopat(`ODEtools/symgen`);
To get infolevel on symgen do
infolevel[`symgen`]:=5;
Or to see line numbers
interface(verboseproc=3); showstat(dsolve)
Or can use the Browse(); command
with(LibraryTools); Browse();
Another option I found is
s:=debugopts(procdump=`showstat`);
Then the above produces listing that can be copied as string with line wrapping ok.
One way
L:=[]: for i from 1 to 3 do : L:=[op(L),i]; end do;
But a better way is to use seq if one knows the length
L:=[seq(i,i=1..3)]; L := [1, 2, 3]
Since list is unmutable, a more eﬃcient method, for long lists, is to use Array, and then convert the result back to list at the end since Array can grow dynamically without preallocation each time something is inserted as follows
L:=Array(): for i from 1 to 3 do : L(i):=i; end do; for i from 1 to numelems(L) do : print(L[i]); end do; L := convert(L,list)
Which wil print
L := [1] L := [1, 2] L := [1, 2, 3] 1 2 3 L := [1, 2, 3]
Notice that to add to an Array, () is used. But to access an entry in an array [] is used.
And ﬁnally, using Array also, it can be done without using any indexing as follows
L:=Array(1..0): for i from 1 to 3 do : L ,= i; end do; L := convert(L,list)
For the above to work, the array must be declared using Array(1..0). The new syntax A ,= i will append to the array, and there is no need to write A(i) := i
By Carol Devore on the net:
Use infolevel. For example, to show what logic dsolve uses, do this: First try > infolevel[all]:= 5; That will probably give more information than you want, but if not, then try > printlevel:= 1000; If you want information about a specific procedure, you can use debug. For example, restart; debug(`int/int`); int(p, x= 0..1); To find out what procedures are being called without getting too much extra information, use excallgraph.
Trying on dsolve
infolevel[dsolve]:= 3; dsolve({eq1},y(x)); Methods for second order ODEs: Trying to isolate the derivative d^2y/dx^2... Successful isolation of d^2y/dx^2 --- Trying classification methods --- trying a quadrature trying high order exact linear fully integrable trying differential order: 2; linear nonhomogeneous with symmetry [0,1] trying a double symmetry of the form [xi=0, eta=F(x)] <- double symmetry of the form [xi=0, eta=F(x)] successful
Here, I am looking at fouries series expansion of \(f(x)=0\) between \(–\pi \) and 0, and \(f(x)=1\) between 0 and \(\pi \).
The Fouries series expansion is worked out to be as below. This shows that the series approximate the above \(f(x)\) as more terms are added
restart; f:=(x)-> 1/2 + (1/Pi)*(sin(x)+sin(3*x)/3+sin(5*x)/5+sin(7*x)/7); plot(f(x),x=-10..10);
From DOS, point to where your cmaple is
>"C:\Program Files\Maple 7\BIN.WNT\"cmaple
To make it execute maple commands use the < foo.txt to pipe maple commands in the ﬁle to it.
A:= Matrix( [ [1, 2, 3] , [3, 6, 7] , [5, 6, 9] , [7, 7, 7] ]); whattype(A); Matrix size:=LinearAlgebra:-Dimension(A); size := 4, 3 row:=size[1]; row := 4 col:=size[2]; col := 3
You can extract any part of the matrix like this:
B:=A[1..3,2..2];
\[ \left [ \begin {array}{c} 2\\ 6\\ 6\end {array} \right ] \]
By Carl Devore http://mathforum.org/kb/message.jspa?messageID=1570678
Maple list and sequence structures are more flexible than Matrices, which are highly structured. A Maple list of lists (called a listlist in Maplese) is akin to a matrix in some other languages. Many matrix operations can be performed directly on the listlist form, but to do serious linear algebra, you should convert to a Matrix. Of course, it is trivial to convert a listlist to Matrix: LL:= [[1,2], [3,4]]; M:= Matrix(LL); So here is another solution in line with your original wishes. This is "index free", but the table-based solution I gave earlier should be faster. (It is usually considered bad form to repeatedly append to a list or sequence.) L:= [][]; # Create a NULL sequence do line:= readline(file); if line::string then if line contains valid data then Z:= a list of that data; L:= L, Z fi else break fi od A:= Matrix([L]); # Note []: seq -> list.
To move move a column into a matrix: Here, I want to copy 2nd column to the 3rd column:
A; \[ \left [ \begin {array}{ccc} 1&2&3\\ 3&6&7\\ 5&6&9\\ 7&7&7 \end {array} \right ] \]
B:=A[1..row,2]; \[ \left [ \begin {array}{c} 2\\ 6\\ 6\\ 7 \end {array} \right ] \]
A[1..row,3]:=B: A;
\[ \left [ \begin {array}{ccc} 1&2&2\\ 3&6&6\\ 5&6&6\\ 7&7&7 \end {array} \right ] \]
Maple can return multiple values. Make sure to use the comma "," in the body of the procedure to separate each return value. Example:
size_matrix:=proc(x) 3*x, 4*x; end proc; row,col :=size_matrix(5);
When passing a variable to maple procesure, the variable VALUE is passed to the procedure (This is diﬀerent from say Fortran where the default is pass by reference). But this is the same as with Mathematica.
For example, if a variable X had value 10, then you call a procedure FOO passing it X, then inside FOO, X will be the number 10, not the argument variable X. So, this means one can not have X on the left hand side inside FOO. Like this x:=1
The only way to assign new value to the input and return new value, is to use a local variable, like this:
one:= proc(x) local y; print(x); y:=x+ 1; print(x); y; end proc; z:='z'; z:=5; f:=one(z); f := 6
Use `type/name` to deﬁne new type name.
`type/char`:= x-> x::string and length(x)=1; P:= proc(c::char) print(c) end proc: P("x"); "x" P("xy"); Error, invalid input: P expects its 1st argument, c, to be of type char, but received xy > `type/byte`:= x-> x::integer and (x>= 0 and x<256); #will define a byte (unsigned integer)
Code from net by Carl Devore:
MMax:= proc(M::{Matrix,matrix}) local C,r,c,mx,L,p; C:= op(`if`(M::Matrix, [1,2], [2,2,2]), eval(M)); L:= map(op, convert(M, listlist)); mx:= max(L[]); member(mx,L,'p'); r:= iquo(p, C, 'c'); mx, `if`(c=0, [r,C], [r+1,c]) end;
Code below from C W
A:=matrix(12,12,rand(100)); Ao:=array((proc(E) local i; [seq(i=(rhs=lhs)(E[i]),i=1..nops(E))]end) (sort(op(3,eval(A)),proc(E1,E2) if rhs(E1)>rhs(E2) then true else false fi end))); Ao[1];
First create the module:
restart; nma:= module() option package; export getMaxMatrix; getMaxMatrix := proc (M::{matrix, Matrix}) local C, r, c, mx, L, p; C := op(`if`(M::Matrix,[1, 2],[2,2,2]),eval(M)); L := map(op,convert(M,listlist)); mx := max(L[]); member(mx,L,'p'); r := iquo(p,C,'c'); mx, `if`(c = 0,[r, C],[r+1, c]) end proc; end module; A:= Matrix( [ [1, 2, 3] , [3, 6, 7] , [5, 6, 9] , [7, 7, 7] ]); nma[getMaxMatrix](A);|
Gives 9, [3, 3]. Now save the module.
savelibname := "C:/MAPLE_PACKAES"; march('create', savelibname, 20);
now save the library to disk. savelib(nma);
Now we can test everything by reinitialize everything and reload the library.
>restart #Add my library to LIBNAME >libname:="C:/MAPLE_PACKAGES",libname; > A:=matrix( [ [1,2,3],[4,6,9] ]); >with(nma); >nma[getMaxMatrix](A);
Now to print a proc() in the package, do
>interface(verboseproc=3); > print(nma[getMaxMatrix]);
Now you can list what packages exist in the archive:
march('list',savelibname); march('extract',savelibname,":-1.m","C:MAPLE_PACKAGES/t.m")
Some notes. need to clean later
> module1lib:=`module1\\lib`; > system("md "||module1lib); > march('create',module1lib,100); > makehelp(module1,`module1/module1.mws`,module1lib): > makehelp(`module1/export1`,`module1/export1.mws`,module1lib): > savelibname:=module1lib: ### doesn't affect current libname > savelib(module1); ### no error message > restart; > module1lib:="module1\\lib": > libname:=module1lib,libname; ### now Maple will find module1 > with(module1); > ?module1
Also there is a long thread here on Maple prime on making personal packages in Maple How-To-Create-A-Personal-Package
From: Robert Israel (israel@math.ubc.ca) Subject: Re: Getting non-integral results in hex Newsgroups: comp.soft-sys.math.maple Date: 2003-06-13 00:07:37 PST I assume you mean floating-point numbers. Note that Maple floats (as opposed to "hardware floats") are in fact stored in base 10. To convert a float to hex with n digits after the ".", you can use this: > `convert/hexfloat`:= proc(x::numeric, n::nonnegint) local A,B,ax,R; if nargs = 1 then return procname(x,round(Digits*log[16](10))) fi; if x = 0 then return cat(`0.`,`0`$n) fi; ax:= abs(x); A:= floor(ax); B:= round(frac(ax)*16^n); if B = 16^n then A:= A+1; B:= 0 fi; R:= cat(convert(A,hex),`.`); if x < 0 then R:= cat(`-`,R) fi; cat(R,substring(convert(16^n+B,hex),2..-1)); end; And then, e.g.: > convert(1234.5678, hexfloat, 4); 4D2.915B
mtaylor(sin(x),[x],10);
\[ x-1/6\,{x}^{3}+{\frac {{x}^{5}}{120}}-{\frac {{x}^{7}}{5040}}+{\frac { {x}^{9}}{362880}} \]
restart; a:=Matrix([ [2,3,4],[4,5,6] ]); nRow,nCol :=LinearAlgebra[Dimension](a); for i from 1 to nRow do for j from 1 to nCol do printf("a(%d,%d)=%d\n",i,j,a[i,j]); end do; end do; a(1,1)=2 a(1,2)=3 a(1,3)=4 a(2,1)=4 a(2,2)=5 a(2,3)=6
restart; a:=Matrix([ [2,4],[5,7] ]); LinearAlgebra:-Determinant(a); -6
H := LinearAlgebra:-HilbertMatrix(5);
\[ \left [ \begin {array}{ccccc} 1&1/2&1/3&1/4&1/5\\ \noalign {\medskip }1/ 2&1/3&1/4&1/5&1/6\\ \noalign {\medskip }1/3&1/4&1/5&1/6&1/7 \\ \noalign {\medskip }1/4&1/5&1/6&1/7&1/8\\ \noalign {\medskip }1/5&1/6&1 /7&1/8&1/9\end {array} \right ] \]
Matlab is much easier here. In maple, need to covert the matrix to a list of list of points ﬁrst.
restart; H := LinearAlgebra:-HilbertMatrix(5): nRow,nCol :=LinearAlgebra[Dimension](H): L:=[seq([seq( [i,j,H[i,j]], i=1..nRow) ], j=1..nCol)]: plots:-surfdata(L);
An error in maple raises an exception. So, use try catch to trap it as follows:
try v,pos:=MMax(4); catch: printf("an error is cought\n"); end try;
From the net, by Carl Devor:
`print/commas`:= proc(N::integer) local n,s,i,b; n:= ListTools:-Reverse(convert(abs(N), base, 1000)); if N<0 then n:= subsop(1= -n[1], n) fi; nprintf("%s", sprintf(cat("%d", ",%03d" $ nops(n)-1), n[])) end proc: commas(456554); 456,554
To convert a string to array of chars use array(StringTools:-Explode(S))
s:="Nasser M. Abbasi": r:=array(StringTools:-Explode(s)); r:=["N" "a" "s" .......]
Now can use the string as normal array
r[4]; "s"
Units[GetDimensions](base); amount_of_information, amount_of_substance, currency, electric_current, length, logarithmic_gain, luminous_intensity, mass, thermodynamic_temperature, time
Use the Sum command.
restart; expr:= (-1)^i/(2*i+1)^2; Sum(expr,i=0..infinity); evalf(%,50); 0.91596559417721901505460351493238411077414937428167
Notice, if I used the sum command instead of the Sum command I get this result:
sum(expr,i=0..infinity); Catalan
This shows how to do a simple package and use it without building a library. Just using a plain text ﬁle.
Create this nma_pkg1.txt ﬁle:
nma_pkg1 := module() export f1; option package; f1:= proc() print("in pakcage nma_pkg1"); end proc; end module;
now save it, and from maple do
>read("c:\\nma_pkg1.txt");
now execute f1() as this:
>nma_pkg1[f1](); "in pakcage nma_pkg1"
now put it in a library (so that we can use with, instead of read)
> savelibname:=("c:/maple"); > march('create', savelibname, 20); > savelib(nma_pkg1); >restart; > libname := "c:/maple",libname; > with(nma_pkg1); > f1(); "in pakcage nma_pkg1"
now make changes to the nma_pkg1.txt ﬁle and updated again as above.
?index,package
restart; f:=3*x^2 + y* cos(x*y); the_grad :=linalg[grad](f,[x,y]); plots[fieldplot](the_grad,x=-2..2,y=-2..2);
or
or can do it in just one command: plots[gradplot](f,x=-2..2,y=-2..2);
Suppose you want the 100 digits of Pi put in a list. This is one way to do it:
restart; L:=evalf(Pi,100); S:=convert(L,string); the_list:=[seq(parse(S[i]),i=3..length(S))]; the_list := [1, 4, 1, 5, 9, 2, 6, 5, 3, ..
This below now tells how many times each digits occurs.
>stats[transform,tally](the_list); [Weight(0, 8), Weight(1, 8), Weight(2, 12), Weight(3, 11), Weight(4, 10), Weight(5, 8), Weight(6, 9), Weight(7, 7), Weight(8, 13), Weight(9, 13)]
Written sometime in 2005? I should really record the time when I write something.
I just run these now, Auust 2014, and now Maple 18 as very fast. So this all below is no longer valid. I will leave it here for now for reference until I update it all later
I have written a few lines of code, which counts how many times each digit occurs after the decimal points of \(\pi \)
Written this in maple ﬁrst. Then did similar thin in mma 5.0. Both are run on the same PC. No other applications are running at the time when I run the code.
The basic idea of the algorithm is to use evalf(Pi,digits) in maple to ﬁnd \(\pi \) for any number of decimal digits, and to use N[Pi,digits] in mma for doing the same. (Where the variable digits above is the number of digits)
Then in maple convert the above \(\pi \) to a string, and generate a sequence of the characters to right of decimal point, then use stats[transform,tally] to do the actual counting.
In mma, I use RealDigits[] to get a list of the digits, and then use Count[] to do the counting.
This is result of some of the runs to ﬁnd Pi to some digits, and the total time (to ﬁnd Pi and do the counting)
All times are in cpu seconds, machine is P4, 2.8 Ghz, 500 MB of RAM, single CPU, hyperthreading enabled, running XP home edition. Maple 9.03 student version, and mma 5.0 student version.
Below is the result, and below that I show the maple code and the mma code.
Because of this, before each run in mma, I exited the application and started it fresh. In maple, it does not matter for the above reason.
100,000 digits: Find_Pi Total Maple 9.0 55 84 Mma 5.0 0.9 1.54
Mma is 60 times faster in ﬁnding pi and about 56 times faster overall
300,000 digits: Find_Pi Total Maple 9.0 309 781 Mma 5.0 3.7 6
Mma is 300 times faster in ﬁnding Pi, and 130 times faster overall.
3,000,000 digits Find_Pi Total Maple 9.0 Mma 5.0 85 118 Maple time in hours ! Still running.
Maple code
> restart; startingTime :=time(); L:=evalf(Pi,100000): timeToFindPiInSecs:=time()-startingTime; S:=convert(L,string): the_list:=[seq(parse(S[i]),i=3..length(S))]: stats[transform,tally](the_list); endingTime :=time(): cpuTimeInSecs := endingTime - startingTime;
mma code
Clear[] startingTime=TimeUsed[] t1=N[Pi,100000]; timeToFindPiInSecs=TimeUsed[]-startingTime {c,d}=RealDigits[t1]; theList=c[[Range[2,Length[c]]]]; f[digit_]:=Count[theList,digit]; r=Range[0,9]; Map[f,r] cpuTimeInSecs=TimeUsed[]-startingTime
update 12/25/03 Changed maple code on how to do the counting : To use
StringTools[CharacterFrequencies](S)
Now the counting in maple is much faster. It is always hard to know which is the best function to use.
restart; startingTime :=time(); L:=evalf(Pi,300000): timeToFindPiInSecs:=time()-startingTime; S:=convert(L,string): StringTools[CharacterFrequencies](S); endingTime :=time(): cpuTimeInSecs := endingTime - startingTime;
From: Ken Lin (maplemath@tp.edu.tw) Subject: Re: how to find which package a function belongs to? Newsgroups: comp.soft-sys.math.maple Date: 2003-12-04 03:49:26 PST When Maple first loaded, There are only two kinds of "internal" commands which can be called directly. One is the "kernal" commands coded in C, and the other includes many "internal" prodecures programmed by the kernal commands which lies in the "Main Library", There are also many other "external" procedures which were categorized into so called "packages", plots[display](...) for example, plots[] is a package(Library), and display() is the prodecure inside plots[]. All the packages can be loaded by with() command, like > with(plots); Because Different Packages include user library might have the same procedure name, Maple doesn't realize the "procedure_name" you type in, it took it for a "symbol". If you really want to know which packages provided by Maple the external procedure lies in, just mark the procedure_name and press F1 key, the Maple Help Browser will show you the packages you might be interested. By the way, plot3d() is a "internal" procedure lies in the Main Library. You can confirm that by: > op(0, eval(plot3d)); procedure or in Maple 9 > type( plot3d, 'std' ); #Is it internal? true > type( plot3d, 'stdlib' ); #Does is lie in "Standard(Main) Library"? true If you are interested the codes inside plot3d()... > interface(verboseproc=2): #Turn on verboseproc > print(plot3d); #eval() also works > interface(verboseproc=1): #Turn off verboseproc I hope this will give you some help. Have fun with Maple. Ken Lin
See http://www.maplesoft.com/applications/view.aspx?SID=1533&view=html&L=G
use select. For example
>restart; >my_list:=[1,3.4,3+I,5]; >select(x->evalb(Im(x)=0),my_list); [1, 3.4, 5]
restart; m:=Matrix( [[1.3,2,3],[3,4,4] ]); matrixTestQ := proc(m::Matrix) local r,c,i,j; (r,c):=LinearAlgebra[Dimensions](m); for i from 1 to r do for j from 1 to c do if( not evalb( whattype(m[i,j]) = integer) ) then return(false); end if; end do; end do; return true; end proc; >matrixTestQ(m); false
I am sure there is a better way than the above. Need to ﬁnd out.
restart; f:= t->sin(omega*t) ; L:=convert(inttrans[laplace](f(t),t,s),int);
\[ {\frac {\omega }{{\omega }^{2}+{s}^{2}}} \]
To ﬁnd the inverse, do:
inttrans[invlaplace](L,s,t);
\[ \sin \left ( \omega \,t \right ) \]
Any difference between using `diffalg/Rosenfeld_Groebner`(args) or diffalg[Rosenfeld_Groebner](args)
restart; f:= (x,y)->x^3-3*x*y^2; plot3d(f,-1..1,-1..1,numpoints=2500,style=patchcontour);
Use map
map(`^`,{1,2,3},3); {1, 8, 27}
incr:=.25; start:=0; last:=3; seq(start+i*incr,i=1..(last/incr));
read ?MVshortcut, ?MVassignment, and ?Mvextract and Transpose(R) can be shortened to R^%T
Written feb 20, 2004
This is problem 7.4 chapter 4, in the Mary Boas book. Given \begin {align*} x s^2+y t^2 &= 1\\ x^2 s+y^2 t &= xy-4 \end {align*}
Find \(\frac {dx}{dt}, \frac {dx}{ds}, \frac {dy}{dt}, \frac {dy}{ds}\) at \(x=1,y=-3,s=2,t=-1\)
This is how I did it in maple:
restart; alias(x=x(s,t)); alias(y=y(s,t)); alias(Xt= diff(x(s,t), t)); alias(Xs= diff(x(s,t), s)); alias(Yt= diff(y(s,t), t)); alias(Ys= diff(y(s,t), s)); eq1:= x*s^2+y*t^2=1; eq2:= x^2*s+y^2*t=x*y-4; r1:=diff(eq1,t); r2:=diff(eq1,s); r3:=diff(eq2,t); r4:=diff(eq2,s); sol:=solve({r1,r2,r3,r4},{Xt,Xs,Yt,Ys});
\begin {align*} {\frac {\partial }{\partial s}}x \left ( s,t \right ) &= -{\frac {x \left ( s,t \right ) \left ( x \left ( s,t \right ) {t}^{2}-4\,y \left ( s,t \right ) st+2\,x \left ( s,t \right ) s \right ) }{2\,x \left ( s,t \right ) s{t}^{2}-2\,y \left ( s,t \right ) t{s}^{2}+x \left ( s,t \right ) {s}^{2}-y \left ( s,t \right ) {t}^{2}}}\\ {\frac {\partial }{\partial t}}x \left ( s,t \right ) &=-{\frac {y \left ( s,t \right ) t \left ( -3\,y \left ( s,t \right ) t+2\,x \left ( s,t \right ) \right ) }{2\,x \left ( s,t \right ) s{t}^{2}-2\,y \left ( s,t \right ) t{ s}^{2}+x \left ( s,t \right ) {s}^{2}-y \left ( s,t \right ) {t}^{2}}}\\ {\frac {\partial }{\partial s}}y \left ( s,t \right ) &=-{\frac {x \left ( s,t \right ) \left ( 3\,x \left ( s,t \right ) s-2\,y \left ( s,t \right ) \right ) s}{2\,x \left ( s,t \right ) s{t}^{2}-2\,y \left ( s,t \right ) t {s}^{2}+x \left ( s,t \right ) {s}^{2}-y \left ( s,t \right ) {t}^{2}}}\\ {\frac {\partial }{\partial t}}y \left ( s,t \right ) &=-{\frac {y \left ( s,t \right ) \left ( 4\,x \left ( s,t \right ) st-y \left ( s,t \right ) {s }^{2}-2\,y \left ( s,t \right ) t \right ) }{2\,x \left ( s,t \right ) s{t} ^{2}-2\,y \left ( s,t \right ) t{s}^{2}+x \left ( s,t \right ) {s}^{2}-y \left ( s,t \right ) {t}^{2}}} \end {align*}
points:= {x=1,y=-3,s=2,t=-1}; subs(points,sol);
This is problem 7.15 chapter 4 in Boas:
Given \(x^2 u-y^2 v=1\) and \(x+y=uv\) Find \(\frac {dx}{du},v\) and \(\frac {dx}{du},y\)
This is the maple code to solve this:
restart; eq1:=x^2*u-y^2*v=1; eq2:=x+y=u*v; r1:=D(eq1); r2:=D(eq2); r1_:=subs(D(v)=0,r1); r2_:=subs(D(v)=0,r2); sol:=solve({r1_,r2_},{D(x),D(u)}); print("dx/du,v="); rhs(sol[1])/rhs(sol[2]); r1_:=subs(D(y)=0,r1); r2_:=subs(D(y)=0,r2); sol:=solve({r1_,r2_},{D(x),D(u)}); print("dx/du,y="); rhs(sol[1])/rhs(sol[2]);
by http://www.math.fsu.edu/~bellenot
restart; t2 := proc(i, x, y) if i < 2 then [[x, y], [x, y - 1]], [[x, y], [x + 2^i, y - 1]] else [[x, y], [x, y - 1]], [[x, y], [x + 2^i, y - 1]], t2(i - 1, x, y - 1), t2(i - 1, x + 2^i, y - 1) end if end proc; PLOT(CURVES(t2(6,0,0)));
restart; z:= Int( sin(t)/t, t=sin(x)..cos(x)); diff(z,x);
\[ -{\frac {\sin \left ( x \right ) \sin \left ( \cos \left ( x \right ) \right ) }{\cos \left ( x \right ) }}-{\frac {\cos \left ( x \right ) \sin \left ( \sin \left ( x \right ) \right ) }{\sin \left ( x \right ) }} \]
restart; c:='c': C:='C': n:='n': P:='P': C := n -> ((n+2)/(3*n+1))^n: ### WARNING: calls to `C` for generating C code should be replaced by codegen[C] `The general term is `, c[n]= C(n); ` `; `The n-th root is:`; ### WARNING: calls to `C` for generating C code should be replaced by codegen[C] P := C(n)^(1/n): abs(c[n])^(1/n) = P; P := simplify(P, assume=positive): abs(c[n])^(1/n) = P;
restart; f:= 1/( (1-2*z)*(5*z-4) ); residue(f,z=4/5);
\[ \frac {-1}{3} \]
_EnvAllSolutions:=true; solve(sin(x)=0);
Pi _Z1~
Subject: Associated Legendre Author: Mehran Basti <Basti@worldnet.att.net> Organization: AT&T Worldnet Date: Mon, 25 Nov 2002 02:48:15 GMT Dear newsgroup: I had mentioned that my methods will solve classical equations without the use of infinite series. The following is a Maple code of my old files. Those days I had Maple2 but the general idea is the same in the process and you see that we can also solve the integrals involved. It does not make sense how are the theory behind it but eventually it will come into light. Just read the procedures and you can see the solution of associated legendre AL at the end. > s1:=-diff(p(t),t)+p(t)^2; >