Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/calculus/desolvers.py
8815 views
1
r"""
2
Solving ordinary differential equations
3
4
This file contains functions useful for solving differential equations
5
which occur commonly in a 1st semester differential equations
6
course. For another numerical solver see :meth:`ode_solver` function
7
and optional package Octave.
8
9
Commands:
10
11
- ``desolve`` - Computes the "general solution" to a 1st or 2nd order
12
ODE via Maxima.
13
14
- ``desolve_laplace`` - Solves an ODE using laplace transforms via
15
Maxima. Initials conditions are optional.
16
17
- ``desolve_system`` - Solves any size system of 1st order odes using
18
Maxima. Initials conditions are optional.
19
20
- ``desolve_rk4`` - Solves numerically IVP for one first order
21
equation, returns list of points or plot
22
23
- ``desolve_system_rk4`` - Solves numerically IVP for system of first
24
order equations, returns list of points
25
26
- ``desolve_odeint`` - Solves numerically a system of first-order ordinary
27
differential equations using ``odeint`` from scipy.integrate module.
28
29
- ``eulers_method`` - Approximate solution to a 1st order DE,
30
presented as a table.
31
32
- ``eulers_method_2x2`` - Approximate solution to a 1st order system
33
of DEs, presented as a table.
34
35
- ``eulers_method_2x2_plot`` - Plots the sequence of points obtained
36
from Euler's method.
37
38
AUTHORS:
39
40
- David Joyner (3-2006) - Initial version of functions
41
42
- Marshall Hampton (7-2007) - Creation of Python module and testing
43
44
- Robert Bradshaw (10-2008) - Some interface cleanup.
45
46
- Robert Marik (10-2009) - Some bugfixes and enhancements
47
48
"""
49
50
##########################################################################
51
# Copyright (C) 2006 David Joyner <[email protected]>, Marshall Hampton,
52
# Robert Marik <[email protected]>
53
#
54
# Distributed under the terms of the GNU General Public License (GPL):
55
#
56
# http://www.gnu.org/licenses/
57
##########################################################################
58
59
from sage.interfaces.maxima import Maxima
60
from sage.plot.all import line
61
from sage.symbolic.expression import is_SymbolicEquation
62
from sage.symbolic.ring import is_SymbolicVariable
63
from sage.calculus.functional import diff
64
from sage.misc.decorators import rename_keyword
65
66
maxima = Maxima()
67
68
def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False):
69
r"""
70
Solves a 1st or 2nd order linear ODE via maxima. Including IVP and BVP.
71
72
*Use* ``desolve? <tab>`` *if the output in truncated in notebook.*
73
74
INPUT:
75
76
- ``de`` - an expression or equation representing the ODE
77
78
- ``dvar`` - the dependent variable (hereafter called ``y``)
79
80
- ``ics`` - (optional) the initial or boundary conditions
81
82
- for a first-order equation, specify the initial ``x`` and ``y``
83
84
- for a second-order equation, specify the initial ``x``, ``y``,
85
and ``dy/dx``, i.e. write `[x_0, y(x_0), y'(x_0)]`
86
87
- for a second-order boundary solution, specify initial and
88
final ``x`` and ``y`` boundary conditions, i.e. write `[x_0, y(x_0), x_1, y(x_1)]`.
89
90
- gives an error if the solution is not SymbolicEquation (as happens for
91
example for Clairaut equation)
92
93
- ``ivar`` - (optional) the independent variable (hereafter called
94
x), which must be specified if there is more than one
95
independent variable in the equation.
96
97
- ``show_method`` - (optional) if true, then Sage returns pair
98
``[solution, method]``, where method is the string describing
99
method which has been used to get solution (Maxima uses the
100
following order for first order equations: linear, separable,
101
exact (including exact with integrating factor), homogeneous,
102
bernoulli, generalized homogeneous) - use carefully in class,
103
see below for the example of the equation which is separable but
104
this property is not recognized by Maxima and equation is solved
105
as exact.
106
107
- ``contrib_ode`` - (optional) if true, desolve allows to solve
108
clairaut, lagrange, riccati and some other equations. May take
109
a long time and thus turned off by default. Initial conditions
110
can be used only if the result is one SymbolicEquation (does not
111
contain singular solution, for example)
112
113
OUTPUT:
114
115
In most cases returns SymbolicEquation which defines the solution
116
implicitly. If the result is in the form y(x)=... (happens for
117
linear eqs.), returns the right-hand side only. The possible
118
constant solutions of separable ODE's are omitted.
119
120
121
EXAMPLES::
122
123
sage: x = var('x')
124
sage: y = function('y', x)
125
sage: desolve(diff(y,x) + y - 1, y)
126
(c + e^x)*e^(-x)
127
128
::
129
130
sage: f = desolve(diff(y,x) + y - 1, y, ics=[10,2]); f
131
(e^10 + e^x)*e^(-x)
132
133
::
134
135
sage: plot(f)
136
137
We can also solve second-order differential equations.::
138
139
sage: x = var('x')
140
sage: y = function('y', x)
141
sage: de = diff(y,x,2) - y == x
142
sage: desolve(de, y)
143
k2*e^(-x) + k1*e^x - x
144
145
146
::
147
148
sage: f = desolve(de, y, [10,2,1]); f
149
-x + 7*e^(x - 10) + 5*e^(-x + 10)
150
151
::
152
153
sage: f(x=10)
154
2
155
156
::
157
158
sage: diff(f,x)(x=10)
159
1
160
161
::
162
163
sage: de = diff(y,x,2) + y == 0
164
sage: desolve(de, y)
165
k2*cos(x) + k1*sin(x)
166
167
::
168
169
sage: desolve(de, y, [0,1,pi/2,4])
170
cos(x) + 4*sin(x)
171
172
::
173
174
sage: desolve(y*diff(y,x)+sin(x)==0,y)
175
-1/2*y(x)^2 == c - cos(x)
176
177
Clairot equation: general and singular solutions::
178
179
sage: desolve(diff(y,x)^2+x*diff(y,x)-y==0,y,contrib_ode=True,show_method=True)
180
[[y(x) == c^2 + c*x, y(x) == -1/4*x^2], 'clairault']
181
182
For equations involving more variables we specify independent variable::
183
184
sage: a,b,c,n=var('a b c n')
185
sage: desolve(x^2*diff(y,x)==a+b*x^n+c*x^2*y^2,y,ivar=x,contrib_ode=True)
186
[[y(x) == 0, (b*x^(n - 2) + a/x^2)*c^2*u == 0]]
187
188
::
189
190
sage: desolve(x^2*diff(y,x)==a+b*x^n+c*x^2*y^2,y,ivar=x,contrib_ode=True,show_method=True)
191
[[[y(x) == 0, (b*x^(n - 2) + a/x^2)*c^2*u == 0]], 'riccati']
192
193
194
Higher orded, not involving independent variable::
195
196
sage: desolve(diff(y,x,2)+y*(diff(y,x,1))^3==0,y).expand()
197
1/6*y(x)^3 + k1*y(x) == k2 + x
198
199
::
200
201
sage: desolve(diff(y,x,2)+y*(diff(y,x,1))^3==0,y,[0,1,1,3]).expand()
202
1/6*y(x)^3 - 5/3*y(x) == x - 3/2
203
204
::
205
206
sage: desolve(diff(y,x,2)+y*(diff(y,x,1))^3==0,y,[0,1,1,3],show_method=True)
207
[1/6*y(x)^3 - 5/3*y(x) == x - 3/2, 'freeofx']
208
209
Separable equations - Sage returns solution in implicit form::
210
211
sage: desolve(diff(y,x)*sin(y) == cos(x),y)
212
-cos(y(x)) == c + sin(x)
213
214
::
215
216
sage: desolve(diff(y,x)*sin(y) == cos(x),y,show_method=True)
217
[-cos(y(x)) == c + sin(x), 'separable']
218
219
::
220
221
sage: desolve(diff(y,x)*sin(y) == cos(x),y,[pi/2,1])
222
-cos(y(x)) == -cos(1) + sin(x) - 1
223
224
Linear equation - Sage returns the expression on the right hand side only::
225
226
sage: desolve(diff(y,x)+(y) == cos(x),y)
227
1/2*((cos(x) + sin(x))*e^x + 2*c)*e^(-x)
228
229
::
230
231
sage: desolve(diff(y,x)+(y) == cos(x),y,show_method=True)
232
[1/2*((cos(x) + sin(x))*e^x + 2*c)*e^(-x), 'linear']
233
234
::
235
236
sage: desolve(diff(y,x)+(y) == cos(x),y,[0,1])
237
1/2*(cos(x)*e^x + e^x*sin(x) + 1)*e^(-x)
238
239
This ODE with separated variables is solved as
240
exact. Explanation - factor does not split `e^{x-y}` in Maxima
241
into `e^{x}e^{y}`::
242
243
sage: desolve(diff(y,x)==exp(x-y),y,show_method=True)
244
[-e^x + e^y(x) == c, 'exact']
245
246
You can solve Bessel equations. You can also use initial
247
conditions, but you cannot put (sometimes desired) initial
248
condition at x=0, since this point is singlar point of the
249
equation. Anyway, if the solution should be bounded at x=0, then
250
k2=0.::
251
252
sage: desolve(x^2*diff(y,x,x)+x*diff(y,x)+(x^2-4)*y==0,y)
253
k1*bessel_J(2, x) + k2*bessel_Y(2, x)
254
255
Difficult ODE produces error::
256
257
sage: desolve(sqrt(y)*diff(y,x)+e^(y)+cos(x)-sin(x+y)==0,y) # not tested
258
Traceback (click to the left for traceback)
259
...
260
NotImplementedError, "Maxima was unable to solve this ODE. Consider to set option contrib_ode to True."
261
262
Difficult ODE produces error - moreover, takes a long time ::
263
264
sage: desolve(sqrt(y)*diff(y,x)+e^(y)+cos(x)-sin(x+y)==0,y,contrib_ode=True) # not tested
265
266
Some more types od ODE's::
267
268
sage: desolve(x*diff(y,x)^2-(1+x*y)*diff(y,x)+y==0,y,contrib_ode=True,show_method=True)
269
[[y(x) == c + log(x), y(x) == c*e^x], 'factor']
270
271
::
272
273
sage: desolve(diff(y,x)==(x+y)^2,y,contrib_ode=True,show_method=True)
274
[[[x == c - arctan(sqrt(t)), y(x) == -x - sqrt(t)], [x == c + arctan(sqrt(t)), y(x) == -x + sqrt(t)]], 'lagrange']
275
276
These two examples produce error (as expected, Maxima 5.18 cannot
277
solve equations from initial conditions). Current Maxima 5.18
278
returns false answer in this case!::
279
280
sage: desolve(diff(y,x,2)+y*(diff(y,x,1))^3==0,y,[0,1,2]).expand() # not tested
281
Traceback (click to the left for traceback)
282
...
283
NotImplementedError, "Maxima was unable to solve this ODE. Consider to set option contrib_ode to True."
284
285
::
286
287
sage: desolve(diff(y,x,2)+y*(diff(y,x,1))^3==0,y,[0,1,2],show_method=True) # not tested
288
Traceback (click to the left for traceback)
289
...
290
NotImplementedError, "Maxima was unable to solve this ODE. Consider to set option contrib_ode to True."
291
292
Second order linear ODE::
293
294
sage: desolve(diff(y,x,2)+2*diff(y,x)+y == cos(x),y)
295
(k2*x + k1)*e^(-x) + 1/2*sin(x)
296
297
::
298
299
sage: desolve(diff(y,x,2)+2*diff(y,x)+y == cos(x),y,show_method=True)
300
[(k2*x + k1)*e^(-x) + 1/2*sin(x), 'variationofparameters']
301
302
::
303
304
sage: desolve(diff(y,x,2)+2*diff(y,x)+y == cos(x),y,[0,3,1])
305
1/2*(7*x + 6)*e^(-x) + 1/2*sin(x)
306
307
::
308
309
sage: desolve(diff(y,x,2)+2*diff(y,x)+y == cos(x),y,[0,3,1],show_method=True)
310
[1/2*(7*x + 6)*e^(-x) + 1/2*sin(x), 'variationofparameters']
311
312
::
313
314
sage: desolve(diff(y,x,2)+2*diff(y,x)+y == cos(x),y,[0,3,pi/2,2])
315
3*(x*(e^(1/2*pi) - 2)/pi + 1)*e^(-x) + 1/2*sin(x)
316
317
::
318
319
sage: desolve(diff(y,x,2)+2*diff(y,x)+y == cos(x),y,[0,3,pi/2,2],show_method=True)
320
[3*(x*(e^(1/2*pi) - 2)/pi + 1)*e^(-x) + 1/2*sin(x), 'variationofparameters']
321
322
::
323
324
sage: desolve(diff(y,x,2)+2*diff(y,x)+y == 0,y)
325
(k2*x + k1)*e^(-x)
326
327
::
328
329
sage: desolve(diff(y,x,2)+2*diff(y,x)+y == 0,y,show_method=True)
330
[(k2*x + k1)*e^(-x), 'constcoeff']
331
332
::
333
334
sage: desolve(diff(y,x,2)+2*diff(y,x)+y == 0,y,[0,3,1])
335
(4*x + 3)*e^(-x)
336
337
::
338
339
sage: desolve(diff(y,x,2)+2*diff(y,x)+y == 0,y,[0,3,1],show_method=True)
340
[(4*x + 3)*e^(-x), 'constcoeff']
341
342
::
343
344
sage: desolve(diff(y,x,2)+2*diff(y,x)+y == 0,y,[0,3,pi/2,2])
345
(2*x*(2*e^(1/2*pi) - 3)/pi + 3)*e^(-x)
346
347
::
348
349
sage: desolve(diff(y,x,2)+2*diff(y,x)+y == 0,y,[0,3,pi/2,2],show_method=True)
350
[(2*x*(2*e^(1/2*pi) - 3)/pi + 3)*e^(-x), 'constcoeff']
351
352
TESTS:
353
354
Trac #9961 fixed (allow assumptions on the dependent variable in desolve)::
355
356
sage: y=function('y',x); assume(x>0); assume(y>0)
357
sage: sage.calculus.calculus.maxima('domain:real') # needed since Maxima 5.26.0 to get the answer as below
358
real
359
sage: desolve(x*diff(y,x)-x*sqrt(y^2+x^2)-y == 0, y, contrib_ode=True)
360
[x - arcsinh(y(x)/x) == c]
361
362
Trac #10682 updated Maxima to 5.26, and it started to show a different
363
solution in the complex domain for the ODE above::
364
365
sage: sage.calculus.calculus.maxima('domain:complex') # back to the default complex domain
366
complex
367
sage: desolve(x*diff(y,x)-x*sqrt(y^2+x^2)-y == 0, y, contrib_ode=True)
368
[1/2*(2*x^2*sqrt(x^(-2)) - 2*x*sqrt(x^(-2))*arcsinh(y(x)/sqrt(x^2)) -
369
2*x*sqrt(x^(-2))*arcsinh(y(x)^2/(x*sqrt(y(x)^2))) +
370
log(4*(2*x^2*sqrt((x^2*y(x)^2 + y(x)^4)/x^2)*sqrt(x^(-2)) + x^2 +
371
2*y(x)^2)/x^2))/(x*sqrt(x^(-2))) == c]
372
373
Trac #6479 fixed::
374
375
sage: x = var('x')
376
sage: y = function('y', x)
377
sage: desolve( diff(y,x,x) == 0, y, [0,0,1])
378
x
379
380
::
381
382
sage: desolve( diff(y,x,x) == 0, y, [0,1,1])
383
x + 1
384
385
Trac #9835 fixed::
386
387
sage: x = var('x')
388
sage: y = function('y', x)
389
sage: desolve(diff(y,x,2)+y*(1-y^2)==0,y,[0,-1,1,1])
390
Traceback (most recent call last):
391
...
392
NotImplementedError: Unable to use initial condition for this equation (freeofx).
393
394
Trac #8931 fixed::
395
396
sage: x=var('x'); f=function('f',x); k=var('k'); assume(k>0)
397
sage: desolve(diff(f,x,2)/f==k,f,ivar=x)
398
k1*e^(sqrt(k)*x) + k2*e^(-sqrt(k)*x)
399
400
401
AUTHORS:
402
403
- David Joyner (1-2006)
404
405
- Robert Bradshaw (10-2008)
406
407
- Robert Marik (10-2009)
408
409
"""
410
if is_SymbolicEquation(de):
411
de = de.lhs() - de.rhs()
412
if is_SymbolicVariable(dvar):
413
raise ValueError("You have to declare dependent variable as a function, eg. y=function('y',x)")
414
# for backwards compatibility
415
if isinstance(dvar, list):
416
dvar, ivar = dvar
417
elif ivar is None:
418
ivars = de.variables()
419
ivars = [t for t in ivars if t is not dvar]
420
if len(ivars) != 1:
421
raise ValueError("Unable to determine independent variable, please specify.")
422
ivar = ivars[0]
423
def sanitize_var(exprs):
424
return exprs.replace("'"+dvar_str+"("+ivar_str+")",dvar_str)
425
de00 = de._maxima_()
426
P = de00.parent()
427
dvar_str=P(dvar.operator()).str()
428
ivar_str=P(ivar).str()
429
de00 = de00.str()
430
de0 = sanitize_var(de00)
431
ode_solver="ode2"
432
cmd="(TEMP:%s(%s,%s,%s), if TEMP=false then TEMP else substitute(%s=%s(%s),TEMP))"%(ode_solver,de0,dvar_str,ivar_str,dvar_str,dvar_str,ivar_str)
433
# we produce string like this
434
# ode2('diff(y,x,2)+2*'diff(y,x,1)+y-cos(x),y(x),x)
435
soln = P(cmd)
436
437
if str(soln).strip() == 'false':
438
if contrib_ode:
439
ode_solver="contrib_ode"
440
P("load('contrib_ode)")
441
cmd="(TEMP:%s(%s,%s,%s), if TEMP=false then TEMP else substitute(%s=%s(%s),TEMP))"%(ode_solver,de0,dvar_str,ivar_str,dvar_str,dvar_str,ivar_str)
442
# we produce string like this
443
# (TEMP:contrib_ode(x*('diff(y,x,1))^2-(x*y+1)*'diff(y,x,1)+y,y,x), if TEMP=false then TEMP else substitute(y=y(x),TEMP))
444
soln = P(cmd)
445
if str(soln).strip() == 'false':
446
raise NotImplementedError("Maxima was unable to solve this ODE.")
447
else:
448
raise NotImplementedError("Maxima was unable to solve this ODE. Consider to set option contrib_ode to True.")
449
450
if show_method:
451
maxima_method=P("method")
452
453
if (ics is not None):
454
if not is_SymbolicEquation(soln.sage()):
455
if not show_method:
456
maxima_method=P("method")
457
raise NotImplementedError("Unable to use initial condition for this equation (%s)."%(str(maxima_method).strip()))
458
if len(ics) == 2:
459
tempic=(ivar==ics[0])._maxima_().str()
460
tempic=tempic+","+(dvar==ics[1])._maxima_().str()
461
cmd="(TEMP:ic1(%s(%s,%s,%s),%s),substitute(%s=%s(%s),TEMP))"%(ode_solver,de00,dvar_str,ivar_str,tempic,dvar_str,dvar_str,ivar_str)
462
cmd=sanitize_var(cmd)
463
# we produce string like this
464
# (TEMP:ic2(ode2('diff(y,x,2)+2*'diff(y,x,1)+y-cos(x),y,x),x=0,y=3,'diff(y,x)=1),substitute(y=y(x),TEMP))
465
soln=P(cmd)
466
if len(ics) == 3:
467
#fixed ic2 command from Maxima - we have to ensure that %k1, %k2 do not depend on variables, should be removed when fixed in Maxima
468
P("ic2_sage(soln,xa,ya,dya):=block([programmode:true,backsubst:true,singsolve:true,temp,%k2,%k1,TEMP_k], \
469
noteqn(xa), noteqn(ya), noteqn(dya), boundtest('%k1,%k1), boundtest('%k2,%k2), \
470
temp: lhs(soln) - rhs(soln), \
471
TEMP_k:solve([subst([xa,ya],soln), subst([dya,xa], lhs(dya)=-subst(0,lhs(dya),diff(temp,lhs(xa)))/diff(temp,lhs(ya)))],[%k1,%k2]), \
472
if not freeof(lhs(ya),TEMP_k) or not freeof(lhs(xa),TEMP_k) then return (false), \
473
temp: maplist(lambda([zz], subst(zz,soln)), TEMP_k), \
474
if length(temp)=1 then return(first(temp)) else return(temp))")
475
tempic=P(ivar==ics[0]).str()
476
tempic=tempic+","+P(dvar==ics[1]).str()
477
tempic=tempic+",'diff("+dvar_str+","+ivar_str+")="+P(ics[2]).str()
478
cmd="(TEMP:ic2_sage(%s(%s,%s,%s),%s),substitute(%s=%s(%s),TEMP))"%(ode_solver,de00,dvar_str,ivar_str,tempic,dvar_str,dvar_str,ivar_str)
479
cmd=sanitize_var(cmd)
480
# we produce string like this
481
# (TEMP:ic2(ode2('diff(y,x,2)+2*'diff(y,x,1)+y-cos(x),y,x),x=0,y=3,'diff(y,x)=1),substitute(y=y(x),TEMP))
482
soln=P(cmd)
483
if str(soln).strip() == 'false':
484
raise NotImplementedError("Maxima was unable to solve this IVP. Remove the initial condition to get the general solution.")
485
if len(ics) == 4:
486
#fixed bc2 command from Maxima - we have to ensure that %k1, %k2 do not depend on variables, should be removed when fixed in Maxima
487
P("bc2_sage(soln,xa,ya,xb,yb):=block([programmode:true,backsubst:true,singsolve:true,temp,%k1,%k2,TEMP_k], \
488
noteqn(xa), noteqn(ya), noteqn(xb), noteqn(yb), boundtest('%k1,%k1), boundtest('%k2,%k2), \
489
TEMP_k:solve([subst([xa,ya],soln), subst([xb,yb],soln)], [%k1,%k2]), \
490
if not freeof(lhs(ya),TEMP_k) or not freeof(lhs(xa),TEMP_k) then return (false), \
491
temp: maplist(lambda([zz], subst(zz,soln)),TEMP_k), \
492
if length(temp)=1 then return(first(temp)) else return(temp))")
493
cmd="bc2_sage(%s(%s,%s,%s),%s,%s=%s,%s,%s=%s)"%(ode_solver,de00,dvar_str,ivar_str,P(ivar==ics[0]).str(),dvar_str,P(ics[1]).str(),P(ivar==ics[2]).str(),dvar_str,P(ics[3]).str())
494
cmd="(TEMP:%s,substitute(%s=%s(%s),TEMP))"%(cmd,dvar_str,dvar_str,ivar_str)
495
cmd=sanitize_var(cmd)
496
# we produce string like this
497
# (TEMP:bc2(ode2('diff(y,x,2)+2*'diff(y,x,1)+y-cos(x),y,x),x=0,y=3,x=%pi/2,y=2),substitute(y=y(x),TEMP))
498
soln=P(cmd)
499
if str(soln).strip() == 'false':
500
raise NotImplementedError("Maxima was unable to solve this BVP. Remove the initial condition to get the general solution.")
501
502
soln=soln.sage()
503
if is_SymbolicEquation(soln) and soln.lhs() == dvar:
504
# Remark: Here we do not check that the right hand side does not depend on dvar.
505
# This probably will not hapen for soutions obtained via ode2, anyway.
506
soln = soln.rhs()
507
if show_method:
508
return [soln,maxima_method.str()]
509
else:
510
return soln
511
512
513
#def desolve_laplace2(de,vars,ics=None):
514
## """
515
## Solves an ODE using laplace transforms via maxima. Initial conditions
516
## are optional.
517
518
## INPUT:
519
## de -- a lambda expression representing the ODE
520
## (eg, de = "diff(f(x),x,2)=diff(f(x),x)+sin(x)")
521
## vars -- a list of strings representing the variables
522
## (eg, vars = ["x","f"], if x is the independent
523
## variable and f is the dependent variable)
524
## ics -- a list of numbers representing initial conditions,
525
## with symbols allowed which are represented by strings
526
## (eg, f(0)=1, f'(0)=2 is ics = [0,1,2])
527
528
## EXAMPLES:
529
## sage: from sage.calculus.desolvers import desolve_laplace
530
## sage: x = var('x')
531
## sage: f = function('f', x)
532
## sage: de = lambda y: diff(y,x,x) - 2*diff(y,x) + y
533
## sage: desolve_laplace(de(f(x)),[f,x])
534
## #x*%e^x*(?%at('diff('f(x),x,1),x=0))-'f(0)*x*%e^x+'f(0)*%e^x
535
## sage: desolve_laplace(de(f(x)),[f,x],[0,1,2]) ## IC option does not work
536
## #x*%e^x*(?%at('diff('f(x),x,1),x=0))-'f(0)*x*%e^x+'f(0)*%e^x
537
538
## AUTHOR: David Joyner (1st version 1-2006, 8-2007)
539
## """
540
# ######## this method seems reasonable but doesn't work for some reason
541
# name0 = vars[0]._repr_()[0:(len(vars[0]._repr_())-2-len(str(vars[1])))]
542
# name1 = str(vars[1])
543
# #maxima("de:"+de+";")
544
# if ics!=None:
545
# ic0 = maxima("ic:"+str(vars[1])+"="+str(ics[0]))
546
# d = len(ics)
547
# for i in range(d-1):
548
# maxima(vars[0](vars[1])).diff(vars[1],i).atvalue(ic0,ics[i+1])
549
# de0 = de._maxima_()
550
# #cmd = "desolve("+de+","+vars[1]+"("+vars[0]+"));"
551
# #return maxima.eval(cmd)
552
# return de0.desolve(vars[0]).rhs()
553
554
555
def desolve_laplace(de, dvar, ics=None, ivar=None):
556
"""
557
Solves an ODE using laplace transforms. Initials conditions are optional.
558
559
INPUT:
560
561
- ``de`` - a lambda expression representing the ODE (eg, de =
562
diff(y,x,2) == diff(y,x)+sin(x))
563
564
- ``dvar`` - the dependent variable (eg y)
565
566
- ``ivar`` - (optional) the independent variable (hereafter called
567
x), which must be specified if there is more than one
568
independent variable in the equation.
569
570
- ``ics`` - a list of numbers representing initial conditions, (eg,
571
f(0)=1, f'(0)=2 is ics = [0,1,2])
572
573
OUTPUT:
574
575
Solution of the ODE as symbolic expression
576
577
EXAMPLES::
578
579
sage: u=function('u',x)
580
sage: eq = diff(u,x) - exp(-x) - u == 0
581
sage: desolve_laplace(eq,u)
582
1/2*(2*u(0) + 1)*e^x - 1/2*e^(-x)
583
584
We can use initial conditions::
585
586
sage: desolve_laplace(eq,u,ics=[0,3])
587
-1/2*e^(-x) + 7/2*e^x
588
589
The initial conditions do not persist in the system (as they persisted
590
in previous versions)::
591
592
sage: desolve_laplace(eq,u)
593
1/2*(2*u(0) + 1)*e^x - 1/2*e^(-x)
594
595
::
596
597
sage: f=function('f', x)
598
sage: eq = diff(f,x) + f == 0
599
sage: desolve_laplace(eq,f,[0,1])
600
e^(-x)
601
602
::
603
604
sage: x = var('x')
605
sage: f = function('f', x)
606
sage: de = diff(f,x,x) - 2*diff(f,x) + f
607
sage: desolve_laplace(de,f)
608
-x*e^x*f(0) + x*e^x*D[0](f)(0) + e^x*f(0)
609
610
::
611
612
sage: desolve_laplace(de,f,ics=[0,1,2])
613
x*e^x + e^x
614
615
TESTS:
616
617
Trac #4839 fixed::
618
619
sage: t=var('t')
620
sage: x=function('x', t)
621
sage: soln=desolve_laplace(diff(x,t)+x==1, x, ics=[0,2])
622
sage: soln
623
e^(-t) + 1
624
625
::
626
627
sage: soln(t=3)
628
e^(-3) + 1
629
630
AUTHORS:
631
632
- David Joyner (1-2006,8-2007)
633
634
- Robert Marik (10-2009)
635
"""
636
#This is the original code from David Joyner (inputs and outputs strings)
637
#maxima("de:"+de._repr_()+"=0;")
638
#if ics!=None:
639
# d = len(ics)
640
# for i in range(0,d-1):
641
# ic = "atvalue(diff("+vars[1]+"("+vars[0]+"),"+str(vars[0])+","+str(i)+"),"+str(vars[0])+"="+str(ics[0])+","+str(ics[1+i])+")"
642
# maxima(ic)
643
#
644
#cmd = "desolve("+de._repr_()+","+vars[1]+"("+vars[0]+"));"
645
#return maxima(cmd).rhs()._maxima_init_()
646
647
## verbatim copy from desolve - begin
648
if is_SymbolicEquation(de):
649
de = de.lhs() - de.rhs()
650
if is_SymbolicVariable(dvar):
651
raise ValueError("You have to declare dependent variable as a function, eg. y=function('y',x)")
652
# for backwards compatibility
653
if isinstance(dvar, list):
654
dvar, ivar = dvar
655
elif ivar is None:
656
ivars = de.variables()
657
ivars = [t for t in ivars if t != dvar]
658
if len(ivars) != 1:
659
raise ValueError("Unable to determine independent variable, please specify.")
660
ivar = ivars[0]
661
## verbatim copy from desolve - end
662
663
def sanitize_var(exprs): # 'y(x) -> y(x)
664
return exprs.replace("'"+str(dvar),str(dvar))
665
de0=de._maxima_()
666
P = de0.parent()
667
cmd = sanitize_var("desolve("+de0.str()+","+str(dvar)+")")
668
soln=P(cmd).rhs()
669
if str(soln).strip() == 'false':
670
raise NotImplementedError("Maxima was unable to solve this ODE.")
671
soln=soln.sage()
672
if ics!=None:
673
d = len(ics)
674
for i in range(0,d-1):
675
soln=eval('soln.substitute(diff(dvar,ivar,i)('+str(ivar)+'=ics[0])==ics[i+1])')
676
return soln
677
678
679
def desolve_system(des, vars, ics=None, ivar=None):
680
"""
681
Solves any size system of 1st order ODE's. Initials conditions are optional.
682
683
Onedimensional systems are passed to :meth:`desolve_laplace`.
684
685
INPUT:
686
687
- ``des`` - list of ODEs
688
689
- ``vars`` - list of dependent variables
690
691
- ``ics`` - (optional) list of initial values for ivar and vars
692
693
- ``ivar`` - (optional) the independent variable, which must be
694
specified if there is more than one independent variable in the
695
equation.
696
697
EXAMPLES::
698
699
sage: t = var('t')
700
sage: x = function('x', t)
701
sage: y = function('y', t)
702
sage: de1 = diff(x,t) + y - 1 == 0
703
sage: de2 = diff(y,t) - x + 1 == 0
704
sage: desolve_system([de1, de2], [x,y])
705
[x(t) == (x(0) - 1)*cos(t) - (y(0) - 1)*sin(t) + 1,
706
y(t) == (y(0) - 1)*cos(t) + (x(0) - 1)*sin(t) + 1]
707
708
Now we give some initial conditions::
709
710
sage: sol = desolve_system([de1, de2], [x,y], ics=[0,1,2]); sol
711
[x(t) == -sin(t) + 1, y(t) == cos(t) + 1]
712
713
::
714
715
sage: solnx, solny = sol[0].rhs(), sol[1].rhs()
716
sage: plot([solnx,solny],(0,1)) # not tested
717
sage: parametric_plot((solnx,solny),(0,1)) # not tested
718
719
TESTS:
720
721
Trac #9823 fixed::
722
723
sage: t = var('t')
724
sage: x = function('x', t)
725
sage: de1 = diff(x,t) + 1 == 0
726
sage: desolve_system([de1], [x])
727
-t + x(0)
728
729
AUTHORS:
730
731
- Robert Bradshaw (10-2008)
732
"""
733
if len(des)==1:
734
return desolve_laplace(des[0], vars[0], ics=ics, ivar=ivar)
735
ivars = set([])
736
for i, de in enumerate(des):
737
if not is_SymbolicEquation(de):
738
des[i] = de == 0
739
ivars = ivars.union(set(de.variables()))
740
if ivar is None:
741
ivars = ivars - set(vars)
742
if len(ivars) != 1:
743
raise ValueError("Unable to determine independent variable, please specify.")
744
ivar = list(ivars)[0]
745
dvars = [v._maxima_() for v in vars]
746
if ics is not None:
747
ivar_ic = ics[0]
748
for dvar, ic in zip(dvars, ics[1:]):
749
dvar.atvalue(ivar==ivar_ic, ic)
750
soln = dvars[0].parent().desolve(des, dvars)
751
if str(soln).strip() == 'false':
752
raise NotImplementedError("Maxima was unable to solve this system.")
753
soln = list(soln)
754
for i, sol in enumerate(soln):
755
soln[i] = sol.sage()
756
if ics is not None:
757
ivar_ic = ics[0]
758
for dvar, ic in zip(dvars, ics[:1]):
759
dvar.atvalue(ivar==ivar_ic, dvar)
760
return soln
761
762
763
def desolve_system_strings(des,vars,ics=None):
764
r"""
765
Solves any size system of 1st order ODE's. Initials conditions are optional.
766
767
This function is obsolete, use desolve_system.
768
769
INPUT:
770
771
- ``de`` - a list of strings representing the ODEs in maxima
772
notation (eg, de = "diff(f(x),x,2)=diff(f(x),x)+sin(x)")
773
774
- ``vars`` - a list of strings representing the variables (eg,
775
vars = ["s","x","y"], where s is the independent variable and
776
x,y the dependent variables)
777
778
- ``ics`` - a list of numbers representing initial conditions
779
(eg, x(0)=1, y(0)=2 is ics = [0,1,2])
780
781
WARNING:
782
783
The given ics sets the initial values of the dependent vars in
784
maxima, so subsequent ODEs involving these variables will have
785
these initial conditions automatically imposed.
786
787
EXAMPLES::
788
789
sage: from sage.calculus.desolvers import desolve_system_strings
790
sage: s = var('s')
791
sage: function('x', s)
792
x(s)
793
794
::
795
796
sage: function('y', s)
797
y(s)
798
799
::
800
801
sage: de1 = lambda z: diff(z[0],s) + z[1] - 1
802
sage: de2 = lambda z: diff(z[1],s) - z[0] + 1
803
sage: des = [de1([x(s),y(s)]),de2([x(s),y(s)])]
804
sage: vars = ["s","x","y"]
805
sage: desolve_system_strings(des,vars)
806
["(1-'y(0))*sin(s)+('x(0)-1)*cos(s)+1", "('x(0)-1)*sin(s)+('y(0)-1)*cos(s)+1"]
807
808
::
809
810
sage: ics = [0,1,-1]
811
sage: soln = desolve_system_strings(des,vars,ics); soln
812
['2*sin(s)+1', '1-2*cos(s)']
813
814
::
815
816
sage: solnx, solny = map(SR, soln)
817
sage: RR(solnx(s=3))
818
1.28224001611973
819
820
::
821
822
sage: P1 = plot([solnx,solny],(0,1))
823
sage: P2 = parametric_plot((solnx,solny),(0,1))
824
825
Now type show(P1), show(P2) to view these.
826
827
828
AUTHORS:
829
830
- David Joyner (3-2006, 8-2007)
831
"""
832
d = len(des)
833
dess = [de._maxima_init_() + "=0" for de in des]
834
for i in range(d):
835
cmd="de:" + dess[int(i)] + ";"
836
maxima.eval(cmd)
837
desstr = "[" + ",".join(dess) + "]"
838
d = len(vars)
839
varss = list("'" + vars[i] + "(" + vars[0] + ")" for i in range(1,d))
840
varstr = "[" + ",".join(varss) + "]"
841
if ics is not None:
842
#d = len(ics) ## must be same as len(des)
843
for i in range(1,d):
844
ic = "atvalue('" + vars[i] + "("+vars[0] + ")," + str(vars[0]) + "=" + str(ics[0]) + "," + str(ics[i]) + ")"
845
maxima.eval(ic)
846
cmd = "desolve(" + desstr + "," + varstr + ");"
847
soln = maxima(cmd)
848
return [f.rhs()._maxima_init_() for f in soln]
849
850
@rename_keyword(deprecation=6094, method="algorithm")
851
def eulers_method(f,x0,y0,h,x1,algorithm="table"):
852
r"""
853
This implements Euler's method for finding numerically the
854
solution of the 1st order ODE ``y' = f(x,y)``, ``y(a)=c``. The "x"
855
column of the table increments from ``x0`` to ``x1`` by ``h`` (so
856
``(x1-x0)/h`` must be an integer). In the "y" column, the new
857
y-value equals the old y-value plus the corresponding entry in the
858
last column.
859
860
*For pedagogical purposes only.*
861
862
EXAMPLES::
863
864
sage: from sage.calculus.desolvers import eulers_method
865
sage: x,y = PolynomialRing(QQ,2,"xy").gens()
866
sage: eulers_method(5*x+y-5,0,1,1/2,1)
867
x y h*f(x,y)
868
0 1 -2
869
1/2 -1 -7/4
870
1 -11/4 -11/8
871
872
::
873
874
sage: x,y = PolynomialRing(QQ,2,"xy").gens()
875
sage: eulers_method(5*x+y-5,0,1,1/2,1,algorithm="none")
876
[[0, 1], [1/2, -1], [1, -11/4], [3/2, -33/8]]
877
878
::
879
880
sage: RR = RealField(sci_not=0, prec=4, rnd='RNDU')
881
sage: x,y = PolynomialRing(RR,2,"xy").gens()
882
sage: eulers_method(5*x+y-5,0,1,1/2,1,algorithm="None")
883
[[0, 1], [1/2, -1.0], [1, -2.7], [3/2, -4.0]]
884
885
::
886
887
sage: RR = RealField(sci_not=0, prec=4, rnd='RNDU')
888
sage: x,y=PolynomialRing(RR,2,"xy").gens()
889
sage: eulers_method(5*x+y-5,0,1,1/2,1)
890
x y h*f(x,y)
891
0 1 -2.0
892
1/2 -1.0 -1.7
893
1 -2.7 -1.3
894
895
::
896
897
sage: x,y=PolynomialRing(QQ,2,"xy").gens()
898
sage: eulers_method(5*x+y-5,1,1,1/3,2)
899
x y h*f(x,y)
900
1 1 1/3
901
4/3 4/3 1
902
5/3 7/3 17/9
903
2 38/9 83/27
904
905
::
906
907
sage: eulers_method(5*x+y-5,0,1,1/2,1,algorithm="none")
908
[[0, 1], [1/2, -1], [1, -11/4], [3/2, -33/8]]
909
910
::
911
912
sage: pts = eulers_method(5*x+y-5,0,1,1/2,1,algorithm="none")
913
sage: P1 = list_plot(pts)
914
sage: P2 = line(pts)
915
sage: (P1+P2).show()
916
917
AUTHORS:
918
919
- David Joyner
920
"""
921
if algorithm=="table":
922
print("%10s %20s %25s"%("x","y","h*f(x,y)"))
923
n=int((1.0)*(x1-x0)/h)
924
x00=x0; y00=y0
925
soln = [[x00,y00]]
926
for i in range(n+1):
927
if algorithm=="table":
928
print("%10r %20r %20r"%(x00,y00,h*f(x00,y00)))
929
y00 = y00+h*f(x00,y00)
930
x00=x00+h
931
soln.append([x00,y00])
932
if algorithm!="table":
933
return soln
934
935
@rename_keyword(deprecation=6094, method="algorithm")
936
def eulers_method_2x2(f,g, t0, x0, y0, h, t1,algorithm="table"):
937
r"""
938
This implements Euler's method for finding numerically the
939
solution of the 1st order system of two ODEs
940
941
``x' = f(t, x, y), x(t0)=x0.``
942
943
``y' = g(t, x, y), y(t0)=y0.``
944
945
The "t" column of the table increments from `t_0` to `t_1` by `h`
946
(so `\\frac{t_1-t_0}{h}` must be an integer). In the "x" column,
947
the new x-value equals the old x-value plus the corresponding
948
entry in the next (third) column. In the "y" column, the new
949
y-value equals the old y-value plus the corresponding entry in the
950
next (last) column.
951
952
*For pedagogical purposes only.*
953
954
EXAMPLES::
955
956
sage: from sage.calculus.desolvers import eulers_method_2x2
957
sage: t, x, y = PolynomialRing(QQ,3,"txy").gens()
958
sage: f = x+y+t; g = x-y
959
sage: eulers_method_2x2(f,g, 0, 0, 0, 1/3, 1,algorithm="none")
960
[[0, 0, 0], [1/3, 0, 0], [2/3, 1/9, 0], [1, 10/27, 1/27], [4/3, 68/81, 4/27]]
961
962
::
963
964
sage: eulers_method_2x2(f,g, 0, 0, 0, 1/3, 1)
965
t x h*f(t,x,y) y h*g(t,x,y)
966
0 0 0 0 0
967
1/3 0 1/9 0 0
968
2/3 1/9 7/27 0 1/27
969
1 10/27 38/81 1/27 1/9
970
971
::
972
973
sage: RR = RealField(sci_not=0, prec=4, rnd='RNDU')
974
sage: t,x,y=PolynomialRing(RR,3,"txy").gens()
975
sage: f = x+y+t; g = x-y
976
sage: eulers_method_2x2(f,g, 0, 0, 0, 1/3, 1)
977
t x h*f(t,x,y) y h*g(t,x,y)
978
0 0 0.00 0 0.00
979
1/3 0.00 0.13 0.00 0.00
980
2/3 0.13 0.29 0.00 0.043
981
1 0.41 0.57 0.043 0.15
982
983
To numerically approximate `y(1)`, where `(1+t^2)y''+y'-y=0`,
984
`y(0)=1`, `y'(0)=-1`, using 4 steps of Euler's method, first
985
convert to a system: `y_1' = y_2`, `y_1(0)=1`; `y_2' =
986
\\frac{y_1-y_2}{1+t^2}`, `y_2(0)=-1`.::
987
988
sage: RR = RealField(sci_not=0, prec=4, rnd='RNDU')
989
sage: t, x, y=PolynomialRing(RR,3,"txy").gens()
990
sage: f = y; g = (x-y)/(1+t^2)
991
sage: eulers_method_2x2(f,g, 0, 1, -1, 1/4, 1)
992
t x h*f(t,x,y) y h*g(t,x,y)
993
0 1 -0.25 -1 0.50
994
1/4 0.75 -0.12 -0.50 0.29
995
1/2 0.63 -0.054 -0.21 0.19
996
3/4 0.63 -0.0078 -0.031 0.11
997
1 0.63 0.020 0.079 0.071
998
999
To numerically approximate y(1), where `y''+ty'+y=0`, `y(0)=1`, `y'(0)=0`::
1000
1001
sage: t,x,y=PolynomialRing(RR,3,"txy").gens()
1002
sage: f = y; g = -x-y*t
1003
sage: eulers_method_2x2(f,g, 0, 1, 0, 1/4, 1)
1004
t x h*f(t,x,y) y h*g(t,x,y)
1005
0 1 0.00 0 -0.25
1006
1/4 1.0 -0.062 -0.25 -0.23
1007
1/2 0.94 -0.11 -0.46 -0.17
1008
3/4 0.88 -0.15 -0.62 -0.10
1009
1 0.75 -0.17 -0.68 -0.015
1010
1011
AUTHORS:
1012
1013
- David Joyner
1014
"""
1015
if algorithm=="table":
1016
print("%10s %20s %25s %20s %20s"%("t", "x","h*f(t,x,y)","y", "h*g(t,x,y)"))
1017
n=int((1.0)*(t1-t0)/h)
1018
t00 = t0; x00 = x0; y00 = y0
1019
soln = [[t00,x00,y00]]
1020
for i in range(n+1):
1021
if algorithm=="table":
1022
print("%10r %20r %25r %20r %20r"%(t00,x00,h*f(t00,x00,y00),y00,h*g(t00,x00,y00)))
1023
x01 = x00 + h*f(t00,x00,y00)
1024
y00 = y00 + h*g(t00,x00,y00)
1025
x00 = x01
1026
t00 = t00 + h
1027
soln.append([t00,x00,y00])
1028
if algorithm!="table":
1029
return soln
1030
1031
def eulers_method_2x2_plot(f,g, t0, x0, y0, h, t1):
1032
r"""
1033
Plots solution of ODE
1034
1035
This plots the soln in the rectangle ``(xrange[0],xrange[1])
1036
x (yrange[0],yrange[1])`` and plots using Euler's method the
1037
numerical solution of the 1st order ODEs `x' = f(t,x,y)`,
1038
`x(a)=x_0`, `y' = g(t,x,y)`, `y(a) = y_0`.
1039
1040
*For pedagogical purposes only.*
1041
1042
EXAMPLES::
1043
1044
sage: from sage.calculus.desolvers import eulers_method_2x2_plot
1045
1046
The following example plots the solution to
1047
`\theta''+\sin(\theta)=0`, `\theta(0)=\frac 34`, `\theta'(0) =
1048
0`. Type ``P[0].show()`` to plot the solution,
1049
``(P[0]+P[1]).show()`` to plot `(t,\theta(t))` and
1050
`(t,\theta'(t))`::
1051
1052
sage: f = lambda z : z[2]; g = lambda z : -sin(z[1])
1053
sage: P = eulers_method_2x2_plot(f,g, 0.0, 0.75, 0.0, 0.1, 1.0)
1054
"""
1055
n=int((1.0)*(t1-t0)/h)
1056
t00 = t0; x00 = x0; y00 = y0
1057
soln = [[t00,x00,y00]]
1058
for i in range(n+1):
1059
x01 = x00 + h*f([t00,x00,y00])
1060
y00 = y00 + h*g([t00,x00,y00])
1061
x00 = x01
1062
t00 = t00 + h
1063
soln.append([t00,x00,y00])
1064
Q1 = line([[x[0],x[1]] for x in soln], rgbcolor=(1/4,1/8,3/4))
1065
Q2 = line([[x[0],x[2]] for x in soln], rgbcolor=(1/2,1/8,1/4))
1066
return [Q1,Q2]
1067
1068
def desolve_rk4_determine_bounds(ics,end_points=None):
1069
"""
1070
Used to determine bounds for numerical integration.
1071
1072
- If end_points is None, the interval for integration is from ics[0]
1073
to ics[0]+10
1074
1075
- If end_points is a or [a], the interval for integration is from min(ics[0],a)
1076
to max(ics[0],a)
1077
1078
- If end_points is [a,b], the interval for integration is from min(ics[0],a)
1079
to max(ics[0],b)
1080
1081
EXAMPLES::
1082
1083
sage: from sage.calculus.desolvers import desolve_rk4_determine_bounds
1084
sage: desolve_rk4_determine_bounds([0,2],1)
1085
(0, 1)
1086
1087
::
1088
1089
sage: desolve_rk4_determine_bounds([0,2])
1090
(0, 10)
1091
1092
::
1093
1094
sage: desolve_rk4_determine_bounds([0,2],[-2])
1095
(-2, 0)
1096
1097
::
1098
1099
sage: desolve_rk4_determine_bounds([0,2],[-2,4])
1100
(-2, 4)
1101
1102
"""
1103
if end_points is None:
1104
return((ics[0],ics[0]+10))
1105
if not isinstance(end_points,list):
1106
end_points=[end_points]
1107
if len(end_points)==1:
1108
return (min(ics[0],end_points[0]),max(ics[0],end_points[0]))
1109
else:
1110
return (min(ics[0],end_points[0]),max(ics[0],end_points[1]))
1111
1112
1113
def desolve_rk4(de, dvar, ics=None, ivar=None, end_points=None, step=0.1, output='list', **kwds):
1114
"""
1115
Solves numerically one first-order ordinary differential
1116
equation. See also ``ode_solver``.
1117
1118
INPUT:
1119
1120
input is similar to ``desolve`` command. The differential equation can be
1121
written in a form close to the plot_slope_field or desolve command
1122
1123
- Variant 1 (function in two variables)
1124
1125
- ``de`` - right hand side, i.e. the function `f(x,y)` from ODE `y'=f(x,y)`
1126
1127
- ``dvar`` - dependent variable (symbolic variable declared by var)
1128
1129
- Variant 2 (symbolic equation)
1130
1131
- ``de`` - equation, including term with ``diff(y,x)``
1132
1133
- ``dvar``` - dependent variable (declared as funciton of independent variable)
1134
1135
- Other parameters
1136
1137
- ``ivar`` - should be specified, if there are more variables or if the equation is autonomous
1138
1139
- ``ics`` - initial conditions in the form [x0,y0]
1140
1141
- ``end_points`` - the end points of the interval
1142
1143
- if end_points is a or [a], we integrate on between min(ics[0],a) and max(ics[0],a)
1144
- if end_points is None, we use end_points=ics[0]+10
1145
1146
- if end_points is [a,b] we integrate on between min(ics[0],a) and max(ics[0],b)
1147
1148
- ``step`` - (optional, default:0.1) the length of the step (positive number)
1149
1150
- ``output`` - (optional, default: 'list') one of 'list',
1151
'plot', 'slope_field' (graph of the solution with slope field)
1152
1153
OUTPUT:
1154
1155
Returns a list of points, or plot produced by list_plot,
1156
optionally with slope field.
1157
1158
1159
EXAMPLES::
1160
1161
sage: from sage.calculus.desolvers import desolve_rk4
1162
1163
Variant 2 for input - more common in numerics::
1164
1165
sage: x,y=var('x y')
1166
sage: desolve_rk4(x*y*(2-y),y,ics=[0,1],end_points=1,step=0.5)
1167
[[0, 1], [0.5, 1.12419127425], [1.0, 1.46159016229]]
1168
1169
Variant 1 for input - we can pass ODE in the form used by
1170
desolve function In this example we integrate bakwards, since
1171
``end_points < ics[0]``::
1172
1173
sage: y=function('y',x)
1174
sage: desolve_rk4(diff(y,x)+y*(y-1) == x-2,y,ics=[1,1],step=0.5, end_points=0)
1175
[[0.0, 8.90425710896], [0.5, 1.90932794536], [1, 1]]
1176
1177
Here we show how to plot simple pictures. For more advanced
1178
aplications use list_plot instead. To see the resulting picture
1179
use ``show(P)`` in Sage notebook. ::
1180
1181
sage: x,y=var('x y')
1182
sage: P=desolve_rk4(y*(2-y),y,ics=[0,.1],ivar=x,output='slope_field',end_points=[-4,6],thickness=3)
1183
1184
ALGORITHM:
1185
1186
4th order Runge-Kutta method. Wrapper for command ``rk`` in
1187
Maxima's dynamics package. Perhaps could be faster by using
1188
fast_float instead.
1189
1190
AUTHORS:
1191
1192
- Robert Marik (10-2009)
1193
"""
1194
if ics is None:
1195
raise ValueError("No initial conditions, specify with ics=[x0,y0].")
1196
1197
if ivar is None:
1198
ivars = de.variables()
1199
ivars = [t for t in ivars if t != dvar]
1200
if len(ivars) != 1:
1201
raise ValueError("Unable to determine independent variable, please specify.")
1202
ivar = ivars[0]
1203
1204
if not is_SymbolicVariable(dvar):
1205
from sage.calculus.var import var
1206
from sage.calculus.all import diff
1207
from sage.symbolic.relation import solve
1208
if is_SymbolicEquation(de):
1209
de = de.lhs() - de.rhs()
1210
dummy_dvar=var('dummy_dvar')
1211
# consider to add warning if the solution is not unique
1212
de=solve(de,diff(dvar,ivar),solution_dict=True)
1213
if len(de) != 1:
1214
raise NotImplementedError("Sorry, cannot find explicit formula for right-hand side of the ODE.")
1215
de=de[0][diff(dvar,ivar)].subs(dvar==dummy_dvar)
1216
else:
1217
dummy_dvar=dvar
1218
1219
step=abs(step)
1220
de0=de._maxima_()
1221
maxima("load('dynamics)")
1222
lower_bound,upper_bound=desolve_rk4_determine_bounds(ics,end_points)
1223
sol_1, sol_2 = [],[]
1224
if lower_bound<ics[0]:
1225
cmd="rk(%s,%s,%s,[%s,%s,%s,%s])\
1226
"%(de0.str(),str(dummy_dvar),str(ics[1]),str(ivar),str(ics[0]),lower_bound,-step)
1227
sol_1=maxima(cmd).sage()
1228
sol_1.pop(0)
1229
sol_1.reverse()
1230
if upper_bound>ics[0]:
1231
cmd="rk(%s,%s,%s,[%s,%s,%s,%s])\
1232
"%(de0.str(),str(dummy_dvar),str(ics[1]),str(ivar),str(ics[0]),upper_bound,step)
1233
sol_2=maxima(cmd).sage()
1234
sol_2.pop(0)
1235
sol=sol_1
1236
sol.extend([[ics[0],ics[1]]])
1237
sol.extend(sol_2)
1238
1239
if output=='list':
1240
return sol
1241
from sage.plot.plot import list_plot
1242
from sage.plot.plot_field import plot_slope_field
1243
R = list_plot(sol,plotjoined=True,**kwds)
1244
if output=='plot':
1245
return R
1246
if output=='slope_field':
1247
XMIN=sol[0][0]
1248
YMIN=sol[0][1]
1249
XMAX=XMIN
1250
YMAX=YMIN
1251
for s,t in sol:
1252
if s>XMAX:XMAX=s
1253
if s<XMIN:XMIN=s
1254
if t>YMAX:YMAX=t
1255
if t<YMIN:YMIN=t
1256
return plot_slope_field(de,(ivar,XMIN,XMAX),(dummy_dvar,YMIN,YMAX))+R
1257
1258
raise ValueError("Option output should be 'list', 'plot' or 'slope_field'.")
1259
1260
def desolve_system_rk4(des, vars, ics=None, ivar=None, end_points=None, step=0.1):
1261
r"""
1262
Solves numerically system of first-order ordinary differential
1263
equations using the 4th order Runge-Kutta method. Wrapper for
1264
Maxima command ``rk``. See also ``ode_solver``.
1265
1266
INPUT:
1267
1268
input is similar to desolve_system and desolve_rk4 commands
1269
1270
- ``des`` - right hand sides of the system
1271
1272
- ``vars`` - dependent variables
1273
1274
- ``ivar`` - (optional) should be specified, if there are more variables or
1275
if the equation is autonomous and the independent variable is
1276
missing
1277
1278
- ``ics`` - initial conditions in the form [x0,y01,y02,y03,....]
1279
1280
- ``end_points`` - the end points of the interval
1281
1282
- if end_points is a or [a], we integrate on between min(ics[0],a) and max(ics[0],a)
1283
- if end_points is None, we use end_points=ics[0]+10
1284
1285
- if end_points is [a,b] we integrate on between min(ics[0],a) and max(ics[0],b)
1286
1287
- ``step`` -- (optional, default: 0.1) the length of the step
1288
1289
OUTPUT:
1290
1291
Returns a list of points.
1292
1293
EXAMPLES::
1294
1295
sage: from sage.calculus.desolvers import desolve_system_rk4
1296
1297
Lotka Volterra system::
1298
1299
sage: from sage.calculus.desolvers import desolve_system_rk4
1300
sage: x,y,t=var('x y t')
1301
sage: P=desolve_system_rk4([x*(1-y),-y*(1-x)],[x,y],ics=[0,0.5,2],ivar=t,end_points=20)
1302
sage: Q=[ [i,j] for i,j,k in P]
1303
sage: LP=list_plot(Q)
1304
1305
sage: Q=[ [j,k] for i,j,k in P]
1306
sage: LP=list_plot(Q)
1307
1308
ALGORITHM:
1309
1310
4th order Runge-Kutta method. Wrapper for command ``rk`` in Maxima's
1311
dynamics package. Perhaps could be faster by using ``fast_float``
1312
instead.
1313
1314
AUTHOR:
1315
1316
- Robert Marik (10-2009)
1317
"""
1318
1319
if ics is None:
1320
raise ValueError("No initial conditions, specify with ics=[x0,y01,y02,...].")
1321
1322
ivars = set([])
1323
1324
for de in des:
1325
ivars = ivars.union(set(de.variables()))
1326
if ivar is None:
1327
ivars = ivars - set(vars)
1328
if len(ivars) != 1:
1329
raise ValueError("Unable to determine independent variable, please specify.")
1330
ivar = list(ivars)[0]
1331
1332
dess = [de._maxima_().str() for de in des]
1333
desstr = "[" + ",".join(dess) + "]"
1334
varss = [varsi._maxima_().str() for varsi in vars]
1335
varstr = "[" + ",".join(varss) + "]"
1336
x0=ics[0]
1337
icss = [ics[i]._maxima_().str() for i in range(1,len(ics))]
1338
icstr = "[" + ",".join(icss) + "]"
1339
step=abs(step)
1340
1341
maxima("load('dynamics)")
1342
lower_bound,upper_bound=desolve_rk4_determine_bounds(ics,end_points)
1343
sol_1, sol_2 = [],[]
1344
if lower_bound<ics[0]:
1345
cmd="rk(%s,%s,%s,[%s,%s,%s,%s])\
1346
"%(desstr,varstr,icstr,str(ivar),str(x0),lower_bound,-step)
1347
sol_1=maxima(cmd).sage()
1348
sol_1.pop(0)
1349
sol_1.reverse()
1350
if upper_bound>ics[0]:
1351
cmd="rk(%s,%s,%s,[%s,%s,%s,%s])\
1352
"%(desstr,varstr,icstr,str(ivar),str(x0),upper_bound,step)
1353
sol_2=maxima(cmd).sage()
1354
sol_2.pop(0)
1355
sol=sol_1
1356
sol.append(ics)
1357
sol.extend(sol_2)
1358
1359
return sol
1360
1361
def desolve_odeint(des, ics, times, dvars, ivar=None, compute_jac=False, args=()
1362
, rtol=None, atol=None, tcrit=None, h0=0.0, hmax=0.0, hmin=0.0, ixpr=0
1363
, mxstep=0, mxhnil=0, mxordn=12, mxords=5, printmessg=0):
1364
r"""
1365
Solves numerically a system of first-order ordinary differential equations
1366
using ``odeint`` from scipy.integrate module.
1367
1368
INPUT:
1369
1370
- ``des`` -- right hand sides of the system
1371
1372
- ``ics`` -- initial conditions
1373
1374
- ``times`` -- a sequence of time points in which the solution must be found
1375
1376
- ``dvars`` -- dependent variables. ATTENTION: the order must be the same as
1377
in des, that means: d(dvars[i])/dt=des[i]
1378
1379
- ``ivar`` -- independent variable, optional.
1380
1381
- ``compute_jac`` -- boolean. If True, the Jacobian of des is computed and
1382
used during the integration of Stiff Systems. Default value is False.
1383
1384
Other Parameters (taken from the documentation of odeint function from
1385
scipy.integrate module)
1386
1387
- ``rtol``, ``atol`` : float
1388
The input parameters rtol and atol determine the error
1389
control performed by the solver. The solver will control the
1390
vector, e, of estimated local errors in y, according to an
1391
inequality of the form:
1392
1393
max-norm of (e / ewt) <= 1
1394
1395
where ewt is a vector of positive error weights computed as:
1396
1397
ewt = rtol * abs(y) + atol
1398
1399
rtol and atol can be either vectors the same length as y or scalars.
1400
1401
- ``tcrit`` : array
1402
Vector of critical points (e.g. singularities) where integration
1403
care should be taken.
1404
1405
- ``h0`` : float, (0: solver-determined)
1406
The step size to be attempted on the first step.
1407
1408
- ``hmax`` : float, (0: solver-determined)
1409
The maximum absolute step size allowed.
1410
1411
- ``hmin`` : float, (0: solver-determined)
1412
The minimum absolute step size allowed.
1413
1414
- ``ixpr`` : boolean.
1415
Whether to generate extra printing at method switches.
1416
1417
- ``mxstep`` : integer, (0: solver-determined)
1418
Maximum number of (internally defined) steps allowed for each
1419
integration point in t.
1420
1421
- ``mxhnil`` : integer, (0: solver-determined)
1422
Maximum number of messages printed.
1423
1424
- ``mxordn`` : integer, (0: solver-determined)
1425
Maximum order to be allowed for the nonstiff (Adams) method.
1426
1427
- ``mxords`` : integer, (0: solver-determined)
1428
Maximum order to be allowed for the stiff (BDF) method.
1429
1430
OUTPUT:
1431
1432
Returns a list with the solution of the system at each time in times.
1433
1434
EXAMPLES:
1435
1436
Lotka Volterra Equations::
1437
1438
sage: from sage.calculus.desolvers import desolve_odeint
1439
sage: x,y=var('x,y')
1440
sage: f=[x*(1-y),-y*(1-x)]
1441
sage: sol=desolve_odeint(f,[0.5,2],srange(0,10,0.1),[x,y])
1442
sage: p=line(zip(sol[:,0],sol[:,1]))
1443
sage: p.show()
1444
1445
Lorenz Equations::
1446
1447
sage: x,y,z=var('x,y,z')
1448
sage: # Next we define the parameters
1449
sage: sigma=10
1450
sage: rho=28
1451
sage: beta=8/3
1452
sage: # The Lorenz equations
1453
sage: lorenz=[sigma*(y-x),x*(rho-z)-y,x*y-beta*z]
1454
sage: # Time and initial conditions
1455
sage: times=srange(0,50.05,0.05)
1456
sage: ics=[0,1,1]
1457
sage: sol=desolve_odeint(lorenz,ics,times,[x,y,z],rtol=1e-13,atol=1e-14)
1458
1459
One-dimensional Stiff system::
1460
1461
sage: y= var('y')
1462
sage: epsilon=0.01
1463
sage: f=y^2*(1-y)
1464
sage: ic=epsilon
1465
sage: t=srange(0,2/epsilon,1)
1466
sage: sol=desolve_odeint(f,ic,t,y,rtol=1e-9,atol=1e-10,compute_jac=True)
1467
sage: p=points(zip(t,sol))
1468
sage: p.show()
1469
1470
Another Stiff system with some optional parameters with no
1471
default value::
1472
1473
sage: y1,y2,y3=var('y1,y2,y3')
1474
sage: f1=77.27*(y2+y1*(1-8.375*1e-6*y1-y2))
1475
sage: f2=1/77.27*(y3-(1+y1)*y2)
1476
sage: f3=0.16*(y1-y3)
1477
sage: f=[f1,f2,f3]
1478
sage: ci=[0.2,0.4,0.7]
1479
sage: t=srange(0,10,0.01)
1480
sage: v=[y1,y2,y3]
1481
sage: sol=desolve_odeint(f,ci,t,v,rtol=1e-3,atol=1e-4,h0=0.1,hmax=1,hmin=1e-4,mxstep=1000,mxords=17)
1482
1483
AUTHOR:
1484
1485
- Oriol Castejon (05-2010)
1486
"""
1487
1488
from scipy.integrate import odeint
1489
from sage.ext.fast_eval import fast_float
1490
from sage.calculus.functions import jacobian
1491
1492
if ivar==None:
1493
if len(dvars)==0 or len(dvars)==1:
1494
if len(dvars)==1:
1495
des=des[0]
1496
dvars=dvars[0]
1497
all_vars = set(des.variables())
1498
else:
1499
all_vars = set([])
1500
for de in des:
1501
all_vars.update(set(de.variables()))
1502
if is_SymbolicVariable(dvars):
1503
ivars = all_vars - set([dvars])
1504
else:
1505
ivars = all_vars - set(dvars)
1506
1507
if len(ivars)==1:
1508
ivar = ivars.pop()
1509
elif not ivars:
1510
from sage.symbolic.ring import var
1511
try:
1512
safe_names = [ 't_' + str(dvar) for dvar in dvars ]
1513
except TypeError: # not iterable
1514
safe_names = [ 't_' + str(dvars) ]
1515
ivar = map(var, safe_names)
1516
else:
1517
raise ValueError("Unable to determine independent variable, please specify.")
1518
1519
# one-dimensional systems:
1520
if is_SymbolicVariable(dvars):
1521
func = fast_float(des,dvars,ivar)
1522
if not compute_jac:
1523
Dfun=None
1524
else:
1525
J = diff(des,dvars)
1526
J = fast_float(J,dvars,ivar)
1527
Dfun = lambda y,t: [J(y,t)]
1528
1529
# n-dimensional systems:
1530
else:
1531
desc = []
1532
variabs = dvars[:]
1533
variabs.append(ivar)
1534
for de in des:
1535
desc.append(fast_float(de,*variabs))
1536
1537
def func(y,t):
1538
v = list(y[:])
1539
v.append(t)
1540
return [dec(*v) for dec in desc]
1541
1542
if not compute_jac:
1543
Dfun=None
1544
else:
1545
J = jacobian(des,dvars)
1546
J = [list(v) for v in J]
1547
J = fast_float(J,*variabs)
1548
def Dfun(y,t):
1549
v = list(y[:])
1550
v.append(t)
1551
return [[element(*v) for element in row] for row in J]
1552
1553
1554
sol=odeint(func, ics, times, args=args, Dfun=Dfun, rtol=rtol, atol=atol,
1555
tcrit=tcrit, h0=h0, hmax=hmax, hmin=hmin, ixpr=ixpr, mxstep=mxstep,
1556
mxhnil=mxhnil, mxordn=mxordn, mxords=mxords, printmessg=printmessg)
1557
1558
return sol
1559
1560