1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use crate::{v1::State, Sampled, VariableID, VariableIDSet};
/// Signal that an evaluation state omits values required by the evaluated object.
///
/// Evaluation APIs continue to return [`crate::Result`]. Callers can add the
/// missing values and retry after downcasting [`crate::Error`] to this signal.
#[derive(Debug, thiserror::Error)]
#[error("state is missing required variable IDs: {ids:?}")]
#[non_exhaustive]
pub struct MissingStateEntries {
/// Variable IDs whose values are required for evaluation.
pub ids: VariableIDSet,
}
/// Signal that an evaluation state contains variables outside the evaluated instance.
///
/// Callers can use the IDs to reject a state associated with another instance
/// or remove the unrelated entries before retrying.
#[derive(Debug, thiserror::Error)]
#[error("state contains unknown variable IDs: {ids:?}")]
#[non_exhaustive]
pub struct UnknownStateEntries {
/// Variable IDs not owned by the evaluated instance.
pub ids: VariableIDSet,
}
/// Signal that a dependent-variable assertion conflicts with its evaluated value.
///
/// The dependency is authoritative. Callers can remove or correct the asserted
/// state entry before retrying partial evaluation.
#[derive(Debug, thiserror::Error)]
#[error(
"state value for dependent variable {id:?} is inconsistent with dependency (state={state_value}, dependency={dependency_value})"
)]
#[non_exhaustive]
pub struct InconsistentDependentValue {
/// Dependent variable whose asserted value is inconsistent.
pub id: VariableID,
/// Value asserted by the caller.
pub state_value: f64,
/// Value computed from the dependency.
pub dependency_value: f64,
}
/// Signal that a dependent-variable assertion cannot yet be verified.
///
/// Callers can omit the assertion or provide the remaining dependency values
/// before retrying partial evaluation.
#[derive(Debug, thiserror::Error)]
#[error(
"Dependent variable (ID={}) cannot be asserted by partial_evaluate before its dependency is fully evaluated; missing dependency variable IDs: {required_ids:?}",
id.into_inner()
)]
#[non_exhaustive]
pub struct UnverifiableDependentAssertion {
/// Dependent variable whose asserted value cannot yet be verified.
pub id: VariableID,
/// Remaining variable IDs required to evaluate the dependency.
pub required_ids: VariableIDSet,
}
/// Evaluate with a [State]
pub trait Evaluate {
type Output;
type SampledOutput;
/// Evaluate to return the output with used variable ids
fn evaluate(&self, state: &State, atol: crate::ATol) -> crate::Result<Self::Output>;
/// Evaluate for each sample
fn evaluate_samples(
&self,
samples: &Sampled<State>,
atol: crate::ATol,
) -> crate::Result<Self::SampledOutput>;
/// Partially evaluate the function to return the used variable ids
fn partial_evaluate(&mut self, state: &State, atol: crate::ATol) -> crate::Result<()>;
/// Decision variable IDs required for evaluation
fn required_ids(&self) -> VariableIDSet;
}
/// Outcome of [`Propagate::propagate`].
///
/// `self` is consumed by propagation; the variant decides where it ends up.
/// Atomicity is the caller's responsibility: on error, the constraint is lost,
/// so callers that need atomicity should clone before calling (or snapshot the
/// containing structure).
#[derive(Debug, Clone)]
pub enum PropagateOutcome<T: Propagate> {
/// Constraint remains active (possibly shrunk / modified).
Active(T),
/// Constraint is fully determined by the state. Move to the removed set as-is.
Consumed(T),
/// Constraint transformed into another type (e.g. IndicatorConstraint → Constraint).
/// `original` goes to the removed set, `new` is the replacement.
Transformed { original: T, new: T::Transformed },
}
/// Unit propagation trait for constraint types.
///
/// Consumes `self` and returns [`PropagateOutcome`] together with any
/// additional variable fixings discovered during propagation.
///
/// Atomicity note: on error, `self` is lost. Callers requiring atomicity must
/// clone `self` before calling, or snapshot the containing structure.
pub trait Propagate: Sized {
type Transformed;
/// Propagate variable fixings from `state` through this constraint.
///
/// Returns `(outcome, additional_fixings)` where `additional_fixings`
/// contains newly discovered variable values.
fn propagate(
self,
state: &State,
atol: crate::ATol,
) -> crate::Result<(PropagateOutcome<Self>, State)>;
}