Skip to content

liblaf.peach.optim

Optimization protocols and solver implementations.

Modules:

  • base

    Base protocols and result containers for optimizers.

  • pncg

    Preconditioned nonlinear conjugate-gradient optimizer.

  • scipy

    SciPy optimizer adapter.

Classes:

  • BaseProblem

    Marker protocol for optimizer problems.

  • Optimizer

    Base class for iterative optimizers.

  • Pncg

    Preconditioned nonlinear conjugate-gradient optimizer.

  • Problem

    Protocol implemented by differentiable optimization problems.

  • Result

    Result code returned by optimizers.

  • ScipyOptimizer

    Adapter around scipy.optimize.minimize.

  • Solution

    Optimizer output bundle.

  • State

    Protocol for optimizer states that expose current parameters.

  • Stats

    Protocol for optimizer-specific summary statistics.

BaseProblem

Bases: Protocol


              flowchart TD
              liblaf.peach.optim.BaseProblem[BaseProblem]

              

              click liblaf.peach.optim.BaseProblem href "" "liblaf.peach.optim.BaseProblem"
            

Marker protocol for optimizer problems.

Optimizer

Base class for iterative optimizers.

Subclasses keep optimizer-specific data in a mutable state object. A step implementation mutates the model state and optimizer state in place, so callbacks and postprocess observe the same final objects.

Methods:

  • init

    Create optimizer state from a model state and parameter vector.

  • minimize

    Run optimization until the configured termination rule stops.

  • postprocess

    Build the final solution object.

  • step

    Advance the optimizer by one step.

  • terminate

    Return whether optimization should stop and why.

init

init[X](
    problem: BaseProblem[X],
    model_state: X,
    params: Vector,
) -> S

Create optimizer state from a model state and parameter vector.

Source code in src/liblaf/peach/optim/base/_optimizer.py
def init[X](self, problem: BaseProblem[X], model_state: X, params: Vector) -> S:
    """Create optimizer state from a model state and parameter vector."""
    raise NotImplementedError

minimize

minimize[X](
    problem: BaseProblem[X],
    model_state: X,
    params: Vector,
) -> Solution[S, T]

Run optimization until the configured termination rule stops.

After every step, Problem.callback is called when the concrete problem implements it. The callback receives the current model state and the same mutable optimizer state that will be stored on the returned Solution.

Parameters:

  • problem (BaseProblem[X]) –

    Optimization problem that supplies objective hooks.

  • model_state (X) –

    Initial model state used by objective hooks.

  • params (Vector) –

    Initial optimizer parameter vector.

Returns:

  • Solution[S, T]

    The final solution.

Source code in src/liblaf/peach/optim/base/_optimizer.py
def minimize[X](
    self, problem: BaseProblem[X], model_state: X, params: Vector
) -> Solution[S, T]:
    """Run optimization until the configured termination rule stops.

    After every step, [`Problem.callback`][liblaf.peach.optim.base.Problem.callback]
    is called when the concrete problem implements it. The callback receives
    the current model state and the same mutable optimizer state that will be
    stored on the returned [`Solution`][liblaf.peach.optim.base.Solution].

    Args:
        problem: Optimization problem that supplies objective hooks.
        model_state: Initial model state used by objective hooks.
        params: Initial optimizer parameter vector.

    Returns:
        The final solution.
    """
    problem: Problem[X] = cast("Problem[X]", problem)
    opt_state: S = self.init(problem, model_state, params)
    while True:
        self.step(problem, model_state, opt_state)
        if is_implemented(problem, Problem.callback):
            problem.callback(model_state, opt_state)
        ok, result = self.terminate(problem, model_state, opt_state)
        if ok:
            break
    solution: Solution[S, T] = self.postprocess(
        problem, model_state, opt_state, result
    )
    return solution

postprocess

postprocess[X](
    problem: BaseProblem[X],
    model_state: X,
    opt_state: S,
    result: Result,
) -> Solution[S, T]

Build the final solution object.

Source code in src/liblaf/peach/optim/base/_optimizer.py
def postprocess[X](
    self, problem: BaseProblem[X], model_state: X, opt_state: S, result: Result
) -> Solution[S, T]:
    """Build the final solution object."""
    del problem, model_state
    stats: T = cast("T", {})
    return Optimizer.Solution(result=result, state=opt_state, stats=stats)

step

step[X](
    problem: BaseProblem[X], model_state: X, opt_state: S
) -> None

Advance the optimizer by one step.

Implementations should mutate model_state and opt_state with any accepted parameter, gradient, or diagnostic updates.

Source code in src/liblaf/peach/optim/base/_optimizer.py
def step[X](self, problem: BaseProblem[X], model_state: X, opt_state: S) -> None:
    """Advance the optimizer by one step.

    Implementations should mutate `model_state` and `opt_state` with any
    accepted parameter, gradient, or diagnostic updates.
    """
    raise NotImplementedError

terminate

terminate[X](
    problem: BaseProblem[X], model_state: X, opt_state: S
) -> tuple[bool, Result]

Return whether optimization should stop and why.

Source code in src/liblaf/peach/optim/base/_optimizer.py
def terminate[X](
    self, problem: BaseProblem[X], model_state: X, opt_state: S
) -> tuple[bool, Result]:
    """Return whether optimization should stop and why."""
    raise NotImplementedError

Pncg

Bases: Optimizer[PncgState, PncgStats]


              flowchart TD
              liblaf.peach.optim.Pncg[Pncg]
              liblaf.peach.optim.base._optimizer.Optimizer[Optimizer]

                              liblaf.peach.optim.base._optimizer.Optimizer --> liblaf.peach.optim.Pncg
                


              click liblaf.peach.optim.Pncg href "" "liblaf.peach.optim.Pncg"
              click liblaf.peach.optim.base._optimizer.Optimizer href "" "liblaf.peach.optim.base._optimizer.Optimizer"
            

Preconditioned nonlinear conjugate-gradient optimizer.

Pncg builds a diagonal preconditioner from a damped Hessian diagonal, computes a Dai-Kou conjugate-gradient direction, and accepts steps with Armijo backtracking. Accepted line-search trials mutate the model state in place, so each call to step expects model_state to already match opt_state.params.

Examples:

Minimize a one-dimensional quadratic objective.

>>> import torch
>>> class QuadraticProblem:
...     def __init__(self, target):
...         self.target = target
...
...     def update(self, state, params, /):
...         state.copy_(params)
...
...     def fun(self, state, /):
...         residual = state - self.target
...         return 0.5 * torch.dot(residual, residual)
...
...     def grad(self, state, /):
...         return state - self.target
...
...     def hess_diag(self, state, /):
...         return torch.ones_like(state)
...
...     def hess_quad(self, state, direction, /):
...         return torch.dot(direction, direction)
>>> problem = QuadraticProblem(target=torch.tensor([3.0]))
>>> params = torch.tensor([0.0])
>>> model_state = params.clone()
>>> solution = Pncg().minimize(problem, model_state, params)
>>> bool(solution.success)
True
>>> torch.testing.assert_close(solution.params, torch.tensor([3.0]))
>>> torch.testing.assert_close(model_state, solution.params)

Parameters:

  • criteria (ConvergenceCriteria, default: <dynamic> ) –

    Gradient-norm stopping criteria for Pncg.

  • direction (DirectionUpdate, default: DirectionUpdate() ) –

    Dai-Kou nonlinear conjugate-gradient direction update.

  • hess_damping (HessianDamping, default: <dynamic> ) –

    Adaptive Levenberg-style diagonal Hessian damping.

  • line_search (LineSearch, default: <dynamic> ) –

    Armijo backtracking with Newton, norm, and problem-specific step bounds.

Type Aliases:

Classes:

  • Result

    Result code returned by optimizers.

Methods:

  • init

    Initialize Pncg state.

  • minimize

    Run optimization until the configured termination rule stops.

  • postprocess

    Build a Pncg solution object.

  • step

    Run one Pncg step.

  • terminate

    Delegate stopping to the configured convergence criteria.

Attributes:

criteria class-attribute instance-attribute

criteria: ConvergenceCriteria = field(
    factory=ConvergenceCriteria
)

direction class-attribute instance-attribute

direction: DirectionUpdate = field(factory=DirectionUpdate)

hess_damping class-attribute instance-attribute

hess_damping: HessianDamping = field(factory=HessianDamping)
line_search: LineSearch = field(factory=LineSearch)

Solution

Optimizer output bundle.

Parameters:

  • result (Result) –
  • state (S) –
  • stats (T) –

Result

Bases: StrEnum


              flowchart TD
              liblaf.peach.optim.Pncg.Result[Result]

              

              click liblaf.peach.optim.Pncg.Result href "" "liblaf.peach.optim.Pncg.Result"
            

Result code returned by optimizers.

Attributes:

INTERRUPT class-attribute instance-attribute

INTERRUPT = auto()

MAX_STEPS_REACHED class-attribute instance-attribute

MAX_STEPS_REACHED = auto()

NAN class-attribute instance-attribute

NAN = auto()

PRIMARY_SUCCESS class-attribute instance-attribute

PRIMARY_SUCCESS = auto()

SECONDARY_SUCCESS class-attribute instance-attribute

SECONDARY_SUCCESS = auto()

STAGNATION class-attribute instance-attribute

STAGNATION = auto()

SUCCESS class-attribute instance-attribute

SUCCESS = auto()

UNKNOWN_ERROR class-attribute instance-attribute

UNKNOWN_ERROR = auto()

success property

success: bool

Whether the result represents an accepted optimization outcome.

init

init[X](
    problem: BaseProblem[X],
    model_state: X,
    params: Vector,
) -> PncgState

Initialize Pncg state.

The stored parameter vector is cloned from params; subsequent steps mutate the clone instead of the caller-owned input tensor.

Source code in src/liblaf/peach/optim/pncg/_pncg.py
@override
def init[X](self, problem: BaseProblem[X], model_state: X, params: Vector) -> State:
    """Initialize `Pncg` state.

    The stored parameter vector is cloned from `params`; subsequent steps
    mutate the clone instead of the caller-owned input tensor.
    """
    problem: Problem[X] = cast("Problem[X]", problem)
    fun: Scalar = problem.fun(model_state)
    return self.State(
        fun=fun,
        params=params.clone(),
        convergence_state=self.criteria.init(),
        hess_damping_state=self.hess_damping.init(),
        line_search_state=self.line_search.init(fun=fun),
    )

minimize

minimize[X](
    problem: BaseProblem[X],
    model_state: X,
    params: Vector,
) -> Solution[S, T]

Run optimization until the configured termination rule stops.

After every step, Problem.callback is called when the concrete problem implements it. The callback receives the current model state and the same mutable optimizer state that will be stored on the returned Solution.

Parameters:

  • problem (BaseProblem[X]) –

    Optimization problem that supplies objective hooks.

  • model_state (X) –

    Initial model state used by objective hooks.

  • params (Vector) –

    Initial optimizer parameter vector.

Returns:

  • Solution[S, T]

    The final solution.

Source code in src/liblaf/peach/optim/base/_optimizer.py
def minimize[X](
    self, problem: BaseProblem[X], model_state: X, params: Vector
) -> Solution[S, T]:
    """Run optimization until the configured termination rule stops.

    After every step, [`Problem.callback`][liblaf.peach.optim.base.Problem.callback]
    is called when the concrete problem implements it. The callback receives
    the current model state and the same mutable optimizer state that will be
    stored on the returned [`Solution`][liblaf.peach.optim.base.Solution].

    Args:
        problem: Optimization problem that supplies objective hooks.
        model_state: Initial model state used by objective hooks.
        params: Initial optimizer parameter vector.

    Returns:
        The final solution.
    """
    problem: Problem[X] = cast("Problem[X]", problem)
    opt_state: S = self.init(problem, model_state, params)
    while True:
        self.step(problem, model_state, opt_state)
        if is_implemented(problem, Problem.callback):
            problem.callback(model_state, opt_state)
        ok, result = self.terminate(problem, model_state, opt_state)
        if ok:
            break
    solution: Solution[S, T] = self.postprocess(
        problem, model_state, opt_state, result
    )
    return solution

postprocess

postprocess[X](
    problem: BaseProblem[X],
    model_state: X,
    opt_state: PncgState,
    result: Result,
) -> Solution

Build a Pncg solution object.

Source code in src/liblaf/peach/optim/pncg/_pncg.py
@override
def postprocess[X](
    self, problem: BaseProblem[X], model_state: X, opt_state: State, result: Result
) -> Solution:
    """Build a `Pncg` solution object."""
    stats: Pncg.Stats = self.Stats()
    return Solution(result=result, state=opt_state, stats=stats)

step

step[X](
    problem: BaseProblem[X],
    model_state: X,
    opt_state: PncgState,
) -> None

Run one Pncg step.

The method evaluates derivatives at model_state, writes the accepted direction, slope, Hessian diagnostics, line-search diagnostics, and parameter update into opt_state, then mutates model_state to the accepted trial.

Source code in src/liblaf/peach/optim/pncg/_pncg.py
@override
def step[X](
    self, problem: BaseProblem[X], model_state: X, opt_state: State
) -> None:
    """Run one `Pncg` step.

    The method evaluates derivatives at `model_state`, writes the accepted
    direction, slope, Hessian diagnostics, line-search diagnostics, and
    parameter update into `opt_state`, then mutates `model_state` to the
    accepted trial.
    """
    problem: Problem[X] = cast("Problem[X]", problem)

    g: Vector = problem.grad(model_state)
    H_diag: Vector = problem.hess_diag(model_state)
    H_diag_damp: Vector = self.hess_damping.hess_diag(
        state=opt_state.hess_damping_state, H_diag=H_diag
    )
    P: Vector = torch.reciprocal(H_diag_damp)

    p: Vector = self.direction(
        g=g,
        g_prev=opt_state.grad,
        P=P,
        p_prev=opt_state.direction,
        restart=opt_state.step == 0 or not opt_state.line_search_state.ok,
    )
    opt_state.direction = p
    opt_state.grad = g
    opt_state.hess_diag = H_diag
    slope: Scalar = torch.dot(g, p)
    opt_state.slope = slope

    pHp: Scalar = problem.hess_quad(model_state, p)
    opt_state.hess_quad = pHp
    pHp_damp: Scalar = self.hess_damping.hess_quad(
        state=opt_state.hess_damping_state, p=p, pHp=pHp
    )
    self.line_search(
        opt_state.line_search_state,
        problem=problem,
        model_state=model_state,
        m=slope,
        p=p,
        params=opt_state.params,
        pHp=pHp_damp,
    )
    ls_state: Pncg.LineSearchState = opt_state.line_search_state
    opt_state.fun = ls_state.f_alpha

    alpha: Scalar = ls_state.alpha
    actual_decrease: Scalar = ls_state.f0 - ls_state.f_alpha
    predicted_decrease: Scalar = -alpha * slope - 0.5 * alpha**2 * pHp_damp
    self.hess_damping.update(
        opt_state.hess_damping_state,
        actual_decrease=actual_decrease,
        line_search_step=ls_state.step,
        predicted_decrease=predicted_decrease,
    )

    self.criteria.update(
        opt_state.convergence_state, g=g, line_search_ok=ls_state.ok
    )

    opt_state.params += alpha * p

terminate

terminate[X](
    problem: BaseProblem[X],
    model_state: X,
    opt_state: PncgState,
) -> tuple[bool, Result]

Delegate stopping to the configured convergence criteria.

Source code in src/liblaf/peach/optim/pncg/_pncg.py
@override
def terminate[X](
    self, problem: BaseProblem[X], model_state: X, opt_state: State
) -> tuple[bool, Result]:
    """Delegate stopping to the configured convergence criteria."""
    return self.criteria.terminate(opt_state.convergence_state)

Problem

Bases: Protocol


              flowchart TD
              liblaf.peach.optim.Problem[Problem]

              

              click liblaf.peach.optim.Problem href "" "liblaf.peach.optim.Problem"
            

Protocol implemented by differentiable optimization problems.

Optimizers evaluate objective derivatives on a model state. Drivers that produce fresh parameter vectors use update to mutate the model state before asking for objective values or derivatives.

Methods:

  • callback

    Run an optional side-effect after an optimizer step.

  • fun

    Evaluate the scalar objective value.

  • grad

    Evaluate the objective gradient.

  • hess_diag

    Evaluate or approximate the Hessian diagonal.

  • hess_prod

    Evaluate the Hessian-vector product along p.

  • hess_quad

    Evaluate the quadratic form p.T @ H @ p.

  • max_step_size

    Return a safe fraction of proposed trial displacement p.

  • update

    Mutate model state for a line-search trial at x.

  • value_and_grad

    Evaluate the objective and gradient together.

callback

callback(model_state: X, opt_state: State) -> None

Run an optional side-effect after an optimizer step.

Source code in src/liblaf/peach/optim/base/_protocols.py
@not_implemented
def callback(self, model_state: X, opt_state: State, /) -> None:
    """Run an optional side-effect after an optimizer step."""

fun

fun(state: X) -> Scalar

Evaluate the scalar objective value.

Source code in src/liblaf/peach/optim/base/_protocols.py
@not_implemented
def fun(self, state: X, /) -> Scalar:
    """Evaluate the scalar objective value."""

grad

grad(state: X) -> Vector

Evaluate the objective gradient.

Source code in src/liblaf/peach/optim/base/_protocols.py
@not_implemented
def grad(self, state: X, /) -> Vector:
    """Evaluate the objective gradient."""

hess_diag

hess_diag(state: X) -> Vector

Evaluate or approximate the Hessian diagonal.

Source code in src/liblaf/peach/optim/base/_protocols.py
@not_implemented
def hess_diag(self, state: X, /) -> Vector:
    """Evaluate or approximate the Hessian diagonal."""

hess_prod

hess_prod(state: X, p: Vector) -> Vector

Evaluate the Hessian-vector product along p.

Source code in src/liblaf/peach/optim/base/_protocols.py
@not_implemented
def hess_prod(self, state: X, p: Vector, /) -> Vector:
    """Evaluate the Hessian-vector product along `p`."""

hess_quad

hess_quad(state: X, p: Vector) -> Scalar

Evaluate the quadratic form p.T @ H @ p.

Source code in src/liblaf/peach/optim/base/_protocols.py
@not_implemented
def hess_quad(self, state: X, p: Vector, /) -> Scalar:
    """Evaluate the quadratic form `p.T @ H @ p`."""

max_step_size

max_step_size(state: X, p: Vector) -> Scalar

Return a safe fraction of proposed trial displacement p.

Source code in src/liblaf/peach/optim/base/_protocols.py
@not_implemented
def max_step_size(self, state: X, p: Vector, /) -> Scalar:
    """Return a safe fraction of proposed trial displacement `p`."""

update

update(state: X, x: Vector) -> None

Mutate model state for a line-search trial at x.

Source code in src/liblaf/peach/optim/base/_protocols.py
@not_implemented
def update(self, state: X, x: Vector, /) -> None:
    """Mutate model state for a line-search trial at `x`."""

value_and_grad

value_and_grad(state: X) -> tuple[Scalar, Vector]

Evaluate the objective and gradient together.

Source code in src/liblaf/peach/optim/base/_protocols.py
@not_implemented
def value_and_grad(self, state: X, /) -> tuple[Scalar, Vector]:
    """Evaluate the objective and gradient together."""

Result

Bases: StrEnum


              flowchart TD
              liblaf.peach.optim.Result[Result]

              

              click liblaf.peach.optim.Result href "" "liblaf.peach.optim.Result"
            

Result code returned by optimizers.

Attributes:

INTERRUPT class-attribute instance-attribute

INTERRUPT = auto()

MAX_STEPS_REACHED class-attribute instance-attribute

MAX_STEPS_REACHED = auto()

NAN class-attribute instance-attribute

NAN = auto()

PRIMARY_SUCCESS class-attribute instance-attribute

PRIMARY_SUCCESS = auto()

SECONDARY_SUCCESS class-attribute instance-attribute

SECONDARY_SUCCESS = auto()

STAGNATION class-attribute instance-attribute

STAGNATION = auto()

SUCCESS class-attribute instance-attribute

SUCCESS = auto()

UNKNOWN_ERROR class-attribute instance-attribute

UNKNOWN_ERROR = auto()

success property

success: bool

Whether the result represents an accepted optimization outcome.

ScipyOptimizer

Bases: Optimizer[ScipyState, ScipyStats]


              flowchart TD
              liblaf.peach.optim.ScipyOptimizer[ScipyOptimizer]
              liblaf.peach.optim.base._optimizer.Optimizer[Optimizer]

                              liblaf.peach.optim.base._optimizer.Optimizer --> liblaf.peach.optim.ScipyOptimizer
                


              click liblaf.peach.optim.ScipyOptimizer href "" "liblaf.peach.optim.ScipyOptimizer"
              click liblaf.peach.optim.base._optimizer.Optimizer href "" "liblaf.peach.optim.base._optimizer.Optimizer"
            

Adapter around scipy.optimize.minimize.

Parameters:

  • method (str | None, default: None ) –
  • options (Mapping[str, Any] | None, default: None ) –
  • tol (float | None, default: None ) –

Type Aliases:

Classes:

  • Result

    Result code returned by optimizers.

Methods:

  • init

    Initialize SciPy state from the starting parameters.

  • minimize

    Run scipy.optimize.minimize against a Peach problem.

  • postprocess

    Build the SciPy optimizer solution object.

  • step

    Advance the optimizer by one step.

  • terminate

    Return whether optimization should stop and why.

Attributes:

method class-attribute instance-attribute

method: str | None = None

options class-attribute instance-attribute

options: Mapping[str, Any] | None = None

tol class-attribute instance-attribute

tol: float | None = None

Solution

Optimizer output bundle.

Parameters:

  • result (Result) –
  • state (S) –
  • stats (T) –

Result

Bases: StrEnum


              flowchart TD
              liblaf.peach.optim.ScipyOptimizer.Result[Result]

              

              click liblaf.peach.optim.ScipyOptimizer.Result href "" "liblaf.peach.optim.ScipyOptimizer.Result"
            

Result code returned by optimizers.

Attributes:

INTERRUPT class-attribute instance-attribute

INTERRUPT = auto()

MAX_STEPS_REACHED class-attribute instance-attribute

MAX_STEPS_REACHED = auto()

NAN class-attribute instance-attribute

NAN = auto()

PRIMARY_SUCCESS class-attribute instance-attribute

PRIMARY_SUCCESS = auto()

SECONDARY_SUCCESS class-attribute instance-attribute

SECONDARY_SUCCESS = auto()

STAGNATION class-attribute instance-attribute

STAGNATION = auto()

SUCCESS class-attribute instance-attribute

SUCCESS = auto()

UNKNOWN_ERROR class-attribute instance-attribute

UNKNOWN_ERROR = auto()

success property

success: bool

Whether the result represents an accepted optimization outcome.

init

init[X](
    problem: BaseProblem[X],
    model_state: X,
    params: Vector,
) -> ScipyState

Initialize SciPy state from the starting parameters.

Source code in src/liblaf/peach/optim/scipy/_scipy.py
@override
def init[X](self, problem: BaseProblem[X], model_state: X, params: Vector) -> State:
    """Initialize SciPy state from the starting parameters."""
    res: OptimizeResult = OptimizeResult({"x": params})  # ty:ignore[too-many-positional-arguments]
    return self.State(res)

minimize

minimize[X](
    problem: BaseProblem[X],
    model_state: X,
    params: Vector,
) -> Solution

Run scipy.optimize.minimize against a Peach problem.

Source code in src/liblaf/peach/optim/scipy/_scipy.py
@override
def minimize[X](
    self, problem: BaseProblem[X], model_state: X, params: Vector
) -> Solution:
    """Run `scipy.optimize.minimize` against a Peach problem."""
    problem: Problem[X] = cast("Problem[X]", problem)
    opt_state: ScipyState = self.init(problem, model_state, params)
    wrapper: _ProblemWrapper[X] = _ProblemWrapper(problem, model_state=model_state)
    fun, jac = (
        (wrapper.fun, wrapper.grad)
        if wrapper.value_and_grad is None
        else (wrapper.value_and_grad, True)
    )
    opt_state.__wrapped__ = scipy.optimize.minimize(
        fun=fun,
        x0=opt_state.params,
        method=self.method,
        jac=jac,
        hessp=wrapper.hessp,
        tol=self.tol,
        callback=self._wraps_callback(wrapper, opt_state),
        options=self.options,
    )  # ty:ignore[no-matching-overload]
    result: Result = (
        Result.SUCCESS if opt_state["success"] else Result.UNKNOWN_ERROR
    )
    solution: ScipyOptimizer.Solution = self.postprocess(
        problem, model_state, opt_state, result
    )
    return solution

postprocess

postprocess[X](
    problem: BaseProblem[X],
    model_state: X,
    opt_state: ScipyState,
    result: Result,
) -> Solution

Build the SciPy optimizer solution object.

Source code in src/liblaf/peach/optim/scipy/_scipy.py
@override
def postprocess[X](
    self, problem: BaseProblem[X], model_state: X, opt_state: State, result: Result
) -> Solution:
    """Build the SciPy optimizer solution object."""
    return Solution(result=result, state=opt_state, stats=self.Stats())

step

step[X](
    problem: BaseProblem[X], model_state: X, opt_state: S
) -> None

Advance the optimizer by one step.

Implementations should mutate model_state and opt_state with any accepted parameter, gradient, or diagnostic updates.

Source code in src/liblaf/peach/optim/base/_optimizer.py
def step[X](self, problem: BaseProblem[X], model_state: X, opt_state: S) -> None:
    """Advance the optimizer by one step.

    Implementations should mutate `model_state` and `opt_state` with any
    accepted parameter, gradient, or diagnostic updates.
    """
    raise NotImplementedError

terminate

terminate[X](
    problem: BaseProblem[X], model_state: X, opt_state: S
) -> tuple[bool, Result]

Return whether optimization should stop and why.

Source code in src/liblaf/peach/optim/base/_optimizer.py
def terminate[X](
    self, problem: BaseProblem[X], model_state: X, opt_state: S
) -> tuple[bool, Result]:
    """Return whether optimization should stop and why."""
    raise NotImplementedError

Solution

Optimizer output bundle.

Parameters:

  • result (Result) –
  • state (S) –
  • stats (T) –

Attributes:

params property

params: Vector

Final optimizer parameters.

result instance-attribute

result: Result

state instance-attribute

state: S

stats instance-attribute

stats: T

success property

success: bool

Whether result is successful.

State

Bases: Protocol


              flowchart TD
              liblaf.peach.optim.State[State]

              

              click liblaf.peach.optim.State href "" "liblaf.peach.optim.State"
            

Protocol for optimizer states that expose current parameters.

Attributes:

  • params (Vector) –

    Current optimizer parameters.

params property

params: Vector

Current optimizer parameters.

Stats

Bases: Protocol


              flowchart TD
              liblaf.peach.optim.Stats[Stats]

              

              click liblaf.peach.optim.Stats href "" "liblaf.peach.optim.Stats"
            

Protocol for optimizer-specific summary statistics.