openscvx 0.5.3.dev30__py3-none-any.whl → 0.5.3.dev32__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
openscvx/_version.py CHANGED
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.5.3.dev30'
22
- __version_tuple__ = version_tuple = (0, 5, 3, 'dev30')
21
+ __version__ = version = '0.5.3.dev32'
22
+ __version_tuple__ = version_tuple = (0, 5, 3, 'dev32')
23
23
 
24
- __commit_id__ = commit_id = None
24
+ __commit_id__ = commit_id = 'gd1964080e'
@@ -52,7 +52,12 @@ from .autotuner import (
52
52
  )
53
53
  from .autotuner.base import AutotuningBase
54
54
  from .base import Algorithm
55
- from .history import AlgorithmHistory, DiscretizationResult
55
+ from .history import (
56
+ AlgorithmHistory,
57
+ DiscretizationResult,
58
+ MultishotPropagation,
59
+ unpack_multishot_V,
60
+ )
56
61
  from .hyperparams import HyperParams
57
62
  from .optimization_results import OptimizationResults
58
63
  from .scvx import PenalizedTrustRegion
@@ -145,6 +150,8 @@ __all__ = [
145
150
  "CandidateIterate",
146
151
  "adaptive_state_code_to_str",
147
152
  "DiscretizationResult",
153
+ "MultishotPropagation",
154
+ "unpack_multishot_V",
148
155
  "Weights",
149
156
  # Core results
150
157
  "OptimizationResults",
@@ -206,7 +206,7 @@ class AugmentedLagrangian(AcceptanceRatioAutotuner):
206
206
 
207
207
  for idx, constraint in enumerate(nodal_constraints.nodal):
208
208
  g = constraint.func(candidate.x, candidate.u, 0, params)
209
- nu = jnp.maximum(0.0, g)
209
+ nu = jnp.abs(g) if constraint.is_equality else jnp.maximum(0.0, g)
210
210
 
211
211
  if constraint.nodes is not None:
212
212
  nodes_array = jnp.asarray(constraint.nodes)
@@ -245,7 +245,7 @@ class AugmentedLagrangian(AcceptanceRatioAutotuner):
245
245
 
246
246
  for idx, constraint in enumerate(nodal_constraints.cross_node):
247
247
  g = constraint.func(candidate.x, candidate.u, params)
248
- nu = jnp.sum(jnp.maximum(0.0, g))
248
+ nu = jnp.sum(jnp.abs(g) if constraint.is_equality else jnp.maximum(0.0, g))
249
249
  current = state.lam_vb_cross[idx]
250
250
  case1 = current + nu * scale
251
251
  case2 = current + (nu**2) / state.hyper.ep * scale
@@ -1,16 +1,21 @@
1
- """CPU-side append-only iteration log for SCP algorithms.
1
+ """CPU-side append-only iteration log and discretization read helpers for SCP.
2
2
 
3
3
  :class:`AlgorithmHistory` is the mutable, host-side container the SCP loop grows
4
4
  after each iteration via :py:meth:`~AlgorithmHistory.record_iteration` — it
5
5
  never crosses the JAX boundary. :class:`DiscretizationResult` unpacks a raw
6
6
  discretization matrix once so history reads are trivial slicing-free access.
7
+ :class:`MultishotPropagation` complements :class:`DiscretizationResult` by
8
+ exposing the full substep state trajectories stored during multishot
9
+ discretization.
7
10
 
8
11
  Last in the algorithms import order: it depends on
9
12
  :mod:`openscvx.algorithms.state` and nothing here depends on it.
10
13
  """
11
14
 
15
+ from __future__ import annotations
16
+
12
17
  from dataclasses import dataclass, field
13
- from typing import TYPE_CHECKING, List, Optional, Tuple, Union
18
+ from typing import TYPE_CHECKING, List, Optional, Sequence, Tuple, Union
14
19
 
15
20
  import jax
16
21
  import numpy as np
@@ -19,6 +24,9 @@ from .state import AdaptiveStateCode, AlgorithmState, adaptive_state_code_to_str
19
24
 
20
25
  if TYPE_CHECKING:
21
26
  from openscvx.config import Config
27
+ from openscvx.symbolic.expr.state import State
28
+
29
+ StateLike = Union[str, "State"]
22
30
 
23
31
 
24
32
  @dataclass(frozen=True, slots=True)
@@ -92,6 +100,150 @@ class DiscretizationResult:
92
100
  )
93
101
 
94
102
 
103
+ # ---------------------------------------------------------------------------
104
+ # Multishot propagation — read-side helpers for integration matrix V
105
+ # ---------------------------------------------------------------------------
106
+
107
+
108
+ def _resolve_state_slice(name_or_state: StateLike, states: Sequence) -> slice:
109
+ from openscvx.symbolic.expr.state import State
110
+
111
+ if isinstance(name_or_state, State):
112
+ if name_or_state._slice is None:
113
+ raise ValueError(f"State {name_or_state.name!r} has no slice assigned")
114
+ return name_or_state._slice
115
+ for state in states:
116
+ if state.name == name_or_state:
117
+ if state._slice is None:
118
+ raise ValueError(f"State {name_or_state!r} has no slice assigned")
119
+ return state._slice
120
+ available = sorted({state.name for state in states})
121
+ raise KeyError(f"Unknown state {name_or_state!r}. Available: {available}")
122
+
123
+
124
+ def segment_size(n_x: int, n_u: int) -> int:
125
+ """Packed row count per multishot segment (state + STM + sensitivities)."""
126
+ return n_x + n_x * n_x + 2 * n_x * n_u
127
+
128
+
129
+ @dataclass(frozen=True)
130
+ class MultishotPropagation:
131
+ """Unpacked SCP multishot integration matrix ``V``.
132
+
133
+ ``V`` shape: ``(n_segments * segment_size, n_substeps)`` where
134
+ ``segment_size = n_x + n_x**2 + 2 * n_x * n_u``.
135
+ """
136
+
137
+ V: np.ndarray
138
+ n_x: int
139
+ n_u: int
140
+ t_nodes: np.ndarray
141
+ states: tuple = ()
142
+
143
+ @property
144
+ def segment_size(self) -> int:
145
+ return segment_size(self.n_x, self.n_u)
146
+
147
+ @property
148
+ def n_segments(self) -> int:
149
+ return int(self.V.shape[0] // self.segment_size)
150
+
151
+ @property
152
+ def n_substeps(self) -> int:
153
+ return int(self.V.shape[1])
154
+
155
+ def segment_states(self, seg_idx: int) -> np.ndarray:
156
+ """Integrated states for one segment, shape ``(n_substeps, n_x)``."""
157
+ seg_start = seg_idx * self.segment_size
158
+ rows = self.V[seg_start : seg_start + self.n_x, :]
159
+ return np.asarray(rows, dtype=np.float64).T
160
+
161
+ def segments(self) -> list[np.ndarray]:
162
+ """All segment state arrays, each shape ``(n_substeps, n_x)``."""
163
+ return [self.segment_states(seg_idx) for seg_idx in range(self.n_segments)]
164
+
165
+ def chronological(self) -> tuple[np.ndarray, np.ndarray]:
166
+ """Time-ordered stitched full states and times.
167
+
168
+ Skips duplicated segment-boundary samples (``j0 = 0`` on seg 0, else ``1``).
169
+
170
+ Returns:
171
+ ``states`` — ``(n_samples, n_x)``
172
+ ``t`` — ``(n_samples,)``, linearly interpolated within each segment
173
+ """
174
+ t_nodes = np.asarray(self.t_nodes, dtype=np.float64).ravel()
175
+ n_sub = self.n_substeps
176
+ state_rows: list[np.ndarray] = []
177
+ t_rows: list[float] = []
178
+ for seg_idx in range(self.n_segments):
179
+ t0, t1 = float(t_nodes[seg_idx]), float(t_nodes[seg_idx + 1])
180
+ j0 = 0 if seg_idx == 0 else 1
181
+ for j in range(j0, n_sub):
182
+ alpha = j / (n_sub - 1) if n_sub > 1 else 0.0
183
+ state_rows.append(self.segment_states(seg_idx)[j])
184
+ t_rows.append((1.0 - alpha) * t0 + alpha * t1)
185
+ if not state_rows:
186
+ return np.empty((0, self.n_x), dtype=np.float64), np.empty(0, dtype=np.float64)
187
+ return np.stack(state_rows, axis=0), np.asarray(t_rows, dtype=np.float64)
188
+
189
+ def state(self, name_or_state: StateLike) -> tuple[np.ndarray, np.ndarray]:
190
+ """Chronological trajectory for one symbolic state.
191
+
192
+ ``name_or_state`` may be a :class:`~openscvx.symbolic.expr.state.State`
193
+ instance or a string name matching ``State.name`` in ``self.states``.
194
+ """
195
+ state_slice = _resolve_state_slice(name_or_state, self.states)
196
+ return self.slice_states(state_slice)
197
+
198
+ def slice_states(self, state_slice: slice) -> tuple[np.ndarray, np.ndarray]:
199
+ """``chronological()`` then apply ``state_slice`` to the state dimension."""
200
+ states, t = self.chronological()
201
+ return states[:, state_slice], t
202
+
203
+
204
+ def unpack_multishot_V(
205
+ V: np.ndarray,
206
+ *,
207
+ n_x: int,
208
+ n_u: int,
209
+ t_nodes: np.ndarray,
210
+ states: Sequence = (),
211
+ ) -> MultishotPropagation:
212
+ """Validate ``V`` layout and return :class:`MultishotPropagation`.
213
+
214
+ Raises:
215
+ ValueError: invalid ``V`` shape, empty matrix, or ``len(t_nodes) != n_segments + 1``.
216
+ """
217
+ V = np.asarray(V, dtype=np.float64)
218
+ if V.size == 0:
219
+ raise ValueError("multishot V matrix is empty")
220
+ seg_size = segment_size(n_x, n_u)
221
+ if seg_size <= 0:
222
+ raise ValueError(f"invalid multishot dimensions n_x={n_x}, n_u={n_u}")
223
+ n_rows, n_sub = V.shape
224
+ if n_sub < 1:
225
+ raise ValueError("multishot V must have at least one substep column")
226
+ if n_rows % seg_size != 0:
227
+ raise ValueError(
228
+ f"multishot V row count {n_rows} is not divisible by segment_size {seg_size}"
229
+ )
230
+ n_segments = n_rows // seg_size
231
+ t_nodes_arr = np.asarray(t_nodes, dtype=np.float64).ravel()
232
+ if len(t_nodes_arr) != n_segments + 1:
233
+ raise ValueError(
234
+ f"multishot t_nodes length {len(t_nodes_arr)} != n_segments + 1 "
235
+ f"({n_segments + 1}); pass optimization node times from "
236
+ "results.nodes['time'] or an explicit array."
237
+ )
238
+ return MultishotPropagation(
239
+ V=V,
240
+ n_x=n_x,
241
+ n_u=n_u,
242
+ t_nodes=t_nodes_arr,
243
+ states=tuple(states),
244
+ )
245
+
246
+
95
247
  # ---------------------------------------------------------------------------
96
248
  # AlgorithmHistory — CPU-side append-only iteration log
97
249
  # ---------------------------------------------------------------------------
@@ -7,7 +7,7 @@ import jax.numpy as jnp
7
7
  import numpy as np
8
8
 
9
9
  if TYPE_CHECKING:
10
- from openscvx.algorithms.history import AlgorithmHistory
10
+ from openscvx.algorithms.history import AlgorithmHistory, MultishotPropagation
11
11
  from openscvx.algorithms.state import AlgorithmState
12
12
  from openscvx.problem import Problem
13
13
 
@@ -180,6 +180,36 @@ class OptimizationResults:
180
180
  """
181
181
  return self.U[-1]
182
182
 
183
+ def multishot_propagation(
184
+ self,
185
+ iteration: int = -1,
186
+ *,
187
+ t_nodes: Optional[np.ndarray] = None,
188
+ ) -> Optional["MultishotPropagation"]:
189
+ """Return unpacked multishot data for SCP iteration ``iteration`` (default: final).
190
+
191
+ Returns ``None`` when ``discretization_history`` is empty (e.g. ``solve_jax``).
192
+
193
+ Attaches ``self._states`` so ``.state("q")`` works without passing slices manually.
194
+ """
195
+ from openscvx.algorithms.history import unpack_multishot_V
196
+
197
+ dh = self.discretization_history
198
+ if not dh:
199
+ return None
200
+ V = np.asarray(dh[iteration], dtype=np.float64)
201
+ n_x, n_u = self.x.shape[1], self.u.shape[1]
202
+ if t_nodes is None:
203
+ t_nodes_raw = self.nodes.get("time")
204
+ if t_nodes_raw is None:
205
+ t_final = float(self.t_final)
206
+ t_nodes = np.linspace(0.0, t_final, self.x.shape[0])
207
+ else:
208
+ t_nodes = np.asarray(t_nodes_raw, dtype=np.float64).ravel()
209
+ return unpack_multishot_V(
210
+ V, n_x=n_x, n_u=n_u, t_nodes=t_nodes, states=tuple(self._states or ())
211
+ )
212
+
183
213
  # Post-processing results (added by propagate_trajectory_results)
184
214
  t_full: Optional[np.ndarray] = field(default=None, metadata={"npz": "optional_array"})
185
215
  x_full: Optional[np.ndarray] = field(default=None, metadata={"npz": "optional_array"})
@@ -96,12 +96,14 @@ def calculate_nonlinear_penalty(
96
96
  else:
97
97
  g_filtered = g
98
98
  w = lam_vb_nodal[:, idx]
99
- nodal_penalty = nodal_penalty + jnp.sum(w * jnp.maximum(0.0, g_filtered))
99
+ viol = jnp.abs(g_filtered) if constraint.is_equality else jnp.maximum(0.0, g_filtered)
100
+ nodal_penalty = nodal_penalty + jnp.sum(w * viol)
100
101
 
101
102
  for idx, constraint in enumerate(nodal_constraints.cross_node):
102
103
  w = lam_vb_cross[idx]
103
104
  g = constraint.func(x_bar, u_bar, params)
104
- nodal_penalty = nodal_penalty + w * jnp.sum(jnp.maximum(0.0, g))
105
+ viol = jnp.abs(g) if constraint.is_equality else jnp.maximum(0.0, g)
106
+ nodal_penalty = nodal_penalty + w * jnp.sum(viol)
105
107
 
106
108
  cost = calculate_cost_from_state(x_bar, settings, lam_cost)
107
109
  x_diff = settings.sim.inv_S_x @ (x_bar[1:, :] - x_prop).T
@@ -145,6 +145,11 @@ def make_scp_iteration(
145
145
  n_u = settings.sim.n_controls
146
146
  n_nodal = len(jax_constraints.nodal)
147
147
 
148
+ # Equality columns measure violation two-sided (|nu_vb|); inequalities use
149
+ # the positive part. Built once at trace time as static boolean masks.
150
+ nodal_eq_mask = jnp.asarray([c.is_equality for c in jax_constraints.nodal], dtype=bool)
151
+ cross_eq_mask = jnp.asarray([c.is_equality for c in jax_constraints.cross_node], dtype=bool)
152
+
148
153
  # Accept either a bare ``jax.jit`` callable or a ``jax.export`` wrapper.
149
154
  dis_continuous = dis_continuous.call if hasattr(dis_continuous, "call") else dis_continuous
150
155
  dis_impulsive = dis_impulsive.call if hasattr(dis_impulsive, "call") else dis_impulsive
@@ -311,9 +316,13 @@ def make_scp_iteration(
311
316
  VC = jnp.abs(inv_S_x @ solution.nu.T).T
312
317
  J_tr = jnp.sum(TR**2)
313
318
  J_vc = jnp.sum(VC)
314
- J_vb = jnp.sum(jnp.maximum(0.0, solution.nu_vb)) + jnp.sum(
315
- jnp.maximum(0.0, solution.nu_vb_cross)
319
+ nodal_vb = jnp.where(
320
+ nodal_eq_mask, jnp.abs(solution.nu_vb), jnp.maximum(0.0, solution.nu_vb)
321
+ )
322
+ cross_vb = jnp.where(
323
+ cross_eq_mask, jnp.abs(solution.nu_vb_cross), jnp.maximum(0.0, solution.nu_vb_cross)
316
324
  )
325
+ J_vb = jnp.sum(nodal_vb) + jnp.sum(cross_vb)
317
326
  state = state.replace(
318
327
  J_tr=jnp.asarray(J_tr, dtype=state.J_tr.dtype),
319
328
  J_vb=jnp.asarray(J_vb, dtype=state.J_vb.dtype),
@@ -37,12 +37,19 @@ class LoweredNodalConstraint:
37
37
 
38
38
  nodes (Optional[List[int]]): List of node indices where this constraint applies.
39
39
  Set after lowering from NodalConstraint.
40
+
41
+ is_equality (bool): True if the source constraint was an ``Equality``
42
+ (``g == 0``), False for an ``Inequality`` (``g <= 0``). Controls the
43
+ virtual-buffer penalty shape downstream: equalities are penalized
44
+ two-sided (``|nu_vb|``), inequalities one-sided (``pos(nu_vb)``).
45
+ Defaults to False, preserving the inequality behavior.
40
46
  """
41
47
 
42
48
  func: Callable[[jnp.ndarray, jnp.ndarray], jnp.ndarray]
43
49
  grad_g_x: Optional[Callable[[jnp.ndarray, jnp.ndarray], jnp.ndarray]] = None
44
50
  grad_g_u: Optional[Callable[[jnp.ndarray, jnp.ndarray], jnp.ndarray]] = None
45
51
  nodes: Optional[List[int]] = None
52
+ is_equality: bool = False
46
53
 
47
54
 
48
55
  @dataclass
@@ -110,6 +117,7 @@ class LoweredCrossNodeConstraint:
110
117
  func: Callable[[jnp.ndarray, jnp.ndarray, dict], jnp.ndarray]
111
118
  grad_g_X: Callable[[jnp.ndarray, jnp.ndarray, dict], jnp.ndarray]
112
119
  grad_g_U: Callable[[jnp.ndarray, jnp.ndarray, dict], jnp.ndarray]
120
+ is_equality: bool = False
113
121
 
114
122
 
115
123
  @dataclass
@@ -102,12 +102,14 @@ def plot_scp_iterations(
102
102
  V_history = result.discretization_history if result.discretization_history else []
103
103
  X_prop_history = []
104
104
  if V_history and show_propagation:
105
- i4 = n_x + n_x * n_x + 2 * n_x * n_u
105
+ from openscvx.algorithms.history import unpack_multishot_V
106
+
106
107
  for V in V_history:
107
- pos_traj = []
108
- for i_multi in range(V.shape[1]):
109
- pos_traj.append(V[:, i_multi].reshape(-1, i4)[:, :n_x])
110
- X_prop_history.append(np.array(pos_traj))
108
+ n_segments = V.shape[0] // (n_x + n_x * n_x + 2 * n_x * n_u)
109
+ placeholder_t = np.linspace(0.0, 1.0, n_segments + 1)
110
+ prop = unpack_multishot_V(V, n_x=n_x, n_u=n_u, t_nodes=placeholder_t, states=())
111
+ segs = prop.segments()
112
+ X_prop_history.append(np.stack(segs, axis=1))
111
113
 
112
114
  n_iterations = len(result.X)
113
115
  if X_prop_history:
@@ -209,8 +209,8 @@ def extract_propagation_positions(
209
209
 
210
210
  The discretization history contains the multi-shot integration results.
211
211
  Each V matrix has shape (flattened_size, n_timesteps) where:
212
- - flattened_size = (N-1) * i4
213
- - i4 = n_x + n_x*n_x + 2*n_x*n_u (state + STM + control influence matrices)
212
+ - flattened_size = (N-1) * segment_size
213
+ - segment_size = n_x + n_x*n_x + 2*n_x*n_u (state + STM + control influence matrices)
214
214
  - n_timesteps = number of integration substeps
215
215
 
216
216
  Args:
@@ -224,35 +224,18 @@ def extract_propagation_positions(
224
224
  List of propagation trajectories per iteration.
225
225
  Each iteration contains a list of (n_substeps, 3) arrays, one per segment.
226
226
  """
227
+ from openscvx.algorithms.history import unpack_multishot_V
228
+
227
229
  if not discretization_history:
228
230
  return []
229
231
 
230
- i4 = n_x + n_x * n_x + 2 * n_x * n_u
231
232
  propagations = []
232
-
233
233
  for V in discretization_history:
234
- # V shape: (flattened_size, n_timesteps)
235
- n_timesteps = V.shape[1]
236
- n_segments = V.shape[0] // i4 # N-1 segments
237
-
238
- iteration_segments = []
239
- for seg_idx in range(n_segments):
240
- # Extract this segment's data across all timesteps
241
- seg_start = seg_idx * i4
242
- seg_end = seg_start + i4
243
-
244
- # For each timestep, extract the position from the state
245
- segment_positions = []
246
- for t_idx in range(n_timesteps):
247
- # Get full state at this segment and timestep
248
- state = V[seg_start:seg_end, t_idx][:n_x]
249
- # Extract position components
250
- pos = state[position_slice] / scene_scale
251
- segment_positions.append(pos)
252
-
253
- iteration_segments.append(np.array(segment_positions, dtype=np.float32))
254
-
255
- propagations.append(iteration_segments)
234
+ n_segments = V.shape[0] // (n_x + n_x * n_x + 2 * n_x * n_u)
235
+ placeholder_t = np.linspace(0.0, 1.0, n_segments + 1)
236
+ prop = unpack_multishot_V(V, n_x=n_x, n_u=n_u, t_nodes=placeholder_t, states=())
237
+ iteration_segments = [seg[:, position_slice] / scene_scale for seg in prop.segments()]
238
+ propagations.append([np.asarray(seg, dtype=np.float32) for seg in iteration_segments])
256
239
 
257
240
  return propagations
258
241
 
@@ -490,18 +490,21 @@ class CVXPyPTRSolver(PTRSolver):
490
490
  # Virtual Control Slack
491
491
  cost += sum(cp.sum(lam_vc[i - 1] * cp.abs(nu[i - 1])) for i in range(1, settings.sim.n))
492
492
 
493
- # Virtual buffer penalty for nodal constraints (per-node weighting)
493
+ # Virtual buffer penalty for nodal constraints (per-node weighting).
494
+ # Equalities penalize |nu_vb|, inequalities penalize pos(nu_vb).
494
495
  idx_ncvx = 0
495
496
  if jax_constraints.nodal:
496
497
  for constraint in jax_constraints.nodal:
497
- cost += lam_vb_nodal[:, idx_ncvx] @ cp.pos(nu_vb[idx_ncvx])
498
+ pen = cp.abs if constraint.is_equality else cp.pos
499
+ cost += lam_vb_nodal[:, idx_ncvx] @ pen(nu_vb[idx_ncvx])
498
500
  idx_ncvx += 1
499
501
 
500
502
  # Virtual slack penalty for cross-node constraints
501
503
  idx_cross = 0
502
504
  if jax_constraints.cross_node:
503
505
  for constraint in jax_constraints.cross_node:
504
- cost += lam_vb_cross[idx_cross] * cp.pos(nu_vb_cross[idx_cross])
506
+ pen = cp.abs if constraint.is_equality else cp.pos
507
+ cost += lam_vb_cross[idx_cross] * pen(nu_vb_cross[idx_cross])
505
508
  idx_cross += 1
506
509
 
507
510
  return cost
@@ -490,6 +490,12 @@ class MoreauPTRSolver(PTRSolver):
490
490
  self._c_u_j = jnp.asarray(self._c_u, dtype=f)
491
491
  self._jax_dtype = f
492
492
 
493
+ if any(c.is_equality for c in jax_constraints.nodal):
494
+ raise NotImplementedError(
495
+ "MoreauPTRSolver does not support L1-penalized equality "
496
+ "constraints. Use CVXPyPTRSolver."
497
+ )
498
+
493
499
  self.layout = _ConicLayout(N=N, n_x=n_x, n_u=n_u, n_nodal=len(jax_constraints.nodal))
494
500
  self._jax_constraints = jax_constraints
495
501
  self._x_unified = x_unified
@@ -329,6 +329,12 @@ class QPAXPTRSolver(PTRSolver):
329
329
  self._c_u_j = jnp.asarray(self._c_u, dtype=f)
330
330
  self._jax_dtype = f
331
331
 
332
+ if any(c.is_equality for c in jax_constraints.nodal):
333
+ raise NotImplementedError(
334
+ "QPAXPTRSolver does not support L1-penalized equality "
335
+ "constraints. Use CVXPyPTRSolver."
336
+ )
337
+
332
338
  self.layout = _QPLayout(N=N, n_x=n_x, n_u=n_u, n_nodal=len(jax_constraints.nodal))
333
339
  self._jax_constraints = jax_constraints
334
340
  self._x_unified = x_unified
@@ -75,30 +75,6 @@ from openscvx.symbolic.expr.state import State
75
75
  from openscvx.symbolic.expr.time import Time
76
76
 
77
77
 
78
- def _check_nonconvex_equality(constraint: Constraint, context: str) -> None:
79
- """Raise a helpful error if constraint is a non-convex equality.
80
-
81
- Non-convex equality constraints are not supported.
82
-
83
- Args:
84
- constraint: The constraint to check
85
- context: Description of where this constraint appears (for error message)
86
-
87
- Raises:
88
- ValueError: If constraint is an Equality and not marked as convex
89
- """
90
- if isinstance(constraint, Equality) and not constraint.is_convex:
91
- raise ValueError(
92
- f"Non-convex equality constraint in {context}. "
93
- f"Equality constraints must be affine (linear) and marked as convex "
94
- f"using .convex(). For example:\n"
95
- f" (velocity.at(0) == velocity.at(n-1)).convex()\n"
96
- f"If your equality constraint is nonlinear, consider reformulating it "
97
- f"as two inequality constraints or using a different approach.\n"
98
- f"Constraint: {constraint}"
99
- )
100
-
101
-
102
78
  def sort_ctcs_constraints(
103
79
  constraints_ctcs: List[CTCS],
104
80
  ) -> Tuple[List[CTCS], List[Tuple[int, int]], int]:
@@ -265,9 +241,6 @@ def separate_constraints(constraint_set: ConstraintSet, n_nodes: int) -> Constra
265
241
  f"Constraint: {c.constraint}"
266
242
  )
267
243
 
268
- # Check for non-convex equality constraints
269
- _check_nonconvex_equality(c.constraint, "nodal constraint")
270
-
271
244
  # Regular nodal constraint - categorize by convexity
272
245
  if c.constraint.is_convex:
273
246
  constraint_set.nodal_convex.append(c)
@@ -277,9 +250,6 @@ def separate_constraints(constraint_set: ConstraintSet, n_nodes: int) -> Constra
277
250
  elif isinstance(c, Constraint):
278
251
  # Bare constraint - check if it's a cross-node constraint
279
252
  if _contains_node_reference(c):
280
- # Check for non-convex equality constraints
281
- _check_nonconvex_equality(c, "cross-node constraint")
282
-
283
253
  # Cross-node constraint: wrap in CrossNodeConstraint
284
254
  cross_node = CrossNodeConstraint(c)
285
255
  if c.is_convex:
@@ -287,9 +257,6 @@ def separate_constraints(constraint_set: ConstraintSet, n_nodes: int) -> Constra
287
257
  else:
288
258
  constraint_set.cross_node.append(cross_node)
289
259
  else:
290
- # Check for non-convex equality constraints
291
- _check_nonconvex_equality(c, "nodal constraint")
292
-
293
260
  # Regular constraint: apply at all nodes
294
261
  all_nodes = list(range(n_nodes))
295
262
  nodal_constraint = NodalConstraint(c, all_nodes)
@@ -310,9 +277,6 @@ def separate_constraints(constraint_set: ConstraintSet, n_nodes: int) -> Constra
310
277
  # Add nodal constraints from CTCS constraints that have check_nodally=True
311
278
  ctcs_nodal_constraints = get_nodal_constraints_from_ctcs(constraint_set.ctcs)
312
279
  for constraint, interval in ctcs_nodal_constraints:
313
- # Check for non-convex equality constraints
314
- _check_nonconvex_equality(constraint, "CTCS check_nodally constraint")
315
-
316
280
  # CTCS check_nodally constraints cannot have NodeReferences (validated above)
317
281
  # Convert CTCS interval (start, end) to list of nodes [start, start+1, ..., end-1]
318
282
  interval_nodes = list(range(interval[0], interval[1]))
@@ -73,7 +73,7 @@ from openscvx.lowered import (
73
73
  LoweredProblem,
74
74
  )
75
75
  from openscvx.symbolic.constraint_set import ConstraintSet
76
- from openscvx.symbolic.expr import Expr, NodeReference, traverse
76
+ from openscvx.symbolic.expr import Equality, Expr, NodeReference, traverse
77
77
  from openscvx.symbolic.expr.control import Control
78
78
  from openscvx.symbolic.unified import unify_controls, unify_states
79
79
 
@@ -628,6 +628,7 @@ def _lower_jax_constraints(
628
628
  grad_g_x=jax.vmap(jacfwd(fn, argnums=0), in_axes=(0, 0, None, None)),
629
629
  grad_g_u=jax.vmap(jacfwd(fn, argnums=1), in_axes=(0, 0, None, None)),
630
630
  nodes=constraints.nodal[i].nodes,
631
+ is_equality=isinstance(constraints.nodal[i].constraint, Equality),
631
632
  )
632
633
  lowered_nodal.append(constraint)
633
634
 
@@ -644,6 +645,7 @@ def _lower_jax_constraints(
644
645
  func=constraint_fn,
645
646
  grad_g_X=grad_g_X,
646
647
  grad_g_U=grad_g_U,
648
+ is_equality=isinstance(cross_node_constraint.constraint, Equality),
647
649
  )
648
650
  lowered_cross_node.append(cross_node_lowered)
649
651
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscvx
3
- Version: 0.5.3.dev30
3
+ Version: 0.5.3.dev32
4
4
  Summary: A general Python-based successive convexification implementation which uses a JAX backend.
5
5
  Author-email: Chris Hayner and Griffin Norris <haynec@uw.edu>
6
6
  License: Apache Software License
@@ -1,27 +1,27 @@
1
1
  openscvx/__init__.py,sha256=sxiqBoS5jv9UmNSxnrokFXeFMnDSkAxUCtC4X-s-vHk,4456
2
2
  openscvx/__main__.py,sha256=Hwm7mtVg3tLdvoUPkpcQv8KF3wxl72PNLBp9axFu8GY,2991
3
- openscvx/_version.py,sha256=948RKF_zwogF4wPVyRjlc14TbZVtWn-Pjer2jbbmK6Y,535
3
+ openscvx/_version.py,sha256=Z4316e4iRgR9n9GBJeiTH0nozaVh11apRsu9LgCQnWE,543
4
4
  openscvx/config.py,sha256=qfDDYoCe6WqJglKsx5b2W48YOglXenKr-PVRRdCFhYE,9898
5
5
  openscvx/loader.py,sha256=5WDLVA6CQxnyi1pRBfP8jQQc0r8RiAM2O_ItKgMXxAk,7660
6
6
  openscvx/problem.py,sha256=0hsAdpj74XXsYfOn8VBPzjCpO3mH5bSzHOwpezIBiYY,96230
7
- openscvx/algorithms/__init__.py,sha256=6J0ip07ec06q0kzPdrh74Wcnro3PGx0X1eGHRXcQfv8,5528
7
+ openscvx/algorithms/__init__.py,sha256=xEB2sZ_fFbQUSOJY4hOzsZe0cdCuD5LawvrF4fzSt8E,5645
8
8
  openscvx/algorithms/base.py,sha256=jMneNL5lc-qg24vvcGQ4NP93d71boRiUGh6uKBykOlM,11638
9
- openscvx/algorithms/history.py,sha256=-Ltrf9VT45nWhrLZ4-PI7JlK1QhP4vGrFd6OhLwoh0U,11716
9
+ openscvx/algorithms/history.py,sha256=aAS3VWT5d4kfsIl5a34YFYglHayPqn0hZGmhXVGbE0g,17456
10
10
  openscvx/algorithms/hyperparams.py,sha256=BmNDHerJ-eWi5wO5aryXMSe9jDzLsMxXNcCSeNHOCYY,4560
11
11
  openscvx/algorithms/loop.py,sha256=S03KlW_z4hWdrP50rfEzWmDRaOOl7XHmlzImpkKXGX4,3927
12
- openscvx/algorithms/optimization_results.py,sha256=wpo9-DtsBqrYkNvIGfEbAsff5dGENtmNDIh6FD3YuIQ,22421
13
- openscvx/algorithms/penalty.py,sha256=zDhp0t7tl9UshaMWeI-WfheZ3YkYr3nn_WyE7ZRpaX8,4196
12
+ openscvx/algorithms/optimization_results.py,sha256=TVZVeoUpbAmEILmxFrLZz0GT9ze4Apzwm_tXfpBOPjQ,23614
13
+ openscvx/algorithms/penalty.py,sha256=sqfH11bVr-ZG1VUUA4FjF6ETYDuA06ZXiOAvxBPBcts,4329
14
14
  openscvx/algorithms/state.py,sha256=-ooDqgPezM-GwcWj8Y4q7yB_GQGjlvVucIeGbl8271Q,15787
15
15
  openscvx/algorithms/weights.py,sha256=DQV9NOR_-59lISv6d_el3cpQ-EcFasSGb1oZr4ma-yA,20682
16
16
  openscvx/algorithms/autotuner/__init__.py,sha256=ZVALKx4lEgIazIQP8-doSi6VIdy9YxUaTiQ1L0C551o,769
17
17
  openscvx/algorithms/autotuner/acceptance_ratio.py,sha256=yP7LnnWBQ6ea9dX3JMkJAlt_VWHvBpsaxk_7i0vA0Nk,9422
18
18
  openscvx/algorithms/autotuner/adaptive_proximal_weight.py,sha256=HgnJfrx8Mue-dN63jIw9hwn465HjFD8wgzNc7TuLvqQ,2436
19
- openscvx/algorithms/autotuner/augmented_lagrangian.py,sha256=1VmzYzJdTMDKdgv8yIpbsAu-13imIkc16dD-V_jVKWg,11762
19
+ openscvx/algorithms/autotuner/augmented_lagrangian.py,sha256=yaBev93800giRBcDXldoDNNmzoTXhYAw-HREDILip1I,11846
20
20
  openscvx/algorithms/autotuner/base.py,sha256=mKV9r7GoxA07e0jXZwWeu00b6z7D6p1m3EAoqPA70Qw,12771
21
21
  openscvx/algorithms/autotuner/constant_proximal_weight.py,sha256=xM6Ruxo6INtaV0HcLZuHqgRukl4p3Dzoo6jcxJHS_b4,2858
22
22
  openscvx/algorithms/autotuner/ramp_proximal_weight.py,sha256=hcJnKCR6CBC8tDlT03p7GC2pQkkaVAueM-bO8kaSj-I,3276
23
23
  openscvx/algorithms/scvx/__init__.py,sha256=uhMalFKvygm7s7LGYwKKg_a8qeKioM92E74o-b8nN2U,154
24
- openscvx/algorithms/scvx/iteration.py,sha256=zfGWeV-0PArEIz_EK5whAMGvhucwgedR20Fc2M1UHlM,14979
24
+ openscvx/algorithms/scvx/iteration.py,sha256=PTVKaXJ76uTSszHhEB9GVhmWZrp5DtnOBjUYCUR-Oow,15509
25
25
  openscvx/algorithms/scvx/penalized_trust_region.py,sha256=JBIH8ASwv70Sih4Dh9Wh1ILEv5Ykou80UJSN7RsHRcA,12572
26
26
  openscvx/discretization/__init__.py,sha256=lDMVvJMuaP3s78IqwQ2N-BmPPp7_vnll-TX5cU3ezlo,2799
27
27
  openscvx/discretization/base.py,sha256=bxjfPk_IGyWDcgooUFHNN9AZFJk5VOLw9Sb1IP6sZw0,10373
@@ -51,21 +51,21 @@ openscvx/lowered/__init__.py,sha256=P1xtMVkXWa3uz4tiJeTllurwGFeugdwxqU8h0Mt6uTo,
51
51
  openscvx/lowered/cvxpy_constraints.py,sha256=CcgJLweSJEeNGfD21HUjHdNbBcvHhNejhq0gncU7lP0,846
52
52
  openscvx/lowered/cvxpy_variables.py,sha256=4ZHZzun6X7m7KZeNrNj2e8-rYaqAdYZgmT8jT7XqGgU,5593
53
53
  openscvx/lowered/dynamics.py,sha256=EzgzQmOnFJoBUC6sMPJSJo_9Nb1IO8D51bsb6prZSSk,2400
54
- openscvx/lowered/jax_constraints.py,sha256=9h96qKx_ZbK35S6OpvmthsI55FAk3-kdx3TVjSKiCVk,6063
54
+ openscvx/lowered/jax_constraints.py,sha256=7QL_KSsCWmYvTjbE5P3HPkC-_HuLjQxjCJCYt_h8Aao,6506
55
55
  openscvx/lowered/parameters.py,sha256=F01f08EKaRMHG_5eO0AXGWsDjaQ1qDJDq9gJSD1SawQ,1992
56
56
  openscvx/lowered/problem.py,sha256=r4Zgw2MMAOmk1K0j-5J2GkYrsSoC9Q3WfU14HFJFLZQ,2689
57
57
  openscvx/lowered/unified.py,sha256=6PxwCi-cNRYqwAkEBE9HmXL8dpRNP6fVK0u3PymVMxc,41761
58
58
  openscvx/plotting/__init__.py,sha256=SadvlFghMOCYSzY8Oz95ttw_MrGMZZfobQ8SZyGtIck,3396
59
59
  openscvx/plotting/plotting.py,sha256=1mNIbeuV-VE1VYPep2O7Y77A08k5aeS--ypjUOsv7aU,32608
60
60
  openscvx/plotting/publication.py,sha256=-htOFrdJoSKtGSfJ_qeexRndNQZEJUMXakIEQhh1_fA,22171
61
- openscvx/plotting/scp_iteration.py,sha256=2EpP1tC1HJNu_Drkrj6ieORqktv0YybhLZQDJAKX8w8,21650
61
+ openscvx/plotting/scp_iteration.py,sha256=9zs4IcYkmLP1_vb0RN16_miwEebzrHfHq7BhrZLikt0,21800
62
62
  openscvx/plotting/viser/__init__.py,sha256=9y9tD8AdwJi8gICYrMn_KoT-K50XtlXl6ZwHDIUg-6o,4160
63
63
  openscvx/plotting/viser/animated.py,sha256=ymm3voHn5-i6nb9Indr5oU79c8MlAVJz7VMddAdTv4s,27541
64
64
  openscvx/plotting/viser/coordinates.py,sha256=KoZmD7aLM5cAS2GbDVdnjXqnE36voWmGfo8IEZDZPXY,643
65
65
  openscvx/plotting/viser/orbits.py,sha256=oh3ss8TX9rezxCRJ-l8YJQVA6dVwxolae_MWgQi6Lk4,2989
66
66
  openscvx/plotting/viser/plotly_integration.py,sha256=UUpiqdLAax-1WzPt2Z6CBPMxw7Qk_tP9YN9KTeVUhII,12229
67
67
  openscvx/plotting/viser/primitives.py,sha256=k8Y7hGtK5miSzbiROOVagdCipcLDcXKVwFO_9qc5zPM,11574
68
- openscvx/plotting/viser/scp.py,sha256=WTGhjg11Qsy-PjDirGt_0U83GKTedVLmkN5Qt54VaFk,16103
68
+ openscvx/plotting/viser/scp.py,sha256=VBguWdgYlJdY1As_bOcLx19Rjc9g4_qs4uadjKMS90c,15652
69
69
  openscvx/plotting/viser/server.py,sha256=rRRHDejVTcqWCkKO1EgwezEcmFktiqqx1YwAE3WfMR8,4156
70
70
  openscvx/propagation/__init__.py,sha256=XjlOoEwgo2Vow0uW0mMCcLIN53VzFBnSuer3ZzPqnbk,1851
71
71
  openscvx/propagation/post_processing.py,sha256=vsPbTj0EMkSgoEBEvjU5QBRNS7YyL5R4KPXjLDatV80,7731
@@ -73,18 +73,18 @@ openscvx/propagation/propagation.py,sha256=RZlAKBSHfp4XLMxHbzk6YXGgyBFv8sxsyBqdh
73
73
  openscvx/solvers/__init__.py,sha256=TCEAag4hzzluzNj3sUNOMdxtbDxHp2WJEzzGQVmxrI0,3715
74
74
  openscvx/solvers/base.py,sha256=1LOYRAbnFh2uQJk-SRqHV4U1Meu6ycPmZnkMahlqRb4,17469
75
75
  openscvx/solvers/cones.py,sha256=h2_ssdTIiFTCVEkCPXLTUZ1zQIRz0XCvQbSr2bWfoww,3491
76
- openscvx/solvers/cvxpy_ptr_solver.py,sha256=_ytZK-3oP7eCiygt3goRJ1ecKY8zlHySgYi9OjoVKLs,53886
77
- openscvx/solvers/moreau_ptr_solver.py,sha256=7tQyC-2rlx6HwVLFnroXPdTmTyrK4cPq76DRdU2YgLE,84315
76
+ openscvx/solvers/cvxpy_ptr_solver.py,sha256=AiXMsUIR16otjC8sH6DnJSAHwPKFTkvdA4ObxsX7OFg,54088
77
+ openscvx/solvers/moreau_ptr_solver.py,sha256=L5066qrLxlxlIej2AvOfn-4iPBCU56hrV6zcutgmF8E,84556
78
78
  openscvx/solvers/ptr_solver.py,sha256=ve72jPObf9NlaXXTKw16KjvNbAgBy7ntkenrlkrabYo,26212
79
- openscvx/solvers/qpax_ptr_solver.py,sha256=J28O96tj4FCg_OSPZhJM_H20ezIG9L5VXZLpfoZuE4U,67749
79
+ openscvx/solvers/qpax_ptr_solver.py,sha256=m8kgmhWNQrqOWj8d-1QdPj-PfxaUKtzxuunI0Wsdlyo,67988
80
80
  openscvx/symbolic/__init__.py,sha256=aBwrKg6OnmbDL9pma9k4G009QWCOjM2Lmt_6MZPF7I4,294
81
81
  openscvx/symbolic/affine.py,sha256=42erm6azKMf8QCgaG2tULevZtw5PEScw4wLvpneCy_k,6935
82
- openscvx/symbolic/augmentation.py,sha256=qZWpcFenJawRM_VIRZmoJtYOLErx0jmb8eLvSf_xCvI,29555
82
+ openscvx/symbolic/augmentation.py,sha256=vl29NH_4hkqETLb75CgH0GyFEtQjioLuuACLiGqmRg4,28036
83
83
  openscvx/symbolic/builder.py,sha256=ivkPY9tecg6KEgguEuOHknh_FRabWXlCUjXrnWuiwe0,24631
84
84
  openscvx/symbolic/canonicalize.py,sha256=htZBm648a1nmeRcm3hJlND1Z99IXpxUKF74L2TOi8gc,11079
85
85
  openscvx/symbolic/constraint_set.py,sha256=HtLKmXvji9B47l1jzd5fEJSAFAKX93vyzDwux1xxayM,3476
86
86
  openscvx/symbolic/hashing.py,sha256=OLSwuqFal9tKKtMxLAM9HBGb1bo5vzo-DG7srSy21lI,4686
87
- openscvx/symbolic/lower.py,sha256=6st0ANLBJTtDnjpCCgY9yaTQK9T1wUvrGoQJGdTxiKA,31662
87
+ openscvx/symbolic/lower.py,sha256=ebP6KUQETEIt-SNcEGe08zSvp3fXh8pTBVA_Q6JBITk,31835
88
88
  openscvx/symbolic/preprocessing.py,sha256=Sqy_i5ntGWXdxcJCvql_034ZWoYU-kBRPEX34wgB344,36318
89
89
  openscvx/symbolic/problem.py,sha256=TblK3Z0orLaI_6_uPBVZP4SMxTSFXnUfNb0SICR-vFg,4853
90
90
  openscvx/symbolic/sparsity.py,sha256=9CVvvOz3FiBSMKL7TAcrq9FzMIis8m3iOq0URXWO0F8,5973
@@ -159,9 +159,11 @@ openscvx/utils/caching.py,sha256=Uw2e0G1UNn_vmMDqUZGszIH-O9LJhR4wdVKSPToHiy0,169
159
159
  openscvx/utils/printing.py,sha256=dsccZ9sXc3TBWShQvBg1Al4UFGMP77ApblfxteEJHLQ,16515
160
160
  openscvx/utils/profiling.py,sha256=k2x-i0CpG_kRe6dNcNBGu-ylrOtQw4B4C1UaOTjUMfU,1678
161
161
  openscvx/utils/utils.py,sha256=M25RHE_7DSr3Reaca0xCXnDSY9KHuqYvXdh5m1ZotEc,3047
162
- openscvx-0.5.3.dev30.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
163
- openscvx-0.5.3.dev30.dist-info/METADATA,sha256=Wekkm0U_c5btIvskOAM8F0FTieyEhOsz33poVFhnLVA,10924
164
- openscvx-0.5.3.dev30.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
165
- openscvx-0.5.3.dev30.dist-info/entry_points.txt,sha256=1Oqek8Sy28hmAZFgZXDxFXYVf56YLYWlHjhh9RYJ7wE,52
166
- openscvx-0.5.3.dev30.dist-info/top_level.txt,sha256=nUT4Ybefzh40H8tVXqc1RzKESy_MAowElb-CIvAbd4Q,9
167
- openscvx-0.5.3.dev30.dist-info/RECORD,,
162
+ openscvx-0.5.3.dev32.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
163
+ openscvx-0.5.3.dev32.dist-info/METADATA,sha256=XXhrDLUkvtNt4WmXaTzoH9bCMVCALAKXrNaka2XPFbw,10924
164
+ openscvx-0.5.3.dev32.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
165
+ openscvx-0.5.3.dev32.dist-info/entry_points.txt,sha256=1Oqek8Sy28hmAZFgZXDxFXYVf56YLYWlHjhh9RYJ7wE,52
166
+ openscvx-0.5.3.dev32.dist-info/scm_file_list.json,sha256=P96pyN84uItLcscnhrCP38tuIysoml65tPPFBc4xp7w,17957
167
+ openscvx-0.5.3.dev32.dist-info/scm_version.json,sha256=z2WLKMlWjAoff2oDizDMr-sKCqbNuFSWdST4g_wFCkI,161
168
+ openscvx-0.5.3.dev32.dist-info/top_level.txt,sha256=nUT4Ybefzh40H8tVXqc1RzKESy_MAowElb-CIvAbd4Q,9
169
+ openscvx-0.5.3.dev32.dist-info/RECORD,,
@@ -0,0 +1,423 @@
1
+ {
2
+ "files": [
3
+ "README.md",
4
+ ".gitmodules",
5
+ "LICENSE",
6
+ "pyproject.toml",
7
+ "mkdocs.yml",
8
+ "CONTRIBUTING.md",
9
+ ".gitignore",
10
+ "scripts/gen_example_pages.py",
11
+ "scripts/gen_ref_pages.py",
12
+ "scripts/mkdocs_copy_viser_client_hook.py",
13
+ "docs/examples.md",
14
+ "docs/citation.md",
15
+ "docs/versions.json",
16
+ "docs/index.md",
17
+ "docs/Foundations/time_dilation.md",
18
+ "docs/Foundations/scvx.md",
19
+ "docs/Foundations/control_parameterization.md",
20
+ "docs/Foundations/constraint_reformulation.md",
21
+ "docs/Foundations/ocp.md",
22
+ "docs/Foundations/discretization.md",
23
+ "docs/UnderTheHood/vectorization_and_vmapping.md",
24
+ "docs/UnderTheHood/custom_algorithms_autotuners.md",
25
+ "docs/UnderTheHood/lowering_architecture.md",
26
+ "docs/UnderTheHood/batching_jit_grad.md",
27
+ "docs/assets/openscvx_logo_square.png",
28
+ "docs/assets/favicon.png",
29
+ "docs/assets/logo.svg",
30
+ "docs/assets/viser-client/index.html",
31
+ "docs/assets/images/ct-scvx_light.png",
32
+ "docs/assets/images/problem_class_light.png",
33
+ "docs/assets/images/problem_class_dark.png",
34
+ "docs/assets/images/ctcs_light.png",
35
+ "docs/assets/images/ct-scvx_dark.png",
36
+ "docs/assets/images/ctcs_dark.png",
37
+ "docs/assets/viser-recordings/franka_fr3v2_pick_place.viser",
38
+ "docs/assets/viser-recordings/drone_racing.viser",
39
+ "docs/javascripts/mathjax.js",
40
+ "docs/UsersGuide/04_viewpoint_constraints.md",
41
+ "docs/UsersGuide/05_visualization.md",
42
+ "docs/UsersGuide/03_obstacle_avoidance_vmap.md",
43
+ "docs/UsersGuide/09_mjx_dynamics.md",
44
+ "docs/UsersGuide/07_lie.md",
45
+ "docs/UsersGuide/06_logic.md",
46
+ "docs/UsersGuide/01_hello_world_brachistochrone.md",
47
+ "docs/UsersGuide/00_introduction.md",
48
+ "docs/UsersGuide/02_drone_racing_constraints.md",
49
+ "docs/UsersGuide/08_mpcc.md",
50
+ "openscvx/__init__.py",
51
+ "openscvx/config.py",
52
+ "openscvx/loader.py",
53
+ "openscvx/problem.py",
54
+ "openscvx/__main__.py",
55
+ "openscvx/discretization/__init__.py",
56
+ "openscvx/discretization/discretize_linearize.py",
57
+ "openscvx/discretization/linearize_discretize_sparse.py",
58
+ "openscvx/discretization/linearize_discretize.py",
59
+ "openscvx/discretization/base.py",
60
+ "openscvx/discretization/sparse_utils/__init__.py",
61
+ "openscvx/discretization/sparse_utils/sparse_jacobian.py",
62
+ "openscvx/discretization/sparse_utils/bcoo_helpers.py",
63
+ "openscvx/symbolic/lower.py",
64
+ "openscvx/symbolic/__init__.py",
65
+ "openscvx/symbolic/hashing.py",
66
+ "openscvx/symbolic/augmentation.py",
67
+ "openscvx/symbolic/unified.py",
68
+ "openscvx/symbolic/builder.py",
69
+ "openscvx/symbolic/sparsity.py",
70
+ "openscvx/symbolic/preprocessing.py",
71
+ "openscvx/symbolic/constraint_set.py",
72
+ "openscvx/symbolic/canonicalize.py",
73
+ "openscvx/symbolic/problem.py",
74
+ "openscvx/symbolic/affine.py",
75
+ "openscvx/symbolic/expr/vmap.py",
76
+ "openscvx/symbolic/expr/stl.py",
77
+ "openscvx/symbolic/expr/control.py",
78
+ "openscvx/symbolic/expr/__init__.py",
79
+ "openscvx/symbolic/expr/time.py",
80
+ "openscvx/symbolic/expr/constraint.py",
81
+ "openscvx/symbolic/expr/spatial.py",
82
+ "openscvx/symbolic/expr/state.py",
83
+ "openscvx/symbolic/expr/stljax.py",
84
+ "openscvx/symbolic/expr/expr.py",
85
+ "openscvx/symbolic/expr/variable.py",
86
+ "openscvx/symbolic/expr/logic.py",
87
+ "openscvx/symbolic/expr/linalg.py",
88
+ "openscvx/symbolic/expr/array.py",
89
+ "openscvx/symbolic/expr/arithmetic.py",
90
+ "openscvx/symbolic/expr/parameter.py",
91
+ "openscvx/symbolic/expr/math.py",
92
+ "openscvx/symbolic/expr/lie/__init__.py",
93
+ "openscvx/symbolic/expr/lie/se3.py",
94
+ "openscvx/symbolic/expr/lie/so3.py",
95
+ "openscvx/symbolic/expr/lie/adjoint.py",
96
+ "openscvx/symbolic/lowerers/__init__.py",
97
+ "openscvx/symbolic/lowerers/jax/vmap.py",
98
+ "openscvx/symbolic/lowerers/jax/stl.py",
99
+ "openscvx/symbolic/lowerers/jax/control.py",
100
+ "openscvx/symbolic/lowerers/jax/__init__.py",
101
+ "openscvx/symbolic/lowerers/jax/constraint.py",
102
+ "openscvx/symbolic/lowerers/jax/spatial.py",
103
+ "openscvx/symbolic/lowerers/jax/state.py",
104
+ "openscvx/symbolic/lowerers/jax/_lowerer.py",
105
+ "openscvx/symbolic/lowerers/jax/stljax.py",
106
+ "openscvx/symbolic/lowerers/jax/expr.py",
107
+ "openscvx/symbolic/lowerers/jax/_registry.py",
108
+ "openscvx/symbolic/lowerers/jax/logic.py",
109
+ "openscvx/symbolic/lowerers/jax/linalg.py",
110
+ "openscvx/symbolic/lowerers/jax/array.py",
111
+ "openscvx/symbolic/lowerers/jax/arithmetic.py",
112
+ "openscvx/symbolic/lowerers/jax/math.py",
113
+ "openscvx/symbolic/lowerers/jax/lie.py",
114
+ "openscvx/symbolic/lowerers/cvxpy/control.py",
115
+ "openscvx/symbolic/lowerers/cvxpy/__init__.py",
116
+ "openscvx/symbolic/lowerers/cvxpy/constraint.py",
117
+ "openscvx/symbolic/lowerers/cvxpy/state.py",
118
+ "openscvx/symbolic/lowerers/cvxpy/_lowerer.py",
119
+ "openscvx/symbolic/lowerers/cvxpy/expr.py",
120
+ "openscvx/symbolic/lowerers/cvxpy/_registry.py",
121
+ "openscvx/symbolic/lowerers/cvxpy/logic.py",
122
+ "openscvx/symbolic/lowerers/cvxpy/linalg.py",
123
+ "openscvx/symbolic/lowerers/cvxpy/array.py",
124
+ "openscvx/symbolic/lowerers/cvxpy/arithmetic.py",
125
+ "openscvx/symbolic/lowerers/cvxpy/math.py",
126
+ "openscvx/symbolic/parser/tokenizer.py",
127
+ "openscvx/symbolic/parser/stl.py",
128
+ "openscvx/symbolic/parser/__init__.py",
129
+ "openscvx/symbolic/parser/constraint.py",
130
+ "openscvx/symbolic/parser/spatial.py",
131
+ "openscvx/symbolic/parser/stljax.py",
132
+ "openscvx/symbolic/parser/parser.py",
133
+ "openscvx/symbolic/parser/_registry.py",
134
+ "openscvx/symbolic/parser/logic.py",
135
+ "openscvx/symbolic/parser/linalg.py",
136
+ "openscvx/symbolic/parser/array.py",
137
+ "openscvx/symbolic/parser/math.py",
138
+ "openscvx/symbolic/parser/lie.py",
139
+ "openscvx/utils/__init__.py",
140
+ "openscvx/utils/utils.py",
141
+ "openscvx/utils/profiling.py",
142
+ "openscvx/utils/printing.py",
143
+ "openscvx/utils/cache.py",
144
+ "openscvx/utils/caching.py",
145
+ "openscvx/init/__init__.py",
146
+ "openscvx/init/interpolation.py",
147
+ "openscvx/init/inverse_kinematics.py",
148
+ "openscvx/integrations/__init__.py",
149
+ "openscvx/integrations/frax.py",
150
+ "openscvx/integrations/mjx.py",
151
+ "openscvx/integrations/menagerie.py",
152
+ "openscvx/integrations/_utils.py",
153
+ "openscvx/integrations/base.py",
154
+ "openscvx/plotting/plotting.py",
155
+ "openscvx/plotting/__init__.py",
156
+ "openscvx/plotting/scp_iteration.py",
157
+ "openscvx/plotting/publication.py",
158
+ "openscvx/plotting/viser/__init__.py",
159
+ "openscvx/plotting/viser/plotly_integration.py",
160
+ "openscvx/plotting/viser/orbits.py",
161
+ "openscvx/plotting/viser/scp.py",
162
+ "openscvx/plotting/viser/animated.py",
163
+ "openscvx/plotting/viser/primitives.py",
164
+ "openscvx/plotting/viser/server.py",
165
+ "openscvx/plotting/viser/coordinates.py",
166
+ "openscvx/expert/__init__.py",
167
+ "openscvx/expert/byof.py",
168
+ "openscvx/expert/lowering.py",
169
+ "openscvx/expert/validation.py",
170
+ "openscvx/propagation/__init__.py",
171
+ "openscvx/propagation/propagation.py",
172
+ "openscvx/propagation/post_processing.py",
173
+ "openscvx/solvers/cones.py",
174
+ "openscvx/solvers/ptr_solver.py",
175
+ "openscvx/solvers/__init__.py",
176
+ "openscvx/solvers/cvxpy_ptr_solver.py",
177
+ "openscvx/solvers/moreau_ptr_solver.py",
178
+ "openscvx/solvers/qpax_ptr_solver.py",
179
+ "openscvx/solvers/base.py",
180
+ "openscvx/algorithms/hyperparams.py",
181
+ "openscvx/algorithms/optimization_results.py",
182
+ "openscvx/algorithms/__init__.py",
183
+ "openscvx/algorithms/state.py",
184
+ "openscvx/algorithms/loop.py",
185
+ "openscvx/algorithms/weights.py",
186
+ "openscvx/algorithms/history.py",
187
+ "openscvx/algorithms/penalty.py",
188
+ "openscvx/algorithms/base.py",
189
+ "openscvx/algorithms/scvx/__init__.py",
190
+ "openscvx/algorithms/scvx/iteration.py",
191
+ "openscvx/algorithms/scvx/penalized_trust_region.py",
192
+ "openscvx/algorithms/autotuner/ramp_proximal_weight.py",
193
+ "openscvx/algorithms/autotuner/__init__.py",
194
+ "openscvx/algorithms/autotuner/acceptance_ratio.py",
195
+ "openscvx/algorithms/autotuner/constant_proximal_weight.py",
196
+ "openscvx/algorithms/autotuner/augmented_lagrangian.py",
197
+ "openscvx/algorithms/autotuner/adaptive_proximal_weight.py",
198
+ "openscvx/algorithms/autotuner/base.py",
199
+ "openscvx/lowered/cvxpy_constraints.py",
200
+ "openscvx/lowered/__init__.py",
201
+ "openscvx/lowered/parameters.py",
202
+ "openscvx/lowered/jax_constraints.py",
203
+ "openscvx/lowered/unified.py",
204
+ "openscvx/lowered/cvxpy_variables.py",
205
+ "openscvx/lowered/dynamics.py",
206
+ "openscvx/lowered/problem.py",
207
+ "openscvx/integrators/__init__.py",
208
+ "openscvx/integrators/diffrax.py",
209
+ "openscvx/integrators/runge_kutta.py",
210
+ "tests/test_loader.py",
211
+ "tests/brachistochrone_analytical.py",
212
+ "tests/test_examples.py",
213
+ "tests/test_optimization_results.py",
214
+ "tests/test_plotting.py",
215
+ "tests/__init__.py",
216
+ "tests/test_integrators.py",
217
+ "tests/test_discretization.py",
218
+ "tests/test_init.py",
219
+ "tests/test_multishot_propagation.py",
220
+ "tests/test_propagation.py",
221
+ "tests/hohmann_analytical.py",
222
+ "tests/test_expert.py",
223
+ "tests/test_solve_batched_inference.py",
224
+ "tests/test_solve_jax_bare_brachistochrone.py",
225
+ "tests/test_cvxpygen_optional.py",
226
+ "tests/test_solve_batched_export_roundtrip.py",
227
+ "tests/test_solve_batched_brachistochrone.py",
228
+ "tests/_marks.py",
229
+ "tests/test_impulsive.py",
230
+ "tests/conftest.py",
231
+ "tests/test_solve_jax_vmap_brachistochrone.py",
232
+ "tests/test_brachistochrone.py",
233
+ "tests/test_solve_jax_jit_brachistochrone.py",
234
+ "tests/test_autotuning.py",
235
+ "tests/test_solve_batched_cvxpy_export_errors.py",
236
+ "tests/test_solve_jax_vmap_converged_no_drift.py",
237
+ "tests/symbolic/test_preprocessing.py",
238
+ "tests/symbolic/__init__.py",
239
+ "tests/symbolic/test_lower_cvxpy.py",
240
+ "tests/symbolic/test_augmentation.py",
241
+ "tests/symbolic/test_unified.py",
242
+ "tests/symbolic/test_sparsity.py",
243
+ "tests/symbolic/test_hashing.py",
244
+ "tests/symbolic/test_lower_jax.py",
245
+ "tests/symbolic/expr/test_node_reference.py",
246
+ "tests/symbolic/expr/test_scaling.py",
247
+ "tests/symbolic/expr/test_math.py",
248
+ "tests/symbolic/expr/test_variable.py",
249
+ "tests/symbolic/expr/__init__.py",
250
+ "tests/symbolic/expr/test_lie.py",
251
+ "tests/symbolic/expr/test_array.py",
252
+ "tests/symbolic/expr/test_expr.py",
253
+ "tests/symbolic/expr/test_logic.py",
254
+ "tests/symbolic/expr/test_arithmetic.py",
255
+ "tests/symbolic/expr/test_constraint.py",
256
+ "tests/symbolic/expr/test_vmap.py",
257
+ "tests/symbolic/expr/test_spatial.py",
258
+ "tests/symbolic/expr/test_stl.py",
259
+ "tests/symbolic/expr/test_parameters.py",
260
+ "tests/symbolic/expr/test_linalg.py",
261
+ "tests/symbolic/parser/test_math.py",
262
+ "tests/symbolic/parser/__init__.py",
263
+ "tests/symbolic/parser/test_lie.py",
264
+ "tests/symbolic/parser/test_array.py",
265
+ "tests/symbolic/parser/test_logic.py",
266
+ "tests/symbolic/parser/test_parser.py",
267
+ "tests/symbolic/parser/test_load.py",
268
+ "tests/symbolic/parser/test_constraint.py",
269
+ "tests/symbolic/parser/test_vmap.py",
270
+ "tests/symbolic/parser/test_tokenizer.py",
271
+ "tests/symbolic/parser/test_spatial.py",
272
+ "tests/symbolic/parser/test_stl.py",
273
+ "tests/symbolic/parser/test_linalg.py",
274
+ "tests/fixtures/brachistochrone.yaml",
275
+ "tests/fixtures/brachistochrone.json",
276
+ "tests/integrations/test_mjx_dynamics.py",
277
+ "tests/integrations/__init__.py",
278
+ "tests/integrations/test_mjx.py",
279
+ "tests/expr/test_gmsr.py",
280
+ "tests/expr/__init__.py",
281
+ "tests/solvers/__init__.py",
282
+ "tests/solvers/test_moreau_ptr_solver.py",
283
+ "tests/solvers/test_qpax_ptr_solver.py",
284
+ "tests/solvers/test_subproblem_pytree.py",
285
+ "tests/solvers/test_iteration_callback_moreau.py",
286
+ "tests/solvers/test_iteration_callback_qpax.py",
287
+ "tests/solvers/test_cvxpy_callback_jit_spike.py",
288
+ "tests/solvers/_iteration_callback_helpers.py",
289
+ "tests/solvers/test_iteration_callback_cvxpy.py",
290
+ "tests/solvers/test_iteration_callback_vmap.py",
291
+ "tests/algorithms/_iteration_helpers.py",
292
+ "tests/algorithms/__init__.py",
293
+ "tests/algorithms/test_make_solve_loop.py",
294
+ "tests/algorithms/test_algorithm_state_pytree.py",
295
+ "tests/algorithms/test_iteration_fn_jit.py",
296
+ "tests/algorithms/test_iteration_fn_parity.py",
297
+ "tests/algorithms/autotuner/__init__.py",
298
+ "tests/algorithms/autotuner/test_defaults_agreement.py",
299
+ "tests/algorithms/autotuner/test_hyper_overrides.py",
300
+ "tests/algorithms/autotuner/test_update_weights_jit.py",
301
+ "figures/dtlos_dr.gif",
302
+ "figures/oscvx_structure_full_dark.svg",
303
+ "figures/ctlos_cine.gif",
304
+ "figures/dtlos_cine.gif",
305
+ "figures/ctlos_dr.gif",
306
+ "figures/openscvx_logo_square.png",
307
+ "figures/openscvx_logo.svg",
308
+ "figures/video_preview.png",
309
+ "material/__init__.py",
310
+ "material/overrides/home.html",
311
+ "material/overrides/main.html",
312
+ "material/overrides/partials/home-pipeline.html",
313
+ "material/overrides/partials/home-dropin-banner.html",
314
+ "material/overrides/partials/home-diagram.html",
315
+ "material/overrides/partials/home-viser-strip.html",
316
+ "material/overrides/partials/home-hero.html",
317
+ "material/overrides/assets/stylesheets/home-viser.css",
318
+ "material/overrides/assets/stylesheets/home-dropin.css",
319
+ "material/overrides/assets/stylesheets/home-hero.css",
320
+ "material/overrides/assets/stylesheets/custom.css",
321
+ "examples/plotting.py",
322
+ "examples/_viser_embed_export.py",
323
+ "examples/plotting_viser.py",
324
+ "examples/spacecraft/halo_orbit.py",
325
+ "examples/spacecraft/let_transfer.py",
326
+ "examples/spacecraft/relative_loitering.py",
327
+ "examples/spacecraft/hohmann_transfer.py",
328
+ "examples/spacecraft/proxops_cw.py",
329
+ "examples/abstract/impulsive.py",
330
+ "examples/abstract/hypersensitive.py",
331
+ "examples/abstract/brachistochrone_batched.py",
332
+ "examples/abstract/flappy_bird.py",
333
+ "examples/abstract/stl_or.py",
334
+ "examples/abstract/stl_integer_variable.py",
335
+ "examples/abstract/brachistochrone.py",
336
+ "examples/aircraft/supersonic_time_to_climb.py",
337
+ "examples/drone/dr_vp.py",
338
+ "examples/drone/openscvx_logo.py",
339
+ "examples/drone/cinema_vp.py",
340
+ "examples/drone/dr_double_integrator.py",
341
+ "examples/drone/drone_racing.py",
342
+ "examples/drone/obstacle_avoidance_vmap.py",
343
+ "examples/drone/dr_vp_polytope.py",
344
+ "examples/drone/obstacle_avoidance_nodal.py",
345
+ "examples/drone/logo.py",
346
+ "examples/drone/obstacle_avoidance.py",
347
+ "examples/drone/2d_obstacle_avoidance_batched_ic.py",
348
+ "examples/drone/drone_racing_batched_gates.py",
349
+ "examples/drone/cinema_vp_nodal.py",
350
+ "examples/drone/obstacle_avoidance_vmap_2d.py",
351
+ "examples/drone/dr_vp_nodal.py",
352
+ "examples/drone/multi_agent_circle_swap.py",
353
+ "examples/drone/logo_utils/svg_path_utils.py",
354
+ "examples/drone/logo_utils/openscvx_logo_single.svg",
355
+ "examples/drone/logo_utils/quadrotor_mesh.py",
356
+ "examples/drone/logo_utils/acl_logo.svg",
357
+ "examples/rocket/3DoF_pdg.py",
358
+ "examples/rocket/ascent_launch_vehicle.py",
359
+ "examples/rocket/6DoF_pdg_batched_ic.py",
360
+ "examples/rocket/6DoF_pdg.py",
361
+ "examples/car/dubins_car_obstacle_stl.py",
362
+ "examples/car/dubins_car_stl_or.py",
363
+ "examples/car/dubins_car_obstacle_conditional.py",
364
+ "examples/car/dubins_car.py",
365
+ "examples/car/dubins_car_disjoint.py",
366
+ "examples/car/dubins_car_waypoint_stl.py",
367
+ "examples/arm/3_dof_arm.py",
368
+ "examples/arm/7_dof_arm_vp.py",
369
+ "examples/arm/7_dof_arm_collision.py",
370
+ "examples/arm/franka_fr3v2_viewplanning_nodal.py",
371
+ "examples/arm/franka_fr3v2_pick_place.py",
372
+ "examples/arm/7_dof_arm.py",
373
+ "examples/arm/franka_fr3v2_viewplanning.py",
374
+ "examples/mjx/triple_cartpole_3d_mjx.py",
375
+ "examples/mjx/skydio_x2_mjx.py",
376
+ "examples/mjx/triple_cartpole_mjx.py",
377
+ "examples/mjx/cartpole_mjx.py",
378
+ "examples/mjx/double_cartpole_mjx.py",
379
+ "examples/mjx/triple_cartpole_game.py",
380
+ "examples/animations/_sensor_view.py",
381
+ "examples/animations/obstacle_avoidance_vmap.py",
382
+ "examples/animations/dr_vp_polytope.py",
383
+ "examples/animations/logo.py",
384
+ "examples/animations/franka_fr3v2_pick_place.py",
385
+ "examples/animations/7_dof_arm.py",
386
+ "examples/animations/_camera.py",
387
+ "examples/animations/_render.py",
388
+ "examples/mpc/dubins_car_circle_analytical.py",
389
+ "examples/mpc/dubins_car_circle_discrete.py",
390
+ "examples/mpc/double_integrator_discrete.py",
391
+ "examples/mpc/double_integrator_drone_racing.py",
392
+ "examples/mpc/realtime_double_integrator_drone_racing.py",
393
+ "examples/realtime/drone_racing_realtime.py",
394
+ "examples/realtime/cinema_vp_realtime.py",
395
+ "examples/realtime/3DoF_pdg_realtime.py",
396
+ "examples/realtime/obstacle_avoidance_realtime.py",
397
+ "examples/realtime/dubins_car_realtime.py",
398
+ "examples/realtime/6DoF_pdg_realtime.py",
399
+ "examples/realtime/base_problems/cinema_vp_realtime_base.py",
400
+ "examples/realtime/base_problems/drone_racing_realtime_base.py",
401
+ "examples/realtime/base_problems/obstacle_avoidance_realtime_base.py",
402
+ "examples/realtime/base_problems/dubins_car_realtime_base.py",
403
+ "examples/realtime/base_problems/6DoF_pdg_realtime_base.py",
404
+ "examples/realtime/base_problems/3DoF_pdg_realtime_base.py",
405
+ "examples/double_integrator/_plotting.py",
406
+ "examples/double_integrator/moving_safe_zones.py",
407
+ "examples/frax/panda_frax_viewplanning.py",
408
+ "examples/frax/panda_frax_pick_place.py",
409
+ "examples/frax/panda_frax.py",
410
+ "examples/frax/panda_frax_waypoint.py",
411
+ ".github/release-drafter.yml",
412
+ ".github/assets/logo.svg",
413
+ ".github/workflows/branch-name.yml",
414
+ ".github/workflows/tests-integration.yml",
415
+ ".github/workflows/release.yml",
416
+ ".github/workflows/docs.yml",
417
+ ".github/workflows/lint.yml",
418
+ ".github/workflows/release-drafter.yml",
419
+ ".github/workflows/_docs.yml",
420
+ ".github/workflows/tests-unit.yml",
421
+ ".github/workflows/nightly.yml"
422
+ ]
423
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "0.5.2",
3
+ "distance": 32,
4
+ "node": "gd1964080e22534c0d78eccd223de88e62365c296",
5
+ "dirty": false,
6
+ "branch": "main",
7
+ "node_date": "2026-07-01"
8
+ }