Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,19 @@ Note: This is the main Changelog file for the Rust solver. The Changelog file fo
--------------------- -->
## [v0.12.0] - Unreleased

### Added

- Richer Rust-side solver errors with human-readable reasons for projection failures, non-finite computations, linear algebra failures, and invalid internal solver states
- Fallible constraint projections via `Constraint::project(...) -> FunctionCallResult`, with error propagation through FBS, PANOC, and ALM
- Checked affine-space construction through `AffineSpace::try_new(...)` and `AffineSpaceError`

### Changed

- Rust solver supports generic float types
- Expanded Rust constraint test coverage with constructor validation, boundary/idempotence checks, and additional `BallP` / epigraph projection cases
- Swap the cross-platform timer dependency to web-time, remove instant-specific wasm feature wiring, update optimizer timing call sites to use `web_time::Instant`, keep existing native and wasm timing behavior without stdweb risk
- Expanded Rust constraint and solver test coverage with constructor validation, boundary/idempotence checks, additional `BallP` / epigraph projection cases, and broader `f32`/`f64` coverage
- Swapped the cross-platform timer dependency to `web-time`, removed the old `instant`-specific wasm feature wiring, and updated optimizer timing call sites to use `web_time::Instant`
- Improved Rust-side error handling across constraints and core solvers so projection failures and invalid numerical states are reported explicitly instead of being silently flattened
- Refined `BallP`, `EpigraphSquaredNorm`, and related constraint implementations and docs for stronger numerical robustness and clearer behavior

<!-- ---------------------
v0.11.1
Expand Down
3 changes: 2 additions & 1 deletion docs/openrust-basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,8 @@ fn main() {
.with_max_iter(max_iters);

// Invoke the solver
let status = panoc.solve(&mut u);
let status = panoc.solve(&mut u).unwrap();
assert!(status.has_converged());
}
```

Expand Down
50 changes: 41 additions & 9 deletions docs/python-tcp-ip.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,24 +131,56 @@ $ echo '{ "Kill" : 1 }' | nc localhost 4598

### Error reporting

In case a request cannot be processed, e.g., because the provided JSON is malformed, the provided vectors have incompatible dimensions, the TCP server will return to the client an error report. This is a JSON with three attributes: (i) a key-value pair `"type": "Error"`, to allow the client to tell that an error has occurred, (ii) a `code`, which can be used to uniquely identify the type of error and (iii) a `message`, which offers some human-readable details.
If a request cannot be processed, for example because the JSON payload is
malformed, the request body is not valid UTF-8, or one of the provided vectors
has the wrong dimension, the TCP server returns an error report instead of a
solution.

For example, if the client provides an incompatible number of parameters, that is, if vector `parameter` is of the wrong length, then the server will return the following error:
The error report is a JSON document with three fields:

- `"type": "Error"` so the client can distinguish errors from successful solver
responses
- `code`, a machine-readable integer code
- `message`, a human-readable string with more context

In particular, the `message` field is now intended to be descriptive. For
dimension-related errors it includes the provided and expected lengths, and for
solver failures it includes the underlying solver-side reason whenever
available.

For example, if the client provides an incompatible number of parameters, that
is, if vector `parameter` has the wrong length, the server returns an error like
the following:

```json
{
"type": "Error",
"code": 3003,
"message": "wrong number of parameters: provided 1, expected 2"
}
```

Likewise, if the solver itself fails, the server returns code `2000` together
with the propagated solver reason, for example:

```json
{
"type": "Error",
"code": 3003,
"message": "wrong number of parameters"
"type": "Error",
"code": 2000,
"message": "problem solution failed: non-finite computation: gradient evaluation returned a non-finite value during an FBS step"
}
```

The following errors may be returned to the client
The following error codes may be returned to the client:

| Code | Explanation |
|-----------|---------------------------------------------|
| 1000 | Invalid request: Malformed or invalid JSON |
| 1000 | Invalid request: malformed JSON or invalid UTF-8 payload |
| 1600 | Initial guess has incompatible dimensions |
| 1700 | Wrong dimension of Langrange multipliers |
| 2000 | Problem solution failed (solver error) |
| 1700 | Wrong dimension of Lagrange multipliers |
| 2000 | Problem solution failed; the message contains the solver-side reason |
| 3003 | Vector `parameter` has wrong length |

When using the Python TCP client, these responses are surfaced as
`opengen.tcp.solver_error.SolverError`, whose `code` and `message` properties
mirror the JSON payload returned by the TCP server.
8 changes: 6 additions & 2 deletions examples/panoc_ex1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ fn main() {
// define the cost function and its gradient
let df = |u: &[f64], grad: &mut [f64]| -> Result<(), SolverError> {
if a < 0.0 || b < 0.0 {
Err(SolverError::Cost)
Err(SolverError::Cost(
"Rosenbrock parameters must be nonnegative",
))
} else {
rosenbrock_grad(a, b, u, grad);
Ok(())
Expand All @@ -38,7 +40,9 @@ fn main() {

let f = |u: &[f64], c: &mut f64| -> Result<(), SolverError> {
if a < 0.0 || b < 0.0 {
Err(SolverError::Cost)
Err(SolverError::Cost(
"Rosenbrock parameters must be nonnegative",
))
} else {
*c = rosenbrock_cost(a, b, u);
Ok(())
Expand Down
2 changes: 2 additions & 0 deletions open-codegen/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ Note: This is the Changelog file of `opengen` - the Python interface of OpEn

- ROS2 package generation support via `BuildConfiguration.with_ros2(...)`, including auto-generated ROS2 templates, launcher, messages, and package wrapper code
- Dedicated ROS2 tests covering package generation, build configuration behavior, rendered custom package settings, and end-to-end execution of a generated ROS2 node
- More informative TCP solver error payloads, including clearer dimension/parameter validation failures and propagated solver-side failure messages

### Changed

- Extended `RosConfiguration` so it can be used for both ROS and ROS2 package generation
- Updated generated TCP server and C interface templates to work with the richer Rust solver error model and expose better failure information to clients


## [0.10.1] - 2026-03-25
Expand Down
4 changes: 2 additions & 2 deletions open-codegen/opengen/tcp/solver_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ def code(self):
Possible error codes are:
- **1000**: Invalid request: Malformed or invalid JSON
- **1000**: Invalid request: malformed JSON or invalid UTF-8 payload
- **1600**: Initial guess has incomplete dimensions
- **1700**: Wrong dimension of Lagrange multipliers
- **2000**: Problem solution failed (solver error)
- **2000**: Problem solution failed (message may include the solver reason)
- **3003**: Parameter vector has wrong length
:return: Error code
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,11 @@ pub unsafe extern "C" fn {{meta.optimizer_name|lower}}_solve(
},
Err(e) => {{meta.optimizer_name}}SolverStatus {
exit_status: match e {
SolverError::Cost => {{meta.optimizer_name}}ExitStatus::{{meta.optimizer_name}}NotConvergedCost,
SolverError::NotFiniteComputation => {{meta.optimizer_name}}ExitStatus::{{meta.optimizer_name}}NotConvergedNotFiniteComputation,
SolverError::Cost(_)
| SolverError::ProjectionFailed(_)
| SolverError::LinearAlgebraFailure(_)
| SolverError::InvalidProblemState(_) => {{meta.optimizer_name}}ExitStatus::{{meta.optimizer_name}}NotConvergedCost,
SolverError::NotFiniteComputation(_) => {{meta.optimizer_name}}ExitStatus::{{meta.optimizer_name}}NotConvergedNotFiniteComputation,
},
num_outer_iterations: u64::MAX as c_ulong,
num_inner_iterations: u64::MAX as c_ulong,
Expand Down Expand Up @@ -209,4 +212,4 @@ pub unsafe extern "C" fn {{meta.optimizer_name|lower}}_free(instance: *mut {{met
assert!(!instance.is_null());
drop(Box::from_raw(instance));
}
{% endif %}
{% endif %}
Loading
Loading