RKkit 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
RKkit/RKformula.py ADDED
@@ -0,0 +1,474 @@
1
+ # -*- coding: utf-8 -*-
2
+ r"""
3
+
4
+ Explore properties of Runge-Kutta methods.
5
+
6
+ AUTHOR:
7
+
8
+ - Thierry Dumont (2016, 2018. 2020: Python 3, _persistance decorator)
9
+ Institut C. Jordan, Lyon, France.
10
+ """
11
+
12
+ # ****************************************************************************
13
+ # Copyright (C) 2018 Thierry Dumont tdumont@math.univ-lyon1.fr
14
+ #
15
+ # This program is free software: you can redistribute it and/or modify
16
+ # it under the terms of the GNU General Public License as published by
17
+ # the Free Software Foundation, either version 2 of the License, or
18
+ # (at your option) any later version.
19
+ # https://www.gnu.org/licenses/
20
+ # ****************************************************************************
21
+
22
+ from sage.structure.sage_object import SageObject
23
+ from sage.rings.infinity import minus_infinity
24
+ from sage.matrix.constructor import Matrix,matrix
25
+ from sage.matrix.special import identity_matrix
26
+ from sage.modules.free_module_element import vector
27
+ from sage.rings.qqbar import *
28
+ from sage.calculus.functional import derivative
29
+ from sage.rings.imaginary_unit import I
30
+ from sage.symbolic.ring import SR
31
+ from sage.rings.real_double import RDF
32
+ from sage.functions.log import exp
33
+ from sage.functions.generalized import sign
34
+ #
35
+ from .RKTrees import *
36
+ #
37
+ from .RKExceptions import *
38
+ from .RKPolutilities import *
39
+ #
40
+ import functools
41
+ #
42
+ class RKformula(SageObject):
43
+ r"""
44
+ Store coefficients of a Runge-Kutta method, compute some of its
45
+ properties.
46
+
47
+ EXAMPLES::
48
+
49
+ sage: F = RKformula(A,B)
50
+
51
+ """
52
+ def __init__(self,F):
53
+ """
54
+ Initilalize ``self``. F is a Runge-Kutta class.
55
+
56
+ EXAMPLES::
57
+
58
+ sage: R = RK4()
59
+ sage: F = RKformula(R)
60
+ """
61
+
62
+ # force coefficients to live in AA:
63
+ self.A = F.A.change_ring(AA)
64
+ self.B = vector([ AA(b) for b in F.B])
65
+ self.C = vector([ AA(c) for c in F.C])
66
+ self.D = AA
67
+ self.R = AA["z"]# PolynomialRing(AA, 'z')
68
+ self.s = self.A.dimensions()[1]
69
+ # as computing properties can be slow, we will cache them here as
70
+ # soon as they are computed:
71
+ self.known_properties={}
72
+
73
+
74
+ def _persistance(foo):
75
+ """
76
+ Decorator: caches results of "foo" in self.known_properties
77
+ """
78
+ @functools.wraps(foo)
79
+ def magic( self, *args, **kwargs ):
80
+ if foo.__name__ in self.known_properties:
81
+ return self.known_properties[foo.__name__]
82
+ else:
83
+ x=foo( self, *args, **kwargs )
84
+ self.known_properties[foo.__name__]=x
85
+ return x
86
+ return magic
87
+
88
+ def _latex_(self):
89
+ r"""
90
+ Return the LaTeX representation of X.
91
+ """
92
+ return latex(self.A)+" "+latex(self.B)
93
+
94
+ def n_stages(self):
95
+ """
96
+ Return number of stages.
97
+ """
98
+ return self.s
99
+
100
+ @_persistance
101
+ def stability_function(self):
102
+ """
103
+
104
+ Compute the stability function.
105
+
106
+ EXAMPLES::
107
+
108
+ sage: F.stability_function()
109
+
110
+ (1/3*z + 1)/(1/6*z^2 - 2/3*z + 1)
111
+ """
112
+ z = self.R.gen()
113
+ Rng = self.D
114
+ K = matrix(Rng,[self.B for i in range(0,self.s)])
115
+ II = identity_matrix(Rng,self.s)
116
+ D = II - z*self.A
117
+ N = D + z*K
118
+ return N.determinant()/D.determinant()
119
+
120
+ @_persistance
121
+ def A_is_invertible(self):
122
+ """
123
+
124
+ Test if the matrix A part of the Butcher array is invertible.
125
+
126
+ EXAMPLES::
127
+
128
+ sage: F.A_is_invertible()
129
+
130
+ True
131
+ """
132
+ return self.A.is_invertible()
133
+
134
+ @_persistance
135
+ def is_explicit(self):
136
+ """
137
+ Test if the method is explicit.
138
+
139
+ EXAMPLES::
140
+
141
+ sage: F.is_explicit()
142
+
143
+ False
144
+
145
+ """
146
+ return self.stability_function().denominator().degree()==0
147
+
148
+ @_persistance
149
+ def poles_of_stability_function(self):
150
+ """
151
+ Compute the poles of the stability function, if any.
152
+ """
153
+ if self.is_explicit():
154
+ return []
155
+ else:
156
+ RD = self.stability_function().denominator()
157
+ Poles,ok,nr = roots_checked(RD,QQbar)
158
+ if not ok:
159
+ raise RootsException(nr,RD)
160
+ else:
161
+ return Poles
162
+
163
+ @_persistance
164
+ def real_part_of_poles_all_positive(self):
165
+ """
166
+ Documentation is in the name of this method!
167
+
168
+ Returns: (all poles have >=0 real part?) and number of poles==0.
169
+ """
170
+ Poles = self.poles_of_stability_function()
171
+ llp = all(s[0].real()>=0 for s in Poles)
172
+ llzero = len([s for s in Poles if s[0].real()==0 ])
173
+ return llp,llzero
174
+
175
+ @_persistance
176
+ def order_of_stability_function(self):
177
+ """
178
+ Documentation is in the name of this method!
179
+ """
180
+ order = 0
181
+ z = self.R.gen()
182
+ while derivative(self.stability_function(),z,order)(z = 0)==1: \
183
+ order+= 1
184
+ return order -1
185
+
186
+ @_persistance
187
+ def module_of_stability_function_squared(self):
188
+ """
189
+ Documentation is in the name of this method!
190
+ """
191
+ return self.stability_function()*conjugate(self.stability_function())
192
+
193
+ @_persistance
194
+ def stability_function_on_im_axis(self):
195
+ """
196
+ Trace of the stability function on the imaginary axis.
197
+ """
198
+ R = self.stability_function()
199
+ P = AA['x']
200
+ x = P.gen()
201
+ return R(z = QQbar(I)*x)
202
+
203
+ @_persistance
204
+ def squared_module_of_stability_function_on_Im(self):
205
+ """
206
+ Square of the module of the trace of the stability function on
207
+ the imaginary axis.
208
+ """
209
+ RIaxe = self.stability_function_on_im_axis()
210
+ RIaxeN = RIaxe.numerator()
211
+ RIaxeD = RIaxe.denominator()
212
+ m2N = RIaxeN*conj(RIaxeD)
213
+ m2NR = realpart(m2N)
214
+ m2NI = impart(m2N)
215
+ m2n = generic_power(m2NR,2)+generic_power(m2NI,2)
216
+ m2d = RIaxeD*conj(RIaxeD)
217
+ return m2n/generic_power(m2d,2)
218
+
219
+ @_persistance
220
+ def is_module_of_stability_function_constant_on_Im(self):
221
+ """
222
+ Documentation is in the name of this method!
223
+ """
224
+ m2 = self.squared_module_of_stability_function_on_Im()
225
+ x = m2.parent().gen()
226
+ return derivative(m2,x) == 0
227
+
228
+ @_persistance
229
+ def is_module_of_stability_function_less_than_1(self):
230
+ """
231
+ Is the module of the trace of the stability funtion on the
232
+ imaginary axis <1 ?
233
+ """
234
+ is_const = self.is_module_of_stability_function_constant_on_Im()
235
+ if is_const:
236
+ return True
237
+ else:
238
+ m2 = self.squared_module_of_stability_function_on_Im()
239
+ l1,ok,n = roots_checked(m2.numerator()-m2.denominator(),QQbar)
240
+ if not ok:
241
+ raise RootsException(n,m2.numerator()-m2.denominator())
242
+ else:
243
+ l1m1 = [s[0] for s in l1 if s[0].imag()==0 ]
244
+ l1m1realp = [s.real() for s in l1m1 if s >= 0]
245
+ l1r = len(l1m1realp)
246
+ if l1r==1:
247
+ # module value is 0, only in x == 0.
248
+ return m2(z=1) <1
249
+ else:
250
+ points = [(l1m1realp[i]+l1m1realp[i+1])/2 \
251
+ for i in range(0,l1r-1)]
252
+ points.append(l1m1realp[l1r-1]*3/2)
253
+ signes = [sign(abs(m2(x = s).real())-1) for s in points]
254
+ return len([x for x in signes if x >=0]) == 0
255
+
256
+ @_persistance
257
+ def is_A_stable(self):
258
+ """
259
+ Do we have an A_stable method ?
260
+
261
+ See: HW II , second edition, page 43.
262
+ """
263
+ return (self.is_module_of_stability_function_constant_on_Im() or \
264
+ self.is_module_of_stability_function_less_than_1()) and \
265
+ self.real_part_of_poles_all_positive()[0]
266
+
267
+ @_persistance
268
+ def is_stiffly_accurate(self):
269
+ """
270
+ Do we have a stiffly accurate method ?
271
+ """
272
+ # Proposition 3.8 in H-W. TII, page 45.
273
+ A = self.A
274
+ B = self.B
275
+ s1 = self.s-1
276
+ return self.is_A_stable() and \
277
+ (len([1 for j in range(0,self.s) if A[s1,j] != B[j]]) == 0 or \
278
+ len([1 for i in range(0,self.s) if A[i,0] != B[0]]) == 0)
279
+
280
+ @_persistance
281
+ def R_infinite(self):
282
+ r"""
283
+ R(\infty). See Hairer-Wanner t.II pages 45 and 375.
284
+ """
285
+ if self.A_is_invertible():
286
+ AI = self.A.inverse()
287
+ One = vector([AA(1) for i in range(0,self.s)])
288
+ ret = AA(1)-self.B.dot_product(AI*One)
289
+ ret.exactify()
290
+ return ret
291
+ else:
292
+ raise MatrixIsSingular("A")
293
+
294
+ @_persistance
295
+ def is_L_stable(self):
296
+ """
297
+ Do we have a L_stable method ?
298
+ """
299
+ if not self.is_A_stable():
300
+ return False
301
+ else:
302
+ R=self.stability_function()
303
+ return R.denominator().degree() > R.numerator().degree() or \
304
+ self.R_infinite().is_zero()
305
+
306
+
307
+ @_persistance
308
+ def M_matrix(self):
309
+ """
310
+
311
+ Compute the matrix M used by is_algebraically_stable(self) and
312
+ conserve_quadratic_invariants(self).
313
+
314
+ """
315
+ B = self.B
316
+ A = self.A
317
+ M = matrix(QQbar,self.s,self.s)
318
+ for i in range(0,self.s):
319
+ for j in range(0,self.s):
320
+ M[i,j] = B[i]*A[i,j]+B[j]*A[j,i]-B[i]*B[j]
321
+ return M
322
+
323
+ @_persistance
324
+ def is_algebraically_stable(self):
325
+ """
326
+
327
+ Is the method algebraically stable ?
328
+
329
+ """
330
+
331
+ if self.is_explicit() or any(s<0 for s in self.B):
332
+ return False
333
+ else:
334
+ M = self.M_matrix()
335
+ return all(s >=0 for s in M.list())
336
+
337
+ @_persistance
338
+ def is_Symmetric(self):
339
+ """
340
+ All is in the title.
341
+ """
342
+ P = matrix(AA,self.s,self.s)
343
+ for i in range(0,self.s):
344
+ P[i,self.s-i-1]=1
345
+ PA =P *self.A+ self.A*P
346
+ return all(self.B == Row for Row in PA.rows()) and \
347
+ self.B == P*self.B
348
+
349
+ @_persistance
350
+ def conserve_quadratic_invariants(self):
351
+ """
352
+ Documentation is in the name of this method!
353
+ """
354
+ return not self.is_explicit() and self.M_matrix().is_zero()
355
+
356
+ @_persistance
357
+ def is_Symplectic(self):
358
+ """
359
+
360
+ Test for simplexity. We can conclude (positively) only if
361
+ the matrix N (see above) is zero.
362
+
363
+ """
364
+ if self.is_explicit():
365
+ return False
366
+ elif self.M_matrix().is_zero():
367
+ return True
368
+ else:
369
+ return "Unknown"
370
+
371
+ def check_order_using_rooted_trees(self,order):
372
+ """
373
+
374
+ Check rooted tree at order 'order'.
375
+
376
+ """
377
+ self.RTrees = RKTrees()
378
+ t = False
379
+ for i in range(1,order+1):
380
+ t = self.RTrees.check_order(self.A,self.B,i)
381
+ if not t:
382
+ break
383
+ return t #True iff formula is at least of order "order".
384
+
385
+ @_persistance
386
+ def make_order_equations(self,order):
387
+ self.RTrees = RKTrees()
388
+ s=[]
389
+ for i in range(1,order+1):
390
+ s+=self.RTrees.make_order_equations(self.A,self.B,i)
391
+ return s
392
+ @_persistance
393
+ def stability_on_real_negative_axis(self):
394
+ """
395
+
396
+ In the case where the method is not A-stable, find the limit
397
+ of stability on the real negative axis.
398
+
399
+ """
400
+ if self.is_A_stable():
401
+ return minus_infinity
402
+ else:
403
+ p=generic_power(self.stability_function(),2)-1
404
+ r=[s[0] for s in sorted(p.numerator().roots(),reverse=True)
405
+ if s[0]<0]
406
+ if len(r)==0:
407
+ return minus_infinity
408
+ else:
409
+ return r[0]
410
+
411
+ @_persistance
412
+ def order(self):
413
+ """
414
+
415
+ Compute order of the method using rooted trees.
416
+
417
+ """
418
+ o = 0
419
+ while self.check_order_using_rooted_trees(o+1):
420
+ o+= 1
421
+ return o
422
+
423
+ @_persistance
424
+ def order_star_function(self):
425
+ """
426
+ Compute the order star function.
427
+ This is for drawing the "star" associated to the formula.
428
+ """
429
+ x=SR.var("x")
430
+ y=SR.var("y")
431
+ Rs = self.stability_function()
432
+ RRs=Rs.numerator().change_ring(RDF)/Rs.denominator().change_ring(RDF)
433
+ s=RRs(x+SR(I)*y)/exp(x+SR(I)*y)
434
+ return s.abs()
435
+
436
+ def compute_all_properties(self):
437
+ """
438
+
439
+ Compute all possible properties of the formula.
440
+
441
+ """
442
+ self.A_is_invertible()
443
+ self.is_explicit()
444
+ self.stability_function()
445
+ self.poles_of_stability_function()
446
+ self.real_part_of_poles_all_positive()
447
+ self.order_of_stability_function()
448
+ self.stability_function_on_im_axis()
449
+ self.squared_module_of_stability_function_on_Im()
450
+ self.is_module_of_stability_function_constant_on_Im()
451
+ self.is_module_of_stability_function_less_than_1()
452
+ self.is_A_stable()
453
+ self.is_stiffly_accurate()
454
+ self.is_L_stable()
455
+ self.is_algebraically_stable()
456
+ self.is_Symmetric()
457
+ self.is_Symplectic()
458
+ self.conserve_quadratic_invariants()
459
+ self.stability_on_real_negative_axis()
460
+ self.stability_on_real_negative_axis()
461
+ self.order()
462
+ self.order_star_function()
463
+
464
+ def print_all_known_properties(self):
465
+ """
466
+
467
+ Print all already computed properties.
468
+
469
+ """
470
+ donotprint=["A","B","C","D","R","s","RTrees","M_matrix"]
471
+ D=self.known_properties
472
+ for key in D:
473
+ if key not in donotprint:
474
+ print("-> ",key," : ",D[key],"\n")
RKkit/RKplot.py ADDED
@@ -0,0 +1,108 @@
1
+ from .RKExceptions import GraphicProblem
2
+ from sage.rings.infinity import minus_infinity
3
+ from sage.symbolic.ring import SR
4
+ from sage.rings.qqbar import QQbar
5
+ from sage.rings.imaginary_unit import I
6
+ from sage.functions.other import conjugate
7
+
8
+ from sage.plot.contour_plot import contour_plot
9
+
10
+ def RKplot(RKf,title="",Enlarge=4,TranslateX=0,
11
+ ncurves=1,limits=[],fill=False,type="stab"):
12
+ r"""
13
+ Plot isovalues of stability function or Order star.
14
+
15
+ PARAMETERS:
16
+ ----------
17
+
18
+ RKf: the formula (instantiation of RKformula).
19
+
20
+ Enlarge: plot is done in a window around (0,0) in \mathbb{C}.
21
+
22
+ We can Enlarge the size of the window by this factor (a real
23
+ positive number).
24
+
25
+ TranslateX : translate the origin in the window along x axis.
26
+
27
+ ncurves: number of curves to plot.
28
+
29
+ limits: [[min of x, max of x],[min of y, max of y]] for the window.
30
+ In most case, this is computed.
31
+
32
+ fill: to fill the plot (see contour_plot documentation).
33
+
34
+ type= 'stab' for stability function (default), 'star' for the Order star.
35
+
36
+ """
37
+ def sf1(x,y,P):
38
+ s = P(x+QQbar(I)*y)
39
+ return (s*conjugate(s)).real()
40
+
41
+ RDroots = RKf.poles_of_stability_function()
42
+ Rstab= RKf.stability_on_real_negative_axis()
43
+
44
+ # try to compute limits:
45
+ if Rstab==minus_infinity and RDroots == [] :
46
+ if limits==[]:
47
+ raise GraphicProblem(
48
+ "Cannot find limits. You must define: limits = [(x1,y1),(x2,y2)]")
49
+ else:
50
+ raise GraphicProblem("Mthod is not A_stable and no stab. limit is known")
51
+
52
+ else:
53
+ if limits == []:
54
+ if len(RDroots) > 0:
55
+ Lr = max(max([abs(s[0].n().real()) for s in RDroots]), \
56
+ max([abs(s[0].n().imag()) for s in RDroots]))
57
+ else:
58
+ Lr = 0
59
+
60
+ if Rstab != minus_infinity:
61
+ L = max(-Rstab,Lr)
62
+ Lm= -L*Enlarge
63
+ Lp= -Lm
64
+ elif RKf.is_A_stable:
65
+ L = Lr
66
+ Lm = -Enlarge*L
67
+ Lp = 3*Enlarge*L/2
68
+ else:
69
+ Lm = -L
70
+ Lp = L
71
+
72
+ L1 = (Lm,Lp)
73
+ L2 = (-Enlarge*L,Enlarge*L)
74
+
75
+ #
76
+ if limits != []:
77
+ L1 = limits[0]
78
+ L2 = limits[1]
79
+
80
+ # What to plot:
81
+ if type == "stab":
82
+ sf = lambda x,y: sf1(x,y,RKf.stability_function())
83
+ elif type == "star":
84
+ sf = RKf.order_star_function()
85
+ else:
86
+ raise AttributeError("RKplot: "+type+" :unknown plot type")
87
+
88
+ if title != "":
89
+ stitle = title
90
+ else:
91
+ if type == "stab":
92
+ stitle= "Stability function."
93
+ else:
94
+ stitle = "Order star."
95
+
96
+ # translate:
97
+ if TranslateX!=0:
98
+ d = (L1[1]-L1[0])*TranslateX/100.
99
+ L1 = (L1[0]-d,L1[1]-d)
100
+
101
+ x = SR.var("x")
102
+ y = SR.var("y")
103
+
104
+ return contour_plot(sf,(x,L1[0],L1[1]), (y,L2[0],L2[1]),
105
+ contours = [0,1],
106
+ labels = True,fill = fill, label_inline = True,
107
+ axes = True,colorbar = True,title = stitle)
108
+
RKkit/__init__.py ADDED
@@ -0,0 +1,10 @@
1
+ #
2
+ from .RKformula import RKformula
3
+ from .RKcolloc import colloc
4
+ from .RKplot import RKplot
5
+ from .RKRungeKutta import RungeKutta
6
+ #
7
+ from sage.rings.qqbar import *
8
+ from sage.rings.polynomial import laurent_polynomial_ring
9
+
10
+ __all__=["RKRungeKutta","RKformula","RKplot","RKcolloc","formulas"]
RKkit/formulas.py ADDED
@@ -0,0 +1,103 @@
1
+ from sage.rings.qqbar import AA
2
+ from sage.structure.sage_object import SageObject
3
+ from sage.matrix.constructor import matrix
4
+ from sage.modules.free_module_element import vector
5
+ from sage.misc.functional import sqrt
6
+ from RKkit.RKRungeKutta import *
7
+ class Gauss4(RungeKutta):
8
+ def __init__(self):
9
+ title="Gauss method, 2 points, order 4."
10
+ A=matrix(AA,[[1/AA(4),1/AA(4)-sqrt(3)/AA(6)],[1/AA(4)+sqrt(3)/AA(6),
11
+ 1/AA(4)]])
12
+ ##
13
+ B=vector([1/AA(2),1/AA(2)])
14
+ super().__init__(A,B,title)
15
+
16
+ class Lobatto4(RungeKutta):
17
+ def __init__(self):
18
+ title="Lobatto method, order 4"
19
+ # Lobatto order 4.See Hairer Nörsett, Wanner TI, page 211.
20
+ A=matrix(AA,[[0,0,0],[1/AA(4),1/AA(4),0],[0,1,0]])
21
+ B=vector(AA,[1/AA(6),AA(2)/AA(3),1/AA(6)])
22
+ super().__init__(A,B,title)
23
+
24
+ class Radau2a(RungeKutta):
25
+ def __init__(self):
26
+ title="Radau 2a method"
27
+ A=matrix(AA,[[5/AA(12),-1/AA(12)],[3/AA(4),1/AA(4)]])
28
+ ##
29
+ B=vector([3/AA(4),1/AA(4)])
30
+ super().__init__(A,B,title)
31
+
32
+ class Radau5(RungeKutta):
33
+ def __init__(self):
34
+ title="Radau5 method"
35
+ #
36
+ A=matrix(AA,3,3)
37
+ s6=AA(sqrt(6))
38
+ A[0,0]=1/AA(9)
39
+ A[0,1]=(-1-s6)/18
40
+ A[0,2]=(-1+s6)/18
41
+ #
42
+ A[1,0]=1/AA(9)
43
+ A[1,1]=(88+7*s6)/360
44
+ A[1,2]=(88-43*s6)/360
45
+ #
46
+ A[2,0]=1/AA(9)
47
+ A[2,1]=(88+43*s6)/360
48
+ A[2,2]=(88-7*s6)/360
49
+ ##
50
+ B=vector([1/AA(9),(16+s6)/36,(16-s6)/36])
51
+ super().__init__(A,B,title)
52
+
53
+
54
+ class RK4(RungeKutta):
55
+ def __init__(self):
56
+ title="Classical Runge-Kutta 4 explicit method"
57
+ A=matrix(AA,[[0,0,0,0],[1/AA(2),0,0,0],
58
+ [0,1/AA(2),0,0],[0,0,1,0]])
59
+ B=vector(AA,[1/AA(6),2/AA(6),2/AA(6),1/AA(6)])
60
+ super().__init__(A,B,title)
61
+
62
+ class SDIRK3(RungeKutta):
63
+ def __init__(self):
64
+ g=AA((2+sqrt(3))/6)
65
+ A=matrix(AA,[[g,0],[1-2*g,g]])
66
+ B=vector([1/AA(2),1/AA(2)])
67
+ title= "Sdirk3 method"
68
+ super().__init__(A,B,title)
69
+
70
+
71
+ class SDIRK5(RungeKutta):
72
+ def __init__(self):
73
+ title="SDIRKmethod, order 5"
74
+ # See Hairer and Wanner II, page 100.
75
+ A=matrix(AA,5,5)
76
+ gamm=1/AA(4)
77
+ for i in range(5):
78
+ A[i,i]=gamm
79
+ A[1,0]=1/AA(2)
80
+ A[2,0]=17/AA(50)
81
+ A[3,0]=371/AA(1360)
82
+ A[4,0]=25/AA(24)
83
+ #
84
+ A[2,1]=-1/AA(25)
85
+ A[3,1]=-137/AA(2720)
86
+
87
+ A[4,1]=-49/AA(48)
88
+ #
89
+ A[3,2]=15/AA(544)
90
+ A[4,2]=125/AA(16)
91
+ #
92
+ A[4,3]=-85/AA(12)
93
+ #
94
+ B=vector([25/AA(24),-49/AA(48),125/AA(16),-85/AA(12),1/AA(4)])
95
+ super().__init__(A,B,title)
96
+ class veryBad(RungeKutta):
97
+ def __init__(self):
98
+ title="Classical Runge-Kutta 4 explicit method, ill coded"
99
+ # 1/2,2/6 ... will be converted into floats, which are not exact!
100
+ A=matrix(AA,[[0,0,0,0],[1/2,0,0,0],
101
+ [0,1/2,0,0],[0,0,1,0]])
102
+ B=vector(AA,[1/6,2/6,2/6,1/6])
103
+ super().__init__(A,B,title)