pympcc.MPCCSolver

class pympcc.MPCCSolver(problem, strategy='scholtes', backend='ipopt', ipopt_options=None, solver_options=None, callback=None, inner_callback=None, time_limit=None, verbose=False, presolve=False, diagnostics=False, autoscale=False, b_stat_max_biactive=10, tnlp_refine=False, tnlp_max_iter=500, **strategy_options)[source]

Bases: object

Solver for Mathematical Programs with Complementarity Constraints (MPCC).

Parameters:
  • problem (MPCCProblem) – The MPCC problem instance.

  • strategy ({'direct', 'scholtes', 'smoothing', 'lin_fukushima', 'augmented_lagrangian', 'slack'}) –

    Reformulation strategy (default 'scholtes').

    • 'direct' — single NLP solve with G·H 0.

    • 'scholtes' — outer loop relaxing G·H ε with ε 0.

    • 'smoothing' — Fischer-Burmeister smoothing φ_ε(G,H) = 0.

    • 'lin_fukushima' — Scholtes + G+H ε to preserve MPCC-MFCQ.

    • 'augmented_lagrangian' — PHR penalty; complementarity in objective only.

    • 'slack' — lifting strategy with slack variables s_G = G(x), s_H = H(x); the complementarity Jacobian rows have zero x-block, which is efficient when n_comp n. Incompatible with backend='filterSQP'.

  • backend ({'ipopt', 'filterSQP', 'scipy'}, optional) –

    NLP backend solver (default 'ipopt').

    • 'ipopt' — uses IPOPT via cyipopt (default, fully supported).

    • 'filterSQP' — uses pyfiltersqp (L-BFGS SQP). Requires the pyfiltersqp package to be installed. The 'slack' strategy is incompatible with this backend.

    • 'scipy' — uses scipy.optimize.minimize with method='trust-constr'. Requires scipy>=1.10. No IPOPT installation required; suitable for small problems and IPOPT-free environments. Dual warm-starting is accepted but not forwarded (scipy does not support multiplier warm-starts); exact Hessians are not used.

  • ipopt_options (dict, optional) – Options passed to the IPOPT backend (e.g. {"max_iter": 500, "tol": 1e-8}). When backend='filterSQP' the common keys "tol" and "max_iter" are translated to filterSQP equivalents; IPOPT-specific keys are silently ignored. Merged with package defaults; user values take precedence.

  • solver_options (dict, optional) – Options forwarded directly to SQPSolver (e.g. {"tol_feas": 1e-7, "lbfgs_memory": 20}). Ignored when backend='ipopt'.

  • **strategy_options – Extra keyword arguments forwarded to the strategy class (e.g. epsilon_0, reduction for Scholtes / smoothing). Pass dual_warmstart=False to disable dual warm-starting between outer iterations (default True for all iterative strategies).

  • callback (callable, optional) – f(k: int, info: IterationInfo) -> None called after each outer iteration, where k is the 0-based iteration index. Not called by the 'direct' strategy (single solve, no outer loop).

  • inner_callback (callable, optional) – f(iter_count: int, info: dict) -> bool invoked once per IPOPT inner iteration. Return False to stop the inner solve early. info mirrors IPOPT’s intermediate arguments. Ignored by the 'filterSQP' and 'scipy' backends.

  • time_limit (float, optional) – Wall-clock budget in seconds for the outer iterative loop. When the budget is exhausted the loop terminates and the best feasible incumbent (smallest comp_residual among accepted iterates) is returned with result.time_limit_hit = True. Does not interrupt an in-flight inner NLP solve — use IPOPT’s max_cpu_secs for that. Ignored by the 'direct' strategy.

  • verbose (bool, optional) – If True and no callback is provided, prints a formatted progress table to stdout after each outer iteration (default False).

  • presolve (bool)

  • diagnostics (bool)

  • autoscale (bool)

  • b_stat_max_biactive (int)

  • tnlp_refine (bool)

  • tnlp_max_iter (int)

Examples

>>> solver = MPCCSolver(problem, strategy='scholtes',
...                     epsilon_0=0.5, reduction=0.1)
>>> result = solver.solve()

Using the filterSQP backend:

result = pympcc.solve(problem, backend='filterSQP',
                      solver_options={'lbfgs_memory': 20})
__init__(problem, strategy='scholtes', backend='ipopt', ipopt_options=None, solver_options=None, callback=None, inner_callback=None, time_limit=None, verbose=False, presolve=False, diagnostics=False, autoscale=False, b_stat_max_biactive=10, tnlp_refine=False, tnlp_max_iter=500, **strategy_options)[source]
Parameters:
  • problem (MPCCProblem | StructuredMPCC)

  • strategy (Literal['direct', 'scholtes', 'smoothing', 'lin_fukushima', 'augmented_lagrangian', 'slack', 'smooth_min', 'chen_chen_kanzow', 'kanzow_schwartz', 'chen_mangasarian', 'billups', 'veelken_ulbrich_pow', 'veelken_ulbrich_sin', 'ncp'])

  • backend (Literal['ipopt', 'filterSQP', 'scipy'])

  • ipopt_options (dict | None)

  • solver_options (dict | None)

  • callback (Callable[[int, IterationInfo], None] | None)

  • inner_callback (Callable[[int, dict], bool] | None)

  • time_limit (float | None)

  • verbose (bool)

  • presolve (bool)

  • diagnostics (bool)

  • autoscale (bool)

  • b_stat_max_biactive (int)

  • tnlp_refine (bool)

  • tnlp_max_iter (int)

Return type:

None

Methods

__init__(problem[, strategy, backend, ...])

resolve(problem, *[, warm_x0, warm_dual])

Re-solve a near-identical MPCC reusing the previous result's state.

solve()

Run the solver and return an MPCCResult.

solve()[source]

Run the solver and return an MPCCResult.

Return type:

MPCCResult

resolve(problem, *, warm_x0=True, warm_dual=True)[source]

Re-solve a near-identical MPCC reusing the previous result’s state.

Parameters:
  • problem (MPCCProblem or StructuredMPCC) – The new problem. Must share the structural signature (n, n_comp, n_eq, n_ineq, every Jacobian sparsity pattern) of the problem this solver was constructed with; numeric values may differ freely.

  • warm_x0 (bool) – When True (default), seed the new problem.x0 with the previous result’s x* (clipped onto the new variable bounds). When False, use whatever x0 the new problem carries.

  • warm_dual (bool) – When True (default), forward the previous solve’s mult_g / mult_x_L / mult_x_U to IPOPT and toggle warm_start_init_point=yes on the first inner solve.

Returns:

result.warmstart_savings_iter carries the IPOPT iter delta vs the cold-baseline solve.

Return type:

MPCCResult

Notes

  • When the structural signature changes, this method emits a UserWarning and falls back to a cold-rebuild of the strategy. The cold-rebuild’s iter count becomes the new baseline.

  • autoscale is intentionally not re-applied on resolve: rescaling comp pairs would invalidate the warm dual. Reconstruct the solver if you need a fresh autoscale probe.

  • Subsequent solve() calls on the same instance also act as warm-restarts (the warm state persists); resolve is the intended entry point because it lets you swap the problem in.