Skip to main content

molpack/constraints/
container.rs

1//! Unified constraints entrypoint used by optimization layers.
2
3use crate::context::PackContext;
4use crate::objective::{compute_f, compute_fg, compute_g};
5use molrs::types::F;
6
7/// Evaluation mode for constraints/objective.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum EvalMode {
10    /// Function value only.
11    FOnly,
12    /// Gradient only.
13    GradientOnly,
14    /// Function + gradient.
15    FAndGradient,
16    /// Restmol mode (same compute path as F+G, semantically explicit for callers).
17    RestMol,
18}
19
20/// Unified evaluation output.
21#[derive(Debug, Clone, Copy, Default)]
22pub struct EvalOutput {
23    pub f_total: F,
24    pub fdist_max: F,
25    pub frest_max: F,
26}
27
28/// Container facade for all constraints-related objective evaluation.
29///
30/// This struct is intentionally zero-sized: all state is carried by `PackContext`.
31#[derive(Debug, Clone, Copy, Default)]
32pub struct Constraints;
33
34impl Constraints {
35    #[allow(clippy::needless_pass_by_value)]
36    pub fn evaluate(
37        self,
38        x: &[F],
39        ctx: &mut PackContext,
40        mode: EvalMode,
41        gradient: Option<&mut [F]>,
42    ) -> EvalOutput {
43        let mut f_total = 0.0;
44
45        match mode {
46            EvalMode::FOnly => {
47                f_total = compute_f(x, ctx);
48            }
49            EvalMode::GradientOnly => {
50                if let Some(g) = gradient {
51                    compute_g(x, ctx, g);
52                } else {
53                    debug_assert!(false, "GradientOnly mode requires gradient buffer");
54                }
55            }
56            EvalMode::FAndGradient | EvalMode::RestMol => {
57                if let Some(g) = gradient {
58                    f_total = compute_fg(x, ctx, g);
59                } else {
60                    debug_assert!(false, "FAndGradient/RestMol mode requires gradient buffer");
61                }
62            }
63        }
64
65        EvalOutput {
66            f_total,
67            fdist_max: ctx.fdist,
68            frest_max: ctx.frest,
69        }
70    }
71
72    #[inline]
73    pub fn violation(self, x: &[F], ctx: &mut PackContext) -> EvalOutput {
74        self.evaluate(x, ctx, EvalMode::FOnly, None)
75    }
76}