-
Notifications
You must be signed in to change notification settings - Fork 23
Open
Labels
documentationImprovements or additions to documentationImprovements or additions to documentationenhancementNew feature or requestNew feature or request
Description
Good morning! I am using Pumpkin-Solver to model part 2 of Advent of Code 2025 Day 10. I got it to work, but I have a question. The constraints::equals function expects an i32 in its right-hand side. For variables a, b, c, d, e, f, and z, we cannot model a + b + c + d + e + f = z directly. Instead, I had to build up the partial sums as intermediate variables using the constraints::plus function. Was there a better way to do this?
Here is my program:
use pumpkin_solver::{
ConstraintOperationError, DefaultBrancher, Solver, constraints,
optimisation::{OptimisationDirection, linear_sat_unsat::LinearSatUnsat},
results::{OptimisationResult, ProblemSolution, SolutionReference},
termination::Indefinite,
};
/// This is the first example from Part 2 of
/// [Advent of Code 2025 Day 10](https://adventofcode.com/2025/day/10#part2).
///
/// This program minimizes the sum of non-negative integers a, b, c, d, e, and
/// f such that the matrix product A times a vector x={a,b,c,d,e,f} is equal
/// to vector y={3,5,4,7}. In the Wolfram language,
///
/// ```mathematica
/// In[1]:= A := {{0, 0, 0, 0, 1, 1}, {0, 1, 0, 0, 0, 1}, {0, 0, 1, 1, 1, 0}, {1, 1, 0, 1, 0, 0}}
///
/// In[2]:= x := {a,b,c,d,e,f}
///
/// In[3]:= y := {3,5,4,7}
///
/// In[4]:= Minimize[Total[x], A.x == y, x, NonNegativeIntegers]
///
/// Out[4]= {10, {a -> 1, b -> 3, c -> 0, d -> 3, e -> 1, f -> 2}}
/// ```
fn main() -> Result<(), ConstraintOperationError> {
let mut solver = Solver::default();
let a = solver.new_bounded_integer(0, 100);
let b = solver.new_bounded_integer(0, 100);
let c = solver.new_bounded_integer(0, 100);
let d = solver.new_bounded_integer(0, 100);
let e = solver.new_bounded_integer(0, 100);
let f = solver.new_bounded_integer(0, 100);
// 3 = e + f
let tag = solver.new_constraint_tag();
solver
.add_constraint(constraints::equals(vec![e, f], 3, tag))
.post()?;
// 5 = b + f
let tag = solver.new_constraint_tag();
solver
.add_constraint(constraints::equals(vec![b, f], 5, tag))
.post()?;
// 4 = c + d + e
let tag = solver.new_constraint_tag();
solver
.add_constraint(constraints::equals(vec![c, d, e], 4, tag))
.post()?;
// 7 = a + b + d
let tag = solver.new_constraint_tag();
solver
.add_constraint(constraints::equals(vec![a, b, d], 7, tag))
.post()?;
// Our goal is to minimize a + b + c + d + e + f, but the function
// `constraints::equals(terms, rhs, constraint_tag)` expects an i32 for the
// right-hand side. You cannot put another variable in the right-hand side.
//
// Instead, you can use `constraints::plus(a, b, c, constraint_tag)` to
// model the dependencies between variables a, b, and c.
//
// We build up the intermediate sums of dependent variables as
// a + b + c + d + e + f = ((((a + b) + c) + d) + e) + f.
let ab = solver.new_bounded_integer(0, 100);
let tag = solver.new_constraint_tag();
solver
.add_constraint(constraints::plus(a, b, ab, tag))
.post()?;
let abc = solver.new_bounded_integer(0, 100);
let tag = solver.new_constraint_tag();
solver
.add_constraint(constraints::plus(ab, c, abc, tag))
.post()?;
let abcd = solver.new_bounded_integer(0, 100);
let tag = solver.new_constraint_tag();
solver
.add_constraint(constraints::plus(ab, d, abcd, tag))
.post()?;
let abcde = solver.new_bounded_integer(0, 100);
let tag = solver.new_constraint_tag();
solver
.add_constraint(constraints::plus(abcd, e, abcde, tag))
.post()?;
// This is our minimization objective, a + b + c + d + e + f.
let abcdef = solver.new_bounded_integer(0, 100);
let tag = solver.new_constraint_tag();
solver
.add_constraint(constraints::plus(abcde, f, abcdef, tag))
.post()?;
// Copied from https://docs.rs/pumpkin-solver/0.2.2/pumpkin_solver/index.html#using-pumpkin
let mut termination = Indefinite;
let mut brancher = solver.default_brancher();
let callback: fn(&pumpkin_solver::Solver, SolutionReference, &DefaultBrancher) = |_, _, _| {};
let result = solver.optimise(
&mut brancher,
&mut termination,
LinearSatUnsat::new(OptimisationDirection::Minimise, abcdef, callback),
);
match result {
OptimisationResult::Optimal(solution) => println!("{}", solution.get_integer_value(abcdef)),
OptimisationResult::Satisfiable(_) => unreachable!(),
OptimisationResult::Unsatisfiable => panic!("Not satisfiable"),
OptimisationResult::Unknown => panic!("No result from solver"),
}
Ok(())
}Metadata
Metadata
Assignees
Labels
documentationImprovements or additions to documentationImprovements or additions to documentationenhancementNew feature or requestNew feature or request