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
//! col-oriented matrix to build a problem variable by variable
use std::borrow::Borrow;
use std::convert::TryInto;
use std::ops::RangeBounds;
use std::os::raw::c_int;
use crate::Problem;
/// Represents a constraint
#[derive(Debug, Clone, Copy)]
pub struct Row(pub(crate) c_int);
/// A constraint matrix to build column-by-column
#[derive(Debug, Clone, PartialEq, Default)]
pub struct ColMatrix {
// column-wise sparse constraints matrix
pub(crate) astart: Vec<c_int>,
pub(crate) aindex: Vec<c_int>,
pub(crate) avalue: Vec<f64>,
}
/// To use these functions, you need to first add all your constraints, and then add variables
/// one by one using the [Row] objects.
impl Problem<ColMatrix> {
/// Add a row (a constraint) to the problem.
/// The concrete factors are added later, when creating columns.
pub fn add_row<N: Into<f64> + Copy, B: RangeBounds<N>>(&mut self, bounds: B) -> Row {
self.add_row_inner(bounds)
}
/// Add a continuous variable to the problem.
/// - `col_factor` represents the factor in front of the variable in the objective function.
/// - `bounds` represents the maximal and minimal allowed values of the variable.
/// - `row_factors` defines how much this variable weights in each constraint.
///
/// ```
/// use highs::{ColProblem, Sense};
/// let mut pb = ColProblem::new();
/// let constraint = pb.add_row(..=5); // adds a constraint that cannot take a value over 5
/// // add a variable that has a coefficient 2 in the objective function, is >=0, and has a coefficient
/// // 2 in the constraint
/// pb.add_column(2., 0.., &[(constraint, 2.)]);
/// ```
pub fn add_column<
N: Into<f64> + Copy,
B: RangeBounds<N>,
ITEM: Borrow<(Row, f64)>,
I: IntoIterator<Item = ITEM>,
>(
&mut self,
col_factor: f64,
bounds: B,
row_factors: I,
) {
self.add_column_with_integrality(col_factor, bounds, row_factors, false);
}
/// Same as add_column, but forces the solution to contain an integer value for this variable.
///
/// ```
/// use highs::{ColProblem, Sense};
/// let mut pb = ColProblem::new();
/// let constraint = pb.add_row(..=5); // adds a constraint that cannot take a value over 5
/// // add an integer variable that has a coefficient 2 in the objective function, is >=0, and has a coefficient
/// // 2 in the constraint
/// pb.add_integer_column(2., 0.., &[(constraint, 2.)]);
/// ```
pub fn add_integer_column<
N: Into<f64> + Copy,
B: RangeBounds<N>,
ITEM: Borrow<(Row, f64)>,
I: IntoIterator<Item = ITEM>,
>(
&mut self,
col_factor: f64,
bounds: B,
row_factors: I,
) {
self.add_column_with_integrality(col_factor, bounds, row_factors, true);
}
/// Same as add_column, but lets you define whether the new variable should be integral or continuous.
#[inline]
pub fn add_column_with_integrality<
N: Into<f64> + Copy,
B: RangeBounds<N>,
ITEM: Borrow<(Row, f64)>,
I: IntoIterator<Item = ITEM>,
>(
&mut self,
col_factor: f64,
bounds: B,
row_factors: I,
is_integer: bool,
) {
self.matrix
.astart
.push(self.matrix.aindex.len().try_into().unwrap());
let iter = row_factors.into_iter();
let (size, _) = iter.size_hint();
self.matrix.aindex.reserve(size);
self.matrix.avalue.reserve(size);
for r in iter {
let &(row, factor) = r.borrow();
self.matrix.aindex.push(row.0);
self.matrix.avalue.push(factor);
}
self.add_column_inner(col_factor, bounds, is_integer);
}
}