3 Solve second order linear ODEs with Liouvillian solutions using Kovacic' algorithm
6 [1] Carolyn J. Smith A discussion and implementation of Kovacic' algorithm,
7 MSC thesis university of waterloo, 1984
8 https://cs.uwaterloo.ca/research/tr/1984/CS-84-35.pdf
9 [2] B.D. Saunders An implementation of Kovacic's algorithm for solving second order linear homogeneous differential equations
10 SYMSAC '81 Proceedings of the fourth ACM symposium on Symbolic and algebraic computation
12 [3] Jerald J. Kovacic, An algorithm for solving second order linear homogeneous differential equations, Journal of Symbolic Computation, v.2 n.1, p.3-43, March 1986
15 Copyright (C) 2014 Nijso Beishuizen
17 This program is free software; you can redistribute it and/or modify
18 it under the terms of the GNU General Public License as published by
19 the Free Software Foundation; either version 2 of the License, or
20 (at your option) any later version.
22 This program is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 GNU General Public License for more details.
27 You should have received a copy of the GNU General Public License
28 along with this program; if not, write to the Free Software
29 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
32 /*****************************************************************************************************/
33 put('kovacic,001,'version)$
34 /*****************************************************************************************************/
36 /* ----- print everything with flag lower than DEBUGFLAG ----- */
39 /* load absimp to get rid of abs(a) when a>0 */
43 /* remember: one of the necessary conditions might be too strict, see kovacic, compare with Saunders !!!*/
44 /* we should check for linear independence by checking that the wronskian has nonzero determinant*/
48 /* (det(W)) = (y1y2' -y1'y2') */
49 /* (det(W))' = (y1y2' -y1'y2')' */
51 /* 1a. check the paper of Ulmer & Weil */
52 /* 1b. check paper of Man, combining with Prelle-Singer algorithm */
53 /* 2: check if linearizable by lie point transformation */
54 /* 3. check if linearizable by generalized Sundman transformation */
55 /* 4. check if linearizable by euler-Liouville transformation */
56 /* 5. check if linearizable by cartan equivalence */
57 /* 6. check if transformable from linear second order without liouvillian */
58 /* _solutions to linear second order with liouvillian _solutions */
59 /* for this we need to have a mapping to a set of standard linear equations */
60 /* for instance, how does y"=xy transform into y"=0 ?*/
63 /*****************************************************************************************************/
64 /* ----- Check if y(x)=_solution is a _solution of (differential) equation ----- */
65 /*****************************************************************************************************/
66 isSolution(_solution,_equation):=block([_eq,_subs1],
67 /* assumes equation contains "=" */
68 _eq:rhs(_equation)-lhs(_equation),
69 /* assumes _solution has the form y(x)=expression */
70 _subs1:subst(_solution,_equation),
71 return(ratsimp(ev(_subs1,diff)))
73 /*****************************************************************************************************/
77 /*****************************************************************************************************/
78 /* ----- Check if expr is an equation (containing a "=" ) ----- */
79 /*****************************************************************************************************/
80 isEquation(_expr) := block([],
81 if freeof("=",_expr) then return(false) else return(true)
84 /* rules for linear second order equation: */
85 /* y'' = b(x)*y'+c(x)*y + d(x) */
87 /*****************************************************************************************************/
88 matchdeclare ([_b,_c,_d],freeof(_dy))$
89 matchdeclare ([_c1,_c2],freeof(_x))$
91 /* match for linear second order equation - can match dependencies:
93 ode2_linear(a*diff(y,x),y,x)
94 as well as regular _variables:
95 ode2_linear(a*diff(y(x),x),y(x),x) */
96 defmatch (ode2_linear, _b*'diff(_dy,_x)+_c*_dy+_d,_dy,_x)$
97 defmatch (linear, _c1*_x+_c2,_x)$
101 /*****************************************************************************************************/
102 /*****************************************************************************************************/
103 kovacicODE(_expr,_y1,_x1) := block([_y,_x,_var,_ode_order,_ddy,_ode,_phi,_b,_c,_d,_s,_t,_r,_ord_inf,_l,_m,_n,_j,_trial,_squo,_srem,_tcont,_nu],
104 /*****************************************************************************************************/
105 /*****************************************************************************************************/
106 /* ----- substitute our own local _variables ----- */
107 _expr:subst(_y,_y1,_expr),
108 _expr:subst(_x,_x1,_expr),
110 /* what do we know about _x */
111 /*print(facts(_x1)), */
115 dprint(0,"equation: ",_expr,", degree = ",derivdegree(_expr,_y,_x)),
116 /* ----- first check if it is an equation (we only check for equal sign) ----- */
117 if isEquation(_expr)=false then (
118 dprint(0,_expr, " is not an equation."),
122 /* ----- find the order of the ODE ----- */
123 _ode_order : derivdegree(_expr,_y,_x),
124 if (_ode_order=2) then (
125 dprint(0,"Second order ODE found.")
128 dprint(0,_expr," is not a second order ODE."),
133 /* ----- make sure the equation is in the form y'' = F(x,y,y') ----- */
134 _ddy: 'diff(_y,_x,2),
135 /* we only take the first _solution, in general, loop over the _solutions! */
136 _ode: solve(_expr,_ddy)[1],
138 if (lhs(_ode)=_ddy) and (freeof(_ddy,_phi)) then (
139 dprint(1,"ODE: ",string(_ode))
142 dprint(0,"could not separate second order differential operator ",_ode),
147 /*print("is ode2 linear: ",ode2_linear(_phi,_y,_x)),*/
148 /* ----- check if the equation is linear ----- */
149 if (ode2_linear(_phi,_y,_x)=false) then (
150 dprint(0,"ODE is not linear!"),
154 dprint(1,"ODE is linear: ",_b,_c,_d),
160 /*print(ev(_phi,_y=0)),*/
162 /* ----- check if equation is homogeneous ----- */
163 if (ev(_phi,_y = 0)=0) then (
164 dprint(1,"ODE is homogeneous:")
167 dprint(1,"equation is nonhomogeneous, first trying homogeneous case")
169 /* could be transformed to third order homogeneous ode l(y)=0 */
170 /* L(y)=b ---> l(y) = b*(L(y))' -b'*L(y) = 0 */
174 /* ----- Transform to normal form y''= (s/t)*y, ----- */
175 _s : 2*diff(_b,_x) + _b*_b - 4*_c,
181 /* remember, maple's normal(in expanded form) is like maxima's ratsimp */
183 dprint(5,"normal = ",_r),
184 _s : ratexpand(num(_r)),
185 _t : ratexpand(denom(_r)),
189 /* ----- determine the quotient and remainder of _s/_t for the main _variable _x ----- */
190 /* catch this because divide is not exactly polynomial long division and the quotient is not uniquely determined */
191 if freeof(_x,_s) and freeof(_x,_t) and (_t#0) then (
192 [_squo,_srem] : [_s/_t,0]
195 [_squo,_srem] : divide(_s,_t,_x),
196 _squo:ratexpand(_squo), /* we need to do a ratexpand because we want to get the coefficient correctly*/
197 _srem:ratexpand(_srem) /* we need to do a ratexpand because we want to get the coefficient correctly*/
199 dprint(5,"quo = ",_squo),
200 dprint(5,"rem = ",_srem),
202 /* ----- compute a square free factorization ----- */
203 [_sdec,_tcont] : sqfree(_t,_var),
204 dprint(5,"_sdec square free = ",_sdec),
206 /* this is the order*/
213 /* TODO: can we replace part(_sdec,i) with _sdec[i] ? */
214 for _i:1 thru _m do (
215 _t : _t*part(_sdec,_i)^_i
232 dprint(5,"_t2 = ",_t2),
234 dprint(5,"_t1 = ",_t1),
235 dprint(5,"_t2 = ",_t2),
238 /* this does not give the same results as kovacic examples in the 1986 paper: is Smith wrong? */
239 _ord_inf : hipow(ratexpand(_t),_var) - hipow(ratexpand(_s),_var),
240 dprint(1,"order at infinity = ",_ord_inf),
245 for _i from 3 step 2 thru _m do (
246 if (part(_sdec,_i)#1) then (
247 dprint(2,"_oddti is false, it cannot be case 1"),
252 if _oddti and (featurep(_ord_inf/2,integer) or (_ord_inf>2)) then (
253 dprint(2,"case 1 applies"),
254 _listl : append(_listl,[1])
257 if not _oddti or (_t2 # 1) then (
258 dprint(2,"case 2 applies"),
259 _listl : append(_listl,[2])
262 if (_m <= 2) and (_ord_inf >= 2) then (
263 _listl : append(_listl,[4,6,12])
266 dprint(1,"list of cases that apply: L=",_listl),
268 if length(_listl)=0 then (
269 dprint(0,"No Liouvillian solutions exist!"),
274 /* ************************************************* */
275 dprint(0, "end of preliminaries..."),
276 /* ************************************************* */
278 /* STEP 1 PART (a) */
279 _dfix : (min(_ord_inf,2) - hipow(ratexpand(_t),_var) - 3*hipow(ratexpand(_t1),_var))/4,
280 _thetafix : ratsimp((diff(_t,_var)/_t + 3*diff(_t1,_var)/_t1)/4),
281 dprint(5,"_dfix=",_dfix),
282 dprint(5,"_thetafix=",_thetafix),
284 /* STEP 1 PART (b) */
285 /* Poles of order 2: find the roots c1,...,c_k2 of _t2 */
286 /* first, get a list of all the unique roots */
287 _rlist2 : rootz(_t2,_var),
288 dprint(5,"_rlist2 = ",_rlist2),
290 /* find the leading coefficient of _t2 */
291 /*lcoeff : ratcoef(ratexpand(_t2),_var,hipow(ratexpand(_t2),_var)),*/
292 _t2 : _t2/lcoeff(_t2,_var),
293 dprint(5,"_t2=",_t2),
295 /* k2 is the number of roots of _t2*/
297 dprint(5,"_k2 = ",_k2),
299 /* loop over all roots */
300 for _i:1 thru _k2 do (
303 trest : _t/_t2^2 * product((_var-_rlist2[_j])^2,_j,1,_i-1) * product((_var-_rlist2[_j])^2,_j,_i+1,_k2),
304 dprint(5,"srem = ",_srem),
305 dprint(5,"trest = ",trest),
306 dprint(5,"var = ",_var),
307 dprint(5,"_rlist2 = ",_rlist2[_i]),
309 _sol:undetcoeff(_srem,trest,_var,_rlist2[_i],2,2),
310 dprint(5,"_sol:",_sol),
311 _dd[_i] : ratsimp(1+4*_sol)^(1/2),
312 _theta[_i] : ratsimp(_dd[_i]/(_var-_rlist2[_i])),
313 dprint(5,"d1 = ",_dd[_i]),
314 dprint(5,"_theta1 = ",_theta[_i])
319 /* high order poles */
320 /* STEP 3 PART (c) */
323 if member(1,_listl) then (
324 dprint(1,"1 is in L so we calculate higher order poles"),
325 for _i:4 thru _m step 2 do (
326 dprint(5,"_i = ",_i, "/",_m),
328 dprint(5,"L_i=",L_i),
329 _rlisthigher : rootz(L_i,_var),
330 /*lcoeff : ratcoef(ratexpand(L_i),_var,hipow(ratexpand(L_i),_var)),*/
331 dprint(5,"sdec = ",_sdec),
332 dprint(5,"_i-1:",_i-1),
334 dprint(5,"part1 = ",create_list(_sdec[ii],ii,1,_i-1)),
335 dprint(5,"part2 = ",L_i/lcoeff(L_i,_var)),
336 dprint(5,"part3 = ",create_list(_sdec[ii],ii,_i+1,_m)),
338 _sdec : append(create_list(_sdec[ii],ii,1,_i-1), [L_i/lcoeff(L_i,_var)], create_list(_sdec[ii],ii,_i+1,_m)),
339 dprint(5,"sdec = ",_sdec),
341 /* we could do for rt in _rlisthigher ??? */
342 for _j from 1 thru length(_rlisthigher) do (
344 rt : _rlisthigher[_j], /* !!! */
345 dprint(5,_t,_sdec[_i],_i),
346 trest : _t/_sdec[_i]^_i * product((_var-_rlisthigher[_l])^_i,_l,1,_j-1) * product((_var-_rlisthigher[_l])^_i,_l,_j+1,length(_rlisthigher)),
347 dprint(5,_srem,trest),
348 /* method of undetermined coefficients */
349 _sol:undetcoeff(_srem,trest,_var,rt,2*_nu,2*_nu),
351 dprint(0,"No Liouvillian solutions exist"),
354 dprint(0, "_sol = ",_sol),
355 _ac[_nu] : ratsimp(_sol^(1/2)),
356 for _k from _nu-1 thru 2 step -1 do (
358 result : sum(_ac[_l]*_ac[_nu+_k-_l],_l,_k,_nu),
360 /* can we do _ac[k]=undetcoeff directly because result is already solved?*/
361 undet : undetcoeff(_srem,trest,_var,rt,2*_nu,_k+_nu),
362 dprint(5,"result:",result),
363 dprint(5,"undet:",undet),
364 _ac[_k] : rhs(solve(result=undet,_vtemp)[1]),
365 dprint(5,"_ac[_k]=",_ac[_k])
368 /* sum of _ac[k]*_ac[_nu+1-k] */
369 result : sum(_ac[_k]*_ac[_nu+1-_k],_k,2,_nu-1),
371 _dd[_k1] : (undetcoeff(_srem,trest,_var,rt,2*_nu,_nu+1)-result)/_ac[_nu],
373 result : sum(_ac[_k]/(_var-rt)^_k,_k,2,_nu),
375 _theta[_k1] : 2*result + _dd[_k1]/(_var-rt)
381 dprint(3,"_dd = ",_dd),
384 dprint(1,"_ord_inf > 2"),
388 if _ord_inf = 2 then (
389 dprint(1,"_ord_inf=2"),
393 _sol:lcoeff(_s,_var)/lcoeff(_t,_var),
394 _dd[0]:ratsimp((1+4*_sol)^(1/2)),
397 else if member(1,_listl) then (
398 dprint(1,"1 is in L"),
400 dprint(5,"_nu = ",_nu),
401 dprint(5,"_squo = ",_squo),
402 dprint(5,"_var = ",_var),
403 dprint(5,"coeff=",coeff(_squo,_var,2*_nu)), /* should we just use lcoeff? it uses ratexpand..*/
404 _ac[_nu]:ratsimp(coeff(_squo,_var,2*_nu)^(1/2)),
405 dprint(5,"_ac[_nu]=",_ac[_nu]),
406 for _i from _nu-1 thru 0 step -1 do (
409 dprint(5,"_ac[_i]=",_ac[_i]),
411 _temp_var : sum(_ac[_j]*_ac[_i+_nu-_j],_j,_i,_nu),
413 dprint(5,"_temp_var = ",_temp_var),
414 dprint(5,"coeff = ",coeff(_squo,_var,_i+_nu)),
415 _ac[_i]:rhs(solve(_temp_var=coeff(_squo,_var,_i+_nu),_vtemp)[1]),
416 dprint(5,"_ac[_i]=",_ac[_i])
418 /* note: ac is not a list, we have just defined ac[1],ac[2]*/
419 dprint(5,"1.ac = ",_ac),
421 dprint(5,"_nu = ",_nu),
422 _temp_var : sum(_ac[_l]*_ac[_nu-_l-1],_l,0,_nu-1),
423 dprint(5,"tempvar = ",_temp_var),
427 _srem : ratexpand(_srem),
428 _squo : ratexpand(_squo),
429 dprint(5,"lcoeff=",lcoeff(_t,_var)),
430 dprint(5,"degree = ",hipow(_t,_var)),
431 dprint(5,"coeff=",coeff(_srem,_var,hipow(_t,_var)-1)),
432 _aa: coeff(_srem,_var,hipow(_t,_var)-1),
433 _bb: lcoeff(_t,_var),
435 _tmp1:_aa/_bb - _temp_var
438 /* or should we simply bail out when the denominator=0?*/
443 _tmp1:coeff(_squo,_var,_nu-1) - _temp_var
445 dprint(5,"_tmp1=",_tmp1),
447 /* the denominator can be zero - we catch it by saying that d[0] should be zero then */
448 /* but is this true?*/
449 if (_tmp1=0 or _ac[_nu]=0) then (
453 _dd[0]:_tmp1/_ac[_nu]
455 dprint(0, "_dd[0] = ",_dd[0]),
457 _tmp2 : sum(_ac[_l]*_var^_l,_l,0,_nu),
462 dprint(2,"otherwise"),
468 dprint(3,"d0 = ",_dd[0]),
469 dprint(3,"_theta0 = ",_theta[0]),
471 /* ************************************************* */
472 /* ***** step 2 - form trial d's and _theta's ***** */
473 /* ************************************************* */
474 dprint(1,"Now entering step 2: forming trial ds and _thetas, length= ",length(_listl)),
478 for i from 1 thru length(_listl) do (
479 dprint(1,"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"),
480 dprint(1,"!!! CASE ",_listl[i], " ",i,"/",length(_listl)," !!!"),
481 dprint(1,"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"),
495 if (_n=2) and (_ord_inf<2) then (
500 /* s = (-n/2,-n/2+1,...n/2)*/
501 for _j from 0 thru _m do (
502 dprint(5,"entering loop!"),
503 /* -n/2, -n/2, -n/2, -n/2, ... */
506 /* in case of n=1 (case 1) this is (-1/2, +1/2) */
509 /* for all sequences s={-n/2, -n/2+1, ..., n/2}*/
511 /* preliminary: d0 should be */
513 while (alls # true) do (
514 dprint(5,"entering do loop"),
516 /*tezt : sq[0]*d[0] - sum(sq[_l]*d[_l],_l,1,_m),*/
518 for _l from 1 thru _m do (
519 tezt : tezt - sq[_l]*_dd[_l]
522 ds:ratsimp(_n*_dfix+tezt),
523 if not member(ds,_dslist) then (
524 _dslist:append(_dslist,[ds]),
525 dprint(3,"dslist:",_dslist),
526 dprint(3,"ds = ",ds, ", should be integer and >=0",featurep(ds,integer), " ",is(ds>=0)),
527 if (integerp(ds)=true) then (
529 dprint(1,"ds is a positive or zero integer"),
530 tmp : sum(sq[_l]*_theta[_l],_l,0,_m),
531 dprint(5,"tmp : ",tmp),
532 _thetas:ratsimp(_n*_thetafix+tmp),
533 dprint(5,"_thetas = ",_thetas),
534 /* step3 - determine polynomial P if possible and hence omega and _solution */
536 _soln:step3(_n,ds,_thetas,_s/_t,_var),
537 dprint(5,"finished step 3"),
538 _soln : radcan(_soln), /* to simplify */
539 dprint(3,"_soln = ",_soln,length([_soln])),
541 if _soln#false then (
542 dprint(0, "finding sol2"),
545 /*gamma_expand:true, */ /* to get rid of incomplete gamma in saunders ex. 1 */
546 /* actually, better to work with gamma to prevent complex logarithms like kamke196 */
547 /* kamke 2.111: we should get exp(-x)/x here */
549 _soln1:exp(integrate(-1/2*ratio,_var))*_soln, /* list [soln] necessary?*/
550 dprint(5,"_soln1 = ",_soln1),
552 /* kill the impaginary part so we don't have to cancel out all the imaginary bits */
553 _soln1:trigsimp(radcan(_soln1)),
554 if not(freeof(%i,_soln1)) then (
555 _soln1 : trigsimp(realpart(_soln1))
557 _soln1:factor(_soln1),
558 _soln1:ev(_soln1,integrate),
560 /* logarc gets rid of arcsinh stuff inside exponentials */
561 /* sometimes this increases the complexity of the solution, so do a check afterwards */
562 _soln11:radcan(logarc(_soln1)),
564 /* if logarc has lead to a better solution, then use it */
565 _nrOps11 : nrOps(_soln11),
566 _nrOps1 : nrOps(_soln1),
568 /* use the radcan-logarc solution when the expression contains less operations
569 or less types of operations */
570 if ((length(_nrOps11) <= length(_nrOps1)) or (length(unique(_nrOps11))<=length(unique(_nrOps1))) ) then (
574 _soln1:radcan(_soln1),
576 dprint(3,"x. _soln1 = ",_soln1),
578 /*soln2 :radcan(soln1*integrate((exp(-integrate(ratio,_var))/(soln1*soln1)),_var)),*/
579 _soln2:-integrate(ratio,_var),
580 _soln2:exp(_soln2)/(_soln1*_soln1),
581 _soln22:radcan(logarc(_soln2)),
583 /* if logarc has lead to a better solution, then use it */
584 _nrOps22 : nrOps(_soln22),
585 _nrOps2 : nrOps(_soln2),
587 /* use the radcan-logarc solution when the expression contains less operations
588 or less types of operations */
589 if ((length(_nrOps22) <= length(_nrOps2)) or (length(unique(_nrOps22))<=length(unique(_nrOps2))) ) then (
593 dprint(3,"x1. _soln2 = ",_soln2,", ",_var),
595 _soln2:integrate(_soln2,_var),
596 dprint(3,"x2. _soln2 = ",_soln2),
597 _soln2:trigsimp(radcan(_soln1*_soln2)),
598 dprint(3,"x3. _soln2 = ",_soln2),
599 if not(freeof(%i,_soln2)) then (
600 dprint(3,"x3a. _soln2 = ",_soln2),
602 _soln2:trigsimp(realpart(_soln2)) /* take the real part and drop the imaginary part */
604 dprint(3,"x4. _soln2 = ",_soln2),
605 _soln2 : radcan(_soln2), /* to further simplify - we still have exp(2^(3/2)*x-sqrt(2)*x) here */
606 dprint(3,"x5. _soln2 = ",_soln2),
607 _soln2 : ev(_soln2,nouns),
608 dprint(3,"length=",length([_soln])),
609 if length([_soln]) = 1 then (
611 _solution: [ratsimp(_soln1),ratsimp(_soln2)], /* realpart because we can have imaginary solutions for y'' + y = 0 and we don't want to deal with the trouble of cancelling them out elegantly by itself, so we just delete them*/
612 dprint(3,"homogeneous solution:",_solution),
618 _solution: [ratsimp(_soln1),ratsimp(_soln2)],op(2,[_soln]),
624 dprint(0,"No Liouvillian solutions exist"),
628 dprint(5,"alls:",alls)
631 ), /* end of dsloop*/
634 dprint(5,"sq_jj = ",sq),
635 for _j from _m thru 0 step -1 do (
636 /*print("j = ",_j,"/",_m), */
637 if (sq[_j] = (1/2 * _n)) then (
644 /* exit (the for loop) */
650 dprint(0,"No Liouvillian solutions exist"),
657 if _solfound=1 then (
663 if (alls=false) then (
664 /* ----- nonhomogeneous _solution -----*/
665 dprint(1,"checking if a nonhomogeneous part exists"),
668 /* 2 independent _solutions of the homogeneous equation */
670 dprint(1,"nonhomogeneous part found - looking for particular solution"),
671 /* -------------------------------------------------- */
673 /* -------------------------------------------------- */
674 _E : exp(integrate(_b,_var)),
675 _PP : integrate(_E*_s[1]*_d,_var),
676 /* we need radcan because of the quotient by zero bug */
677 _PP : (_PP/(_E*_s[1]*_s[1])),
679 /* there is an error somewhere - see kamke 184*/
680 _PP : -_s[1]*integrate(_PP,_var),
682 /* perform the simplification after we have compared the two solutions */
683 /* we do this because simplification can lead to expression swell, like for */
684 /* example kamke 2.234 */
685 /* so we hope that simplification of the smallest term will still lead to */
686 /* a simpler expression than simplification of the largest term */
687 /* -------------------------------------------------- */
689 /* -------------------------------------------------- */
690 /* Determinant of the Wronskian */
691 _W : _s[2]*diff(_s[1],_var) - diff(_s[2],_var)*_s[1],
697 dprint(5,"5. wronskian : ", _W),
698 dprint(1,"nonhomogeneous part : ", _d),
699 /* particular _solution of the ODE */
700 _P : _s[2]*integrate(_s[1]*_d/_W,_var) - _s[1]*integrate(_s[2]*_d/_W,_var),
702 dprint(1,"0. particular solution : ", _P),
703 /*_P : ratexpand(_P),*/
704 /*_P : trigexpand(_P),*/
707 dprint(0, "1. particular solution : ", _P),
709 dprint(0, "2. particular solution : ", _P),
711 dprint(0, "3. particular solution : ", _P),
713 /* substitute back the original _variables */
714 /* -------------------------------------------------- */
717 /*_P : subst([_x=_x1],_P),*/
718 /* use method one, because it supposedly can give less complicated particular solutions */
720 /*print("x1=",_x1),*/
723 dprint(0, "complexity of method 1 for particular solution: ",length(nrOps(_PP))),
724 dprint(0, "complexity of method 2 for particular solution: ",length(nrOps(_P))),
726 /* if method 1 has less operation than method 2, use method 1 */
727 if (length(nrOps(_PP)) <= length(nrOps(_P)) ) then (
728 _P : subst([_x=_x1],_PP)
731 _P : subst([_x=_x1],_P)
737 dprint(5,"3. particular solution : ", _P)
741 dprint(1,"substituting original variables back"),
742 _solution[1]:subst([_x=_x1,_y=_y1], _solution[1]),
743 _solution[2]:subst([_x=_x1,_y=_y1], _solution[2]),
746 _solution[1]:trigsimp(trigreduce(_solution[1])),
747 _solution[2]:trigsimp(trigreduce(_solution[2])),
748 _solution[1]:ev(_solution[1],nouns),
749 _solution[2]:ev(_solution[2],nouns),
750 _solution[1]:trigsimp(trigreduce(_solution[1])),
751 _solution[2]:trigsimp(trigreduce(_solution[2])),
754 _C1 : constant_factors(factor(_solution[1])),
755 _C2 : constant_factors(factor(_solution[2])),
756 _solution[1] : ratsimp(_solution[1]/_C1), /* ratsimp necessary for kamke 2.286*/
757 _solution[2] : ratsimp(_solution[2]/_C2),
758 _solution: [_y1 = _solution[1]*%k1 + _solution[2]*%k2 + _P],
760 /*_solution:[subst([_x=_x1,_y=_y1], _y = _solution[1]*%k1 + _solution[2]*%k2 + _P)],*/
765 dprint(1,"11d:",string(_solution))
768 dprint(1,"solution:",string(_solution)),
769 /* also return the method used to solve the ode */
777 /*****************************************************************************************************/
778 step3(_n,_d,_theta,rhs1,_var):=block([_p,_listv,_i,_a,_a0,_a1,_a2,_a3,_a4,_a5,_pr,_sete,_soln,_trial,_w],
779 /*****************************************************************************************************/
780 /* ************************************************* */
781 /* ***** step 3 - ***** */
782 /* ************************************************* */
784 /*# form P in terms of undetermined coefficients a_i */
785 /*# P = a-{d}*x^{d} + a_{d-1}*x^{d-1} + ... + a_0*/
786 dprint(3,"_var=",_var),
787 dprint(3,"degree of polynomial = ",_d),
789 dprint(3,"_theta=",_theta),
790 dprint(3,"rhs1=",rhs1),
792 /* start with highest order*/
796 /* [a_d-1, a_d-2, ..., a_2, a_1, a_0] */
797 _listv : makelist(concat(_a,_d-1 -_i),_i,_d-1,0,-1),
799 _p : _p + sum(_listv[_i]*_var^(_i-1),_i,1,length(_listv)),
801 dprint(3,"_listv=",_listv),
803 /*# generate recursive relations P_i */
805 for _i from _n thru 0 step -1 do (
806 _pr[_i-1]: ratsimp(-diff(_pr[_i],_var) - _theta * _pr[_i] - (_n-_i)*(_i+1)*rhs1*_pr[_i+1])
809 dprint(3,"P-1 = ",_pr[-1]),
810 _trial:ratexpand(num(ratsimp(_pr[-1]))),
811 dprint(3,"_trial = ",_trial),
815 dprint(3,"low degree = ",lopow(_trial,_var)),
816 dprint(3,"high degree = ",hipow(_trial,_var)),
818 for _i from lopow(_trial,_var) thru hipow(_trial,_var) do (
819 lset:coeff(_trial,_var,_i),
820 dprint(3,"lset:",lset),
821 if (lset # []) then (
822 _sete:endcons(lset,_sete) /* create list as [sete,lset] */
825 dprint(5,"sete = ",_sete),
826 dprint(5,"_listv = ",_listv),
828 if(length(_listv)=0) then (
829 dprint(0,"No Liouvillian solutions exist"),
833 dprint(5,"a0 = ",_a0),
834 _soln:solve(_sete,_listv),
836 dprint(1,"_soln=",_soln),
837 /*if (_soln[1]=[] or length(_soln)=0 ) then (*/
838 if (length(_soln)=0 ) then (
839 dprint(1,"no solutions"),
843 /* for more than 1 element, the solution has two brackets: [[1,2,3,...]]*/
844 if (length(_listv)#1) then (
848 dprint(5,"determining ais"),
849 /* ai = _soln[d-_i] */
850 /* finally, we are getting some stuff here for kamke 2.129 */
851 for _i from _d-1 thru 0 step -1 do(
852 map(":",[concat(_a,_i)],[rhs(_soln[_d-_i])])
855 dprint(5,"a0 = ",_a0),
857 _trial : sum(_pr[_i]*_w^_i/(_n-_i)!,_i,0,_n),
860 dprint(3,"_trial = ",string(_trial)),
863 _solution:solve(ev(_trial),_w),
865 if _solution=[] then (
866 dprint(2,"w is empty: ",_w),
867 dprint(2,"NOTE: we still need to correct the return value!"),
868 return(exp(int(_w,_var)),_trial=0)
870 dprint(1,"we have a genuine _solution:",_solution),
872 _w:ratsimp(factor(_solution[1])), /* we need factor, it's like bringing it in normal form */
874 /* this call to ratexpand causes radcan to behave strangely (doesn't simplify certain things) later on */
875 /* dprint(0, "expanded = ",ratexpand(_w)),*/
877 sol:exp(integrate(rhs(_w),_var)),
878 dprint(3,"sol=",sol),
884 /*****************************************************************************************************/
885 /*****************************************************************************************************/
886 lcoeff(_t2,_var):=block([],
887 return(ratcoef(ratexpand(_t2),_var,hipow(ratexpand(_t2),_var)))
891 /*****************************************************************************************************/
892 /* ----- calculate the roots and expand the multiple roots ----- */
893 /*****************************************************************************************************/
894 rootz(_expr,_var):=block([_sol,_mult,_rlist2,_j,_i],
895 /* note: this has the potential to fail */
896 _sol:solve(_expr,_var),
897 /* get the multiplicities of the roots*/
898 _mult:multiplicities,
899 /* create a list of roots including multiple roots */
900 _rlist2: flatten( makelist( makelist(rhs(_sol[_j]),_i,1,_mult[_j]),_j,1,length(_sol))),
905 /*****************************************************************************************************/
906 /* ----- determine coefficient of a factor in a partial fraction expansion ----- */
907 /*****************************************************************************************************/
908 undetcoeff(_num,_rden,_var,_root,_m,_ex):=block([_k,_p,_er],
909 dprint(0, "undetcoeff:",_m-_ex,_rden,_num),
913 /* differentiate p with respect to _var k times */
914 _p : ratsimp(diff(_p,_var,_k)),
916 /* catch errors, it now returns empty when an error occurs (e.g. when we have non-polynomial expressions) */
917 _er: errcatch(subst(_root,_var,_p)/_k!),
923 dprint(0, "_er:",_er),
928 /*****************************************************************************************************/
929 /* ----- Transform second order ode y"=f(x)y'+g(x)y to normal form y"=F(x)y ----- */
930 /*****************************************************************************************************/
931 Normalform(_phi,_y,_x) := block([_f,_g,_df,_F],
932 _f : coeff(ratexpand(_phi),_y),
933 _g : coeff(ratexpand(_phi),'diff(_y,_x)),
935 _F: radcan(ratsimp(_g - _f*_f/4 - _df/2)),
940 /*****************************************************************************************************/
941 /* ----- transforms _expr from normal form solution to actual solution using rhs of the ode ----- */
942 /*****************************************************************************************************/
943 NormalSolutionToActualSolution(_expr,_phi) :=block([_f,_g,_df,_sol],
944 _f : coeff(ratexpand(_phi),_y),
945 _g : coeff(ratexpand(_phi),'diff(_y,_x)),
947 _sol : radcan(ratsimp(_expr*exp(integrate(_f,_x)/2))),
952 /*****************************************************************************************************/
953 /* ----- return the primary part of the polynomial -----*/
954 /*****************************************************************************************************/
955 primpart(_expr,_x):=block([],
956 return(_expr/content(_expr,_x))
960 /*****************************************************************************************************/
961 /* ----- Squar-free decomposition (Yun, On square free decomposition algorithms) ----- */
962 /*****************************************************************************************************/
963 sqfree(_expr,_var):=block([_i,_signp,_tc,_tlist1,_c,_d,_n,_cc,_re,_pp,_cont,_g,_PPP],
965 /* if polynomial is a constant, then return the constant */
966 if freeof(_var,_expr) then (
967 dprint(0, "polynomial is constant"),
971 _expr:ratexpand(_expr),
973 /* get the highest power of the expression, sorted by leading terms first, then get the operator */
974 _n:hipow(_expr,_var),
976 if atom(_expr) then (
980 _cc:coeff(_expr,_var,_n),
985 if op(_cc)="-" then (
994 _re: ratexpand(_expr)/_signp,
996 _pp: primpart(_re,_var)[1],
997 _tc:content(_re,_var)[1],
1003 _re:ratexpand(diff(_pp,_var)),
1005 [_PPP,_c,_d]:[_g,ratsimp(_pp/_g),ratsimp(_re/_g)],
1008 _re: ratexpand(_d-diff(_c,_var)),
1010 [_PPP,_c,_d]:[_g,ratsimp(_c/_g),ratsimp(_re/_g)],
1011 _tlist1 : append(_tlist1,[_PPP])
1014 return([_tlist1,_cont])
1018 /*****************************************************************************************************/
1019 /* ----- simple method of finding the constant factor in front of an equation ----- */
1020 /*****************************************************************************************************/
1021 constant_factors(_expr) := block([inflag:true],
1022 if not mapatom(_expr) and op(_expr)="*"
1023 then xreduce("*",listify(subset(setify(args(_expr)),'constantp)))
1029 /*****************************************************************************************************/
1030 /* ----- calculates the number of operators in the expression ----- */
1031 /* ----- this is a simple measure of complexity ----- */
1032 /*****************************************************************************************************/
1035 dprint(0, "expression:",expression),
1036 allOpsPriv (expression, [])
1039 allOpsPriv(expression, opList) :=
1040 block ( [x, args, newList],
1041 /* if expression is an atom, then we return opList */
1046 args: args(expression),
1047 /* add the operators to the opList */
1048 newList: cons(x, opList),
1050 /* also expand all the subexpressions and count the operators */
1051 newList: allOpsPriv(arg, newList),
1057 /*****************************************************************************************************/
1058 /* ----- dprint(flag,expr) is debuggingprint. It only prints expr when flag<DEBUGFLAG ----- */
1059 /*****************************************************************************************************/
1060 dprint(flag,[expr])::= if flag < DEBUGFLAG then buildq ([expr], print (splice (expr)));