1use serde::{Deserialize, Serialize};
4
5use crate::{Expr, VarId};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
10#[serde(rename_all = "camelCase")]
11pub enum Strength {
12 Weak,
14 Medium,
16 Strong,
18 #[default]
20 Required,
21}
22
23impl Strength {
24 #[must_use]
28 pub const fn weight(self) -> f64 {
29 match self {
30 Self::Required => f64::INFINITY,
31 Self::Strong => 1.0,
32 Self::Medium => 0.1,
33 Self::Weak => 0.01,
34 }
35 }
36}
37
38pub const STAY_WEIGHT: f64 = 1e-3;
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
44#[serde(rename_all = "camelCase")]
45pub enum Relation {
46 Eq,
48 Le,
50}
51
52#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
54pub struct Row {
55 pub expr: Expr,
57 pub relation: Relation,
59 pub scale: f64,
62}
63
64#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
66pub struct Rule {
67 pub id: u64,
69 pub rows: Vec<Row>,
71 pub strength: Strength,
73 pub label: String,
75 pub source: String,
77 pub entities: Vec<u64>,
79 #[serde(default, skip_serializing_if = "Option::is_none")]
82 pub unsupported: Option<String>,
83}
84
85impl Rule {
86 #[must_use]
88 pub fn new(id: u64, rows: Vec<Row>, strength: Strength) -> Self {
89 Self {
90 id,
91 rows,
92 strength,
93 label: String::new(),
94 source: String::new(),
95 entities: Vec::new(),
96 unsupported: None,
97 }
98 }
99
100 #[must_use]
102 pub fn label(mut self, l: impl Into<String>) -> Self {
103 self.label = l.into();
104 self
105 }
106
107 #[must_use]
109 pub fn source(mut self, s: impl Into<String>) -> Self {
110 self.source = s.into();
111 self
112 }
113
114 #[must_use]
116 pub fn entities(mut self, e: Vec<u64>) -> Self {
117 self.entities = e;
118 self
119 }
120
121 #[must_use]
123 pub fn vars(&self) -> Vec<VarId> {
124 let mut v: Vec<VarId> = self.rows.iter().flat_map(|r| r.expr.vars()).collect();
125 v.sort_unstable();
126 v.dedup();
127 v
128 }
129
130 #[must_use]
132 pub fn is_hard(&self) -> bool {
133 self.strength == Strength::Required
134 }
135}
136
137#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
139pub struct Variable {
140 pub value: f64,
142 #[serde(default)]
144 pub fixed: bool,
145 pub scale: f64,
147 #[serde(default)]
149 pub label: String,
150 #[serde(default = "one")]
153 pub stay: f64,
154}
155
156fn one() -> f64 {
157 1.0
158}
159
160#[must_use]
162pub fn stay_factor(v: f64) -> f64 {
163 if v.is_finite() { v.clamp(0.1, 5.0) } else { 1.0 }
164}
165
166impl Variable {
167 #[must_use]
169 pub fn new(value: f64) -> Self {
170 Self { value, fixed: false, scale: 1.0, label: String::new(), stay: 1.0 }
171 }
172
173 #[must_use]
175 pub fn stay(mut self, s: f64) -> Self {
176 self.stay = stay_factor(s);
177 self
178 }
179
180 #[must_use]
182 pub fn scale(mut self, s: f64) -> Self {
183 self.scale = s;
184 self
185 }
186
187 #[must_use]
189 pub fn fixed(mut self, f: bool) -> Self {
190 self.fixed = f;
191 self
192 }
193
194 #[must_use]
196 pub fn label(mut self, l: impl Into<String>) -> Self {
197 self.label = l.into();
198 self
199 }
200}
201
202#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
205pub struct Target {
206 pub var: VarId,
208 pub value: f64,
210 pub strength: Strength,
212}
213
214#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
216pub struct Problem {
217 pub vars: Vec<Variable>,
219 pub rules: Vec<Rule>,
221 pub targets: Vec<Target>,
223}
224
225impl Problem {
226 pub fn add_var(&mut self, v: Variable) -> VarId {
228 let id = VarId(u32::try_from(self.vars.len()).unwrap_or(u32::MAX));
229 self.vars.push(v);
230 id
231 }
232
233 #[must_use]
235 pub fn values(&self) -> Vec<f64> {
236 self.vars.iter().map(|v| v.value).collect()
237 }
238
239 #[must_use]
241 pub fn is_fixed(&self, v: VarId) -> bool {
242 self.vars.get(v.index()).is_none_or(|x| x.fixed)
243 }
244}
245
246#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
248pub struct SolveOptions {
249 pub tolerance: f64,
251 pub max_iterations: u32,
253 pub conflict_search_limit: usize,
255 pub analyze: bool,
258}
259
260impl Default for SolveOptions {
261 fn default() -> Self {
262 Self { tolerance: 1e-9, max_iterations: 100, conflict_search_limit: 64, analyze: true }
263 }
264}