1use std::borrow::Borrow;
3use std::convert::TryInto;
4use std::ops::RangeBounds;
5use std::os::raw::c_int;
6
7use crate::matrix_col::ColMatrix;
8use crate::{Integrality, Problem};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub struct Col(pub(crate) usize);
13
14impl Col {
15 pub fn index(self) -> usize {
17 self.0
18 }
19}
20
21#[derive(Debug, Clone, PartialEq, Default)]
23pub struct RowMatrix {
24 columns: Vec<(Vec<c_int>, Vec<f64>)>,
27}
28
29impl Problem<RowMatrix> {
31 pub fn add_column<N: Into<f64> + Copy, B: RangeBounds<N>>(
35 &mut self,
36 col_factor: f64,
37 bounds: B,
38 ) -> Col {
39 self.add_column_with_integrality(col_factor, bounds, false)
40 }
41
42 pub fn add_integer_column<N: Into<f64> + Copy, B: RangeBounds<N>>(
44 &mut self,
45 col_factor: f64,
46 bounds: B,
47 ) -> Col {
48 self.add_column_with_integrality(col_factor, bounds, true)
49 }
50
51 #[inline]
53 pub fn add_column_with_integrality<N: Into<f64> + Copy, B: RangeBounds<N>>(
54 &mut self,
55 col_factor: f64,
56 bounds: B,
57 is_integer: bool,
58 ) -> Col {
59 self.add_column_with_integrality_kind(col_factor, bounds, is_integer.into())
60 }
61
62 #[inline]
65 pub fn add_column_with_integrality_kind<N: Into<f64> + Copy, B: RangeBounds<N>>(
66 &mut self,
67 col_factor: f64,
68 bounds: B,
69 integrality: Integrality,
70 ) -> Col {
71 let col = Col(self.num_cols());
72 self.add_column_inner(col_factor, bounds, integrality);
73 self.matrix.columns.push((vec![], vec![]));
74 col
75 }
76
77 pub fn add_semi_continuous_column<N: Into<f64> + Copy, B: RangeBounds<N>>(
83 &mut self,
84 col_factor: f64,
85 bounds: B,
86 ) -> Col {
87 self.add_column_with_integrality_kind(col_factor, bounds, Integrality::SemiContinuous)
88 }
89
90 pub fn add_semi_integer_column<N: Into<f64> + Copy, B: RangeBounds<N>>(
96 &mut self,
97 col_factor: f64,
98 bounds: B,
99 ) -> Col {
100 self.add_column_with_integrality_kind(col_factor, bounds, Integrality::SemiInteger)
101 }
102
103 pub fn add_row<
116 N: Into<f64> + Copy,
117 B: RangeBounds<N>,
118 ITEM: Borrow<(Col, f64)>,
119 I: IntoIterator<Item = ITEM>,
120 >(
121 &mut self,
122 bounds: B,
123 row_factors: I,
124 ) {
125 let num_rows: c_int = self.num_rows().try_into().expect("too many rows");
126 for r in row_factors {
127 let &(col, factor) = r.borrow();
128 let c = &mut self.matrix.columns[col.0];
129 c.0.push(num_rows);
130 c.1.push(factor);
131 }
132 self.add_row_inner(bounds);
133 }
134}
135
136impl From<RowMatrix> for ColMatrix {
137 fn from(m: RowMatrix) -> Self {
138 let mut astart = Vec::with_capacity(m.columns.len());
139 astart.push(0);
140 let size: usize = m.columns.iter().map(|(v, _)| v.len()).sum();
141 let mut aindex = Vec::with_capacity(size);
142 let mut avalue = Vec::with_capacity(size);
143 for (row_indices, factors) in m.columns {
144 aindex.extend_from_slice(&row_indices);
145 avalue.extend_from_slice(&factors);
146 astart.push(aindex.len().try_into().expect("invalid matrix size"));
147 }
148 Self {
149 astart,
150 aindex,
151 avalue,
152 }
153 }
154}
155
156#[allow(clippy::float_cmp)]
157#[test]
158fn test_conversion() {
159 use crate::status::HighsModelStatus::Optimal;
160 use crate::{ColProblem, Model, RowProblem, Sense};
161 let inf = f64::INFINITY;
162 let neg_inf = f64::NEG_INFINITY;
163 let mut p = RowProblem::default();
164 let x: Col = p.add_column(1., -1..2);
165 let y: Col = p.add_column(9., 4f64..inf);
166 p.add_row(-999f64..inf, [(x, 666.), (y, 777.)]);
167 p.add_row(neg_inf..8880f64, [(y, 888.)]);
168 assert_eq!(
169 p,
170 RowProblem {
171 colcost: vec![1., 9.],
172 collower: vec![-1., 4.],
173 colupper: vec![2., inf],
174 rowlower: vec![-999., neg_inf],
175 rowupper: vec![inf, 8880.],
176 integrality: None,
177 matrix: RowMatrix {
178 columns: vec![(vec![0], vec![666.]), (vec![0, 1], vec![777., 888.])],
179 },
180 }
181 );
182 let colpb = ColProblem::from(p.clone());
183 assert_eq!(
184 colpb,
185 ColProblem {
186 colcost: vec![1., 9.],
187 collower: vec![-1., 4.],
188 colupper: vec![2., inf],
189 rowlower: vec![-999., neg_inf],
190 rowupper: vec![inf, 8880.],
191 integrality: None,
192 matrix: ColMatrix {
193 astart: vec![0, 1, 3],
194 aindex: vec![0, 0, 1],
195 avalue: vec![666., 777., 888.],
196 },
197 }
198 );
199 let mut m = Model::new(p);
200 m.make_quiet();
201 m.set_sense(Sense::Maximise);
202 let solved = m.solve();
203 assert_eq!(solved.status(), Optimal);
204 assert_eq!(solved.get_solution().columns(), &[2., 10.]);
205}
206
207impl From<Problem<RowMatrix>> for Problem<ColMatrix> {
208 fn from(pb: Problem<RowMatrix>) -> Problem<ColMatrix> {
209 Self {
210 colcost: pb.colcost,
211 collower: pb.collower,
212 colupper: pb.colupper,
213 rowlower: pb.rowlower,
214 rowupper: pb.rowupper,
215 integrality: pb.integrality,
216 matrix: pb.matrix.into(),
217 }
218 }
219}