openscvx 0.5.3.dev35__py3-none-any.whl → 0.5.3.dev37__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.dev35'
22
- __version_tuple__ = version_tuple = (0, 5, 3, 'dev35')
21
+ __version__ = version = '0.5.3.dev37'
22
+ __version_tuple__ = version_tuple = (0, 5, 3, 'dev37')
23
23
 
24
- __commit_id__ = commit_id = 'g43f7b7f31'
24
+ __commit_id__ = commit_id = 'gdb86fef25'
@@ -63,8 +63,10 @@ Example:
63
63
  canonical = expr.canonicalize() # Simplifies to: x + x
64
64
  """
65
65
 
66
+ import functools
66
67
  import hashlib
67
68
  import struct
69
+ import threading
68
70
  from typing import TYPE_CHECKING, Callable, List, Tuple, Union
69
71
 
70
72
  if TYPE_CHECKING:
@@ -72,6 +74,40 @@ if TYPE_CHECKING:
72
74
 
73
75
  import numpy as np
74
76
 
77
+ # Per-thread memo for canonicalize(). The memo canonicalizes each node once and
78
+ # returns the same object to every parent, preserving sharing.
79
+ _canon_state = threading.local()
80
+
81
+
82
+ def _memoized_canonicalize(fn: Callable) -> Callable:
83
+ """Wrap a subclass ``canonicalize`` so shared subexpressions are reused.
84
+
85
+ Keyed by ``id(self)``; the node is held alive in the memo for the duration of
86
+ the pass so its id cannot be recycled, with an identity guard on lookup. The
87
+ top-level call owns the memo and clears it on exit, so each independent
88
+ canonicalize pass starts fresh.
89
+ """
90
+
91
+ @functools.wraps(fn)
92
+ def wrapper(self):
93
+ memo = getattr(_canon_state, "memo", None)
94
+ owns = memo is None
95
+ if owns:
96
+ memo = {}
97
+ _canon_state.memo = memo
98
+ try:
99
+ entry = memo.get(id(self))
100
+ if entry is not None and entry[0] is self:
101
+ return entry[1]
102
+ result = fn(self)
103
+ memo[id(self)] = (self, result)
104
+ return result
105
+ finally:
106
+ if owns:
107
+ _canon_state.memo = None
108
+
109
+ return wrapper
110
+
75
111
 
76
112
  class Expr:
77
113
  """Base class for symbolic expressions in optimization problems.
@@ -101,6 +137,13 @@ class Expr:
101
137
  # Give Expr objects higher priority than numpy arrays in operations
102
138
  __array_priority__ = 1000
103
139
 
140
+ def __init_subclass__(cls, **kwargs):
141
+ # Wrap each subclass's own canonicalize() with identity memoization so a
142
+ # shared DAG node is canonicalized once rather than re-expanded per parent.
143
+ super().__init_subclass__(**kwargs)
144
+ if "canonicalize" in cls.__dict__:
145
+ cls.canonicalize = _memoized_canonicalize(cls.__dict__["canonicalize"])
146
+
104
147
  def __le__(self, other):
105
148
  from .constraint import Inequality
106
149
 
@@ -7,9 +7,58 @@ The visitor methods that populate the registry live in sibling modules
7
7
 
8
8
  from typing import Callable
9
9
 
10
+ import jax
11
+
10
12
  from openscvx.symbolic.expr import Expr
11
13
  from openscvx.symbolic.lowerers.jax._registry import dispatch
12
14
 
15
+ _MISSING = object()
16
+
17
+ # Value-memoization is only valid within a single JAX trace. Visitors bracket
18
+ # their nested trace with :func:`pause_memo` / :func:`resume_memo` so the wrapped
19
+ # closures recompute instead of returning a cross-trace value.
20
+ _memo_paused = [0]
21
+
22
+
23
+ def pause_memo() -> None:
24
+ """Disable value-memoization for the duration of a nested sub-trace."""
25
+ _memo_paused[0] += 1
26
+
27
+
28
+ def resume_memo() -> None:
29
+ """Re-enable value-memoization after a nested sub-trace finishes tracing."""
30
+ _memo_paused[0] -= 1
31
+
32
+
33
+ def _memoize_call(fn: Callable) -> Callable:
34
+ """Cache a lowered closure's result so it emits its subgraph once per trace.
35
+
36
+ Within a trace every node receives the same argument objects, so caching on
37
+ argument identity (``is``) lets a shared subexpression emit its subgraph once
38
+ instead of once per consumer. The cache only applies while tracing (at least
39
+ one positional argument is a JAX tracer); eager calls, keyword-argument
40
+ calls, and paused sections (nested sub-traces, see module note) bypass it.
41
+ """
42
+ last_args = None
43
+ last_val = _MISSING
44
+
45
+ def wrapped(*args, **kwargs):
46
+ nonlocal last_args, last_val
47
+ if kwargs or _memo_paused[0] or not any(isinstance(a, jax.core.Tracer) for a in args):
48
+ return fn(*args, **kwargs)
49
+ if (
50
+ last_val is not _MISSING
51
+ and last_args is not None
52
+ and len(last_args) == len(args)
53
+ and all(a is b for a, b in zip(last_args, args))
54
+ ):
55
+ return last_val
56
+ last_val = fn(*args)
57
+ last_args = args
58
+ return last_val
59
+
60
+ return wrapped
61
+
13
62
 
14
63
  class JaxLowerer:
15
64
  """JAX backend for lowering symbolic expressions to executable functions.
@@ -22,8 +71,9 @@ class JaxLowerer:
22
71
  first, then composes them into a JAX operation. All lowered functions have
23
72
  a standardized signature (x, u, node, params) -> result.
24
73
 
25
- Note:
26
- This is a stateless lowerer - all state is in the expression tree.
74
+ Shared subexpressions are lowered once: :meth:`lower` caches the closure for
75
+ each AST node by object identity, and each closure is wrapped so it emits its
76
+ subgraph only once per trace (see :func:`_memoize_call`).
27
77
 
28
78
  Example:
29
79
  Set up the JaxLowerer and lower an expression to a JAX function::
@@ -34,14 +84,24 @@ class JaxLowerer:
34
84
  result = f(x_val, u_val, node=0, params={})
35
85
 
36
86
  Note:
37
- The lowerer is stateless and can be reused for multiple expressions.
87
+ A fresh lowerer is created per lowering pass; its node cache is keyed by
88
+ ``id(expr)`` and is valid only while the expression tree is alive.
38
89
  """
39
90
 
91
+ def __init__(self) -> None:
92
+ # Maps id(expr) -> (expr, lowered closure) so each node is lowered once,
93
+ # no matter how many parents share it. Storing the expr itself keeps it
94
+ # alive: if it were garbage-collected, Python could reuse its id for a
95
+ # new node and the cache would return the wrong closure. The identity
96
+ # check on lookup guards against exactly that.
97
+ self._node_cache: dict[int, tuple[Expr, Callable]] = {}
98
+
40
99
  def lower(self, expr: Expr) -> Callable:
41
100
  """Lower a symbolic expression to a JAX function.
42
101
 
43
102
  Main entry point for lowering. Delegates to dispatch() which looks up
44
- the appropriate visitor method based on the expression type.
103
+ the appropriate visitor method based on the expression type. Results are
104
+ memoized by node identity so shared subexpressions are lowered once.
45
105
 
46
106
  Args:
47
107
  expr: Symbolic expression to lower (any Expr subclass)
@@ -53,4 +113,10 @@ class JaxLowerer:
53
113
  NotImplementedError: If no visitor exists for the expression type
54
114
  ValueError: If the expression is malformed (e.g., State without slice)
55
115
  """
56
- return dispatch(self, expr)
116
+ eid = id(expr)
117
+ cached = self._node_cache.get(eid)
118
+ if cached is not None and cached[0] is expr:
119
+ return cached[1]
120
+ fn = _memoize_call(dispatch(self, expr))
121
+ self._node_cache[eid] = (expr, fn)
122
+ return fn
@@ -8,6 +8,7 @@ from jax.lax import cond
8
8
 
9
9
  # Expression types to handle — uncomment as you paste visitors:
10
10
  from openscvx.symbolic.expr.logic import All, Any, Cond
11
+ from openscvx.symbolic.lowerers.jax._lowerer import pause_memo, resume_memo
11
12
  from openscvx.symbolic.lowerers.jax._registry import visitor # noqa: F401
12
13
 
13
14
  # Module-level default dtype for conditional branches
@@ -189,11 +190,20 @@ def _visit_cond(lowerer, node: Cond):
189
190
  def _false_branch(_):
190
191
  return jnp.asarray(false_fn(x, u, node_arg, params), dtype=default_dtype)
191
192
 
192
- return cond(
193
- pred_bool,
194
- _true_branch,
195
- _false_branch,
196
- operand=None,
197
- )
193
+ # lax.cond traces each branch separately, but both branch closures see
194
+ # the same captured (x, u, node, params). Pause the memo so shared
195
+ # subexpressions are recomputed inside each branch: a value cached in
196
+ # one trace is not valid in another.
197
+ pause_memo()
198
+ try:
199
+ result = cond(
200
+ pred_bool,
201
+ _true_branch,
202
+ _false_branch,
203
+ operand=None,
204
+ )
205
+ finally:
206
+ resume_memo()
207
+ return result
198
208
 
199
209
  return cond_fn
@@ -8,6 +8,7 @@ import jax.numpy as jnp
8
8
 
9
9
  # Expression types to handle — uncomment as you paste visitors:
10
10
  from openscvx.symbolic.expr.vmap import Vmap, _Placeholder
11
+ from openscvx.symbolic.lowerers.jax._lowerer import pause_memo, resume_memo
11
12
  from openscvx.symbolic.lowerers.jax._registry import visitor # noqa: F401
12
13
 
13
14
 
@@ -129,9 +130,14 @@ def _visit_vmap(lowerer, node: Vmap):
129
130
  new_params[key] = v
130
131
  return inner_fn(x, u, node_idx, new_params)
131
132
 
132
- # vmap over all batch arguments along the same axis
133
+ # Nested sub-trace reusing captured (x, u, node_idx): pause
134
+ # value-memo so no cached tracer crosses the trace boundary.
133
135
  in_axes = tuple(axis for _ in range(num_batches))
134
- return jax.vmap(inner, in_axes=in_axes)(*data_arrays)
136
+ pause_memo()
137
+ try:
138
+ return jax.vmap(inner, in_axes=in_axes)(*data_arrays)
139
+ finally:
140
+ resume_memo()
135
141
 
136
142
  else:
137
143
  # All Constants: bake all data into closure at lowering time
@@ -145,8 +151,12 @@ def _visit_vmap(lowerer, node: Vmap):
145
151
  new_params[key] = v
146
152
  return inner_fn(x, u, node_idx, new_params)
147
153
 
148
- # vmap over all batch arguments along the same axis
154
+ # Nested sub-trace: pause value-memo (see runtime branch above).
149
155
  in_axes = tuple(axis for _ in range(num_batches))
150
- return jax.vmap(inner, in_axes=in_axes)(*baked_data)
156
+ pause_memo()
157
+ try:
158
+ return jax.vmap(inner, in_axes=in_axes)(*baked_data)
159
+ finally:
160
+ resume_memo()
151
161
 
152
162
  return vmapped_fn
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscvx
3
- Version: 0.5.3.dev35
3
+ Version: 0.5.3.dev37
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
@@ -41,11 +41,8 @@ Provides-Extra: frax
41
41
  Requires-Dist: frax>=0.0.4; extra == "frax"
42
42
  Provides-Extra: test
43
43
  Requires-Dist: pytest; extra == "test"
44
- Requires-Dist: scipy; extra == "test"
45
- Requires-Dist: jaxlie; extra == "test"
46
- Requires-Dist: svgpathtools; extra == "test"
47
44
  Requires-Dist: pytest-xdist; extra == "test"
48
- Requires-Dist: frax>=0.0.4; extra == "test"
45
+ Requires-Dist: scipy; extra == "test"
49
46
  Dynamic: license-file
50
47
 
51
48
  <a id="readme-top"></a>
@@ -53,8 +50,8 @@ Dynamic: license-file
53
50
  <img src="figures/openscvx_logo.svg" width="1200"/>
54
51
  <p align="center">
55
52
  <a href="https://github.com/OpenSCvx/OpenSCvx/actions/workflows/lint.yml"><img src="https://github.com/OpenSCvx/OpenSCvx/actions/workflows/lint.yml/badge.svg"/></a>
56
- <a href="https://github.com/OpenSCvx/OpenSCvx/actions/workflows/tests-unit.yml"><img src="https://github.com/OpenSCvx/OpenSCvx/actions/workflows/tests-unit.yml/badge.svg"/></a>
57
- <a href="https://github.com/OpenSCvx/OpenSCvx/actions/workflows/tests-integration.yml"><img src="https://github.com/OpenSCvx/OpenSCvx/actions/workflows/tests-integration.yml/badge.svg"/></a>
53
+ <a href="https://github.com/OpenSCvx/OpenSCvx/actions/workflows/tests.yml"><img src="https://github.com/OpenSCvx/OpenSCvx/actions/workflows/tests.yml/badge.svg"/></a>
54
+ <a href="https://github.com/OpenSCvx/OpenSCvx/actions/workflows/tests-examples.yml"><img src="https://github.com/OpenSCvx/OpenSCvx/actions/workflows/tests-examples.yml/badge.svg"/></a>
58
55
  <a href="https://github.com/OpenSCvx/OpenSCvx/actions/workflows/nightly.yml"><img src="https://github.com/OpenSCvx/OpenSCvx/actions/workflows/nightly.yml/badge.svg"/></a>
59
56
  <a href="https://github.com/OpenSCvx/OpenSCvx/actions/workflows/release.yml"><img src="https://github.com/OpenSCvx/OpenSCvx/actions/workflows/release.yml/badge.svg?event=release"/></a>
60
57
  </p>
@@ -253,6 +250,22 @@ This repo has the following features:
253
250
 
254
251
  <p align="right">(<a href="#readme-top">back to top</a>)</p>
255
252
 
253
+ ## Third-Party Integrations
254
+
255
+ OpenSCvx integrates with several optional third-party packages. Each installs as a pip extra (`pip install openscvx[<extra>]`); without it, the library and its tests degrade gracefully — the corresponding tests simply skip. Integrations with a badge are tested against `main` on every push and re-checked weekly against the latest upstream releases.
256
+
257
+ | Integration | Extra | Provides | Status |
258
+ | --- | --- | --- | --- |
259
+ | [MuJoCo MJX](https://mujoco.readthedocs.io/en/stable/mjx.html) | `mjx` | MJX dynamics adapter (`openscvx.integrations`) | [![mjx](https://github.com/OpenSCvx/OpenSCvx/actions/workflows/tests-mjx.yml/badge.svg?branch=main)](https://github.com/OpenSCvx/OpenSCvx/actions/workflows/tests-mjx.yml) |
260
+ | [jaxlie](https://github.com/brentyi/jaxlie) | `lie` | SO(3)/SE(3) Lie-group operations and IK initialization | [![jaxlie](https://github.com/OpenSCvx/OpenSCvx/actions/workflows/tests-lie.yml/badge.svg?branch=main)](https://github.com/OpenSCvx/OpenSCvx/actions/workflows/tests-lie.yml) |
261
+ | [qpax](https://github.com/qpax-solver/qpax) | `qpax` | JAX-native QP solver backend | [![qpax](https://github.com/OpenSCvx/OpenSCvx/actions/workflows/tests-qpax.yml/badge.svg?branch=main)](https://github.com/OpenSCvx/OpenSCvx/issues/550) |
262
+ | [CVXPYGen](https://github.com/cvxgrp/cvxpygen) | `cvxpygen` | Generated C solver code for the convex subproblem | [![cvxpygen](https://github.com/OpenSCvx/OpenSCvx/actions/workflows/tests-cvxpygen.yml/badge.svg?branch=main)](https://github.com/OpenSCvx/OpenSCvx/issues/551) |
263
+ | [moreau](https://pypi.org/project/moreau/) | `moreau` | Licensed QP solver backend | ![moreau](https://img.shields.io/badge/moreau-license%20required-lightgrey) |
264
+ | [stljax](https://github.com/UW-CTRL/stljax) | `stl` | Signal Temporal Logic robustness bridge | ![stljax](https://img.shields.io/badge/stljax-no%20tests%20yet-lightgrey) |
265
+ | [frax](https://github.com/danielpmorton/frax) | `frax` | Robot dynamics for the manipulator examples | [![frax](https://img.shields.io/badge/frax-weekly%20examples%20sweep-blue)](https://github.com/OpenSCvx/OpenSCvx/actions/workflows/tests-examples.yml) |
266
+
267
+ <p align="right">(<a href="#readme-top">back to top</a>)</p>
268
+
256
269
  ## Acknowledgements
257
270
 
258
271
  This work was supported by a NASA Space Technology Graduate Research Opportunity and the Office of Naval Research under grant N00014-17-1-2433. The authors would like to acknowledge Natalia Pavlasek, Fabio Spada, Samuel Buckner, Abhi Kamath, Govind Chari, and Purnanand Elango as well as the other Autonomous Controls Laboratory members, for their many helpful discussions and support throughout this work.
@@ -1,6 +1,6 @@
1
1
  openscvx/__init__.py,sha256=sxiqBoS5jv9UmNSxnrokFXeFMnDSkAxUCtC4X-s-vHk,4456
2
2
  openscvx/__main__.py,sha256=Hwm7mtVg3tLdvoUPkpcQv8KF3wxl72PNLBp9axFu8GY,2991
3
- openscvx/_version.py,sha256=zYvwhGhbvK9AbmgQE8S1Hgf833v_MnaaaIPnD1k7tlc,543
3
+ openscvx/_version.py,sha256=wrtUWaZvN1dRuGdNce-UD3bBkmXuuOeUdFGztGeNVcY,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=aJlXbpwQ9_8gP8N2Ff1pz8biF0FoIZTyDYvLX65DLfw,100224
@@ -94,7 +94,7 @@ openscvx/symbolic/expr/arithmetic.py,sha256=bKrlvN6cLelDVPJVPVPXCHbZg2KTdn_4T7wp
94
94
  openscvx/symbolic/expr/array.py,sha256=o_hxHYMNukW0dpyfxsKZKNXOXLmRe87aYik8aUpvNwI,24088
95
95
  openscvx/symbolic/expr/constraint.py,sha256=orrUjChf8YZ-_Nx7QKqEQWKYF8LEdzfaOBb7zSag_GQ,35007
96
96
  openscvx/symbolic/expr/control.py,sha256=xnuZDM-er755i-H8UW2RCaikeQ82x35rf8SCkvvH-CQ,10286
97
- openscvx/symbolic/expr/expr.py,sha256=ERUcfHJ1LdMUSbGZv9mq3YT0JOl55bunVQt9KIx8-00,23577
97
+ openscvx/symbolic/expr/expr.py,sha256=wzrCVxmWCRlNQtcJ4UC5F83eLqLiM740oeQfh56VgXA,25126
98
98
  openscvx/symbolic/expr/linalg.py,sha256=uJn82IyUqPUCYwmLLQcAg-DuPQP1WwrsfDZsd7lS3SQ,12618
99
99
  openscvx/symbolic/expr/logic.py,sha256=K994Ml2GorIze_xJYI40SYkEqc8cNFVA_wKvKSmQ8gw,17023
100
100
  openscvx/symbolic/expr/math.py,sha256=RDTGvv9Ai3NHftJqGfbEaD1cJFo6F99Asu6h-Qw1xVk,44304
@@ -124,7 +124,7 @@ openscvx/symbolic/lowerers/cvxpy/logic.py,sha256=50upTkfukok_L28tjyi4GXVHs4Hv6Qt
124
124
  openscvx/symbolic/lowerers/cvxpy/math.py,sha256=zE7rQ_M1GEsptH2cM5BWcZmiPGxTvTAoop-q4IM1Xt8,12453
125
125
  openscvx/symbolic/lowerers/cvxpy/state.py,sha256=tGJcJBhWD16fBg0DI5-wvqFQGAWh1x-5fEwRcvvLMGI,1174
126
126
  openscvx/symbolic/lowerers/jax/__init__.py,sha256=LjpwfXNQT2lz5jIULYjYHp4glJAf0YpoYTQoaEtMwlc,1332
127
- openscvx/symbolic/lowerers/jax/_lowerer.py,sha256=i5zanUwpNGbyKt9NaJFlQPojWh5bQQMa0olO6BlQ-uQ,1992
127
+ openscvx/symbolic/lowerers/jax/_lowerer.py,sha256=O6kvZ0UMwEPvDuZ_QbqJ66ghoqSDAQ-ff-4I6xRZFys,4648
128
128
  openscvx/symbolic/lowerers/jax/_registry.py,sha256=JKvu5msl1CZv8RJMswZFfdNLQV9TqiGSErxHgk5R48k,2461
129
129
  openscvx/symbolic/lowerers/jax/arithmetic.py,sha256=iqGrLgvqrACPR_wS8qhwi5pwkv8VgafHdzpCjSXjcng,2664
130
130
  openscvx/symbolic/lowerers/jax/array.py,sha256=cOyH29GHqdJy-07GpkUFTcdwxhA4LB5zQCiVN4VCKTg,4486
@@ -133,13 +133,13 @@ openscvx/symbolic/lowerers/jax/control.py,sha256=hTomNujMk2HXp9a6gxErfNgK50qLxrr
133
133
  openscvx/symbolic/lowerers/jax/expr.py,sha256=_umaeTNHeLQ4_HFEF0PommhGeoUtKFzlgFfYMQCkPAU,3652
134
134
  openscvx/symbolic/lowerers/jax/lie.py,sha256=S0o-wkWARG27Tx0_lR-exuJSIZpKz-R9hGCByooV2xg,8473
135
135
  openscvx/symbolic/lowerers/jax/linalg.py,sha256=RohwfRDHbyC39x6EKlie2ER9jQKoPV6UBJU-0bRDRjQ,2302
136
- openscvx/symbolic/lowerers/jax/logic.py,sha256=urIkwKDu3_hZaUKBAa-wOE5ztuhAbo7l-rSZTii6yaU,6969
136
+ openscvx/symbolic/lowerers/jax/logic.py,sha256=4shrgPxe5L-5nZcJhuwUM2VePoyUlXCuzaJhPB83FrQ,7447
137
137
  openscvx/symbolic/lowerers/jax/math.py,sha256=rB_k_I2TwweaywrdEA1PIfaXWVy4SITxXXcfLOOerVA,10759
138
138
  openscvx/symbolic/lowerers/jax/spatial.py,sha256=zbY4v3aYH3HIl4n5FLUeo8zQldn91CeKUdFT3ZqvPic,3466
139
139
  openscvx/symbolic/lowerers/jax/state.py,sha256=AQQnAChd4uqmepiKgIOPp50TpTHnHHOzzClk37nz2ic,945
140
140
  openscvx/symbolic/lowerers/jax/stl.py,sha256=li_USuDolYEq016ppbwgDfGFSIj7Dr5lSXXGNTZcIcc,9246
141
141
  openscvx/symbolic/lowerers/jax/stljax.py,sha256=D5MzTnGQHBbxTHiCPCpX7jJPEREvGRjCksuEMhgna6M,3099
142
- openscvx/symbolic/lowerers/jax/vmap.py,sha256=S-4t-o1n-635OqaccWTlkwdXpX3ouBK5lRvEaKVUMYQ,5983
142
+ openscvx/symbolic/lowerers/jax/vmap.py,sha256=8q7l_KginGTtifOK788G9WtRWrkbAQWdYoErGkUXiG8,6347
143
143
  openscvx/symbolic/parser/__init__.py,sha256=aNxQSewXSZZjEgsnmxzWqH2UDyIs333vBYGUMSYX5lE,1824
144
144
  openscvx/symbolic/parser/_registry.py,sha256=u-Pl9TKEbVnKPO0vMxd_7gzerZb1QkJZD2AliCFw2Gk,1693
145
145
  openscvx/symbolic/parser/array.py,sha256=TSwHnY3VsIIQQKo1EuErtKIZ6bQK8jtAvVoJ8HqEfHE,1089
@@ -159,11 +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.dev35.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
163
- openscvx-0.5.3.dev35.dist-info/METADATA,sha256=zPUnmSxAXkoEBCyp8VD6xerMp6zXIyMuix9WvCgrIUI,10924
164
- openscvx-0.5.3.dev35.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
165
- openscvx-0.5.3.dev35.dist-info/entry_points.txt,sha256=1Oqek8Sy28hmAZFgZXDxFXYVf56YLYWlHjhh9RYJ7wE,52
166
- openscvx-0.5.3.dev35.dist-info/scm_file_list.json,sha256=SlBGAndk9F0hQaBc0H6_xbLqjPOhzhpxfYjAYRvf1M8,18093
167
- openscvx-0.5.3.dev35.dist-info/scm_version.json,sha256=Cx_YDZgQq_q8oE32xSp_vT9pYGqjZ26SBE6bmsrbdT4,161
168
- openscvx-0.5.3.dev35.dist-info/top_level.txt,sha256=nUT4Ybefzh40H8tVXqc1RzKESy_MAowElb-CIvAbd4Q,9
169
- openscvx-0.5.3.dev35.dist-info/RECORD,,
162
+ openscvx-0.5.3.dev37.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
163
+ openscvx-0.5.3.dev37.dist-info/METADATA,sha256=xKxT5ShgB9zei0HLDWMnLp4EnrdaVPOZzKcELOleV8o,12998
164
+ openscvx-0.5.3.dev37.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
165
+ openscvx-0.5.3.dev37.dist-info/entry_points.txt,sha256=1Oqek8Sy28hmAZFgZXDxFXYVf56YLYWlHjhh9RYJ7wE,52
166
+ openscvx-0.5.3.dev37.dist-info/scm_file_list.json,sha256=VDDQ6kOec1NxVc9evE4Q_lhnpoHi_Hw2HmjvfIizIwA,18303
167
+ openscvx-0.5.3.dev37.dist-info/scm_version.json,sha256=gRpnF7DxENzblmw2-fp2YIBAmqYHGPH2mYG04hNBoOU,161
168
+ openscvx-0.5.3.dev37.dist-info/top_level.txt,sha256=nUT4Ybefzh40H8tVXqc1RzKESy_MAowElb-CIvAbd4Q,9
169
+ openscvx-0.5.3.dev37.dist-info/RECORD,,
@@ -216,27 +216,17 @@
216
216
  "tests/test_integrators.py",
217
217
  "tests/test_discretization.py",
218
218
  "tests/test_init.py",
219
- "tests/test_multishot_propagation.py",
220
219
  "tests/test_propagation.py",
221
220
  "tests/hohmann_analytical.py",
222
221
  "tests/test_expert.py",
223
222
  "tests/test_results_parameters.py",
224
223
  "tests/test_post_process_batched_tau_clamp.py",
225
224
  "tests/test_solve_batched_inference.py",
226
- "tests/test_solve_jax_bare_brachistochrone.py",
227
225
  "tests/test_cvxpygen_optional.py",
228
- "tests/test_solve_batched_export_roundtrip.py",
229
- "tests/test_solve_batched_brachistochrone.py",
230
- "tests/_marks.py",
231
- "tests/test_impulsive.py",
232
226
  "tests/conftest.py",
233
- "tests/test_solve_jax_vmap_brachistochrone.py",
234
- "tests/test_brachistochrone.py",
235
- "tests/test_solve_jax_jit_brachistochrone.py",
236
227
  "tests/test_autotuning.py",
237
- "tests/test_solve_batched_cvxpy_export_errors.py",
238
- "tests/test_solve_jax_vmap_converged_no_drift.py",
239
228
  "tests/symbolic/test_preprocessing.py",
229
+ "tests/symbolic/test_gmsr.py",
240
230
  "tests/symbolic/__init__.py",
241
231
  "tests/symbolic/test_lower_cvxpy.py",
242
232
  "tests/symbolic/test_augmentation.py",
@@ -278,8 +268,17 @@
278
268
  "tests/integrations/test_mjx_dynamics.py",
279
269
  "tests/integrations/__init__.py",
280
270
  "tests/integrations/test_mjx.py",
281
- "tests/expr/test_gmsr.py",
282
- "tests/expr/__init__.py",
271
+ "tests/e2e/__init__.py",
272
+ "tests/e2e/test_multishot_propagation.py",
273
+ "tests/e2e/test_solve_jax_bare_brachistochrone.py",
274
+ "tests/e2e/test_solve_batched_export_roundtrip.py",
275
+ "tests/e2e/test_solve_batched_brachistochrone.py",
276
+ "tests/e2e/test_impulsive.py",
277
+ "tests/e2e/test_solve_jax_vmap_brachistochrone.py",
278
+ "tests/e2e/test_brachistochrone.py",
279
+ "tests/e2e/test_solve_jax_jit_brachistochrone.py",
280
+ "tests/e2e/test_solve_batched_cvxpy_export_errors.py",
281
+ "tests/e2e/test_solve_jax_vmap_converged_no_drift.py",
283
282
  "tests/solvers/__init__.py",
284
283
  "tests/solvers/test_moreau_ptr_solver.py",
285
284
  "tests/solvers/test_qpax_ptr_solver.py",
@@ -413,14 +412,19 @@
413
412
  "examples/frax/panda_frax_waypoint.py",
414
413
  ".github/release-drafter.yml",
415
414
  ".github/assets/logo.svg",
415
+ ".github/workflows/tests-qpax.yml",
416
416
  ".github/workflows/branch-name.yml",
417
- ".github/workflows/tests-integration.yml",
417
+ ".github/workflows/tests-lie.yml",
418
418
  ".github/workflows/release.yml",
419
+ ".github/workflows/tests-cvxpygen.yml",
420
+ ".github/workflows/tests-mjx.yml",
419
421
  ".github/workflows/docs.yml",
422
+ ".github/workflows/tests.yml",
420
423
  ".github/workflows/lint.yml",
421
424
  ".github/workflows/release-drafter.yml",
425
+ ".github/workflows/tests-examples.yml",
422
426
  ".github/workflows/_docs.yml",
423
- ".github/workflows/tests-unit.yml",
427
+ ".github/workflows/_extra.yml",
424
428
  ".github/workflows/nightly.yml"
425
429
  ]
426
430
  }
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "0.5.2",
3
+ "distance": 37,
4
+ "node": "gdb86fef25495d5de3c07e7b227642a8e763af01d",
5
+ "dirty": false,
6
+ "branch": "main",
7
+ "node_date": "2026-07-09"
8
+ }
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "0.5.2",
3
- "distance": 35,
4
- "node": "g43f7b7f313888f19e8586f108d8e11bed332f87f",
5
- "dirty": false,
6
- "branch": "main",
7
- "node_date": "2026-07-06"
8
- }