use crate::{ffi, proto};
use proto::constraint_proto::Constraint as CstEnum;
use smallvec::SmallVec;
#[derive(Default, Debug)]
pub struct CpModelBuilder {
proto: proto::CpModelProto,
}
impl CpModelBuilder {
pub fn proto(&self) -> &proto::CpModelProto {
&self.proto
}
pub fn new_bool_var(&mut self) -> BoolVar {
self.new_bool_var_with_name("")
}
pub fn new_bool_var_with_name(&mut self, name: impl Into<String>) -> BoolVar {
let index = self.proto.variables.len() as i32;
self.proto.variables.push(proto::IntegerVariableProto {
name: name.into(),
domain: vec![0, 1],
});
BoolVar(index)
}
pub fn new_int_var(&mut self, domain: impl IntoIterator<Item = (i64, i64)>) -> IntVar {
self.new_int_var_with_name(domain, "")
}
pub fn new_int_var_with_name(
&mut self,
domain: impl IntoIterator<Item = (i64, i64)>,
name: impl Into<String>,
) -> IntVar {
let index = self.proto.variables.len() as i32;
self.proto.variables.push(proto::IntegerVariableProto {
name: name.into(),
domain: domain.into_iter().flat_map(|(b, e)| [b, e]).collect(),
});
IntVar(index)
}
pub fn var_name(&self, var: impl Into<IntVar>) -> &str {
&self.proto.variables[var.into().0 as usize].name
}
pub fn set_var_name(&mut self, var: impl Into<IntVar>, name: &str) {
self.proto.variables[var.into().0 as usize].name = name.into();
}
pub fn constraint_name(&self, constraint: Constraint) -> &str {
&self.proto.constraints[constraint.0].name
}
pub fn set_constraint_name(&mut self, constraint: Constraint, name: &str) {
self.proto.constraints[constraint.0].name = name.into();
}
pub fn only_enforce_if(
&mut self,
constraint: Constraint,
literals: impl IntoIterator<Item = BoolVar>,
) {
self.proto.constraints[constraint.0]
.enforcement_literal
.extend(literals.into_iter().map(|v| v.0));
}
pub fn add_implication(&mut self, a: BoolVar, b: BoolVar) -> Constraint {
self.add_or([!a, b])
}
pub fn add_or(&mut self, vars: impl IntoIterator<Item = BoolVar>) -> Constraint {
self.add_cst(CstEnum::BoolOr(proto::BoolArgumentProto {
literals: vars.into_iter().map(|v| v.0).collect(),
}))
}
pub fn add_and(&mut self, vars: impl IntoIterator<Item = BoolVar>) -> Constraint {
self.add_cst(CstEnum::BoolAnd(proto::BoolArgumentProto {
literals: vars.into_iter().map(|v| v.0).collect(),
}))
}
pub fn add_at_most_one(&mut self, vars: impl IntoIterator<Item = BoolVar>) -> Constraint {
self.add_cst(CstEnum::AtMostOne(proto::BoolArgumentProto {
literals: vars.into_iter().map(|v| v.0).collect(),
}))
}
pub fn add_exactly_one(&mut self, vars: impl IntoIterator<Item = BoolVar>) -> Constraint {
self.add_cst(CstEnum::ExactlyOne(proto::BoolArgumentProto {
literals: vars.into_iter().map(|v| v.0).collect(),
}))
}
pub fn add_xor(&mut self, vars: impl IntoIterator<Item = BoolVar>) -> Constraint {
self.add_cst(CstEnum::BoolXor(proto::BoolArgumentProto {
literals: vars.into_iter().map(|v| v.0).collect(),
}))
}
pub fn add_all_different(
&mut self,
exprs: impl IntoIterator<Item = impl Into<LinearExpr>>,
) -> Constraint {
self.add_cst(CstEnum::AllDiff(proto::AllDifferentConstraintProto {
exprs: exprs
.into_iter()
.map(Into::into)
.map(|expr| proto::LinearExpressionProto {
vars: expr.vars.into_vec(),
coeffs: expr.coeffs.into_vec(),
offset: expr.constant,
})
.collect(),
}))
}
pub fn add_linear_constraint(
&mut self,
expr: impl Into<LinearExpr>,
domain: impl IntoIterator<Item = (i64, i64)>,
) -> Constraint {
let expr = expr.into();
let constant = expr.constant;
self.add_cst(CstEnum::Linear(proto::LinearConstraintProto {
vars: expr.vars.into_vec(),
coeffs: expr.coeffs.into_vec(),
domain: domain
.into_iter()
.flat_map(|(begin, end)| {
[
if begin == i64::MIN {
i64::MIN
} else {
begin.saturating_sub(constant)
},
if end == i64::MAX {
i64::MAX
} else {
end.saturating_sub(constant)
},
]
})
.collect(),
}))
}
pub fn add_eq<T: Into<LinearExpr>, U: Into<LinearExpr>>(
&mut self,
lhs: T,
rhs: U,
) -> Constraint {
self.add_linear_constraint(lhs.into() - rhs.into(), [(0, 0)])
}
pub fn add_ge<T: Into<LinearExpr>, U: Into<LinearExpr>>(
&mut self,
lhs: T,
rhs: U,
) -> Constraint {
self.add_linear_constraint(lhs.into() - rhs.into(), [(0, i64::MAX)])
}
pub fn add_le<T: Into<LinearExpr>, U: Into<LinearExpr>>(
&mut self,
lhs: T,
rhs: U,
) -> Constraint {
self.add_linear_constraint(lhs.into() - rhs.into(), [(i64::MIN, 0)])
}
pub fn add_gt<T: Into<LinearExpr>, U: Into<LinearExpr>>(
&mut self,
lhs: T,
rhs: U,
) -> Constraint {
self.add_linear_constraint(lhs.into() - rhs.into(), [(1, i64::MAX)])
}
pub fn add_lt<T: Into<LinearExpr>, U: Into<LinearExpr>>(
&mut self,
lhs: T,
rhs: U,
) -> Constraint {
self.add_linear_constraint(lhs.into() - rhs.into(), [(i64::MIN, -1)])
}
pub fn add_ne<T: Into<LinearExpr>, U: Into<LinearExpr>>(
&mut self,
lhs: T,
rhs: U,
) -> Constraint {
self.add_linear_constraint(lhs.into() - rhs.into(), [(i64::MIN, -1), (1, i64::MAX)])
}
pub fn add_min_eq(
&mut self,
target: impl Into<LinearExpr>,
exprs: impl IntoIterator<Item = impl Into<LinearExpr>>,
) -> Constraint {
self.add_cst(CstEnum::LinMax(proto::LinearArgumentProto {
target: Some((-target.into()).into()),
exprs: exprs
.into_iter()
.map(|expr| (-expr.into()).into())
.collect(),
}))
}
pub fn add_max_eq(
&mut self,
target: impl Into<LinearExpr>,
exprs: impl IntoIterator<Item = impl Into<LinearExpr>>,
) -> Constraint {
self.add_cst(CstEnum::LinMax(proto::LinearArgumentProto {
target: Some(target.into().into()),
exprs: exprs.into_iter().map(|e| e.into().into()).collect(),
}))
}
fn add_cst(&mut self, cst: CstEnum) -> Constraint {
let index = self.proto.constraints.len();
self.proto.constraints.push(proto::ConstraintProto {
constraint: Some(cst),
..Default::default()
});
Constraint(index)
}
pub fn add_hint(&mut self, var: impl Into<IntVar>, value: i64) {
let var = var.into();
let hints = self
.proto
.solution_hint
.get_or_insert_with(Default::default);
if var.0 < 0 {
hints.vars.push(var.not().0);
hints.values.push(1 - value);
} else {
hints.vars.push(var.0);
hints.values.push(value);
}
}
pub fn del_hints(&mut self) {
self.proto.solution_hint = None;
}
pub fn minimize<T: Into<LinearExpr>>(&mut self, expr: T) {
let expr = expr.into();
self.proto.objective = Some(proto::CpObjectiveProto {
vars: expr.vars.into_vec(),
coeffs: expr.coeffs.into_vec(),
offset: expr.constant as f64,
scaling_factor: 1.,
domain: vec![],
scaling_was_exact: false,
integer_before_offset: 0,
integer_after_offset: 0,
integer_scaling_factor: 0,
});
}
pub fn maximize<T: Into<LinearExpr>>(&mut self, expr: T) {
let mut expr = expr.into();
for coeff in &mut expr.coeffs {
*coeff *= -1;
}
self.proto.objective = Some(proto::CpObjectiveProto {
vars: expr.vars.into_vec(),
coeffs: expr.coeffs.into_vec(),
offset: -expr.constant as f64,
scaling_factor: -1.,
domain: vec![],
scaling_was_exact: false,
integer_before_offset: 0,
integer_after_offset: 0,
integer_scaling_factor: 0,
});
}
pub fn stats(&self) -> String {
ffi::cp_model_stats(self.proto())
}
pub fn validate_cp_model(&self) -> String {
ffi::validate_cp_model(self.proto())
}
pub fn solve(&self) -> proto::CpSolverResponse {
ffi::solve(self.proto())
}
pub fn solve_with_parameters(&self, params: &proto::SatParameters) -> proto::CpSolverResponse {
ffi::solve_with_parameters(self.proto(), params)
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BoolVar(i32);
impl BoolVar {
#[track_caller]
pub fn solution_value(self, response: &proto::CpSolverResponse) -> bool {
if self.0 < 0 {
use std::ops::Not;
!self.not().solution_value(response)
} else {
response.solution[self.0 as usize] != 0
}
}
}
impl std::ops::Not for BoolVar {
type Output = Self;
fn not(self) -> Self {
Self(-self.0 - 1)
}
}
impl std::fmt::Debug for BoolVar {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
if self.0 < 0 {
write!(f, "Not({:?})", !*self)
} else {
write!(f, "BoolVar({})", self.0)
}
}
}
impl std::ops::Mul<i64> for BoolVar {
type Output = LinearExpr;
fn mul(self, rhs: i64) -> Self::Output {
LinearExpr::from((rhs, self))
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct IntVar(i32);
impl From<BoolVar> for IntVar {
fn from(bool_var: BoolVar) -> IntVar {
IntVar(bool_var.0)
}
}
impl IntVar {
#[track_caller]
pub fn solution_value(self, response: &proto::CpSolverResponse) -> i64 {
if self.0 < 0 {
1 - self.not().solution_value(response)
} else {
response.solution[self.0 as usize]
}
}
fn not(self) -> Self {
IntVar::from(!BoolVar(self.0))
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Constraint(usize);
#[derive(Clone, Default, Debug)]
pub struct LinearExpr {
vars: SmallVec<[i32; 4]>,
coeffs: SmallVec<[i64; 2]>,
constant: i64,
}
impl<E: Into<LinearExpr>> std::ops::AddAssign<E> for LinearExpr {
fn add_assign(&mut self, rhs: E) {
let mut rhs = rhs.into();
if self.vars.len() < rhs.vars.len() {
std::mem::swap(self, &mut rhs);
}
self.vars.extend_from_slice(&rhs.vars);
self.coeffs.extend_from_slice(&rhs.coeffs);
self.constant += rhs.constant;
}
}
impl std::ops::Neg for LinearExpr {
type Output = LinearExpr;
fn neg(mut self) -> Self::Output {
for c in &mut self.coeffs {
*c = -*c;
}
self.constant = -self.constant;
self
}
}
impl<L: Into<LinearExpr>> std::ops::SubAssign<L> for LinearExpr {
fn sub_assign(&mut self, rhs: L) {
*self += -rhs.into();
}
}
impl<V: Into<IntVar>> From<V> for LinearExpr {
fn from(var: V) -> Self {
Self::from((1, var))
}
}
impl From<i64> for LinearExpr {
fn from(constant: i64) -> Self {
let mut res = Self::default();
res.constant += constant;
res
}
}
impl<V: Into<IntVar>> From<(i64, V)> for LinearExpr {
fn from((coeff, var): (i64, V)) -> Self {
let mut res = Self::default();
let var = var.into();
if var.0 < 0 {
res.vars.push(var.not().0);
res.coeffs.push(-coeff);
res.constant += coeff;
} else {
res.vars.push(var.0);
res.coeffs.push(coeff);
}
res
}
}
impl<V: Into<IntVar>, const L: usize> From<[(i64, V); L]> for LinearExpr {
fn from(expr: [(i64, V); L]) -> Self {
let mut res = Self::default();
for term in expr {
res += term;
}
res
}
}
impl<T: Into<LinearExpr>> std::ops::Add<T> for LinearExpr {
type Output = LinearExpr;
fn add(mut self, rhs: T) -> Self::Output {
self += rhs.into();
self
}
}
impl<T: Into<LinearExpr>> std::ops::Sub<T> for LinearExpr {
type Output = LinearExpr;
fn sub(mut self, rhs: T) -> Self::Output {
self -= rhs.into();
self
}
}
impl std::ops::Mul<i64> for LinearExpr {
type Output = LinearExpr;
fn mul(mut self, rhs: i64) -> Self::Output {
for c in &mut self.coeffs {
*c *= rhs;
}
self.constant *= rhs;
self
}
}
impl From<LinearExpr> for proto::LinearExpressionProto {
fn from(expr: LinearExpr) -> Self {
proto::LinearExpressionProto {
vars: expr.vars.into_vec(),
coeffs: expr.coeffs.into_vec(),
offset: expr.constant,
}
}
}
impl<T: Into<LinearExpr>> std::iter::Extend<T> for LinearExpr {
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
for e in iter {
*self += e;
}
}
}
impl<T: Into<LinearExpr>> std::iter::FromIterator<T> for LinearExpr {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut res = LinearExpr::default();
res.extend(iter);
res
}
}