1use crate::{
2 parse::{Parse, ParseError, RawParseError},
3 v1::{self},
4 Constraint, ConstraintID, DecisionVariable, Function, RemovedConstraint, VariableID,
5};
6use std::collections::{BTreeSet, HashMap};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub enum Sense {
10 Minimize,
11 Maximize,
12}
13
14impl Parse for v1::instance::Sense {
15 type Output = Sense;
16 type Context = ();
17 fn parse(self, _: &Self::Context) -> Result<Self::Output, ParseError> {
18 match self {
19 v1::instance::Sense::Minimize => Ok(Sense::Minimize),
20 v1::instance::Sense::Maximize => Ok(Sense::Maximize),
21 v1::instance::Sense::Unspecified => Err(RawParseError::UnspecifiedEnum {
22 enum_name: "ommx.v1.instance.Sense",
23 }
24 .into()),
25 }
26 }
27}
28
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct OneHot {
31 pub id: ConstraintID,
32 pub variables: BTreeSet<VariableID>,
33}
34
35impl Parse for v1::OneHot {
36 type Output = OneHot;
37 type Context = (
38 HashMap<VariableID, DecisionVariable>,
39 HashMap<ConstraintID, Constraint>,
40 );
41 fn parse(
42 self,
43 (decision_variable, constraints): &Self::Context,
44 ) -> Result<Self::Output, ParseError> {
45 let message = "ommx.v1.OneHot";
46 let constraint_id = as_constraint_id(constraints, self.constraint_id)
47 .map_err(|e| e.context(message, "constraint_id"))?;
48 let mut variables = BTreeSet::new();
49 for v in &self.decision_variables {
50 let id = as_variable_id(decision_variable, *v)
51 .map_err(|e| e.context(message, "decision_variables"))?;
52 if !variables.insert(id) {
53 return Err(RawParseError::NonUniqueVariableID { id }
54 .context(message, "decision_variables"));
55 }
56 }
57 Ok(OneHot {
58 id: constraint_id,
59 variables,
60 })
61 }
62}
63
64#[derive(Debug, Clone, PartialEq, Eq)]
65pub struct Sos1 {
66 pub binary_constraint_id: ConstraintID,
67 pub big_m_constraint_ids: BTreeSet<ConstraintID>,
68 pub variables: BTreeSet<VariableID>,
69}
70
71impl Parse for v1::Sos1 {
72 type Output = Sos1;
73 type Context = (
74 HashMap<VariableID, DecisionVariable>,
75 HashMap<ConstraintID, Constraint>,
76 );
77 fn parse(
78 self,
79 (decision_variable, constraints): &Self::Context,
80 ) -> Result<Self::Output, ParseError> {
81 let message = "ommx.v1.Sos1";
82 let binary_constraint_id = as_constraint_id(constraints, self.binary_constraint_id)
83 .map_err(|e| e.context(message, "binary_constraint_id"))?;
84 let mut big_m_constraint_ids = BTreeSet::new();
85 for id in &self.big_m_constraint_ids {
86 let id = as_constraint_id(constraints, *id)
87 .map_err(|e| e.context(message, "big_m_constraint_ids"))?;
88 if !big_m_constraint_ids.insert(id) {
89 return Err(RawParseError::NonUniqueConstraintID { id }
90 .context(message, "big_m_constraint_ids"));
91 }
92 }
93 let mut variables = BTreeSet::new();
94 for id in &self.decision_variables {
95 let id = as_variable_id(decision_variable, *id)
96 .map_err(|e| e.context(message, "decision_variables"))?;
97 if !variables.insert(id) {
98 return Err(RawParseError::NonUniqueVariableID { id }
99 .context(message, "decision_variables"));
100 }
101 }
102 Ok(Sos1 {
103 binary_constraint_id,
104 big_m_constraint_ids,
105 variables,
106 })
107 }
108}
109
110#[derive(Debug, Default, Clone, PartialEq, Eq)]
111pub struct ConstraintHints {
112 pub one_hot_constraints: Vec<OneHot>,
113 pub sos1_constraints: Vec<Sos1>,
114}
115
116impl Parse for v1::ConstraintHints {
117 type Output = ConstraintHints;
118 type Context = (
119 HashMap<VariableID, DecisionVariable>,
120 HashMap<ConstraintID, Constraint>,
121 );
122 fn parse(self, context: &Self::Context) -> Result<Self::Output, ParseError> {
123 let message = "ommx.v1.ConstraintHints";
124 let one_hot_constraints = self
125 .one_hot_constraints
126 .into_iter()
127 .map(|c| c.parse_as(context, message, "one_hot_constraints"))
128 .collect::<Result<Vec<_>, ParseError>>()?;
129 let sos1_constraints = self
130 .sos1_constraints
131 .into_iter()
132 .map(|c| c.parse_as(context, message, "sos1_constraints"))
133 .collect::<Result<_, ParseError>>()?;
134 Ok(ConstraintHints {
135 one_hot_constraints,
136 sos1_constraints,
137 })
138 }
139}
140
141#[derive(Debug, Clone, PartialEq)]
150pub struct Instance {
151 sense: Sense,
152 objective: Function,
153 decision_variables: HashMap<VariableID, DecisionVariable>,
154 constraints: HashMap<ConstraintID, Constraint>,
155 removed_constraints: HashMap<ConstraintID, RemovedConstraint>,
156 decision_variable_dependency: HashMap<VariableID, Function>,
157 parameters: Option<v1::Parameters>,
158 description: Option<v1::instance::Description>,
159 constraint_hints: ConstraintHints,
160}
161
162impl TryFrom<v1::Instance> for Instance {
163 type Error = ParseError;
164 fn try_from(value: v1::Instance) -> Result<Self, Self::Error> {
165 let message = "ommx.v1.Instance";
166 let sense = value.sense().parse_as(&(), message, "sense")?;
167
168 let decision_variables =
169 value
170 .decision_variables
171 .parse_as(&(), message, "decision_variables")?;
172
173 let objective = value
174 .objective
175 .ok_or(RawParseError::MissingField {
176 message,
177 field: "objective",
178 })?
179 .parse_as(&(), message, "objective")?;
180
181 let constraints = value.constraints.parse_as(&(), message, "constraints")?;
182 let removed_constraints =
183 value
184 .removed_constraints
185 .parse_as(&constraints, message, "removed_constraints")?;
186
187 let mut decision_variable_dependency = HashMap::new();
188 for (id, f) in value.decision_variable_dependency {
189 decision_variable_dependency.insert(
190 as_variable_id(&decision_variables, id)
191 .map_err(|e| e.context(message, "decision_variable_dependency"))?,
192 f.parse_as(&(), message, "decision_variable_dependency")?,
193 );
194 }
195
196 let context = (decision_variables, constraints);
197 let constraint_hints = if let Some(hints) = value.constraint_hints {
198 hints.parse_as(&context, message, "constraint_hints")?
199 } else {
200 Default::default()
201 };
202 let (decision_variables, constraints) = context;
203
204 Ok(Self {
205 sense,
206 objective,
207 constraints,
208 decision_variables,
209 removed_constraints,
210 decision_variable_dependency,
211 parameters: value.parameters,
212 description: value.description,
213 constraint_hints,
214 })
215 }
216}
217
218fn as_constraint_id(
219 constraints: &HashMap<ConstraintID, Constraint>,
220 id: u64,
221) -> Result<ConstraintID, ParseError> {
222 let id = ConstraintID::from(id);
223 if !constraints.contains_key(&id) {
224 return Err(RawParseError::UndefinedConstraintID { id }.into());
225 }
226 Ok(id)
227}
228
229fn as_variable_id(
230 decision_variables: &HashMap<VariableID, DecisionVariable>,
231 id: u64,
232) -> Result<VariableID, ParseError> {
233 let id = VariableID::from(id);
234 if !decision_variables.contains_key(&id) {
235 return Err(RawParseError::UndefinedVariableID { id }.into());
236 }
237 Ok(id)
238}