What’s New in v1.3.0

Scalable expressions, MILP support, vectorized gradients, and modeling convenience
Published

May 28, 2026

1 Optyx v1.3.0

Theme: Scalable Expressions, Vectorized Gradients, Sparse Computation, Basic MIP, Modeling Convenience

This release addresses critical scalability limitations discovered in v1.2.x benchmarks and adds major new capabilities.


1.1 Mixed-Integer Linear Programming (MILP)

Optyx now supports integer and binary decision variables, automatically routing problems with discrete variables to the MILP solver (SciPy’s HiGHS backend via scipy.optimize.milp()).

  • BinaryVariable() and IntegerVariable() constructor aliases
  • VectorVariable(domain="binary") and VectorVariable(domain="integer") for vectorized discrete variables
  • Solution.mip_gap and Solution.best_bound for optimality reporting
  • Domain validation enforces correct bounds for binary variables
  • Clear error when attempting unsupported MIQP (quadratic + integer)

See the Integer Programming tutorial and the Mine Equipment MILP example.


1.2 Vectorized Gradients & Scalability

Cold-start performance for quadratic programs has been dramatically improved through automatic vectorized gradient detection.

Problem Cold Overhead vs SciPy Warm Overhead vs SciPy
CQP n=500 2.2x 1.2x
CQP n=1,000 1.8x 1.2x
CQP n=5,000 1.1x 1.0x
  • VectorGradientPattern detects expressions with vectorizable gradient structure (∇f = Ax + b)
  • NarySum / NaryProduct flatten deep loop-built trees to O(1) depth
  • VectorBinaryOp preserves vector structure for element-wise operations

See the Benchmarks and the Performance & Scaling guide.


1.3 Sparse Computation

Large-scale LPs with 100,000+ variables are now practical.

  • Problem.subject_to(A @ x <= b) supports matrix blocks directly, with as_matrix(...) for sparse coefficient matrices
  • as_matrix(storage="auto" | "dense" | "sparse") gives explicit control over matrix-block storage, with automatic CSR conversion for large low-density dense arrays
  • Sparse Jacobian compilation reduces memory from O(m×n) to O(nnz)
  • Sparse constrained NLPs now bias toward trust-constr sooner, lazily compiling the batched sparse Jacobian only when that solver path is actually used
  • n=100,000 LP with 1% density constraint matrix solves end-to-end

1.4 Modeling Convenience

  • VariableDict — dict-indexed variables keyed by strings, with .prod() and .sum(subset) aggregation. See the VariableDict tutorial.
  • Expression.between(lb, ub) — range constraints in one call
  • subject_to() accepts generatorsprob.subject_to(x[i] >= 0 for i in range(n))
  • Problem context managerwith Problem() as p: ...
  • Problem.reset() — clear solver cache and warm-start state
  • Problem.remove_constraint() — incremental model modification
  • Warm starts — re-solves automatically use the previous solution
  • Bounds correctness fix — variable bounds are never cached, enabling fix-and-dive patterns

1.5 Solver Callbacks & Termination

  • SolverProgress dataclass with iteration, objective, violation, elapsed time, and current x
  • callback= parameter on solve() — return True to stop early
  • time_limit= parameter on solve() — wall-clock budget
  • SolverStatus.TERMINATED for callback-initiated stops

See the Solver Callbacks example.


1.6 Serialization & I/O

  • Problem.write("model.lp") — export to LP format (linear and quadratic objectives, constraints, bounds, integer/binary sections)
  • Solution.to_dict() and Solution.to_json() — solution serialization for logging and auditing

See the LP Export example.


1.7 Per-Element Array Bounds & Fancy Indexing

  • Array bounds on VectorVariable: VectorVariable("x", 3, lb=np.array([0, 0.5, 0.2]))
  • Fancy indexing: x[[0, 2, 5]] returns a subset vector expression

See the Vector Variables tutorial.