Skip to main content

loeres_device/
problem.rs

1//! Fixed-size problem wrappers.
2//!
3//! The baseline projected first-order problem contract (RFC 006): a
4//! box/bound-constrained family exposing a first-order oracle plus static box
5//! bounds, built over fixed static storage. Available under the `owned-arrays`
6//! feature, since the primal iterate and gradient scratch are fixed-size owned
7//! vectors (`FixedVector<S, N>`).
8
9#[cfg(feature = "owned-arrays")]
10use loeres::{ContiguousVectorAccess, SolverError};
11#[cfg(feature = "owned-arrays")]
12use loeres_backend_static::array::FixedVector;
13
14/// A box/bound-constrained problem for the baseline projected first-order device
15/// kernel (RFC 006).
16///
17/// Each iteration needs only a first-order oracle ([`gradient_at`]) and the
18/// static box bounds; the kernel performs `x <- clamp(x - alpha * grad, lo, hi)`
19/// and stops on small iterate change. The primal iterate and gradient scratch
20/// are fixed-size owned vectors (`FixedVector<S, N>`), distinct in type from the
21/// read-only bound storage ([`Bounds`](ProjectedFirstOrderProblem::Bounds)) per
22/// the implementation-decision pass (I3).
23///
24/// [`gradient_at`]: ProjectedFirstOrderProblem::gradient_at
25#[cfg(feature = "owned-arrays")]
26pub trait ProjectedFirstOrderProblem<S, const N: usize> {
27    /// Read-only contiguous storage for the lower/upper box bounds.
28    type Bounds: ContiguousVectorAccess<Scalar = S>;
29
30    /// Validate problem data before the iteration loop: finite, correctly
31    /// dimensioned bounds with `lower <= upper` elementwise. Returns the
32    /// appropriate [`SolverError`] (e.g. [`SolverError::InvalidInput`] for an
33    /// inverted bound) on failure.
34    fn validate_boundary(&self) -> Result<(), SolverError>;
35
36    /// The lower box bound `lo`.
37    fn lower_bound(&self) -> &Self::Bounds;
38
39    /// The upper box bound `hi`.
40    fn upper_bound(&self) -> &Self::Bounds;
41
42    /// The problem-provided step scale `alpha`.
43    ///
44    /// Problem-provided rather than config-provided (I6), so the kernel performs
45    /// no internal division and stays at the `FiniteScalar + MetricScalar` bound.
46    fn step_scale(&self) -> S;
47
48    /// Write the gradient `grad f(x)` into `grad`.
49    ///
50    /// `x` is the read-only current iterate; `grad` is caller-owned scratch
51    /// (the workspace gradient buffer). Returns a [`SolverError`] on a numerical
52    /// failure in the oracle.
53    fn gradient_at(
54        &self,
55        x: &FixedVector<S, N>,
56        grad: &mut FixedVector<S, N>,
57    ) -> Result<(), SolverError>;
58
59    /// The objective value `f(x)`.
60    ///
61    /// Reporting-only: the baseline iterate-change kernel (I7) does not call this
62    /// in the hot loop, and [`DeviceSolveReport`] carries no objective field. It
63    /// is part of the contract for callers and for future objective-based
64    /// criteria.
65    ///
66    /// [`DeviceSolveReport`]: crate::solve::DeviceSolveReport
67    fn objective_at(&self, x: &FixedVector<S, N>) -> Result<S, SolverError>;
68}