slsqp_jax.slsqp.bounds

NLP-level bound machinery: clipping iterates, building the bound Jacobian, and post-line-search bound-multiplier recovery.

slsqp_jax.slsqp.bounds

NLP-level box-bound machinery for the SLSQP outer loop.

These helpers handle the three points where bounds enter the outer SQP iteration (the reduced-space QP-level bound-fixing loop lives in slsqp_jax.qp.bound_fixing instead):

  • clip_to_bounds() — defensively project an iterate onto the feasible box (used in init and after every line search).

  • compute_bound_constraint_values() — evaluate c(x) = x lb / ub x for the finite bound rows; the result is appended to the user inequality vector.

  • build_bound_jacobian() — build the constant [I; −I] Jacobian rows for the bound constraints. Computed once during init and stored on SLSQPState.

  • recover_bound_multipliers() — post-line-search refresh of the bound multipliers from the partial Lagrangian gradient at x_{k+1}. See AGENTS.md for the rationale.

All functions are pure and free of solver-class state; they take the precomputed lower_indices / upper_indices tuples and the bounds array directly so they can be reused outside SLSQP (e.g. by the eventual standalone QP solver).

slsqp_jax.slsqp.bounds.build_bound_jacobian(n, bounds, lower_indices, upper_indices)[source]

Constant Jacobian of the bound constraints.

+I rows for lower bounds, −I rows for upper bounds. Empty matrix when no finite bounds are present.

Return type:

Float[Array, 'm_bounds n']

Parameters:
  • n (int)

  • bounds (Float[Array, 'n 2'] | None)

  • lower_indices (tuple[int, ...])

  • upper_indices (tuple[int, ...])

slsqp_jax.slsqp.bounds.clip_to_bounds(y, bounds)[source]

Project y onto the box defined by bounds.

Returns y unchanged when bounds is None.

Return type:

Float[Array, 'n']

Parameters:
  • y (Float[Array, 'n'])

  • bounds (Float[Array, 'n 2'] | None)

slsqp_jax.slsqp.bounds.compute_bound_constraint_values(y, bounds, lower_indices, upper_indices)[source]

Compute bound constraint values c(x) >= 0 for finite bounds.

Returns the empty vector when no finite bounds are present.

Return type:

Float[Array, 'm_bounds']

Parameters:
  • y (Float[Array, 'n'])

  • bounds (Float[Array, 'n 2'] | None)

  • lower_indices (tuple[int, ...])

  • upper_indices (tuple[int, ...])

slsqp_jax.slsqp.bounds.recover_bound_multipliers(*, y_new, grad_new, eq_jac_new, ineq_jac_new, mult_eq, mult_ineq_general, bounds, lower_indices, upper_indices, m_general)[source]

Post-line-search NLP-level bound-multiplier refresh.

Reads off the bound multiplier at the active bounds from the partial Lagrangian gradient at x_{k+1}, with the sign convention μ_lower = +partial_grad_L / μ_upper = -partial_grad_L inherited from build_bound_jacobian() (+I / −I). Clamped to 0 for dual feasibility.

See AGENTS.md for the full motivation; in short, the QP-level bound multipliers were recovered from B d + g Aᵀ λ at x_k using the L-BFGS HVP and the QP active set, so they inherit an O(L-BFGS) + O(line-search) + O(active-set) error budget that on bound-heavy problems pins ||∇L|| / |L| above rtol even at a true KKT point. Splicing the partial-gradient recovery zeros that residual exactly at the bound-active indices by construction.

Return type:

tuple[Float[Array, 'n_lower'], Float[Array, 'n_upper']]

Parameters:
  • y_new (Float[Array, 'n'])

  • grad_new (Float[Array, 'n'])

  • eq_jac_new (Float[Array, 'm_eq n'])

  • ineq_jac_new (Float[Array, 'm_ineq n'])

  • mult_eq (Float[Array, 'm_eq'])

  • mult_ineq_general (Float[Array, 'm_general'])

  • bounds (Float[Array, 'n 2'] | None)

  • lower_indices (tuple[int, ...])

  • upper_indices (tuple[int, ...])

  • m_general (int)