from scipy.integrate import solve_ivp
def myfunc(a,b,c,d,e,f,g,h):
t=a+2
d=b+3
return t,d
a=1
b=2
c=3
d=4
e=5
f=6
g=7
e=8
solve_ivp(lambda t,s:myfunc(a,b,c,d,e,f,g,h),np.linspace(0,100),3)
This is the type of problem I'm facing. Scipy's solve_ivp()
solves a function with the syntax solve_ivp(function,timerange,initial guess,**kwargs)
.
I have a function (say myfunc()
) that takes in a large amount of arguments, does some mathematical manipulation on them, and returns 2 variables. I've included a sample here. I want to make this function the reference for solve_ivp()
. I've done it with lambda
but I'm running into a too many values to unpack (expected 2)
error.
How do I resolve this?
This is another case where I should have just asked for the full traceback, rather than making a quick guess as to the problem (see my comment).
Running your code:
...: integrate.solve_ivp(lambda t,s:myfunc(a,b,c,d,e,f,g,h),np.linspace(0,100),3)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[9], line 15
12 g=7
13 e=8
---> 15 integrate.solve_ivp(lambda t,s:myfunc(a,b,c,d,e,f,g,h),np.linspace(0,100),3)
File ~/.local/lib/python3.10/site-packages/scipy/integrate/_ivp/ivp.py:524, in solve_ivp(fun, t_span, y0, method, t_eval, dense_output, events, vectorized, args, **options)
519 if method not in METHODS and not (
520 inspect.isclass(method) and issubclass(method, OdeSolver)):
521 raise ValueError("`method` must be one of {} or OdeSolver class."
522 .format(METHODS))
--> 524 t0, tf = map(float, t_span)
526 if args is not None:
527 # Wrap the user's fun (and jac, if given) in lambdas to hide the
528 # additional parameters. Pass in the original fun as a keyword
529 # argument to keep it in the scope of the lambda.
530 try:
ValueError: too many values to unpack (expected 2)
This is an unpacking error, in the
t0, tf = map(float, t_span)
This trying to get 2 float values the the t_span
argument, and them in 2 variables, t0,tf
. Instead you supply a arange(100)
!
Running it instead with (0,100):
In [12]: integrate.solve_ivp(lambda t,s:myfunc(a,b,c,d,e,f,g,h),(0,100),3)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[12], line 1
----> 1 integrate.solve_ivp(lambda t,s:myfunc(a,b,c,d,e,f,g,h),(0,100),3)
File ~/.local/lib/python3.10/site-packages/scipy/integrate/_ivp/ivp.py:568, in solve_ivp(fun, t_span, y0, method, t_eval, dense_output, events, vectorized, args, **options)
565 if method in METHODS:
566 method = METHODS[method]
--> 568 solver = method(fun, t0, y0, tf, vectorized=vectorized, **options)
570 if t_eval is None:
571 ts = [t0]
File ~/.local/lib/python3.10/site-packages/scipy/integrate/_ivp/rk.py:89, in RungeKutta.__init__(self, fun, t0, y0, t_bound, max_step, rtol, atol, vectorized, first_step, **extraneous)
85 def __init__(self, fun, t0, y0, t_bound, max_step=np.inf,
86 rtol=1e-3, atol=1e-6, vectorized=False,
87 first_step=None, **extraneous):
88 warn_extraneous(extraneous)
---> 89 super().__init__(fun, t0, y0, t_bound, vectorized,
90 support_complex=True)
91 self.y_old = None
92 self.max_step = validate_max_step(max_step)
File ~/.local/lib/python3.10/site-packages/scipy/integrate/_ivp/base.py:135, in OdeSolver.__init__(self, fun, t0, y0, t_bound, vectorized, support_complex)
133 self.t_old = None
134 self.t = t0
--> 135 self._fun, self.y = check_arguments(fun, y0, support_complex)
136 self.t_bound = t_bound
137 self.vectorized = vectorized
File ~/.local/lib/python3.10/site-packages/scipy/integrate/_ivp/base.py:17, in check_arguments(fun, y0, support_complex)
14 y0 = y0.astype(dtype, copy=False)
16 if y0.ndim != 1:
---> 17 raise ValueError("`y0` must be 1-dimensional.")
19 if not np.isfinite(y0).all():
20 raise ValueError("All components of the initial state `y0` must be finite.")
ValueError: `y0` must be 1-dimensional.
So now it doesn't like the scalar 3
for y0
. Change that to a list [3]
(or equivalent array):
In [13]: integrate.solve_ivp(lambda t,s:myfunc(a,b,c,d,e,f,g,h),(0,100),[3])
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[13], line 1
----> 1 integrate.solve_ivp(lambda t,s:myfunc(a,b,c,d,e,f,g,h),(0,100),[3])
File ~/.local/lib/python3.10/site-packages/scipy/integrate/_ivp/ivp.py:568, in solve_ivp(fun, t_span, y0, method, t_eval, dense_output, events, vectorized, args, **options)
565 if method in METHODS:
566 method = METHODS[method]
--> 568 solver = method(fun, t0, y0, tf, vectorized=vectorized, **options)
570 if t_eval is None:
571 ts = [t0]
File ~/.local/lib/python3.10/site-packages/scipy/integrate/_ivp/rk.py:94, in RungeKutta.__init__(self, fun, t0, y0, t_bound, max_step, rtol, atol, vectorized, first_step, **extraneous)
92 self.max_step = validate_max_step(max_step)
93 self.rtol, self.atol = validate_tol(rtol, atol, self.n)
---> 94 self.f = self.fun(self.t, self.y)
95 if first_step is None:
96 self.h_abs = select_initial_step(
97 self.fun, self.t, self.y, self.f, self.direction,
98 self.error_estimator_order, self.rtol, self.atol)
File ~/.local/lib/python3.10/site-packages/scipy/integrate/_ivp/base.py:154, in OdeSolver.__init__.<locals>.fun(t, y)
152 def fun(t, y):
153 self.nfev += 1
--> 154 return self.fun_single(t, y)
File ~/.local/lib/python3.10/site-packages/scipy/integrate/_ivp/base.py:23, in check_arguments.<locals>.fun_wrapped(t, y)
22 def fun_wrapped(t, y):
---> 23 return np.asarray(fun(t, y), dtype=dtype)
Cell In[13], line 1, in <lambda>(t, s)
----> 1 integrate.solve_ivp(lambda t,s:myfunc(a,b,c,d,e,f,g,h),(0,100),[3])
NameError: name 'h' is not defined
Now we have a missing variable, h
. Where's that supposed to come from?
Correct the global variable assignments:
In [17]: e=5; h=8
In [18]: integrate.solve_ivp(lambda t,s:myfunc(a,b,c,d,e,f,g,h),(0,100),[3,3])
Out[18]:
message: The solver successfully reached the end of the integration interval.
success: True
status: 0
t: [ 0.000e+00 9.384e-02 1.032e+00 1.042e+01 1.000e+02]
y: [[ 3.000e+00 3.282e+00 6.097e+00 3.425e+01 3.030e+02]
[ 3.000e+00 3.469e+00 8.161e+00 5.508e+01 5.030e+02]]
sol: None
t_events: None
y_events: None
nfev: 26
njev: 0
nlu: 0
Now it runs, though I'm not sure what good that is. The function always returns (a+2,b+3), i.e. (3,5). Which you integrate over the range (0,100), so the returned y
is the initial value + 100*(3,5), ie. (303, 503):
In [20]: 3+_18['t']*3
Out[20]:
array([ 3. , 3.28153316, 6.09686481, 34.25018124,
303. ])
In [21]: 3+_18['t']*5
Out[21]:
array([ 3. , 3.46922194, 8.16144135, 55.0836354 ,
503. ])
Your function returns 2 values, a function of globals a
and b
. So y0
also needs to be shape (2). solve_ivp
will a scalar t
, and the 2 element y
value, though your lambda
throws those away. I tried to use lambda t,s: my_func(t,s,a,b,...)
, but kept having problems with it trying to return (t,[s+(2,3)]), a 'ragged array'.
I could use
integrate.solve_ivp(lambda t,s:myfunc(a,b,t,s,c,d,e,f),(0,100),[0,0])
putting t,s
in slots where myfunc
will ignore them.