High memory usage when doing direct transcription with sympy equations - python

I used sympy to derive, via lagrange, the equations of motion of my 3 link robot. The resultant equation of motion in the form (theta_dot_dot = f(theta, theta_dot)) turned out very complicated with A LOT of cos and sin. I then lambdified the functions to use with drake, replacing all the sympy.sin and sympy.cos with drake.sin, drake.cos.
The final function can be evaluated numerically (i.e. given theta, theta_dot, find theta_dot_dot) somewhat efficiently in the milliseconds range.
I then tried to use direct transcription to do trajectory optimization. Note I did not use the DirectTranscription library, instead manually added the necessary constraints.
The constraints are added roughly as follows:
for i in range(NUM_TIME_STEPS-1):
print("Adding constraints for t = " + str(i))
tau = mp.NewContinuousVariables(3, "tau_%d" % i)
next_state = mp.NewContinuousVariables(8, "state_%d" % (i+1))
for j in range(8):
mp.AddConstraint(next_state[j] <= (state_over_time[i] + TIME_INTERVAL*derivs(state_over_time[i], tau))[j])
mp.AddConstraint(next_state[j] >= (state_over_time[i] + TIME_INTERVAL*derivs(state_over_time[i], tau))[j])
state_over_time[i+1] = next_state
tau_over_time[i] = tau
The problem I'm facing right now is that on each iteration of adding constraints, I observe that my memory usage increases by around 70-100MB. This means that my number of time steps cannot go more than around 50 before the program crashes due to out of memory.
I'm wondering what I can do to make trajectory optimization work for my robot. Obviously I can try to simplify (by hand or otherwise) the equations of motions... but is there anything else I can try? Is it even normal that the constraints are taking up so much memory? Am I doing something very wrong here?

You're pushing drake's symbolic through your complex equations. Making that better is a good goal, but probably you want to avoid it by using the other overload for AddConstraint:
AddConstraint(your_method, lb, ub, vars)
https://drake.mit.edu/pydrake/pydrake.solvers.mathematicalprogram.html?highlight=addconstraint#pydrake.solvers.mathematicalprogram.MathematicalProgram.AddConstraint
That will use your python code as a function pointer, and should use autodiff instead of symbolic.

Related

solve an equation (with max-statement) in python

i am seeking a solution for the following equation in Python.
345-0.25*t = 37.5 * x_a
'with'
t = max(0, 10-x_a)*(20-10) + max(0,25-5*x_a)*(3-4) + max(0,4-0.25*x_a)*(30-12.5)
'x_a = ??'
If there is more than one solution to the problem (I am not even sure, whether this can happen from a mathematical point of view?), I want my code to return a positive(!) value for x_a, that minimizes t.
With my previous knowledge in the Basics of Python, Pandas and NumPy I actually have no clue, how to tackle this problem. Can someone give me a hint?
For Clarification: I inserted some exemplary numbers in the equation to make it easier to gasp the problem. In my final code, there might of course be different numbers for different scenarios. However, in every scenario x_a is the only unknown variable.
Update
I thought about the problem again and came up with the following solution, which yields the same result as the calculations done by Michał Mazur:
import itertools
from sympy import Eq, Symbol, solve
import numpy as np
x_a = Symbol('x_a')
possible_elements = np.array([10-x_a, 25-5*x_a, 4-0.25*x_a])
assumptions = np.array(list(itertools.product([True, False], repeat=3)))
for assumption in assumptions:
x_a = Symbol('x_a')
elements = assumption.astype(int) * possible_elements
t = elements[0]*(20-10) + elements[1]*(3-4) + elements[2]*(30-12.5)
eqn = Eq(300-0.25*t, 40*x_a)
solution = solve(eqn)
if len(solution)>2:
print('Warning! the code may suppress possible solutions')
if len(solution)==1:
solution = solution[0]
if (((float(possible_elements[0].subs(x_a,solution))) > 0) == assumption[0]) &\
(((float(possible_elements[1].subs(x_a,solution))) > 0) == assumption[1]) &\
(((float(possible_elements[2].subs(x_a,solution)))> 0) == assumption[2]):
print('solution:', solution)
Compared to the already suggested approach this may have an little advantage as it does not rely on testing all possible values and therefore can be used for very small as well as very big solutions without taking a lot of time (?). However, it probably only is useful as long as you don't have more complex functions for t (even having for example 5 max(...) statements and therefore (2^5=)32 scenarios to test seems quite cumbersome).
As far as I'm concerned, I just realized that my problem is even more complex as i thought. For my project the calculations needed to derive the value of "t" are pretty entangled and can not be written in just one equation. However it still is a function, that only relies on x_a. So I am still hoping for a Python-Solution similar to the proposed Solver in Excel... or I will stick to the approach of simply testing out all possible numbers.
If you are interested in a solution a bit different than the Python one, then I will give you a hand. Open up your Excel, with Solver extention and plug in
the data you are interested in cheking, as the following:
Into the E2 you plug the command I just have writen, into E4 you plug in
=300-0,25*E2
Into the F4 you plug:
=40*F2
Then you open up your Solver menu
Into the Set Objective you put the variable t, which you want to minimize.
Into Changing Variables you put the a.
Into Constraint Menu you put the equality of E4 and F4 cells.
You check the "Make Unconstarained Variables be non-negative" which will prevent your a variable to go below 0. Your method of computing is strictly non-linear, so you leave this option there.
You click solve. The computed value is presented in the screen.
The python approach I can think of:
minimumval=10100
minxa=10000
eps=0.01
for i in range(100000):
k=i/10000
x_a=k
t = max(0, 10-x_a)*(20-10) + max(0,25-5*x_a)*(3-4) + max(0,4-0.25*x_a)*(30-12.5)
val=abs(300-0.25*t-40*x_a)
if (val<eps):
if t<minimumval:
minimumval=t
minxa=x_a
It isn't direct solution, as in it only controls the error that you make in the equality by eps value. However, it gives solution.

Python Z3 solver not correctly reporting satisfiability for exponent constraints

I noticed the Z3 Solver library for python wasn't correctly reporting satisfiability for a problem involving exponents that I was working on. Specifically, it reported finding no solutions on cases where I knew a valid one -- unless I added constraints that effectively "told it the answer".
I simplified the problem to isolate it. In the code below, I'm asking it to find q and m such that q^m == 100. With the constraint 0 <= q < 100, you have, of course, q=10, m=2. But with the code below, it reports finding no solution (raise Z3Exception("model is not available")):
import z3.z3 as z
slv = z.Solver()
m = z.Int('m')
q = z.Int('q')
slv.add(100 == (q ** m))
slv.add(q >= 0)
slv.add(q < 100)
slv.add(m >= 0)
slv.add(m <= 100)
slv.check()
However, if you replace slv.add(m <= 100)) with slv.add(m <= 2) (or slv.add(m == 2)!), it has no problem finding the solution (of q=10, m=2).
Am I using Z3 wrong somehow?
I thought it would only report unsatisfiability ("model is not available") if it proved there was no solution and would otherwise hang while searching for a solution. Is that wrong? I didn't expect to be in a position where it only finds the solution if you shrink down the search space enough.
I haven't had this problem with any other operation besides exponentiation (e.g. addition, modulo, etc.).
You're misinterpreting what z3 is telling you. Change your line:
slv.check()
to:
print(slv.check())
print(slv.reason_unknown())
And you'll see it prints:
unknown
smt tactic failed to show goal to be sat/unsat (incomplete (theory arithmetic))
So, z3 doesn't know if your problem is sat or unsat; so you cannot ask for a model. The reason for this is the power operator: It introduces non-linearity, and the theory of non-linear integer equations is undecidable in general. That is, z3's solver is incomplete for this problem. In practice, this means z3 will apply a bunch of heuristics, and will hopefully solve the problem for you. But you can get unknown as well, as you observed.
It's not surprising that if you add extra constraints you're helping the solver and thus it finds an answer. You're just helping it further and those heuristics have an easier time. With different versions of z3, you can observe different behavior. (i.e., in the future, they might be able to solve this problem out-of-the-box, or maybe the heuristics will get worse and you helping it this way won't resolve the issue either.) Such is the nature of automatic-theorem proving with undecidable theories.
Bottom line: Any call to check can return sat, unsat, or unknown. Your program should check for all three possibilities and interpret the output accordingly.

Computing simultaneous Partial differential equation using FIPY in python

I was trying to solve pde using fipy, since I'm new to it; I'm unable to debug error in my code.
It is not giving any spatial pattern but just different monochromatic square(s) over a period of time.The equation which I'm trying to solve are-
(∂u(x,y,t))/∂t=G_1 (u,v)+d_11 ∇^2 u+d_12 ∇^2 v and
(∂v(x,y,t))/∂t=G_2 (u,v)+d_21 ∇^2 u+d_22 ∇^2 v
from fipy import *
nx=ny=200
dx=dy=0.25
L=dx*nx
dt=0.01
E=1.0
A=0.91881 #Alpha
B=0.0327 #Beta
D=0.05 #Delta
G=0.15 #Gamma
d11=0.1
d12=0.01
d21=0.5
d22=1.5
mesh =Grid2D(dx=dx,dy=dy,nx=nx,ny=ny)
u=CellVariable(name='u Variable',mesh=mesh) #pray
v=CellVariable(name='v Variable',mesh=mesh) #predator
u.setValue(GaussianNoiseVariable(mesh=mesh,mean=0.18,variance=0.01))
v.setValue(GaussianNoiseVariable(mesh=mesh,mean=0.6,variance=0.01))
eq_u=(TransientTerm(coeff=1,var=u)==u*(1-u)-(u*v*E)/(u+A*v)+ImplicitDiffusionTerm(coeff=d11,var=u)+ImplicitDiffusionTerm(coeff=d12,var=v))
eq_v=(TransientTerm(coeff=1,var=v)==B*v*(1-v)+(u*v*G)/(u+A*v)-D*v+ImplicitDiffusionTerm(coeff=d21,var=u)+ImplicitDiffusionTerm(coeff=d22,var=v))
#creating viewer
if __name__ == "__main__":
viewer_u=Viewer(vars=u,datamin=0.16,datamax=0.18)
viewer_u.plot()
viewer_v=Viewer(vars=v,datamin=0.0,datamax=0.4)
viewer_v.plot()
#solving
steps=250
for step in range(steps):
eq_u.solve(var=u,dt=dt)
eq_v.solve(var=v,dt=dt)
if __name__ == "__main__":
viewer_u.plot()
viewer_v.plot()
These equations are ODEs, not PDEs. FiPy may be able to solve them, but there are other tools much better optimized for solving ODEs, such as SciPy's odeint and scikits.odes.
I am not sure exactly what you mean by "It is not giving any pattern but just different monochromatic square(s) over a period of time." The random initial condition does not appear to evolve, but if I increase dt to 1.0, then V evolves to a uniform value of about 0.5. U does not appear to evolve for me, but that's a display glitch of some kind. V evolves to 0.598 and U evolves to 0.179. There is no pattern in space (if that's what you were expecting) because these equations have no spatial dependence; they only have partial derivatives in time, so there is absolutely nothing to cause the variables to flux from one cell to another.
You appear to get away with it here, but your variables should always be defined on the same mesh, rather than u_mesh for U and v_mesh for V.
No-flux boundary conditions are the default in FiPy, so there's no reason to specify U.faceGrad.constrain, etc.
Furthermore, there are no fluxes in these equations (because they're ODEs), so setting boundary fluxes isn't meaningful.
Normally I would recommend making your equations more implicit, coupling them together, and sweeping, but none of this seems to have much effect on their evolution in this case.

Use solve_ivp's 'events' to check for convergence

Problem:
Assume a simple decay process as described by the following ode:
def exponential_decay(t,y):
return -0.5 * y
This can easily be integrated with the help of scipy's solve_ivp()
t_min = 0; t_max = 25; y0 = 1
sol = solve_ivp(exponential_decay, [t_min, t_max],[y0],dense_output=True)
The resulting solution might look like this:
Question:
I would like to use solve_ivp's "event"-finder to check for convergence to reduce the computational time spent after convergence is reached.
However, the designed signature of the event tracker when an event function is provided is:
event(t,y) -> t_event
An event occurs when the return of the event function is equal to zero.
Because event(t,y) only knows the current y(t) it can not be used straightforwardly to implement standard convergence criteria as they all require a series of y.
So to cut this short: Is there a good way to do so, to use the event finder to check for convergence?
Or to make use of any kind of range of y(t) in the convergence tracker?
This seems like something that would be helpful in many applications
A (bad way) to do so I found is to pass a global variable in and out of event(t,y) that stores the the differnt (t,y(t). However, this is not only extreamly unelegant, it also offsets the computational efficiency provided by solive_ivp()

How does scipy.integrate.ode.integrate() work?

I have obviously read through the documentation, but I have not been able to find a more detailed description of what is happening under the covers. Specifically, there are a few behaviors that I am very confused about:
General setup
import numpy as np
from scipy.integrate import ode
#Constants in ODE
N = 30
K = 0.5
w = np.random.normal(np.pi, 0.1, N)
#Integration parameters
y0 = np.linspace(0, 2*np.pi, N, endpoint=False)
t0 = 0
#Set up the solver
solver = ode(lambda t,y: w + K/N*np.sum( np.sin( y - y.reshape(N,1) ), axis=1))
solver.set_integrator('vode', method='bdf')
solver.set_initial_value(y0, t0)
Problem 1: solver.integrate(t0) fails
Setting up the integrator, and asking for the value at t0 the first time returns a successful integration. Repeating this returns the correct number, but the solver.successful() method returns false:
solver.integrate(t0)
>>> array([ 0. , 0.20943951, 0.41887902, ..., 5.65486678,
5.86430629, 6.0737458 ])
solver.successful()
>>> True
solver.integrate(t0)
>>> array([ 0. , 0.20943951, 0.41887902, ..., 5.65486678,
5.86430629, 6.0737458 ])
solver.successful()
>>> False
My question is, what is happening in the solver.integrate(t) method that causes it to succeed the first time, and fail subsequently, and what does it mean to have an “unsuccessful” integration? Furthermore, why does the integrator fail silently, and continue to produce useful-looking outputs until I ask it explicitly whether it was successful?
Related, is there a way to reset the failed integration, or do I need to re-instantiate the solver from scratch?
Problem 2: solver.integrate(t) immediately returns an answer for almost any value of t
Even though my initial value of y0 is given at t0=0, I can request the value at t=10000 and get the answer immediately. I would expect that the numerical integration over such a large time span should take at least a few seconds (e.g. in Matlab, asking to integrate over 10000 time steps would take several minutes).
For example, re-run the setup from above and execute:
solver.integrate(10000)
>>> array([ 2153.90803383, 2153.63023706, 2153.60964064, ..., 2160.00982959,
2159.90446056, 2159.82900895])
Is Python really that fast, or is this output total nonsense?
Problem 0
Don’t ignore error messages. Yes, ode’s error messages can be cryptic at times, but you still want to avoid them.
Problem 1
As you already integrated up to t0 with the first call of solver.integrate(t0), you are integrating for a time step of 0 with the second call. This throws the cryptic error:
DVODE-- ISTATE (=I1) .gt. 1 but DVODE not initialized
In above message, I1 = 2
/usr/lib/python3/dist-packages/scipy/integrate/_ode.py:869: UserWarning: vode: Illegal input detected. (See printed message.)
'Unexpected istate=%s' % istate))
Problem 2.1
There is a maximum number of (internal) steps that a solver is going to take in one call without throwing an error. This can be set with the nsteps argument of set_integrator. If you integrate a large time at once, nsteps will be exceeded even if nothing is wrong, and the following error message is thrown:
/usr/lib/python3/dist-packages/scipy/integrate/_ode.py:869: UserWarning: vode: Excess work done on this call. (Perhaps wrong MF.)
'Unexpected istate=%s' % istate))
The integrator then stops at whenever this happens.
Problem 2.2
If you set nsteps=10**10, the integration runs without problems. It still is pretty fast though (roughly 1 s on my machine). The reason for this is as follows:
For a multi-dimensional system such as yours, there are two main runtime sinks when integrating:
Vector and matrix operations within the integrator. In scipy.ode, these are all realised with NumPy operations or ported Fortran or C code. Anyway, they are realised with compiled code without Python overhead and thus very efficient.
Evaluating the derivative (lambda t,y: w + K/N*np.sum( np.sin( y - y.reshape(N,1) ), axis=1) in your case). You realised this with NumPy operations, which again are realised with compiled code and very efficient. You may improve this a little bit with a purely compiled function, but that will grant you at most a small factor. If you used Python lists and loops instead, it would be horribly slow.
Therefore, for your problem, everything relevant is handled with compiled code under the hood and the integration is handled with an efficiency comparable to that of, e.g., a pure C program. I do not know how the two above aspects are handled in Matlab, but if either of the above challenges is handled with interpreted instead of compiled loops, this would explain the runtime discrepancy you observe.
To the second question, yes, the output might be nonsense. Local errors, be they from discretization or floating point operations, accumulate with a compounding factor which is about the Lipschitz constant of the ODE function. In a first estimate, the Lipschitz constant here is K=0.5. The magnification rate of early errors, that is, their coefficient as part of the global error, can thus be as large as exp(0.5*10000), which is a huge number.
On the other hand it is not surprising that the integration is fast. Most of the provided methods use step size adaptation, and with the standard error tolerances this might result in only some tens of internal steps. Reducing the error tolerances will increase the number of internal steps and may change the numerical result drastically.

Categories

Resources