Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/calculus/desolvers.py
4057 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
k1*e^x + k2*e^(-x) - x
144
145
146
::
147
148
sage: f = desolve(de, y, [10,2,1]); f
149
-x + 5*e^(-x + 10) + 7*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
k1*sin(x) + k2*cos(x)
166
167
::
168
169
sage: desolve(de, y, [0,1,pi/2,4])
170
4*sin(x) + cos(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)) == sin(x) - cos(1) - 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*((sin(x) + cos(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*((sin(x) + cos(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*(e^x*sin(x) + e^x*cos(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*((e^(1/2*pi) - 2)*x/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*((e^(1/2*pi) - 2)*x/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*(2*e^(1/2*pi) - 3)*x/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*(2*e^(1/2*pi) - 3)*x/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/(sqrt(y(x)^2)*x))
370
+ log(4*(2*x^2*sqrt((x^2*y(x)^2 + y(x)^4)/x^2)*sqrt(x^(-2)) + x^2 + 2*y(x)^2)/x^2))/(x*sqrt(x^(-2))) == c]
371
372
Trac #6479 fixed::
373
374
sage: x = var('x')
375
sage: y = function('y', x)
376
sage: desolve( diff(y,x,x) == 0, y, [0,0,1])
377
x
378
379
::
380
381
sage: desolve( diff(y,x,x) == 0, y, [0,1,1])
382
x + 1
383
384
Trac #9835 fixed::
385
386
sage: x = var('x')
387
sage: y = function('y', x)
388
sage: desolve(diff(y,x,2)+y*(1-y^2)==0,y,[0,-1,1,1])
389
Traceback (most recent call last):
390
...
391
NotImplementedError: Unable to use initial condition for this equation (freeofx).
392
393
Trac #8931 fixed::
394
395
sage: x=var('x'); f=function('f',x); k=var('k'); assume(k>0)
396
sage: desolve(diff(f,x,2)/f==k,f,ivar=x)
397
k1*e^(sqrt(k)*x) + k2*e^(-sqrt(k)*x)
398
399
400
AUTHORS:
401
402
- David Joyner (1-2006)
403
404
- Robert Bradshaw (10-2008)
405
406
- Robert Marik (10-2009)
407
408
"""
409
if is_SymbolicEquation(de):
410
de = de.lhs() - de.rhs()
411
if is_SymbolicVariable(dvar):
412
raise ValueError, "You have to declare dependent variable as a function, eg. y=function('y',x)"
413
# for backwards compatibility
414
if isinstance(dvar, list):
415
dvar, ivar = dvar
416
elif ivar is None:
417
ivars = de.variables()
418
ivars = [t for t in ivars if t is not dvar]
419
if len(ivars) != 1:
420
raise ValueError, "Unable to determine independent variable, please specify."
421
ivar = ivars[0]
422
def sanitize_var(exprs):
423
return exprs.replace("'"+dvar_str+"("+ivar_str+")",dvar_str)
424
de00 = de._maxima_()
425
P = de00.parent()
426
dvar_str=P(dvar.operator()).str()
427
ivar_str=P(ivar).str()
428
de00 = de00.str()
429
de0 = sanitize_var(de00)
430
ode_solver="ode2"
431
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)
432
# we produce string like this
433
# ode2('diff(y,x,2)+2*'diff(y,x,1)+y-cos(x),y(x),x)
434
soln = P(cmd)
435
436
if str(soln).strip() == 'false':
437
if contrib_ode:
438
ode_solver="contrib_ode"
439
P("load('contrib_ode)")
440
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)
441
# we produce string like this
442
# (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))
443
soln = P(cmd)
444
if str(soln).strip() == 'false':
445
raise NotImplementedError, "Maxima was unable to solve this ODE."
446
else:
447
raise NotImplementedError, "Maxima was unable to solve this ODE. Consider to set option contrib_ode to True."
448
449
if show_method:
450
maxima_method=P("method")
451
452
if (ics is not None):
453
if not is_SymbolicEquation(soln.sage()):
454
if not show_method:
455
maxima_method=P("method")
456
raise NotImplementedError, "Unable to use initial condition for this equation (%s)."%(str(maxima_method).strip())
457
if len(ics) == 2:
458
tempic=(ivar==ics[0])._maxima_().str()
459
tempic=tempic+","+(dvar==ics[1])._maxima_().str()
460
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)
461
cmd=sanitize_var(cmd)
462
# we produce string like this
463
# (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))
464
soln=P(cmd)
465
if len(ics) == 3:
466
#fixed ic2 command from Maxima - we have to ensure that %k1, %k2 do not depend on variables, should be removed when fixed in Maxima
467
P("ic2_sage(soln,xa,ya,dya):=block([programmode:true,backsubst:true,singsolve:true,temp,%k2,%k1,TEMP_k], \
468
noteqn(xa), noteqn(ya), noteqn(dya), boundtest('%k1,%k1), boundtest('%k2,%k2), \
469
temp: lhs(soln) - rhs(soln), \
470
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]), \
471
if not freeof(lhs(ya),TEMP_k) or not freeof(lhs(xa),TEMP_k) then return (false), \
472
temp: maplist(lambda([zz], subst(zz,soln)), TEMP_k), \
473
if length(temp)=1 then return(first(temp)) else return(temp))")
474
tempic=P(ivar==ics[0]).str()
475
tempic=tempic+","+P(dvar==ics[1]).str()
476
tempic=tempic+",'diff("+dvar_str+","+ivar_str+")="+P(ics[2]).str()
477
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)
478
cmd=sanitize_var(cmd)
479
# we produce string like this
480
# (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))
481
soln=P(cmd)
482
if str(soln).strip() == 'false':
483
raise NotImplementedError, "Maxima was unable to solve this IVP. Remove the initial condition to get the general solution."
484
if len(ics) == 4:
485
#fixed bc2 command from Maxima - we have to ensure that %k1, %k2 do not depend on variables, should be removed when fixed in Maxima
486
P("bc2_sage(soln,xa,ya,xb,yb):=block([programmode:true,backsubst:true,singsolve:true,temp,%k1,%k2,TEMP_k], \
487
noteqn(xa), noteqn(ya), noteqn(xb), noteqn(yb), boundtest('%k1,%k1), boundtest('%k2,%k2), \
488
TEMP_k:solve([subst([xa,ya],soln), subst([xb,yb],soln)], [%k1,%k2]), \
489
if not freeof(lhs(ya),TEMP_k) or not freeof(lhs(xa),TEMP_k) then return (false), \
490
temp: maplist(lambda([zz], subst(zz,soln)),TEMP_k), \
491
if length(temp)=1 then return(first(temp)) else return(temp))")
492
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())
493
cmd="(TEMP:%s,substitute(%s=%s(%s),TEMP))"%(cmd,dvar_str,dvar_str,ivar_str)
494
cmd=sanitize_var(cmd)
495
# we produce string like this
496
# (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))
497
soln=P(cmd)
498
if str(soln).strip() == 'false':
499
raise NotImplementedError, "Maxima was unable to solve this BVP. Remove the initial condition to get the general solution."
500
501
soln=soln.sage()
502
if is_SymbolicEquation(soln) and soln.lhs() == dvar:
503
# Remark: Here we do not check that the right hand side does not depend on dvar.
504
# This probably will not hapen for soutions obtained via ode2, anyway.
505
soln = soln.rhs()
506
if show_method:
507
return [soln,maxima_method.str()]
508
else:
509
return soln
510
511
512
#def desolve_laplace2(de,vars,ics=None):
513
## """
514
## Solves an ODE using laplace transforms via maxima. Initial conditions
515
## are optional.
516
517
## INPUT:
518
## de -- a lambda expression representing the ODE
519
## (eg, de = "diff(f(x),x,2)=diff(f(x),x)+sin(x)")
520
## vars -- a list of strings representing the variables
521
## (eg, vars = ["x","f"], if x is the independent
522
## variable and f is the dependent variable)
523
## ics -- a list of numbers representing initial conditions,
524
## with symbols allowed which are represented by strings
525
## (eg, f(0)=1, f'(0)=2 is ics = [0,1,2])
526
527
## EXAMPLES:
528
## sage: from sage.calculus.desolvers import desolve_laplace
529
## sage: x = var('x')
530
## sage: f = function('f', x)
531
## sage: de = lambda y: diff(y,x,x) - 2*diff(y,x) + y
532
## sage: desolve_laplace(de(f(x)),[f,x])
533
## #x*%e^x*(?%at('diff('f(x),x,1),x=0))-'f(0)*x*%e^x+'f(0)*%e^x
534
## sage: desolve_laplace(de(f(x)),[f,x],[0,1,2]) ## IC option does not work
535
## #x*%e^x*(?%at('diff('f(x),x,1),x=0))-'f(0)*x*%e^x+'f(0)*%e^x
536
537
## AUTHOR: David Joyner (1st version 1-2006, 8-2007)
538
## """
539
# ######## this method seems reasonable but doesn't work for some reason
540
# name0 = vars[0]._repr_()[0:(len(vars[0]._repr_())-2-len(str(vars[1])))]
541
# name1 = str(vars[1])
542
# #maxima("de:"+de+";")
543
# if ics!=None:
544
# ic0 = maxima("ic:"+str(vars[1])+"="+str(ics[0]))
545
# d = len(ics)
546
# for i in range(d-1):
547
# maxima(vars[0](vars[1])).diff(vars[1],i).atvalue(ic0,ics[i+1])
548
# de0 = de._maxima_()
549
# #cmd = "desolve("+de+","+vars[1]+"("+vars[0]+"));"
550
# #return maxima.eval(cmd)
551
# return de0.desolve(vars[0]).rhs()
552
553
554
def desolve_laplace(de, dvar, ics=None, ivar=None):
555
"""
556
Solves an ODE using laplace transforms. Initials conditions are optional.
557
558
INPUT:
559
560
- ``de`` - a lambda expression representing the ODE (eg, de =
561
diff(y,x,2) == diff(y,x)+sin(x))
562
563
- ``dvar`` - the dependent variable (eg y)
564
565
- ``ivar`` - (optional) the independent variable (hereafter called
566
x), which must be specified if there is more than one
567
independent variable in the equation.
568
569
- ``ics`` - a list of numbers representing initial conditions, (eg,
570
f(0)=1, f'(0)=2 is ics = [0,1,2])
571
572
OUTPUT:
573
574
Solution of the ODE as symbolic expression
575
576
EXAMPLES::
577
578
sage: u=function('u',x)
579
sage: eq = diff(u,x) - exp(-x) - u == 0
580
sage: desolve_laplace(eq,u)
581
1/2*(2*u(0) + 1)*e^x - 1/2*e^(-x)
582
583
We can use initial conditions::
584
585
sage: desolve_laplace(eq,u,ics=[0,3])
586
-1/2*e^(-x) + 7/2*e^x
587
588
The initial conditions do not persist in the system (as they persisted
589
in previous versions)::
590
591
sage: desolve_laplace(eq,u)
592
1/2*(2*u(0) + 1)*e^x - 1/2*e^(-x)
593
594
::
595
596
sage: f=function('f', x)
597
sage: eq = diff(f,x) + f == 0
598
sage: desolve_laplace(eq,f,[0,1])
599
e^(-x)
600
601
::
602
603
sage: x = var('x')
604
sage: f = function('f', x)
605
sage: de = diff(f,x,x) - 2*diff(f,x) + f
606
sage: desolve_laplace(de,f)
607
-x*e^x*f(0) + x*e^x*D[0](f)(0) + e^x*f(0)
608
609
::
610
611
sage: desolve_laplace(de,f,ics=[0,1,2])
612
x*e^x + e^x
613
614
TESTS:
615
616
Trac #4839 fixed::
617
618
sage: t=var('t')
619
sage: x=function('x', t)
620
sage: soln=desolve_laplace(diff(x,t)+x==1, x, ics=[0,2])
621
sage: soln
622
e^(-t) + 1
623
624
::
625
626
sage: soln(t=3)
627
e^(-3) + 1
628
629
AUTHORS:
630
631
- David Joyner (1-2006,8-2007)
632
633
- Robert Marik (10-2009)
634
"""
635
#This is the original code from David Joyner (inputs and outputs strings)
636
#maxima("de:"+de._repr_()+"=0;")
637
#if ics!=None:
638
# d = len(ics)
639
# for i in range(0,d-1):
640
# ic = "atvalue(diff("+vars[1]+"("+vars[0]+"),"+str(vars[0])+","+str(i)+"),"+str(vars[0])+"="+str(ics[0])+","+str(ics[1+i])+")"
641
# maxima(ic)
642
#
643
#cmd = "desolve("+de._repr_()+","+vars[1]+"("+vars[0]+"));"
644
#return maxima(cmd).rhs()._maxima_init_()
645
646
## verbatim copy from desolve - begin
647
if is_SymbolicEquation(de):
648
de = de.lhs() - de.rhs()
649
if is_SymbolicVariable(dvar):
650
raise ValueError, "You have to declare dependent variable as a function, eg. y=function('y',x)"
651
# for backwards compatibility
652
if isinstance(dvar, list):
653
dvar, ivar = dvar
654
elif ivar is None:
655
ivars = de.variables()
656
ivars = [t for t in ivars if t != dvar]
657
if len(ivars) != 1:
658
raise ValueError, "Unable to determine independent variable, please specify."
659
ivar = ivars[0]
660
## verbatim copy from desolve - end
661
662
def sanitize_var(exprs): # 'y(x) -> y(x)
663
return exprs.replace("'"+str(dvar),str(dvar))
664
de0=de._maxima_()
665
P = de0.parent()
666
cmd = sanitize_var("desolve("+de0.str()+","+str(dvar)+")")
667
soln=P(cmd).rhs()
668
if str(soln).strip() == 'false':
669
raise NotImplementedError, "Maxima was unable to solve this ODE."
670
soln=soln.sage()
671
if ics!=None:
672
d = len(ics)
673
for i in range(0,d-1):
674
soln=eval('soln.substitute(diff(dvar,ivar,i)('+str(ivar)+'=ics[0])==ics[i+1])')
675
return soln
676
677
678
def desolve_system(des, vars, ics=None, ivar=None):
679
"""
680
Solves any size system of 1st order ODE's. Initials conditions are optional.
681
682
Onedimensional systems are passed to :meth:`desolve_laplace`.
683
684
INPUT:
685
686
- ``des`` - list of ODEs
687
688
- ``vars`` - list of dependent variables
689
690
- ``ics`` - (optional) list of initial values for ivar and vars
691
692
- ``ivar`` - (optional) the independent variable, which must be
693
specified if there is more than one independent variable in the
694
equation.
695
696
EXAMPLES::
697
698
sage: t = var('t')
699
sage: x = function('x', t)
700
sage: y = function('y', t)
701
sage: de1 = diff(x,t) + y - 1 == 0
702
sage: de2 = diff(y,t) - x + 1 == 0
703
sage: desolve_system([de1, de2], [x,y])
704
[x(t) == (x(0) - 1)*cos(t) - (y(0) - 1)*sin(t) + 1,
705
y(t) == (x(0) - 1)*sin(t) + (y(0) - 1)*cos(t) + 1]
706
707
Now we give some initial conditions::
708
709
sage: sol = desolve_system([de1, de2], [x,y], ics=[0,1,2]); sol
710
[x(t) == -sin(t) + 1, y(t) == cos(t) + 1]
711
712
::
713
714
sage: solnx, solny = sol[0].rhs(), sol[1].rhs()
715
sage: plot([solnx,solny],(0,1)) # not tested
716
sage: parametric_plot((solnx,solny),(0,1)) # not tested
717
718
TESTS:
719
720
Trac #9823 fixed::
721
722
sage: t = var('t')
723
sage: x = function('x', t)
724
sage: de1 = diff(x,t) + 1 == 0
725
sage: desolve_system([de1], [x])
726
-t + x(0)
727
728
AUTHORS:
729
730
- Robert Bradshaw (10-2008)
731
"""
732
if len(des)==1:
733
return desolve_laplace(des[0], vars[0], ics=ics, ivar=ivar)
734
ivars = set([])
735
for i, de in enumerate(des):
736
if not is_SymbolicEquation(de):
737
des[i] = de == 0
738
ivars = ivars.union(set(de.variables()))
739
if ivar is None:
740
ivars = ivars - set(vars)
741
if len(ivars) != 1:
742
raise ValueError, "Unable to determine independent variable, please specify."
743
ivar = list(ivars)[0]
744
dvars = [v._maxima_() for v in vars]
745
if ics is not None:
746
ivar_ic = ics[0]
747
for dvar, ic in zip(dvars, ics[1:]):
748
dvar.atvalue(ivar==ivar_ic, ic)
749
soln = dvars[0].parent().desolve(des, dvars)
750
if str(soln).strip() == 'false':
751
raise NotImplementedError, "Maxima was unable to solve this system."
752
soln = list(soln)
753
for i, sol in enumerate(soln):
754
soln[i] = sol.sage()
755
if ics is not None:
756
ivar_ic = ics[0]
757
for dvar, ic in zip(dvars, ics[:1]):
758
dvar.atvalue(ivar==ivar_ic, dvar)
759
return soln
760
761
762
def desolve_system_strings(des,vars,ics=None):
763
r"""
764
Solves any size system of 1st order ODE's. Initials conditions are optional.
765
766
This function is obsolete, use desolve_system.
767
768
INPUT:
769
770
- ``de`` - a list of strings representing the ODEs in maxima
771
notation (eg, de = "diff(f(x),x,2)=diff(f(x),x)+sin(x)")
772
773
- ``vars`` - a list of strings representing the variables (eg,
774
vars = ["s","x","y"], where s is the independent variable and
775
x,y the dependent variables)
776
777
- ``ics`` - a list of numbers representing initial conditions
778
(eg, x(0)=1, y(0)=2 is ics = [0,1,2])
779
780
WARNING:
781
782
The given ics sets the initial values of the dependent vars in
783
maxima, so subsequent ODEs involving these variables will have
784
these initial conditions automatically imposed.
785
786
EXAMPLES::
787
788
sage: from sage.calculus.desolvers import desolve_system_strings
789
sage: s = var('s')
790
sage: function('x', s)
791
x(s)
792
793
::
794
795
sage: function('y', s)
796
y(s)
797
798
::
799
800
sage: de1 = lambda z: diff(z[0],s) + z[1] - 1
801
sage: de2 = lambda z: diff(z[1],s) - z[0] + 1
802
sage: des = [de1([x(s),y(s)]),de2([x(s),y(s)])]
803
sage: vars = ["s","x","y"]
804
sage: desolve_system_strings(des,vars)
805
["(1-'y(0))*sin(s)+('x(0)-1)*cos(s)+1", "('x(0)-1)*sin(s)+('y(0)-1)*cos(s)+1"]
806
807
::
808
809
sage: ics = [0,1,-1]
810
sage: soln = desolve_system_strings(des,vars,ics); soln
811
['2*sin(s)+1', '1-2*cos(s)']
812
813
::
814
815
sage: solnx, solny = map(SR, soln)
816
sage: RR(solnx(s=3))
817
1.28224001611973
818
819
::
820
821
sage: P1 = plot([solnx,solny],(0,1))
822
sage: P2 = parametric_plot((solnx,solny),(0,1))
823
824
Now type show(P1), show(P2) to view these.
825
826
827
AUTHORS:
828
829
- David Joyner (3-2006, 8-2007)
830
"""
831
d = len(des)
832
dess = [de._maxima_init_() + "=0" for de in des]
833
for i in range(d):
834
cmd="de:" + dess[int(i)] + ";"
835
maxima.eval(cmd)
836
desstr = "[" + ",".join(dess) + "]"
837
d = len(vars)
838
varss = list("'" + vars[i] + "(" + vars[0] + ")" for i in range(1,d))
839
varstr = "[" + ",".join(varss) + "]"
840
if ics is not None:
841
#d = len(ics) ## must be same as len(des)
842
for i in range(1,d):
843
ic = "atvalue('" + vars[i] + "("+vars[0] + ")," + str(vars[0]) + "=" + str(ics[0]) + "," + str(ics[i]) + ")"
844
maxima.eval(ic)
845
cmd = "desolve(" + desstr + "," + varstr + ");"
846
soln = maxima(cmd)
847
return [f.rhs()._maxima_init_() for f in soln]
848
849
@rename_keyword(deprecated='Sage version 4.6', method="algorithm")
850
def eulers_method(f,x0,y0,h,x1,algorithm="table"):
851
r"""
852
This implements Euler's method for finding numerically the
853
solution of the 1st order ODE ``y' = f(x,y)``, ``y(a)=c``. The "x"
854
column of the table increments from ``x0`` to ``x1`` by ``h`` (so
855
``(x1-x0)/h`` must be an integer). In the "y" column, the new
856
y-value equals the old y-value plus the corresponding entry in the
857
last column.
858
859
*For pedagogical purposes only.*
860
861
EXAMPLES::
862
863
sage: from sage.calculus.desolvers import eulers_method
864
sage: x,y = PolynomialRing(QQ,2,"xy").gens()
865
sage: eulers_method(5*x+y-5,0,1,1/2,1)
866
x y h*f(x,y)
867
0 1 -2
868
1/2 -1 -7/4
869
1 -11/4 -11/8
870
871
::
872
873
sage: x,y = PolynomialRing(QQ,2,"xy").gens()
874
sage: eulers_method(5*x+y-5,0,1,1/2,1,algorithm="none")
875
[[0, 1], [1/2, -1], [1, -11/4], [3/2, -33/8]]
876
877
::
878
879
sage: RR = RealField(sci_not=0, prec=4, rnd='RNDU')
880
sage: x,y = PolynomialRing(RR,2,"xy").gens()
881
sage: eulers_method(5*x+y-5,0,1,1/2,1,algorithm="None")
882
[[0, 1], [1/2, -1.0], [1, -2.7], [3/2, -4.0]]
883
884
::
885
886
sage: RR = RealField(sci_not=0, prec=4, rnd='RNDU')
887
sage: x,y=PolynomialRing(RR,2,"xy").gens()
888
sage: eulers_method(5*x+y-5,0,1,1/2,1)
889
x y h*f(x,y)
890
0 1 -2.0
891
1/2 -1.0 -1.7
892
1 -2.7 -1.3
893
894
::
895
896
sage: x,y=PolynomialRing(QQ,2,"xy").gens()
897
sage: eulers_method(5*x+y-5,1,1,1/3,2)
898
x y h*f(x,y)
899
1 1 1/3
900
4/3 4/3 1
901
5/3 7/3 17/9
902
2 38/9 83/27
903
904
::
905
906
sage: eulers_method(5*x+y-5,0,1,1/2,1,algorithm="none")
907
[[0, 1], [1/2, -1], [1, -11/4], [3/2, -33/8]]
908
909
::
910
911
sage: pts = eulers_method(5*x+y-5,0,1,1/2,1,algorithm="none")
912
sage: P1 = list_plot(pts)
913
sage: P2 = line(pts)
914
sage: (P1+P2).show()
915
916
AUTHORS:
917
918
- David Joyner
919
"""
920
if algorithm=="table":
921
print "%10s %20s %25s"%("x","y","h*f(x,y)")
922
n=int((1.0)*(x1-x0)/h)
923
x00=x0; y00=y0
924
soln = [[x00,y00]]
925
for i in range(n+1):
926
if algorithm=="table":
927
print "%10r %20r %20r"%(x00,y00,h*f(x00,y00))
928
y00 = y00+h*f(x00,y00)
929
x00=x00+h
930
soln.append([x00,y00])
931
if algorithm!="table":
932
return soln
933
934
@rename_keyword(deprecated='Sage version 4.6', method="algorithm")
935
def eulers_method_2x2(f,g, t0, x0, y0, h, t1,algorithm="table"):
936
r"""
937
This implements Euler's method for finding numerically the
938
solution of the 1st order system of two ODEs
939
940
``x' = f(t, x, y), x(t0)=x0.``
941
942
``y' = g(t, x, y), y(t0)=y0.``
943
944
The "t" column of the table increments from `t_0` to `t_1` by `h`
945
(so `\\frac{t_1-t_0}{h}` must be an integer). In the "x" column,
946
the new x-value equals the old x-value plus the corresponding
947
entry in the next (third) column. In the "y" column, the new
948
y-value equals the old y-value plus the corresponding entry in the
949
next (last) column.
950
951
*For pedagogical purposes only.*
952
953
EXAMPLES::
954
955
sage: from sage.calculus.desolvers import eulers_method_2x2
956
sage: t, x, y = PolynomialRing(QQ,3,"txy").gens()
957
sage: f = x+y+t; g = x-y
958
sage: eulers_method_2x2(f,g, 0, 0, 0, 1/3, 1,algorithm="none")
959
[[0, 0, 0], [1/3, 0, 0], [2/3, 1/9, 0], [1, 10/27, 1/27], [4/3, 68/81, 4/27]]
960
961
::
962
963
sage: eulers_method_2x2(f,g, 0, 0, 0, 1/3, 1)
964
t x h*f(t,x,y) y h*g(t,x,y)
965
0 0 0 0 0
966
1/3 0 1/9 0 0
967
2/3 1/9 7/27 0 1/27
968
1 10/27 38/81 1/27 1/9
969
970
::
971
972
sage: RR = RealField(sci_not=0, prec=4, rnd='RNDU')
973
sage: t,x,y=PolynomialRing(RR,3,"txy").gens()
974
sage: f = x+y+t; g = x-y
975
sage: eulers_method_2x2(f,g, 0, 0, 0, 1/3, 1)
976
t x h*f(t,x,y) y h*g(t,x,y)
977
0 0 0.00 0 0.00
978
1/3 0.00 0.13 0.00 0.00
979
2/3 0.13 0.29 0.00 0.043
980
1 0.41 0.57 0.043 0.15
981
982
To numerically approximate `y(1)`, where `(1+t^2)y''+y'-y=0`,
983
`y(0)=1`, `y'(0)=-1`, using 4 steps of Euler's method, first
984
convert to a system: `y_1' = y_2`, `y_1(0)=1`; `y_2' =
985
\\frac{y_1-y_2}{1+t^2}`, `y_2(0)=-1`.::
986
987
sage: RR = RealField(sci_not=0, prec=4, rnd='RNDU')
988
sage: t, x, y=PolynomialRing(RR,3,"txy").gens()
989
sage: f = y; g = (x-y)/(1+t^2)
990
sage: eulers_method_2x2(f,g, 0, 1, -1, 1/4, 1)
991
t x h*f(t,x,y) y h*g(t,x,y)
992
0 1 -0.25 -1 0.50
993
1/4 0.75 -0.12 -0.50 0.29
994
1/2 0.63 -0.054 -0.21 0.19
995
3/4 0.63 -0.0078 -0.031 0.11
996
1 0.63 0.020 0.079 0.071
997
998
To numerically approximate y(1), where `y''+ty'+y=0`, `y(0)=1`, `y'(0)=0`::
999
1000
sage: t,x,y=PolynomialRing(RR,3,"txy").gens()
1001
sage: f = y; g = -x-y*t
1002
sage: eulers_method_2x2(f,g, 0, 1, 0, 1/4, 1)
1003
t x h*f(t,x,y) y h*g(t,x,y)
1004
0 1 0.00 0 -0.25
1005
1/4 1.0 -0.062 -0.25 -0.23
1006
1/2 0.94 -0.11 -0.46 -0.17
1007
3/4 0.88 -0.15 -0.62 -0.10
1008
1 0.75 -0.17 -0.68 -0.015
1009
1010
AUTHORS:
1011
1012
- David Joyner
1013
"""
1014
if algorithm=="table":
1015
print "%10s %20s %25s %20s %20s"%("t", "x","h*f(t,x,y)","y", "h*g(t,x,y)")
1016
n=int((1.0)*(t1-t0)/h)
1017
t00 = t0; x00 = x0; y00 = y0
1018
soln = [[t00,x00,y00]]
1019
for i in range(n+1):
1020
if algorithm=="table":
1021
print "%10r %20r %25r %20r %20r"%(t00,x00,h*f(t00,x00,y00),y00,h*g(t00,x00,y00))
1022
x01 = x00 + h*f(t00,x00,y00)
1023
y00 = y00 + h*g(t00,x00,y00)
1024
x00 = x01
1025
t00 = t00 + h
1026
soln.append([t00,x00,y00])
1027
if algorithm!="table":
1028
return soln
1029
1030
def eulers_method_2x2_plot(f,g, t0, x0, y0, h, t1):
1031
r"""
1032
Plots solution of ODE
1033
1034
This plots the soln in the rectangle ``(xrange[0],xrange[1])
1035
x (yrange[0],yrange[1])`` and plots using Euler's method the
1036
numerical solution of the 1st order ODEs `x' = f(t,x,y)`,
1037
`x(a)=x_0`, `y' = g(t,x,y)`, `y(a) = y_0`.
1038
1039
*For pedagogical purposes only.*
1040
1041
EXAMPLES::
1042
1043
sage: from sage.calculus.desolvers import eulers_method_2x2_plot
1044
1045
The following example plots the solution to
1046
`\theta''+\sin(\theta)=0`, `\theta(0)=\frac 34`, `\theta'(0) =
1047
0`. Type ``P[0].show()`` to plot the solution,
1048
``(P[0]+P[1]).show()`` to plot `(t,\theta(t))` and
1049
`(t,\theta'(t))`::
1050
1051
sage: f = lambda z : z[2]; g = lambda z : -sin(z[1])
1052
sage: P = eulers_method_2x2_plot(f,g, 0.0, 0.75, 0.0, 0.1, 1.0)
1053
"""
1054
n=int((1.0)*(t1-t0)/h)
1055
t00 = t0; x00 = x0; y00 = y0
1056
soln = [[t00,x00,y00]]
1057
for i in range(n+1):
1058
x01 = x00 + h*f([t00,x00,y00])
1059
y00 = y00 + h*g([t00,x00,y00])
1060
x00 = x01
1061
t00 = t00 + h
1062
soln.append([t00,x00,y00])
1063
Q1 = line([[x[0],x[1]] for x in soln], rgbcolor=(1/4,1/8,3/4))
1064
Q2 = line([[x[0],x[2]] for x in soln], rgbcolor=(1/2,1/8,1/4))
1065
return [Q1,Q2]
1066
1067
def desolve_rk4_determine_bounds(ics,end_points=None):
1068
"""
1069
Used to determine bounds for numerical integration.
1070
1071
- If end_points is None, the interval for integration is from ics[0]
1072
to ics[0]+10
1073
1074
- If end_points is a or [a], the interval for integration is from min(ics[0],a)
1075
to max(ics[0],a)
1076
1077
- If end_points is [a,b], the interval for integration is from min(ics[0],a)
1078
to max(ics[0],b)
1079
1080
EXAMPLES::
1081
1082
sage: from sage.calculus.desolvers import desolve_rk4_determine_bounds
1083
sage: desolve_rk4_determine_bounds([0,2],1)
1084
(0, 1)
1085
1086
::
1087
1088
sage: desolve_rk4_determine_bounds([0,2])
1089
(0, 10)
1090
1091
::
1092
1093
sage: desolve_rk4_determine_bounds([0,2],[-2])
1094
(-2, 0)
1095
1096
::
1097
1098
sage: desolve_rk4_determine_bounds([0,2],[-2,4])
1099
(-2, 4)
1100
1101
"""
1102
if end_points is None:
1103
return((ics[0],ics[0]+10))
1104
if not isinstance(end_points,list):
1105
end_points=[end_points]
1106
if len(end_points)==1:
1107
return (min(ics[0],end_points[0]),max(ics[0],end_points[0]))
1108
else:
1109
return (min(ics[0],end_points[0]),max(ics[0],end_points[1]))
1110
1111
1112
def desolve_rk4(de, dvar, ics=None, ivar=None, end_points=None, step=0.1, output='list', **kwds):
1113
"""
1114
Solves numerically one first-order ordinary differential
1115
equation. See also ``ode_solver``.
1116
1117
INPUT:
1118
1119
input is similar to ``desolve`` command. The differential equation can be
1120
written in a form close to the plot_slope_field or desolve command
1121
1122
- Variant 1 (function in two variables)
1123
1124
- ``de`` - right hand side, i.e. the function `f(x,y)` from ODE `y'=f(x,y)`
1125
1126
- ``dvar`` - dependent variable (symbolic variable declared by var)
1127
1128
- Variant 2 (symbolic equation)
1129
1130
- ``de`` - equation, including term with ``diff(y,x)``
1131
1132
- ``dvar``` - dependent variable (declared as funciton of independent variable)
1133
1134
- Other parameters
1135
1136
- ``ivar`` - should be specified, if there are more variables or if the equation is autonomous
1137
1138
- ``ics`` - initial conditions in the form [x0,y0]
1139
1140
- ``end_points`` - the end points of the interval
1141
1142
- if end_points is a or [a], we integrate on between min(ics[0],a) and max(ics[0],a)
1143
- if end_points is None, we use end_points=ics[0]+10
1144
1145
- if end_points is [a,b] we integrate on between min(ics[0],a) and max(ics[0],b)
1146
1147
- ``step`` - (optional, default:0.1) the length of the step (positive number)
1148
1149
- ``output`` - (optional, default: 'list') one of 'list',
1150
'plot', 'slope_field' (graph of the solution with slope field)
1151
1152
OUTPUT:
1153
1154
Returns a list of points, or plot produced by list_plot,
1155
optionally with slope field.
1156
1157
1158
EXAMPLES::
1159
1160
sage: from sage.calculus.desolvers import desolve_rk4
1161
1162
Variant 2 for input - more common in numerics::
1163
1164
sage: x,y=var('x y')
1165
sage: desolve_rk4(x*y*(2-y),y,ics=[0,1],end_points=1,step=0.5)
1166
[[0, 1], [0.5, 1.12419127425], [1.0, 1.46159016229]]
1167
1168
Variant 1 for input - we can pass ODE in the form used by
1169
desolve function In this example we integrate bakwards, since
1170
``end_points < ics[0]``::
1171
1172
sage: y=function('y',x)
1173
sage: desolve_rk4(diff(y,x)+y*(y-1) == x-2,y,ics=[1,1],step=0.5, end_points=0)
1174
[[0.0, 8.90425710896], [0.5, 1.90932794536], [1, 1]]
1175
1176
Here we show how to plot simple pictures. For more advanced
1177
aplications use list_plot instead. To see the resulting picture
1178
use ``show(P)`` in Sage notebook. ::
1179
1180
sage: x,y=var('x y')
1181
sage: P=desolve_rk4(y*(2-y),y,ics=[0,.1],ivar=x,output='slope_field',end_points=[-4,6],thickness=3)
1182
1183
ALGORITHM:
1184
1185
4th order Runge-Kutta method. Wrapper for command ``rk`` in
1186
Maxima's dynamics package. Perhaps could be faster by using
1187
fast_float instead.
1188
1189
AUTHORS:
1190
1191
- Robert Marik (10-2009)
1192
"""
1193
if ics is None:
1194
raise ValueError, "No initial conditions, specify with ics=[x0,y0]."
1195
1196
if ivar is None:
1197
ivars = de.variables()
1198
ivars = [t for t in ivars if t != dvar]
1199
if len(ivars) != 1:
1200
raise ValueError, "Unable to determine independent variable, please specify."
1201
ivar = ivars[0]
1202
1203
if not is_SymbolicVariable(dvar):
1204
from sage.calculus.var import var
1205
from sage.calculus.all import diff
1206
from sage.symbolic.relation import solve
1207
if is_SymbolicEquation(de):
1208
de = de.lhs() - de.rhs()
1209
dummy_dvar=var('dummy_dvar')
1210
# consider to add warning if the solution is not unique
1211
de=solve(de,diff(dvar,ivar),solution_dict=True)
1212
if len(de) != 1:
1213
raise NotImplementedError, "Sorry, cannot find explicit formula for right-hand side of the ODE."
1214
de=de[0][diff(dvar,ivar)].subs(dvar==dummy_dvar)
1215
else:
1216
dummy_dvar=dvar
1217
1218
step=abs(step)
1219
de0=de._maxima_()
1220
maxima("load('dynamics)")
1221
lower_bound,upper_bound=desolve_rk4_determine_bounds(ics,end_points)
1222
sol_1, sol_2 = [],[]
1223
if lower_bound<ics[0]:
1224
cmd="rk(%s,%s,%s,[%s,%s,%s,%s])\
1225
"%(de0.str(),str(dummy_dvar),str(ics[1]),str(ivar),str(ics[0]),lower_bound,-step)
1226
sol_1=maxima(cmd).sage()
1227
sol_1.pop(0)
1228
sol_1.reverse()
1229
if upper_bound>ics[0]:
1230
cmd="rk(%s,%s,%s,[%s,%s,%s,%s])\
1231
"%(de0.str(),str(dummy_dvar),str(ics[1]),str(ivar),str(ics[0]),upper_bound,step)
1232
sol_2=maxima(cmd).sage()
1233
sol_2.pop(0)
1234
sol=sol_1
1235
sol.extend([[ics[0],ics[1]]])
1236
sol.extend(sol_2)
1237
1238
if output=='list':
1239
return sol
1240
from sage.plot.plot import list_plot
1241
from sage.plot.plot_field import plot_slope_field
1242
R = list_plot(sol,plotjoined=True,**kwds)
1243
if output=='plot':
1244
return R
1245
if output=='slope_field':
1246
XMIN=sol[0][0]
1247
YMIN=sol[0][1]
1248
XMAX=XMIN
1249
YMAX=YMIN
1250
for s,t in sol:
1251
if s>XMAX:XMAX=s
1252
if s<XMIN:XMIN=s
1253
if t>YMAX:YMAX=t
1254
if t<YMIN:YMIN=t
1255
return plot_slope_field(de,(ivar,XMIN,XMAX),(dummy_dvar,YMIN,YMAX))+R
1256
1257
raise ValueError, "Option output should be 'list', 'plot' or 'slope_field'."
1258
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
sol=odeint(func, ics, times, args=args, Dfun=Dfun, rtol=rtol, atol=atol,
1554
tcrit=tcrit, h0=h0, hmax=hmax, hmin=hmin, ixpr=ixpr, mxstep=mxstep,
1555
mxhnil=mxhnil, mxordn=mxordn, mxords=mxords, printmessg=printmessg)
1556
1557
return sol
1558
1559