use pumpkin_conflict_resolvers::resolvers::ResolutionResolver;
use pumpkin_solver::Solver;
use pumpkin_solver::core::results::ProblemSolution;
use pumpkin_solver::core::results::SatisfactionResult;
use pumpkin_solver::core::termination::Indefinite;
use pumpkin_solver::core::variables::DomainId;
struct Bibd {
rows: u32,
columns: u32,
row_sum: u32,
column_sum: u32,
max_dot_product: u32,
}
impl Bibd {
fn from_args() -> Option<Bibd> {
let args = std::env::args()
.skip(1)
.map(|arg| arg.parse::<u32>())
.collect::<Result<Vec<u32>, _>>()
.ok()?;
if args.len() != 3 {
return None;
}
let v = args[0];
let k = args[1];
let l = args[2];
let r = l * (v - 1) / (k - 1);
let b = v * r / k;
Some(Self {
rows: v,
columns: b,
row_sum: r,
column_sum: k,
max_dot_product: l,
})
}
}
fn create_matrix(solver: &mut Solver, bibd: &Bibd) -> Vec<Vec<DomainId>> {
(0..bibd.rows)
.map(|_| {
(0..bibd.columns)
.map(|_| solver.new_bounded_integer(0, 1))
.collect::<Vec<_>>()
})
.collect::<Vec<_>>()
}
fn main() {
env_logger::init();
let Some(bibd) = Bibd::from_args() else {
eprintln!("Usage: {} <v> <k> <l>", std::env::args().next().unwrap());
return;
};
println!(
"bibd: (v = {}, b = {}, r = {}, k = {}, l = {})",
bibd.rows, bibd.columns, bibd.row_sum, bibd.column_sum, bibd.max_dot_product
);
let mut solver = Solver::default();
let constraint_tag = solver.new_constraint_tag();
let matrix = create_matrix(&mut solver, &bibd);
for row in matrix.iter() {
solver
.add_constraint(pumpkin_constraints::equals(
row.clone(),
bibd.row_sum as i32,
constraint_tag,
))
.post();
}
for row in transpose(&matrix) {
solver
.add_constraint(pumpkin_constraints::equals(
row,
bibd.column_sum as i32,
constraint_tag,
))
.post();
}
let pairwise_product = (0..bibd.rows)
.map(|_| create_matrix(&mut solver, &bibd))
.collect::<Vec<_>>();
for r1 in 0..bibd.rows as usize {
for r2 in r1 + 1..bibd.rows as usize {
for col in 0..bibd.columns as usize {
solver
.add_constraint(pumpkin_constraints::times(
matrix[r1][col],
matrix[r2][col],
pairwise_product[r1][r2][col],
constraint_tag,
))
.post();
}
solver
.add_constraint(pumpkin_constraints::less_than_or_equals(
pairwise_product[r1][r2].clone(),
bibd.max_dot_product as i32,
constraint_tag,
))
.post();
}
}
let mut brancher = solver.default_brancher();
let mut resolver = ResolutionResolver::default();
match solver.satisfy(&mut brancher, &mut Indefinite, &mut resolver) {
SatisfactionResult::Satisfiable(satisfiable) => {
let solution = satisfiable.solution();
let row_separator = format!("{}+", "+---".repeat(bibd.columns as usize));
for row in matrix.iter() {
let line = row
.iter()
.map(|var| {
if solution.get_integer_value(*var) == 1 {
String::from("| * ")
} else {
String::from("| ")
}
})
.collect::<String>();
println!("{row_separator}\n{line}|");
}
println!("{row_separator}");
}
SatisfactionResult::Unsatisfiable(_, _, _) => {
println!("UNSATISFIABLE")
}
SatisfactionResult::Unknown(_, _, _) => {
println!("UNKNOWN")
}
};
}
fn transpose<T: Clone, Inner: AsRef<[T]>>(matrix: &[Inner]) -> Vec<Vec<T>> {
let rows = matrix.len();
let cols = matrix[0].as_ref().len();
(0..cols)
.map(|col| {
(0..rows)
.map(|row| matrix[row].as_ref()[col].clone())
.collect()
})
.collect()
}