GeezerVison is showing github.com — Tests fails · Issue #1165 · python-control/python-control
Description___________________ test_optimal_doc[shooting-3-None-xfail] ____________________
method = 'shooting', npts = 3, initial_guess = None, fail = 'xfail'
@pytest.mark.slow
@pytest.mark.parametrize(
"method, npts, initial_guess, fail", [
('shooting', 3, None, 'xfail'), # doesn't converge
('shooting', 3, 'zero', 'xfail'), # doesn't converge
# ('shooting', 3, 'u0', None), # github issue #782
('shooting', 3, 'input', 'endpoint'), # doesn't converge to optimal
('shooting', 5, 'input', 'endpoint'), # doesn't converge to optimal
('collocation', 3, 'u0', 'endpoint'), # doesn't converge to optimal
('collocation', 5, 'u0', 'endpoint'),
('collocation', 5, 'input', 'openloop'),# open loop sim fails
('collocation', 10, 'input', None),
('collocation', 10, 'u0', None), # from documentation
('collocation', 10, 'state', None),
('collocation', 20, 'state', None),
])
def test_optimal_doc(method, npts, initial_guess, fail):
"""Test optimal control problem from documentation"""
def vehicle_update(t, x, u, params):
# Get the parameters for the model
l = params.get('wheelbase', 3.) # vehicle wheelbase
phimax = params.get('maxsteer', 0.5) # max steering angle (rad)
# Saturate the steering input
phi = np.clip(u[1], -phimax, phimax)
# Return the derivative of the state
return np.array([
np.cos(x[2]) * u[0], # xdot = cos(theta) v
np.sin(x[2]) * u[0], # ydot = sin(theta) v
(u[0] / l) * np.tan(phi) # thdot = v/l tan(phi)
])
def vehicle_output(t, x, u, params):
return x # return x, y, theta (full state)
# Define the vehicle steering dynamics as an input/output system
vehicle = ct.NonlinearIOSystem(
vehicle_update, vehicle_output, states=3, name='vehicle',
inputs=('v', 'phi'), outputs=('x', 'y', 'theta'))
# Define the initial and final points and time interval
x0 = np.array([0., -2., 0.]); u0 = np.array([10., 0.])
xf = np.array([100., 2., 0.]); uf = np.array([10., 0.])
Tf = 10
# Define the cost functions
Q = np.diag([0, 0, 0.1]) # don't turn too sharply
R = np.diag([1, 1]) # keep inputs small
P = np.diag([1000, 1000, 1000]) # get close to final point
traj_cost = opt.quadratic_cost(vehicle, Q, R, x0=xf, u0=uf)
term_cost = opt.quadratic_cost(vehicle, P, 0, x0=xf)
# Define the constraints
constraints = [ opt.input_range_constraint(vehicle, [8, -0.1], [12, 0.1]) ]
# Define an initial guess at the trajectory
timepts = np.linspace(0, Tf, npts, endpoint=True)
if initial_guess == 'zero':
initial_guess = 0
elif initial_guess == 'u0':
initial_guess = u0
elif initial_guess == 'input':
# Velocity = constant that gets us from start to end
initial_guess = np.zeros((vehicle.ninputs, timepts.size))
initial_guess[0, :] = (xf[0] - x0[0]) / Tf
# Steering = rate required to turn to proper slope in first segment
approximate_angle = math.atan2(xf[1] - x0[1], xf[0] - x0[0])
initial_guess[1, 0] = approximate_angle / (timepts[1] - timepts[0])
initial_guess[1, -1] = -approximate_angle / (timepts[-1] - timepts[-2])
elif initial_guess == 'state':
input_guess = np.outer(u0, np.ones((1, npts)))
state_guess = np.array([
x0 + (xf - x0) * time/Tf for time in timepts]).transpose()
initial_guess = (state_guess, input_guess)
# Solve the optimal control problem
with warnings.catch_warnings():
warnings.filterwarnings(
'ignore', message="unable to solve", category=UserWarning)
result = opt.solve_optimal_trajectory(
vehicle, timepts, x0, traj_cost, constraints,
terminal_cost=term_cost, initial_guess=initial_guess,
trajectory_method=method,
# minimize_method='COBYLA', # SLSQP',
)
if fail == 'xfail':
> assert not result.success
E assert not True
E + where True = message: Optimization terminated successfully\n success: True\n status: 0\n fun: 2.4136235872518714\n x: [ 9.480e+00 1.025e+01 1.006e+01 3.297e-03 2.840e-03\n -4.371e-03]\n nit: 75\n jac: [-2.905e+01 -5.013e+01 -2.577e+01 1.936e+07 -1.171e+03\n 1.935e+07]\n nfev: 678\n njev: 74\n multipliers: [ 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00\n 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00\n 0.000e+00 0.000e+00]\n problem: <control.optimal.OptimalControlProblem object at 0x7f4fb7b074d0>\n cost: 2.4136235872518714\n time: [ 0.000e+00 5.000e+00 1.000e+01]\n inputs: [[ 9.480e+00 1.025e+01 1.006e+01]\n [ 3.297e-03 2.840e-03 -4.371e-03]]\n states: [[ 0.000e+00 4.929e+01 9.999e+01]\n [-2.000e+00 -7.256e-01 1.998e+00]\n [ 0.000e+00 5.001e-02 3.711e-02]].success
control/tests/optimal_test.py:756: AssertionError
----------------------------- Captured stdout call -----------------------------
Summary statistics:
* Cost function calls: 678
* Constraint calls: 760
* System simulations: 1204
* Final cost: 2.4136235872518714
___________________ test_optimal_doc[shooting-3-zero-xfail] ____________________
method = 'shooting', npts = 3, initial_guess = 0, fail = 'xfail'
@pytest.mark.slow
@pytest.mark.parametrize(
"method, npts, initial_guess, fail", [
('shooting', 3, None, 'xfail'), # doesn't converge
('shooting', 3, 'zero', 'xfail'), # doesn't converge
# ('shooting', 3, 'u0', None), # github issue #782
('shooting', 3, 'input', 'endpoint'), # doesn't converge to optimal
('shooting', 5, 'input', 'endpoint'), # doesn't converge to optimal
('collocation', 3, 'u0', 'endpoint'), # doesn't converge to optimal
('collocation', 5, 'u0', 'endpoint'),
('collocation', 5, 'input', 'openloop'),# open loop sim fails
('collocation', 10, 'input', None),
('collocation', 10, 'u0', None), # from documentation
('collocation', 10, 'state', None),
('collocation', 20, 'state', None),
])
def test_optimal_doc(method, npts, initial_guess, fail):
"""Test optimal control problem from documentation"""
def vehicle_update(t, x, u, params):
# Get the parameters for the model
l = params.get('wheelbase', 3.) # vehicle wheelbase
phimax = params.get('maxsteer', 0.5) # max steering angle (rad)
# Saturate the steering input
phi = np.clip(u[1], -phimax, phimax)
# Return the derivative of the state
return np.array([
np.cos(x[2]) * u[0], # xdot = cos(theta) v
np.sin(x[2]) * u[0], # ydot = sin(theta) v
(u[0] / l) * np.tan(phi) # thdot = v/l tan(phi)
])
def vehicle_output(t, x, u, params):
return x # return x, y, theta (full state)
# Define the vehicle steering dynamics as an input/output system
vehicle = ct.NonlinearIOSystem(
vehicle_update, vehicle_output, states=3, name='vehicle',
inputs=('v', 'phi'), outputs=('x', 'y', 'theta'))
# Define the initial and final points and time interval
x0 = np.array([0., -2., 0.]); u0 = np.array([10., 0.])
xf = np.array([100., 2., 0.]); uf = np.array([10., 0.])
Tf = 10
# Define the cost functions
Q = np.diag([0, 0, 0.1]) # don't turn too sharply
R = np.diag([1, 1]) # keep inputs small
P = np.diag([1000, 1000, 1000]) # get close to final point
traj_cost = opt.quadratic_cost(vehicle, Q, R, x0=xf, u0=uf)
term_cost = opt.quadratic_cost(vehicle, P, 0, x0=xf)
# Define the constraints
constraints = [ opt.input_range_constraint(vehicle, [8, -0.1], [12, 0.1]) ]
# Define an initial guess at the trajectory
timepts = np.linspace(0, Tf, npts, endpoint=True)
if initial_guess == 'zero':
initial_guess = 0
elif initial_guess == 'u0':
initial_guess = u0
elif initial_guess == 'input':
# Velocity = constant that gets us from start to end
initial_guess = np.zeros((vehicle.ninputs, timepts.size))
initial_guess[0, :] = (xf[0] - x0[0]) / Tf
# Steering = rate required to turn to proper slope in first segment
approximate_angle = math.atan2(xf[1] - x0[1], xf[0] - x0[0])
initial_guess[1, 0] = approximate_angle / (timepts[1] - timepts[0])
initial_guess[1, -1] = -approximate_angle / (timepts[-1] - timepts[-2])
elif initial_guess == 'state':
input_guess = np.outer(u0, np.ones((1, npts)))
state_guess = np.array([
x0 + (xf - x0) * time/Tf for time in timepts]).transpose()
initial_guess = (state_guess, input_guess)
# Solve the optimal control problem
with warnings.catch_warnings():
warnings.filterwarnings(
'ignore', message="unable to solve", category=UserWarning)
result = opt.solve_optimal_trajectory(
vehicle, timepts, x0, traj_cost, constraints,
terminal_cost=term_cost, initial_guess=initial_guess,
trajectory_method=method,
# minimize_method='COBYLA', # SLSQP',
)
if fail == 'xfail':
> assert not result.success
E assert not True
E + where True = message: Optimization terminated successfully\n success: True\n status: 0\n fun: 2.4136235872518714\n x: [ 9.480e+00 1.025e+01 1.006e+01 3.297e-03 2.840e-03\n -4.371e-03]\n nit: 75\n jac: [-2.905e+01 -5.013e+01 -2.577e+01 1.936e+07 -1.171e+03\n 1.935e+07]\n nfev: 678\n njev: 74\n multipliers: [ 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00\n 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00\n 0.000e+00 0.000e+00]\n problem: <control.optimal.OptimalControlProblem object at 0x7f4fb7b07750>\n cost: 2.4136235872518714\n time: [ 0.000e+00 5.000e+00 1.000e+01]\n inputs: [[ 9.480e+00 1.025e+01 1.006e+01]\n [ 3.297e-03 2.840e-03 -4.371e-03]]\n states: [[ 0.000e+00 4.929e+01 9.999e+01]\n [-2.000e+00 -7.256e-01 1.998e+00]\n [ 0.000e+00 5.001e-02 3.711e-02]].success
control/tests/optimal_test.py:756: AssertionError
----------------------------- Captured stdout call -----------------------------
Summary statistics:
* Cost function calls: 678
* Constraint calls: 760
* System simulations: 1204
* Final cost: 2.4136235872518714
Activitymurrayrm commented on Jul 6, 2025 The example that you included is one where the optimization was expected to fail but actually ended up succeeding (so its a good thing). If you change 'xfail' to None in the first line of of the parametrize list, you can see if that fixes the problem.
Please provide information on which version of control, numpy, and scipy you are using as well as your operating system. medaminezghal commented on Jul 6, 2025 @murrayrm I'am using Arch Linux with numpy 2.3.1 and scipy 1.16.0. I'm using latest version of control 0.10.2. medaminezghal commented on Jul 6, 2025 @murrayrm If I replace xfail with None, I go this:
____________________ test_optimal_doc[shooting-3-None-None] ____________________
method = 'shooting', npts = 3, initial_guess = None, fail = None
@pytest.mark.slow
@pytest.mark.parametrize(
"method, npts, initial_guess, fail", [
('shooting', 3, None, None), # doesn't converge
('shooting', 3, 'zero', None), # doesn't converge
('shooting', 3, 'u0', None), # github issue #782
('shooting', 3, 'input', 'endpoint'), # doesn't converge to optimal
('shooting', 5, 'input', 'endpoint'), # doesn't converge to optimal
('collocation', 3, 'u0', 'endpoint'), # doesn't converge to optimal
('collocation', 5, 'u0', 'endpoint'),
('collocation', 5, 'input', 'openloop'),# open loop sim fails
('collocation', 10, 'input', None),
('collocation', 10, 'u0', None), # from documentation
('collocation', 10, 'state', None),
('collocation', 20, 'state', None),
])
def test_optimal_doc(method, npts, initial_guess, fail):
"""Test optimal control problem from documentation"""
def vehicle_update(t, x, u, params):
# Get the parameters for the model
l = params.get('wheelbase', 3.) # vehicle wheelbase
phimax = params.get('maxsteer', 0.5) # max steering angle (rad)
# Saturate the steering input
phi = np.clip(u[1], -phimax, phimax)
# Return the derivative of the state
return np.array([
np.cos(x[2]) * u[0], # xdot = cos(theta) v
np.sin(x[2]) * u[0], # ydot = sin(theta) v
(u[0] / l) * np.tan(phi) # thdot = v/l tan(phi)
])
def vehicle_output(t, x, u, params):
return x # return x, y, theta (full state)
# Define the vehicle steering dynamics as an input/output system
vehicle = ct.NonlinearIOSystem(
vehicle_update, vehicle_output, states=3, name='vehicle',
inputs=('v', 'phi'), outputs=('x', 'y', 'theta'))
# Define the initial and final points and time interval
x0 = np.array([0., -2., 0.]); u0 = np.array([10., 0.])
xf = np.array([100., 2., 0.]); uf = np.array([10., 0.])
Tf = 10
# Define the cost functions
Q = np.diag([0, 0, 0.1]) # don't turn too sharply
R = np.diag([1, 1]) # keep inputs small
P = np.diag([1000, 1000, 1000]) # get close to final point
traj_cost = opt.quadratic_cost(vehicle, Q, R, x0=xf, u0=uf)
term_cost = opt.quadratic_cost(vehicle, P, 0, x0=xf)
# Define the constraints
constraints = [ opt.input_range_constraint(vehicle, [8, -0.1], [12, 0.1]) ]
# Define an initial guess at the trajectory
timepts = np.linspace(0, Tf, npts, endpoint=True)
if initial_guess == 'zero':
initial_guess = 0
elif initial_guess == 'u0':
initial_guess = u0
elif initial_guess == 'input':
# Velocity = constant that gets us from start to end
initial_guess = np.zeros((vehicle.ninputs, timepts.size))
initial_guess[0, :] = (xf[0] - x0[0]) / Tf
# Steering = rate required to turn to proper slope in first segment
approximate_angle = math.atan2(xf[1] - x0[1], xf[0] - x0[0])
initial_guess[1, 0] = approximate_angle / (timepts[1] - timepts[0])
initial_guess[1, -1] = -approximate_angle / (timepts[-1] - timepts[-2])
elif initial_guess == 'state':
input_guess = np.outer(u0, np.ones((1, npts)))
state_guess = np.array([
x0 + (xf - x0) * time/Tf for time in timepts]).transpose()
initial_guess = (state_guess, input_guess)
# Solve the optimal control problem
with warnings.catch_warnings():
warnings.filterwarnings(
'ignore', message="unable to solve", category=UserWarning)
result = opt.solve_optimal_trajectory(
vehicle, timepts, x0, traj_cost, constraints,
terminal_cost=term_cost, initial_guess=initial_guess,
trajectory_method=method,
# minimize_method='COBYLA', # SLSQP',
)
if fail == 'xfail':
assert not result.success
pytest.xfail("optimization fails to converge")
elif fail == 'precision':
assert result.status == 2
pytest.xfail("optimization precision not achieved")
else:
# Make sure the optimization was successful
assert result.success
# Make sure we started and stopped at the right spot
if fail == 'endpoint':
assert not np.allclose(result.states[:, -1], xf, rtol=1e-4)
pytest.xfail("optimization does not converge to endpoint")
else:
np.testing.assert_almost_equal(result.states[:, 0], x0, decimal=4)
> np.testing.assert_almost_equal(result.states[:, -1], xf, decimal=2)
E AssertionError:
E Arrays are not almost equal to 2 decimals
E
E Mismatched elements: 1 / 3 (33.3%)
E Max absolute difference among violations: 0.03711455
E Max relative difference among violations: inf
E ACTUAL: NamedSignal([1.00e+02, 2.00e+00, 3.71e-02])
E DESIRED: array([100., 2., 0.])
control/tests/optimal_test.py:771: AssertionError
----------------------------- Captured stdout call -----------------------------
Summary statistics:
* Cost function calls: 678
* Constraint calls: 760
* System simulations: 1204
* Final cost: 2.4136235872518714
____________________ test_optimal_doc[shooting-3-zero-None] ____________________
method = 'shooting', npts = 3, initial_guess = 0, fail = None
@pytest.mark.slow
@pytest.mark.parametrize(
"method, npts, initial_guess, fail", [
('shooting', 3, None, None), # doesn't converge
('shooting', 3, 'zero', None), # doesn't converge
('shooting', 3, 'u0', None), # github issue #782
('shooting', 3, 'input', 'endpoint'), # doesn't converge to optimal
('shooting', 5, 'input', 'endpoint'), # doesn't converge to optimal
('collocation', 3, 'u0', 'endpoint'), # doesn't converge to optimal
('collocation', 5, 'u0', 'endpoint'),
('collocation', 5, 'input', 'openloop'),# open loop sim fails
('collocation', 10, 'input', None),
('collocation', 10, 'u0', None), # from documentation
('collocation', 10, 'state', None),
('collocation', 20, 'state', None),
])
def test_optimal_doc(method, npts, initial_guess, fail):
"""Test optimal control problem from documentation"""
def vehicle_update(t, x, u, params):
# Get the parameters for the model
l = params.get('wheelbase', 3.) # vehicle wheelbase
phimax = params.get('maxsteer', 0.5) # max steering angle (rad)
# Saturate the steering input
phi = np.clip(u[1], -phimax, phimax)
# Return the derivative of the state
return np.array([
np.cos(x[2]) * u[0], # xdot = cos(theta) v
np.sin(x[2]) * u[0], # ydot = sin(theta) v
(u[0] / l) * np.tan(phi) # thdot = v/l tan(phi)
])
def vehicle_output(t, x, u, params):
return x # return x, y, theta (full state)
# Define the vehicle steering dynamics as an input/output system
vehicle = ct.NonlinearIOSystem(
vehicle_update, vehicle_output, states=3, name='vehicle',
inputs=('v', 'phi'), outputs=('x', 'y', 'theta'))
# Define the initial and final points and time interval
x0 = np.array([0., -2., 0.]); u0 = np.array([10., 0.])
xf = np.array([100., 2., 0.]); uf = np.array([10., 0.])
Tf = 10
# Define the cost functions
Q = np.diag([0, 0, 0.1]) # don't turn too sharply
R = np.diag([1, 1]) # keep inputs small
P = np.diag([1000, 1000, 1000]) # get close to final point
traj_cost = opt.quadratic_cost(vehicle, Q, R, x0=xf, u0=uf)
term_cost = opt.quadratic_cost(vehicle, P, 0, x0=xf)
# Define the constraints
constraints = [ opt.input_range_constraint(vehicle, [8, -0.1], [12, 0.1]) ]
# Define an initial guess at the trajectory
timepts = np.linspace(0, Tf, npts, endpoint=True)
if initial_guess == 'zero':
initial_guess = 0
elif initial_guess == 'u0':
initial_guess = u0
elif initial_guess == 'input':
# Velocity = constant that gets us from start to end
initial_guess = np.zeros((vehicle.ninputs, timepts.size))
initial_guess[0, :] = (xf[0] - x0[0]) / Tf
# Steering = rate required to turn to proper slope in first segment
approximate_angle = math.atan2(xf[1] - x0[1], xf[0] - x0[0])
initial_guess[1, 0] = approximate_angle / (timepts[1] - timepts[0])
initial_guess[1, -1] = -approximate_angle / (timepts[-1] - timepts[-2])
elif initial_guess == 'state':
input_guess = np.outer(u0, np.ones((1, npts)))
state_guess = np.array([
x0 + (xf - x0) * time/Tf for time in timepts]).transpose()
initial_guess = (state_guess, input_guess)
# Solve the optimal control problem
with warnings.catch_warnings():
warnings.filterwarnings(
'ignore', message="unable to solve", category=UserWarning)
result = opt.solve_optimal_trajectory(
vehicle, timepts, x0, traj_cost, constraints,
terminal_cost=term_cost, initial_guess=initial_guess,
trajectory_method=method,
# minimize_method='COBYLA', # SLSQP',
)
if fail == 'xfail':
assert not result.success
pytest.xfail("optimization fails to converge")
elif fail == 'precision':
assert result.status == 2
pytest.xfail("optimization precision not achieved")
else:
# Make sure the optimization was successful
assert result.success
# Make sure we started and stopped at the right spot
if fail == 'endpoint':
assert not np.allclose(result.states[:, -1], xf, rtol=1e-4)
pytest.xfail("optimization does not converge to endpoint")
else:
np.testing.assert_almost_equal(result.states[:, 0], x0, decimal=4)
> np.testing.assert_almost_equal(result.states[:, -1], xf, decimal=2)
E AssertionError:
E Arrays are not almost equal to 2 decimals
E
E Mismatched elements: 1 / 3 (33.3%)
E Max absolute difference among violations: 0.03711455
E Max relative difference among violations: inf
E ACTUAL: NamedSignal([1.00e+02, 2.00e+00, 3.71e-02])
E DESIRED: array([100., 2., 0.])
control/tests/optimal_test.py:771: AssertionError
----------------------------- Captured stdout call -----------------------------
Summary statistics:
* Cost function calls: 678
* Constraint calls: 760
* System simulations: 1204
* Final cost: 2.4136235872518714
Note that when I remove the comment tag from this line, the test succeed. medaminezghal commented on Jul 6, 2025 @murrayrm change it to endpoint fix the test problem. murrayrm commented on Jul 6, 2025 Thanks for the info. I'll try to recreate it and then update the unit test. My guess is that the newer version of SciPy is converging on some cases when the old one wasn't. medaminezghal commented on Jul 9, 2026 @murrayrm If I use BLAS with NumPy, The tests can go to endpoint and succeeds, if I use OpenBLAS, the tests will fails.
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|