Skip to content

Release v1.3.0#128

Merged
daggbt merged 25 commits intomainfrom
release/v1.3.0
Apr 22, 2026
Merged

Release v1.3.0#128
daggbt merged 25 commits intomainfrom
release/v1.3.0

Conversation

@daggbt
Copy link
Copy Markdown
Collaborator

@daggbt daggbt commented Apr 12, 2026

Release v1.3.0

Merges the full v1.3.0 release into main. This PR now reflects the current release branch, including the final docs, benchmark, org-migration, and release-polish updates added after the PR was opened.

Highlights

  • MILP support - BinaryVariable, IntegerVariable, and VectorVariable(domain="binary"|"integer") route linear discrete models to SciPy HiGHS via scipy.optimize.milp()
  • Vectorized gradients and flatter expression trees - VectorGradientPattern, NarySum / NaryProduct, VectorBinaryOp, and DotProduct fast paths reduce compile overhead and keep large models scalable
  • Sparse computation - sparse LP blocks via Problem.subject_to(A @ x <= b) and as_matrix(...), plus sparse Jacobian/objective paths and improved sparse NLP routing
  • VariableDict and modeling ergonomics - dict-indexed variables, generator support in subject_to(), between(), fancy indexing, per-element array bounds, and Problem context manager
  • Incremental solving and observability - remove_constraint(), reset(), warm starts, solver callbacks, time_limit=, and SolverStatus.TERMINATED
  • Interop and persistence - LP export via Problem.write("model.lp"), plus Solution.to_dict(), to_json(), and from_json()
  • Performance - LP near parity with raw SciPy, CQP around 1.2-2.2x with exact Jacobians, and 2x-900x repeated-solve speedups
  • Release polish - updated docs and benchmarks for v1.3.0, repo transfer to optyx-dev, version and dependency bumps, lint/format cleanup, typing fixes, and sparse-solver documentation polish

Notes

  • Docs, badges, and repository links now point at the optyx-dev organization.
  • Benchmark assets and release documentation were refreshed as part of the final release pass.
  • See CHANGELOG.md for the complete v1.3.0 entry.

* Implement NarySum, NaryProduct and flatten_expression optimizer

- Add NarySum and NaryProduct expression nodes for flat n-ary trees
- Add iterative flatten_expression() to coalesce nested BinaryOp chains
- Eliminates recursion depth limit for loop-summed expressions
- ~7x evaluation speedup for loop-constructed sums

* Add gradient rules for NarySum/NaryProduct and additional tests

- Handle NarySum/NaryProduct in _gradient_cached and _gradient_iterative
- Add 6 new test cases (gradients, get_variables, edge cases)
- 15 total nary expression tests passing

* wire flatten_expression into solve pipeline, add NarySum/NaryProduct to compiler

- Call flatten_expression() on objective and constraint expressions in
  scipy_solver._build_solver_cache() before compilation
- Add NarySum/NaryProduct handlers to _build_evaluator() (recursive path)
- Add NarySum/NaryProduct handlers to _build_evaluator_iterative() (iterative path)
- All 819 tests pass
…lation (#111)

* feat: add VectorBinaryOp for efficient vector arithmetic

Introduce VectorBinaryOp as a subclass of VectorExpression that stores
a single (left, right, op) triple instead of N individual BinaryOp
nodes. This reduces expression tree size from O(n) nodes to O(1) for
element-wise vector operations like x + y, x - target, x * weights.

- Add VectorBinaryOp class with numpy-vectorized evaluate()
- Update _vector_binary_op() to return VectorBinaryOp
- Add compiler fast path using numpy ufuncs for VectorBinaryOp
- Add fast paths in VectorExpressionSum and LinearCombination compilation
- Fix __rsub__, __rtruediv__, __neg__ on VectorVariable and VectorExpression
- Add 37 tests covering construction, evaluation, compilation, gradients,
  constraints, and end-to-end solves

Closes #87

* feat: complete Task 0.1 & 0.2 — lazy VectorBinaryOp, vectorized gradients, flat NarySum output

Task 0.2 (VectorBinaryOp):
- Make _expressions lazy via @Property (O(1) construction)
- VectorExpressionSum.evaluate() bypasses materialization
- Add @register_gradient(VectorBinaryOp) for O(1) symbolic gradients
- Add compile_gradient fast path for sum(VectorBinaryOp) patterns
- Add compile_gradient fast path for NarySum with VectorBinaryOp terms

Task 0.1 (NarySum — carried from closed branch):
- NarySum gradient produces flat NarySum instead of nested BinaryOp chain
- Both recursive and iterative gradient paths updated

Tests: 26 new tests (882 total, 0 failures)
…tection (#113)

- Implemented np.empty() pre-allocation in compiler.py for 5-10% gradient speedup (Issue #89)
- Added Constant Hessian Detection for QuadraticForm, DotProduct, and LinearCombination in autodiff.py (Task 0.5)
- Added comprehensive tests for Hessian optimization
Implements efficient O(1) Hessian computation for sums of quadratic forms (e.g. 2*x'Q1x + 3*x'Q2x) by pre-calculating the summed matrix at compile time.

Changes:
- Added recursive constant detection in compute_hessian (BinaryOp handling)
- Added global optimization in compile_hessian: returns cached constant matrix function if all entries are constant
- Fixed LinearCombination detecting zero Hessian for non-linear vectors
- Added _hash initialization to VectorPowerSum/VectorUnarySum to fix caching bugs
- Added regression tests for linear combinations of quadratic forms
…, #93) (#115)

* feat(autodiff): implement VectorGradientPattern enum for expression classification (#91)

Renamed existing VectorGradientPattern dataclass to AffineGradientPattern to avoid conflict and better reflect purpose. Implemented new VectorGradientPattern Enum and detector for general structural classification.

* perf: optimize AffineGradientPattern with structured metadata (#91, #92, #93)

- Add linear_type/linear_scale/linear_diag metadata to AffineGradientPattern
  to avoid O(n²) matrix materialization during pattern detection
- Rewrite compile_vector_gradient to use metadata-first fast paths,
  reducing compilation from O(n²) to O(1) for common patterns
- Optimize _combine_patterns to check structured types before materializing
- Use scipy.optimize.Bounds instead of list-of-tuples in solver
- Update benchmark results reflecting performance improvements
Detect which variables an expression depends on to skip zero-gradient
columns during Jacobian compilation. Uses extract_all_linear_coefficients
for O(n) constant detection on linear expressions.

Extends NLP benchmarks to n=10000, fixes SciPy baseline to use L-BFGS-B.
Add compile_sparse_gradient(), compile_gradient_with_sparsity() to
compiler.py and compile_sparse_jacobian() to autodiff.py. Returns
scipy.sparse.csr_matrix with O(nnz) memory for sparse constraint
systems, falls back to dense when density > 0.5.

Integrate batched sparse constraint Jacobian in scipy_solver.py.
27 new tests, 939 total passing.
Add subject_to_matrix(A, x, sense, b) to Problem, accepting dense or
scipy.sparse constraint matrices. Bypasses expression tree entirely and
passes sparse A directly through to HiGHS via linprog.

- _MatrixConstraint dataclass stores raw matrix constraints
- _merge_matrix_constraints() in analysis.py handles column mapping,
  sense conversion, and merging with expression-tree constraints
- 22 new tests covering dense/sparse, all senses, mixed constraints
- Zero overhead vs raw scipy at n=100K (1.0x cold/warm)
- Wire Variable.obj into LP objective extraction (analysis.py)
- Wire Variable.obj into NLP objective building (scipy_solver.py)
- Fix pyright warning: guard sub_expr None check in autodiff.py
- Fix pyright warning: type-ignore scipy Bounds stub mismatch
- Add 34 tests for between(), generator subject_to, context manager, obj=
- Solution.to_dict(): 4 tests (basic fields, None fields, multipliers, all statuses)
- Solution.to_json(): 3 tests (string output, file write, roundtrip)
- Solution.from_json(): 5 tests (string, file, missing fields, all statuses, solve roundtrip)
- Problem.reset(): 6 tests (clears caches, preserves definition, cold re-solve, NLP)
- SolverStatus.TERMINATED: 4 tests (exists, is_feasible, serialization, enum members)
- Solution.print_vars(): 3 tests (output format, no objective, sorted order)

All 1021 tests passing.
- MatrixVariable.diagonal(offset): 13 tests (main/super/sub diagonals, rectangular, bounds, symmetric)
- Per-element bounds arrays: 10 tests (numpy/list/scalar/None, size validation, LP/NLP solves)
- Fancy indexing x[[0,2]]: 13 tests (list/ndarray/tuple/boolean, negative, error cases, solves)
- Combined features: 2 integration tests

All 3 features were already implemented. Updated benchmark results.
1059 tests passing.
Route linear problems with integer/binary variables to HiGHS MILP solver.

- Add milp_solver.py wrapping scipy.optimize.milp()
- Add BinaryVariable/IntegerVariable aliases, VectorVariable domain support
- Route integer vars from lp_solver to solve_milp automatically
- Guard against MIQP/MINLP (raises ValueError)
- Add Solution.mip_gap and Solution.best_bound fields
- Domain validation in Variable.__init__
- 28 new MILP tests, 7 updated solver tests
- MILP scaling benchmarks (loop n<=500, vector n<=5000)
- VariableDict: dict-indexed variable collection with string keys,
  per-key bounds, sum/prod/subset operations, and Solution extraction
- 43 tests covering creation, access, expressions, solving, edge cases
- Mine production planning example and quarto doc (VariableDict showcase)
- Mine equipment MILP example and quarto doc (BinaryVariable, IntegerVariable,
  VectorVariable with binary/integer domains, Big-M constraints)
- Sparse benchmark: added comparison plots for compile/eval speedup
  and memory reduction
- Add Problem.remove_constraint(index_or_name) for removal by int index or str name
- Selective cache invalidation: preserve objective/gradient cache when only constraints change
- Warm start: store previous solution, use as x0 on re-solve (warm_start=True default)
- Problem.reset() now clears warm start state
- Fix LP bounds caching bug: always re-read v.lb/v.ub instead of stale LPData.bounds
- Add _rebuild_constraint_cache() for partial NLP cache rebuild
- 33 new tests covering add/remove, warm start, bounds freshness, fix/unfix, staleness
- Add modify_and_resolve.py example and quarto doc
- Add SolverProgress dataclass with iteration, objective_value,
  constraint_violation, elapsed_time, x fields
- Add callback= and time_limit= parameters to Problem.solve()
- Implement _EarlyTermination exception and composite callback builder
  in scipy_solver.py, supporting SLSQP, trust-constr, and L-BFGS-B
- Export SolverProgress from optyx.__init__
- Fix ElementwisePower support in compiler, autodiff, and vector helpers
  to enable vectorized VectorVariable slicing expressions
- Add 26 callback/time-limit tests and 5 vector slicing tests
- Add examples/solver_callbacks.py demo
- Add docs/examples/solver-callbacks.qmd quarto documentation

Closes #105
- Add src/optyx/io.py with write_lp(), format_lp(), and _format_lp()
  supporting LP, QP, and MIP problems in CPLEX LP format
- Add Problem.write(filename) and Problem.to_lp() methods
- Add extract_quadratic_coefficients() to analysis.py
- Handle quadratic objectives (QuadraticForm, DotProduct, var*var, expr**2)
  with correct [ Q ] / 2 notation
- Support Generals/Binaries sections for integer/binary variables
- Support matrix constraints (dense and sparse)
- Add 59 tests in test_lp_export.py
- Add examples/lp_export.py demo
- Add docs/examples/lp-export.qmd documentation
- Update docs/_quarto.yml navigation

Closes #106
* docs: implement v1.3.0 documentation plan

* Restructure docs nav and add What's New page

- Group tutorials by topic (Modeling, Variables, Advanced)
- Group examples by category (Industry, Techniques, Large-Scale)
- Rename API section to Topic Guides
- Add What's New page covering v1.3.0 features

* Fix tutorial code cells for integer-programming and variable-dict

- Use complete dicts for VariableDict bounds (all keys required)
- Replace non-existent .to_dict() with manual dict comprehension

* Update benchmarks with latest run data and add MILP section

- Refresh all LP, NLP, CQP tables from benchmark_output.txt
- Add MILP scaling section with knapsack benchmark
- Add milp, sparse PNGs to assets
- Update overhead summary to include MILP
- Fix NLP narrative to reflect actual benchmark characteristics

* Update docs and benchmarks for v1.3.0
@daggbt daggbt merged commit f693dd8 into main Apr 22, 2026
3 checks passed
@daggbt daggbt deleted the release/v1.3.0 branch April 22, 2026 02:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant