1use oximo_expr::VarId;
2use rustc_hash::{FxBuildHasher, FxHashSet};
3use smol_str::SmolStr;
4
5use crate::model::Model;
6use crate::reformulation::{
7 ReformulatedModel, ReformulationError, SosReformulationArtifacts, SosReformulationOptions,
8};
9
10#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
12pub enum SosType {
13 Sos1,
15 Sos2,
17}
18
19impl SosType {
20 #[must_use]
21 pub const fn label(self) -> &'static str {
22 match self {
23 Self::Sos1 => "SOS1",
24 Self::Sos2 => "SOS2",
25 }
26 }
27}
28
29impl std::fmt::Display for SosType {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 f.write_str(self.label())
32 }
33}
34
35#[derive(Copy, Clone, Debug, PartialEq)]
36pub struct SosMember {
37 pub variable: VarId,
38 pub weight: f64,
39}
40
41#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
42pub struct SosConstraintId(pub u32);
43
44impl SosConstraintId {
45 #[inline]
46 pub fn index(self) -> usize {
47 self.0 as usize
48 }
49}
50
51#[derive(Copy, Clone)]
56pub struct SosConstraintHandle<'a> {
57 pub(crate) model: &'a Model,
58 pub(crate) id: SosConstraintId,
59}
60
61impl std::fmt::Debug for SosConstraintHandle<'_> {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 f.debug_struct("SosConstraintHandle").field("id", &self.id).finish()
64 }
65}
66
67impl SosConstraintHandle<'_> {
68 #[must_use]
69 pub const fn id(self) -> SosConstraintId {
70 self.id
71 }
72
73 #[must_use]
74 pub fn index(self) -> usize {
75 self.id.index()
76 }
77
78 pub fn to_reformulated_model(
86 self,
87 options: SosReformulationOptions,
88 ) -> Result<ReformulatedModel, ReformulationError> {
89 self.model.to_reformulated_sos_constraint_model(self.id, options)
90 }
91
92 pub fn reformulate(
100 self,
101 options: SosReformulationOptions,
102 ) -> Result<Option<SosReformulationArtifacts>, ReformulationError> {
103 self.model.reformulate_sos_constraint(self.id, options)
104 }
105}
106
107impl From<SosConstraintHandle<'_>> for SosConstraintId {
108 fn from(value: SosConstraintHandle<'_>) -> Self {
109 value.id
110 }
111}
112
113#[derive(Clone, Debug)]
115pub struct SosConstraint {
116 pub name: SmolStr,
117 pub sos_type: SosType,
118 pub members: Vec<SosMember>,
119 pub active: bool,
120}
121
122pub(crate) fn validate_members(name: &str, members: &[SosMember]) {
124 assert!(!members.is_empty(), "SOS constraint {name:?} has no members");
125 let mut vars = FxHashSet::with_capacity_and_hasher(members.len(), FxBuildHasher);
126 let mut weights = FxHashSet::with_capacity_and_hasher(members.len(), FxBuildHasher);
127 for member in members {
128 assert!(member.weight.is_finite(), "SOS constraint {name:?} has a non-finite weight");
129 assert!(
130 vars.insert(member.variable),
131 "SOS constraint {name:?} contains a duplicate variable"
132 );
133 let key = match member.weight.to_bits() {
134 0 | 0x8000_0000_0000_0000 => 0,
135 bits => bits,
136 };
137 assert!(weights.insert(key), "SOS constraint {name:?} contains duplicate weights");
138 }
139}