-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathtest_solver_errors.py
More file actions
149 lines (112 loc) · 4.25 KB
/
Copy pathtest_solver_errors.py
File metadata and controls
149 lines (112 loc) · 4.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
"""Regression tests for Python-facing solver validation."""
import pathlib
import numpy as np
import pytest
import shutil
import subprocess
import sys
import pycddp
def _make_solver(horizon=6, dt=0.1):
x0 = np.array([0.0, 0.0])
xref = np.array([0.0, 0.0])
opts = pycddp.CDDPOptions()
opts.verbose = False
opts.print_solver_header = False
return pycddp.CDDP(x0, xref, horizon, dt, opts)
def test_solve_by_name_raises_for_unknown_solver():
solver = _make_solver()
with pytest.raises(ValueError, match="Unknown solver 'NONEXISTENT'"):
solver.solve_by_name("NONEXISTENT")
@pytest.mark.parametrize(
("solver_name", "expected_solver_name"),
[
("CLDDP", "CLDDP"),
("CLCDDP", "CLDDP"),
("LOGDDP", "LogDDP"),
],
)
def test_solve_by_name_accepts_core_aliases(solver_name, expected_solver_name):
dt = 0.05
horizon = 20
x0 = np.array([np.pi, 0.0])
xref = np.array([0.0, 0.0])
opts = pycddp.CDDPOptions()
opts.max_iterations = 20
opts.verbose = False
opts.print_solver_header = False
solver = pycddp.CDDP(x0, xref, horizon, dt, opts)
solver.set_dynamical_system(
pycddp.Pendulum(dt, length=0.5, mass=1.0, damping=0.01)
)
solver.set_objective(
pycddp.QuadraticObjective(
np.zeros((2, 2)), 0.1 * np.eye(1), 100.0 * np.eye(2), xref, [], dt
)
)
solver.add_constraint(
"ctrl", pycddp.ControlConstraint(np.array([-50.0]), np.array([50.0]))
)
solution = solver.solve_by_name(solver_name)
assert solution.solver_name == expected_solver_name
assert solution.status_message
assert len(solution.state_trajectory) == horizon + 1
def test_set_initial_trajectory_requires_dynamical_system():
solver = _make_solver()
X = [np.zeros(2) for _ in range(solver.horizon + 1)]
U = [np.zeros(1) for _ in range(solver.horizon)]
with pytest.raises(ValueError, match="is a dynamical system set"):
solver.set_initial_trajectory(X, U)
def test_set_dynamical_system_rejects_abstract_base():
solver = _make_solver()
with pytest.raises(TypeError, match="DynamicalSystem is an abstract base class"):
solver.set_dynamical_system(pycddp.DynamicalSystem(2, 1, 0.1))
def test_set_objective_rejects_abstract_base():
with pytest.raises(TypeError, match="No constructor defined"):
pycddp.Objective()
def test_set_initial_trajectory_rejects_bad_lengths():
solver = _make_solver()
solver.set_dynamical_system(pycddp.Pendulum(0.1))
X = [np.zeros(2) for _ in range(solver.horizon)]
U = [np.zeros(1) for _ in range(solver.horizon)]
with pytest.raises(ValueError, match="expected X length"):
solver.set_initial_trajectory(X, U)
def test_set_initial_trajectory_rejects_bad_state_dimension():
solver = _make_solver()
solver.set_dynamical_system(pycddp.Pendulum(0.1))
X = [np.zeros(2) for _ in range(solver.horizon + 1)]
X[2] = np.zeros(3)
U = [np.zeros(1) for _ in range(solver.horizon)]
with pytest.raises(ValueError, match="state vector 2"):
solver.set_initial_trajectory(X, U)
def test_set_initial_trajectory_rejects_bad_control_dimension():
solver = _make_solver()
solver.set_dynamical_system(pycddp.Pendulum(0.1))
X = [np.zeros(2) for _ in range(solver.horizon + 1)]
U = [np.zeros(1) for _ in range(solver.horizon)]
U[1] = np.zeros(2)
with pytest.raises(ValueError, match="control vector 1"):
solver.set_initial_trajectory(X, U)
def test_import_error_message_is_actionable(tmp_path):
package_dir = tmp_path / "pycddp"
package_dir.mkdir()
source_dir = pathlib.Path(__file__).resolve().parents[1] / "pycddp"
shutil.copy(source_dir / "__init__.py", package_dir / "__init__.py")
shutil.copy(source_dir / "_version.py", package_dir / "_version.py")
proc = subprocess.run(
[
sys.executable,
"-I",
"-S",
"-c",
(
"import sys; "
f"sys.path.insert(0, {str(tmp_path)!r}); "
"import pycddp"
),
],
capture_output=True,
text=True,
check=False,
)
assert proc.returncode != 0
assert "Failed to import the native pycddp extension" in proc.stderr