New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DOC: integrate: clarify that event and jac must have the same signature as fun #18876
Comments
Could you give a minimal example showing the problem, and also the Scipy version? The below, which has an event function without # ball falling under gravity with friction; terminate when y = 0
import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import solve_ivp
def fallde(t, y):
pos, vel = y
acc = -9.8 - 0.1*vel
return np.array([vel,acc])
def hitground(t, y):
return y[0]
hitground.terminal = True
sol = solve_ivp(fallde, [0, 10], [10, 0], events=[hitground])
plt.plot(sol.t, sol.y.T, '-x') Resulting figure: |
from within jupyter-lab
what's the right way to find the scipy version from within python?
-----------------
# scipy version 1.10.1. # this is line 1
# python version 3.11.3
import numpy as np
import scipy.integrate as sint
tmax = 4000
def rhs(t, y, alpha,beta):
xdot = alpha*y[1]*y[2]
ydot = -beta*y[2]*y[0]
zdot = -y[0]*y[1]
return np.array([xdot, ydot, zdot])
def eventx1(t,y,alpha,beta):
return y[0]
eventx1.direction = 1.0
def eventx2(t,y):
return y[0]
eventx2.direction = 1.0
# with *args works
out1 = sint.solve_ivp(rhs, [0,tmax], [0.5, 0.5, 0], args=[0.4, 0.4],
dense_output=True, events = eventx1)
print('works with *args')
#without *args. The following line is line 29
out2 = sint.solve_ivp(rhs, [0,tmax], [0.5, 0.5, 0], args=[0.4, 0.4],
dense_output=True, events = eventx2)
print("doesn't work with *args")
##################
works with *args
…---------------------------------------------------------------------------TypeError
Traceback (most recent call last)
Cell In[25], line 29 26 print('works with *args') 28 #without
*args---> 29 out2 = sint.solve_ivp(rhs, [0,tmax], [0.5, 0.5, 0],
args=[0.4, 0.4], dense_output=True, events = eventx2) 30
print("doesn't work with *args")
File /usr/local/lib/python3.11/site-packages/scipy/integrate/_ivp/ivp.py:582,
in solve_ivp(fun, t_span, y0, method, t_eval, dense_output, events,
vectorized, args, **options) 575 if args is not None: 576 #
Wrap user functions in lambdas to hide the additional parameters.
577 # The original event function is passed as a keyword argument
to the 578 # lambda to keep the original function in scope
(i.e., avoid the 579 # late binding closure "gotcha"). 580
events = [lambda t, x, event=event: event(t, x, *args) 581
for event in events]--> 582 g = [event(t0, y0) for event in
events] 583 t_events = [[] for _ in range(len(events))] 584
y_events = [[] for _ in range(len(events))]
File /usr/local/lib/python3.11/site-packages/scipy/integrate/_ivp/ivp.py:582,
in <listcomp>(.0) 575 if args is not None: 576 # Wrap user
functions in lambdas to hide the additional parameters. 577 #
The original event function is passed as a keyword argument to the
578 # lambda to keep the original function in scope (i.e., avoid
the 579 # late binding closure "gotcha"). 580 events =
[lambda t, x, event=event: event(t, x, *args) 581 for
event in events]--> 582 g = [event(t0, y0) for event in events] 583
t_events = [[] for _ in range(len(events))] 584 y_events = [[] for
_ in range(len(events))]
File /usr/local/lib/python3.11/site-packages/scipy/integrate/_ivp/ivp.py:580,
in solve_ivp.<locals>.<lambda>(t, x, event) 574 if events is not
None: 575 if args is not None: 576 # Wrap user
functions in lambdas to hide the additional parameters. 577
# The original event function is passed as a keyword argument to the
578 # lambda to keep the original function in scope (i.e.,
avoid the 579 # late binding closure "gotcha").--> 580
events = [lambda t, x, event=event: event(t, x, *args) 581
for event in events] 582 g = [event(t0, y0) for
event in events] 583 t_events = [[] for _ in
range(len(events))]
TypeError: eventx2() takes 2 positional arguments but 4 were given
On Fri, Jul 14, 2023 at 8:03 PM Rory Yorke ***@***.***> wrote:
Could give a minimal example showing the problem, and also the Scipy
version?
The below, which has an event function without *args, works with Scipy
1.10.1, Numpy 1.24.3, Python 3.10.11, running on an Ubuntu 20.04 x64
systems:
# ball falling under gravity with friction; terminate when y = 0
import matplotlib.pyplot as plt
import numpy as npfrom scipy.integrate import solve_ivp
def fallde(t, y):
pos, vel = y
acc = -9.8 - 0.1*vel
return np.array([vel,acc])
def hitground(t, y):
return y[0]
hitground.terminal = True
sol = solve_ivp(fallde, [0, 10], [10, 0], events=[hitground])
plt.plot(sol.t, sol.y.T, '-x')
Resulting figure:
[image: image]
<https://user-images.githubusercontent.com/110974/253712858-1b0593ba-368f-47f9-b3e7-35afae598406.png>
—
Reply to this email directly, view it on GitHub
<#18876 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABT3XVWFTHBNYOIY4C6LM6LXQIB7NANCNFSM6AAAAAA2KAFHFU>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
--
John D Sahr --- ***@***.***
|
import scipy
print(scipy.__version__) |
Thanks. I did a google search "how to find scipy version within python"
and all I found was junk. It's amazing how simple the correct answer is.
…On Sat, Jul 15, 2023 at 9:08 AM Robert Kern ***@***.***> wrote:
import scipyprint(scipy.__version__)
—
Reply to this email directly, view it on GitHub
<#18876 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABT3XVXHMOZ5H5F76HIA2ZDXQK56LANCNFSM6AAAAAA2KAFHFU>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
--
John D Sahr --- ***@***.***
|
I see: it's because you're using the
|
So, I was about a quarter right.
I appreciate you taking the time on this.
jds
…On Sat, Jul 15, 2023 at 8:13 PM Rory Yorke ***@***.***> wrote:
I see: it's because you're using the args parameter to solve_ivp. These
extra arguments are passed not only to fun (the DE function), but also
events and jac. This *is* documented with the args parameter (see below
and in the on-line docs
<https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.solve_ivp.html#scipy-integrate-solve-ivp>
), but it is a little confusing (I certainly didn't realize this when
trying to reproduce your problem), and there could be a reference to args
in the definitions of the fun, events, and jac parameters ("see also
args", or "if specified, args is also passed to this function", etc.)
args : tuple, optional
Additional arguments to pass to the user-defined functions. If given, the
additional arguments are passed to all user-defined functions. So if, for
example, fun has the signature fun(t, y, a, b, c), then jac (if given) and
any event functions must have the same signature, and args must be a tuple
of length 3.
—
Reply to this email directly, view it on GitHub
<#18876 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABT3XVR5BGBSS6V3HI7BZQTXQNL57ANCNFSM6AAAAAA2KAFHFU>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
--
John D Sahr --- ***@***.***
|
Issue with current documentation:
scipy.integrate.solve_ivp()
the doc for the the keyword argument "events" begins as follows ---
events: callable, or list of callables, optional
Events to track. If None (default), no events will be tracked. Each event occurs at the zeros of a continuous function of time and state. Each function must have the signature event(t, y) and return a float.
The signature as stated is incorrect; it should in fact be
event(t, y, *args)
I figured this out by staring at error messages along the lines of "the event signature has 2 arguments; 4 were given")
The extra arguments were being supplied internally by solve_ivp()
Idea or request for content:
Two proposed solutions:
(1) update the docs to show the proper signature, or
(2) In principle you could detect whether or not the *args had been included (and add them if they weren't), but that just seems like asking for trouble somehow; for example, you'd have to modify the source code and then test the bejeezus out of it.
I'd recommend (1), because it won't break any existing user's code, doesn't touch the source code, and will help new users use the existing code correctly.
The reason that *args are needed is that solve_ivp() is going to tiptoe through a region where an event occurred, probably calling itself with a reduced time-step or custom t_eval array. It's going to need *args to do that. Alternatively, solve_ivp() will do some sort of interpolation fun and games (e.g. dense_output=True) with a zero-finder. If the latter is true, then the existing signature would be correct ... but it obviously isn't correct.
On the other hand, one could argue that the interpolation approach is simpler (and superior) to the "tiptoe" approach, in which case the *args shouldn't be necessary, and the original signature correct.
If this was an initial offering of solve_ivp then I'd recommend solution (3), but solve_ivp has been out in the world for a while, so better to just update the docs. It won't hurt any code which doesn't have *args.
Additional context (e.g. screenshots, GIFs)
No response
The text was updated successfully, but these errors were encountered: