Basic Usage

This guide covers the fundamental usage patterns for Moreau.

Single Problem Solving

For solving a single optimization problem, use the Solver class:

import moreau
import numpy as np
from scipy import sparse

# Define problem data
P = sparse.diags([1.0, 2.0], format='csr')
q = np.array([1.0, 1.0])
A = sparse.csr_matrix([[1.0, 1.0], [-1.0, 0.0], [0.0, -1.0]])
b = np.array([1.0, 0.0, 0.0])

# Define cones
cones = moreau.Cones(
    num_zero_cones=1,    # First row: equality
    num_nonneg_cones=2,  # Remaining rows: inequalities
)

# Create solver and solve
solver = moreau.Solver(P, q, A, b, cones=cones)
solution = solver.solve()

# Access results
print(f"Optimal x: {solution.x}")
print(f"Dual variables: {solution.z}")
print(f"Slack: {solution.s}")

Accessing Solver Information

After calling solve(), metadata is available via solver.info:

solution = solver.solve()
info = solver.info

# Status
print(f"Status: {info.status}")  # SolverStatus.Solved

# Objective value
print(f"Objective: {info.obj_val}")

# Timing
print(f"Solve time: {info.solve_time:.4f}s")
print(f"Setup time: {info.setup_time:.4f}s")
print(f"Construction time: {info.construction_time:.4f}s")

# Iterations
print(f"IPM iterations: {info.iterations}")

Solver Status

The solver returns a status indicating the solve outcome:

from moreau import SolverStatus

status = solver.info.status

if status == SolverStatus.Solved:
    print("Optimal solution found")
elif status == SolverStatus.PrimalInfeasible:
    print("Problem is infeasible")
elif status == SolverStatus.DualInfeasible:
    print("Problem is unbounded")
elif status == SolverStatus.MaxIterations:
    print("Maximum iterations reached")
All Status Values

Status

Meaning

Solved

Optimal solution found

AlmostSolved

Near-optimal (relaxed tolerances)

PrimalInfeasible

No feasible solution exists

DualInfeasible

Problem is unbounded

MaxIterations

Iteration limit reached

NumericalError

Numerical issues encountered


Matrix Requirements

P Matrix (Quadratic Objective)

The P matrix must be:

  • Symmetric: Both upper and lower triangles must be provided

  • Positive semidefinite: For convex problems

  • Sparse: CSR format recommended

# Correct: Full symmetric matrix
P = sparse.csr_matrix([
    [2.0, 1.0],
    [1.0, 2.0],
])

# Incorrect: Upper triangle only (will raise error)
P_bad = sparse.csr_matrix([
    [2.0, 1.0],
    [0.0, 2.0],
])

A Matrix (Constraints)

The constraint matrix A has shape (m, n) where:

  • m = total number of cone constraints

  • n = number of variables

# m=3 constraints, n=2 variables
A = sparse.csr_matrix([
    [1.0, 1.0],   # Constraint 1
    [1.0, 0.0],   # Constraint 2
    [0.0, 1.0],   # Constraint 3
])

Cone Specification

Cones define the constraint types. The order matters:

cones = moreau.Cones(
    num_zero_cones=1,      # Equality constraints (first)
    num_nonneg_cones=3,    # Inequality constraints
    num_so_cones=2,        # Second-order cones (dim 3 each)
    num_exp_cones=1,       # Exponential cones (dim 3)
    power_alphas=[0.5],    # Power cones (dim 3 each)
)

# Total constraints = 1 + 3 + 2*3 + 1*3 + 1*3 = 16

Constraint Ordering

Constraints in A and b must be ordered to match the cone specification:

  1. Zero cone rows first

  2. Nonnegative cone rows

  3. Second-order cone rows (3 per cone)

  4. Exponential cone rows (3 per cone)

  5. Power cone rows (3 per cone)


Settings

Customize solver behavior with Settings:

settings = moreau.Settings(
    device='auto',       # 'auto', 'cpu', or 'cuda'
    batch_size=1,        # For CompiledSolver
    verbose=False,       # Print solver progress
    max_iter=200,        # Maximum iterations
)

solver = moreau.Solver(P, q, A, b, cones=cones, settings=settings)

IPM Settings

For fine-grained control over the interior-point method:

ipm_settings = moreau.IPMSettings(
    tol_gap_abs=1e-8,    # Absolute gap tolerance
    tol_gap_rel=1e-8,    # Relative gap tolerance
    tol_feas=1e-8,       # Feasibility tolerance
)

settings = moreau.Settings(
    ipm_settings=ipm_settings,
)

Error Handling

Moreau validates inputs and raises descriptive errors:

try:
    solver = moreau.Solver(P, q, A, b, cones=cones)
    solution = solver.solve()
except ValueError as e:
    print(f"Invalid input: {e}")
except RuntimeError as e:
    print(f"Solver error: {e}")

Common validation errors:

  • Dimension mismatches between P, q, A, b

  • Non-symmetric P matrix

  • Cone dimensions don’t sum to constraint count

  • Invalid CSR structure