Skip to content
Specular Differentiation

specular.optimization.OptimizationResult

OptimizationResult(solution, func_val, iteration, runtime, history, stop_reason, success, method='speg')

Store the last accepted point and numerical termination information from minimize or specular_gradient. Also available as specular.OptimizationResult.

Attributes

Attribute Description
solution Last accepted point, as a float or one-dimensional NumPy array.
func_val Objective value at solution, as a float.
iteration Number of accepted updates.
runtime Elapsed solver time in seconds.
history Dictionary of NumPy arrays with keys variables, values, and step_sizes.
stop_reason Text describing why the solver stopped.
success Whether the raw gradient norm met the requested tolerance.
method Direction name, or "custom" for a callable direction. Defaults to "speg" when constructing a result manually.

Methods

Method Returns
last_record() (solution, func_val, runtime)
get_history() (history["variables"], history["values"], runtime)

Notes

With history enabled, variables and values include the initial point and every accepted iterate. variables has shape (iteration + 1,) for a scalar problem and (iteration + 1, dimension) for a vector problem. step_sizes has iteration entries. All three arrays are empty when record_history=False.

The result is a frozen dataclass: fields cannot be reassigned, but contained NumPy arrays and the history dictionary are still mutable. The accessors return the stored objects, without making copies.

success=True means the computed raw gradient norm met the tolerance; it does not certify a global minimizer. Stopping reasons distinguish gradient tolerance, an exhausted update budget, a failed line search, a zero direction with a larger gradient, lost floating-point progress, and a non-finite proposed point or objective. Failed updates do not change the last accepted point or append history.

Examples

from specular.optimization import specular_gradient

result = specular_gradient(
    lambda x: x * x,
    initial_point=2.0,
    gradient=lambda x: 2.0 * x,
    step_size=1.0,
    max_iter=5,
)
print(result.solution, result.iteration, result.success)
# 0.0 2 True

points, values, runtime = result.get_history()
print(points)
# [2. 1. 0.]
Source code in specular/optimization/solver.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@dataclass(frozen=True, slots=True)
class OptimizationResult:
    """Last accepted point and numerical termination information.

    ``iteration`` counts accepted updates. History contains ``variables`` and
    ``values`` including the initial point, and one ``step_sizes`` entry per
    update. All three arrays are empty when history recording is disabled.
    ``success`` means the computed gradient norm met ``tol``; it does not
    certify a global minimizer.
    """

    solution: float | np.ndarray
    func_val: float
    iteration: int
    runtime: float
    history: dict[str, np.ndarray]
    stop_reason: str
    success: bool
    method: str = "speg"

    def last_record(self) -> tuple[float | np.ndarray, float, float]:
        """Return the last accepted point, objective value, and elapsed seconds."""
        return self.solution, self.func_val, self.runtime

    def get_history(self) -> tuple[np.ndarray, np.ndarray, float]:
        """Return recorded points, objective values, and elapsed seconds."""
        return self.history["variables"], self.history["values"], self.runtime