Solver

Struct Solver 

Source
pub struct Solver { /* private fields */ }
Expand description

The main interaction point which allows the creation of variables, the addition of constraints, and solving problems.

§Creating Variables

As stated in crate::variables, we can create two types of variables: propositional variables and integer variables.

let mut solver = Solver::default();

// Integer Variables

// We can create an integer variable with a domain in the range [0, 10]
let integer_between_bounds = solver.new_bounded_integer(0, 10);

// We can also create such a variable with a name
let named_integer_between_bounds = solver.new_named_bounded_integer(0, 10, "x");

// We can also create an integer variable with a non-continuous domain in the follow way
let mut sparse_integer = solver.new_sparse_integer(vec![0, 3, 5]);

// We can also create such a variable with a name
let named_sparse_integer = solver.new_named_sparse_integer(vec![0, 3, 5], "y");

// Additionally, we can also create an affine view over a variable with both a scale and an offset (or either)
let view_over_integer = integer_between_bounds.scaled(-1).offset(15);


// Propositional Variable

// We can create a literal
let literal = solver.new_literal();

// We can also create such a variable with a name
let named_literal = solver.new_named_literal("z");

// We can also get the predicate from the literal
let true_predicate = literal.get_true_predicate();

// We can also create an iterator of new literals and get a number of them at once
let list_of_5_literals = solver.new_literals().take(5).collect::<Vec<_>>();
assert_eq!(list_of_5_literals.len(), 5);

§Using the Solver

For examples on how to use the solver, see the root-level crate documentation or one of these examples.

Implementations§

Source§

impl Solver

Source

pub fn with_options(solver_options: SatisfactionSolverOptions) -> Solver

Creates a solver with the provided SolverOptions.

Examples found in repository?
examples/nqueens.rs (lines 49-52)
25fn main() {
26    let Cli {
27        n,
28        proof: proof_path,
29    } = Cli::parse();
30
31    if n < 2 {
32        println!("Please provide an 'n > 1'");
33        return;
34    }
35
36    let Ok(proof_log) = proof_path
37        .as_ref()
38        .map(|path| ProofLog::cp(path, true))
39        .transpose()
40        .map(|proof| proof.unwrap_or_default())
41    else {
42        eprintln!(
43            "Failed to create proof file at {}",
44            proof_path.unwrap().display()
45        );
46        return;
47    };
48
49    let mut solver = Solver::with_options(SolverOptions {
50        proof_log,
51        ..Default::default()
52    });
53
54    // Create the constraint tags for the three all_different constraints.
55    let c1_tag = solver.new_constraint_tag();
56    let c2_tag = solver.new_constraint_tag();
57    let c3_tag = solver.new_constraint_tag();
58
59    let variables = (0..n)
60        .map(|i| solver.new_named_bounded_integer(0, n as i32 - 1, format!("q{i}")))
61        .collect::<Vec<_>>();
62
63    let _ = solver
64        .add_constraint(constraints::all_different(variables.clone(), c1_tag))
65        .post();
66
67    let diag1 = variables
68        .iter()
69        .cloned()
70        .enumerate()
71        .map(|(i, var)| var.offset(i as i32))
72        .collect::<Vec<_>>();
73    let diag2 = variables
74        .iter()
75        .cloned()
76        .enumerate()
77        .map(|(i, var)| var.offset(-(i as i32)))
78        .collect::<Vec<_>>();
79
80    let _ = solver
81        .add_constraint(constraints::all_different(diag1, c2_tag))
82        .post();
83    let _ = solver
84        .add_constraint(constraints::all_different(diag2, c3_tag))
85        .post();
86
87    let mut brancher = solver.default_brancher();
88    match solver.satisfy(&mut brancher, &mut Indefinite) {
89        SatisfactionResult::Satisfiable(satisfiable) => {
90            let solution = satisfiable.solution();
91
92            let row_separator = format!("{}+", "+---".repeat(n as usize));
93
94            for row in 0..n {
95                println!("{row_separator}");
96
97                let queen_col = solution.get_integer_value(variables[row as usize]) as u32;
98
99                for col in 0..n {
100                    let string = if queen_col == col { "| * " } else { "|   " };
101
102                    print!("{string}");
103                }
104
105                println!("|");
106            }
107
108            println!("{row_separator}");
109        }
110        SatisfactionResult::Unsatisfiable(_, _) => {
111            println!("{n}-queens is unsatisfiable.");
112        }
113        SatisfactionResult::Unknown(_, _) => {
114            println!("Timeout.");
115        }
116    };
117}
Source

pub fn log_statistics_with_objective( &self, brancher: Option<&impl Brancher>, objective_value: i64, verbose: bool, )

Logs the statistics currently present in the solver with the provided objective value.

Source

pub fn log_statistics(&self, brancher: Option<&impl Brancher>, verbose: bool)

Logs the statistics currently present in the solver.

Source

pub fn get_solution_reference(&self) -> SolutionReference<'_>

Source§

impl Solver

Methods to retrieve information about variables

Source

pub fn get_literal_value(&self, literal: Literal) -> Option<bool>

Get the value of the given Literal at the root level (after propagation), which could be unassigned.

Source

pub fn lower_bound(&self, variable: &impl IntegerVariable) -> i32

Get the lower-bound of the given IntegerVariable at the root level (after propagation).

Source

pub fn upper_bound(&self, variable: &impl IntegerVariable) -> i32

Get the upper-bound of the given IntegerVariable at the root level (after propagation).

Source§

impl Solver

Functions to create and retrieve integer and propositional variables.

Source

pub fn new_literals(&mut self) -> impl Iterator<Item = Literal>

Returns an infinite iterator of positive literals of new variables. The new variables will be unnamed.

§Example
let mut solver = Solver::default();
let literals: Vec<Literal> = solver.new_literals().take(5).collect();

// `literals` contains 5 positive literals of newly created propositional variables.
assert_eq!(literals.len(), 5);

Note that this method captures the lifetime of the immutable reference to self.

Source

pub fn new_literal(&mut self) -> Literal

Create a fresh propositional variable and return the literal with positive polarity.

§Example
let mut solver = Solver::default();

// We can create a literal
let literal = solver.new_literal();
Examples found in repository?
examples/disjunctive_scheduling.rs (line 56)
20fn main() {
21    let mut args = std::env::args();
22
23    let n_tasks = args
24        .nth(1)
25        .expect("Please provide a number of tasks")
26        .parse::<usize>()
27        .expect("Not a valid usized");
28    let processing_times = args
29        .take(n_tasks)
30        .map(|arg| arg.parse::<usize>())
31        .collect::<Result<Vec<_>, _>>()
32        .expect("The provided processing times are not valid unsigned integers");
33    assert_eq!(
34        processing_times.len(),
35        n_tasks,
36        "Provided fewer than `n_tasks` processing times."
37    );
38
39    let horizon = processing_times.iter().sum::<usize>();
40
41    let mut solver = Solver::default();
42
43    // Creates a dummy constraint tag; since this example does not support proof logging the
44    // constraint tag does not matter.
45    let constraint_tag = solver.new_constraint_tag();
46
47    let start_variables = (0..n_tasks)
48        .map(|i| solver.new_bounded_integer(0, (horizon - processing_times[i]) as i32))
49        .collect::<Vec<_>>();
50
51    // Literal which indicates precedence (i.e. precedence_literals[x][y] <=> x ends before y
52    // starts)
53    let precedence_literals = (0..n_tasks)
54        .map(|_| {
55            (0..n_tasks)
56                .map(|_| solver.new_literal())
57                .collect::<Vec<_>>()
58        })
59        .collect::<Vec<_>>();
60
61    for x in 0..n_tasks {
62        for y in 0..n_tasks {
63            if x == y {
64                continue;
65            }
66            // precedence_literals[x][y] <=> x ends before y starts
67            let literal = precedence_literals[x][y];
68            // literal <=> (s_x + p_x <= s_y)
69            // equivelent to literal <=> (s_x - s_y <= -p_x)
70            // So the variables are -s_y and s_x, and the rhs is -p_x
71            let variables = vec![start_variables[y].scaled(-1), start_variables[x].scaled(1)];
72            let _ = constraints::less_than_or_equals(
73                variables,
74                -(processing_times[x] as i32),
75                constraint_tag,
76            )
77            .reify(&mut solver, literal);
78
79            // Either x starts before y or y start before x
80            let _ = solver.add_clause(
81                [
82                    literal.get_true_predicate(),
83                    precedence_literals[y][x].get_true_predicate(),
84                ],
85                constraint_tag,
86            );
87        }
88    }
89
90    let mut brancher = solver.default_brancher();
91    if matches!(
92        solver.satisfy(&mut brancher, &mut Indefinite),
93        SatisfactionResult::Unsatisfiable(_, _),
94    ) {
95        panic!("Infeasibility Detected")
96    }
97    match solver.satisfy(&mut brancher, &mut Indefinite) {
98        SatisfactionResult::Satisfiable(satisfiable) => {
99            let solution = satisfiable.solution();
100
101            let mut start_variables_and_processing_times = start_variables
102                .iter()
103                .zip(processing_times)
104                .collect::<Vec<_>>();
105            start_variables_and_processing_times.sort_by(|(s1, _), (s2, _)| {
106                solution
107                    .get_integer_value(**s1)
108                    .cmp(&solution.get_integer_value(**s2))
109            });
110
111            println!(
112                "{}",
113                start_variables_and_processing_times
114                    .iter()
115                    .map(|(var, processing_time)| format!(
116                        "[{}, {}]",
117                        solution.get_integer_value(**var),
118                        solution.get_integer_value(**var) + *processing_time as i32
119                    ))
120                    .collect::<Vec<_>>()
121                    .join(" - ")
122            );
123        }
124        SatisfactionResult::Unsatisfiable(_, _) => panic!("Infeasibility Detected"),
125        SatisfactionResult::Unknown(_, _) => println!("Timeout."),
126    };
127}
Source

pub fn new_literal_for_predicate( &mut self, predicate: Predicate, constraint_tag: ConstraintTag, ) -> Literal

Source

pub fn new_named_literal(&mut self, name: impl Into<String>) -> Literal

Create a fresh propositional variable with a given name and return the literal with positive polarity.

§Example
let mut solver = Solver::default();

// We can also create such a variable with a name
let named_literal = solver.new_named_literal("z");
Source

pub fn get_true_literal(&self) -> Literal

Get a literal which is always true.

Source

pub fn get_false_literal(&self) -> Literal

Get a literal which is always false.

Source

pub fn new_bounded_integer( &mut self, lower_bound: i32, upper_bound: i32, ) -> DomainId

Create a new integer variable with the given bounds.

§Example
let mut solver = Solver::default();

// We can create an integer variable with a domain in the range [0, 10]
let integer_between_bounds = solver.new_bounded_integer(0, 10);
Examples found in repository?
examples/bibd.rs (line 69)
65fn create_matrix(solver: &mut Solver, bibd: &Bibd) -> Vec<Vec<DomainId>> {
66    (0..bibd.rows)
67        .map(|_| {
68            (0..bibd.columns)
69                .map(|_| solver.new_bounded_integer(0, 1))
70                .collect::<Vec<_>>()
71        })
72        .collect::<Vec<_>>()
73}
More examples
Hide additional examples
examples/disjunctive_scheduling.rs (line 48)
20fn main() {
21    let mut args = std::env::args();
22
23    let n_tasks = args
24        .nth(1)
25        .expect("Please provide a number of tasks")
26        .parse::<usize>()
27        .expect("Not a valid usized");
28    let processing_times = args
29        .take(n_tasks)
30        .map(|arg| arg.parse::<usize>())
31        .collect::<Result<Vec<_>, _>>()
32        .expect("The provided processing times are not valid unsigned integers");
33    assert_eq!(
34        processing_times.len(),
35        n_tasks,
36        "Provided fewer than `n_tasks` processing times."
37    );
38
39    let horizon = processing_times.iter().sum::<usize>();
40
41    let mut solver = Solver::default();
42
43    // Creates a dummy constraint tag; since this example does not support proof logging the
44    // constraint tag does not matter.
45    let constraint_tag = solver.new_constraint_tag();
46
47    let start_variables = (0..n_tasks)
48        .map(|i| solver.new_bounded_integer(0, (horizon - processing_times[i]) as i32))
49        .collect::<Vec<_>>();
50
51    // Literal which indicates precedence (i.e. precedence_literals[x][y] <=> x ends before y
52    // starts)
53    let precedence_literals = (0..n_tasks)
54        .map(|_| {
55            (0..n_tasks)
56                .map(|_| solver.new_literal())
57                .collect::<Vec<_>>()
58        })
59        .collect::<Vec<_>>();
60
61    for x in 0..n_tasks {
62        for y in 0..n_tasks {
63            if x == y {
64                continue;
65            }
66            // precedence_literals[x][y] <=> x ends before y starts
67            let literal = precedence_literals[x][y];
68            // literal <=> (s_x + p_x <= s_y)
69            // equivelent to literal <=> (s_x - s_y <= -p_x)
70            // So the variables are -s_y and s_x, and the rhs is -p_x
71            let variables = vec![start_variables[y].scaled(-1), start_variables[x].scaled(1)];
72            let _ = constraints::less_than_or_equals(
73                variables,
74                -(processing_times[x] as i32),
75                constraint_tag,
76            )
77            .reify(&mut solver, literal);
78
79            // Either x starts before y or y start before x
80            let _ = solver.add_clause(
81                [
82                    literal.get_true_predicate(),
83                    precedence_literals[y][x].get_true_predicate(),
84                ],
85                constraint_tag,
86            );
87        }
88    }
89
90    let mut brancher = solver.default_brancher();
91    if matches!(
92        solver.satisfy(&mut brancher, &mut Indefinite),
93        SatisfactionResult::Unsatisfiable(_, _),
94    ) {
95        panic!("Infeasibility Detected")
96    }
97    match solver.satisfy(&mut brancher, &mut Indefinite) {
98        SatisfactionResult::Satisfiable(satisfiable) => {
99            let solution = satisfiable.solution();
100
101            let mut start_variables_and_processing_times = start_variables
102                .iter()
103                .zip(processing_times)
104                .collect::<Vec<_>>();
105            start_variables_and_processing_times.sort_by(|(s1, _), (s2, _)| {
106                solution
107                    .get_integer_value(**s1)
108                    .cmp(&solution.get_integer_value(**s2))
109            });
110
111            println!(
112                "{}",
113                start_variables_and_processing_times
114                    .iter()
115                    .map(|(var, processing_time)| format!(
116                        "[{}, {}]",
117                        solution.get_integer_value(**var),
118                        solution.get_integer_value(**var) + *processing_time as i32
119                    ))
120                    .collect::<Vec<_>>()
121                    .join(" - ")
122            );
123        }
124        SatisfactionResult::Unsatisfiable(_, _) => panic!("Infeasibility Detected"),
125        SatisfactionResult::Unknown(_, _) => println!("Timeout."),
126    };
127}
Source

pub fn new_named_bounded_integer( &mut self, lower_bound: i32, upper_bound: i32, name: impl Into<String>, ) -> DomainId

Create a new named integer variable with the given bounds.

§Example
let mut solver = Solver::default();

// We can also create such a variable with a name
let named_integer_between_bounds = solver.new_named_bounded_integer(0, 10, "x");
Examples found in repository?
examples/nqueens.rs (line 60)
25fn main() {
26    let Cli {
27        n,
28        proof: proof_path,
29    } = Cli::parse();
30
31    if n < 2 {
32        println!("Please provide an 'n > 1'");
33        return;
34    }
35
36    let Ok(proof_log) = proof_path
37        .as_ref()
38        .map(|path| ProofLog::cp(path, true))
39        .transpose()
40        .map(|proof| proof.unwrap_or_default())
41    else {
42        eprintln!(
43            "Failed to create proof file at {}",
44            proof_path.unwrap().display()
45        );
46        return;
47    };
48
49    let mut solver = Solver::with_options(SolverOptions {
50        proof_log,
51        ..Default::default()
52    });
53
54    // Create the constraint tags for the three all_different constraints.
55    let c1_tag = solver.new_constraint_tag();
56    let c2_tag = solver.new_constraint_tag();
57    let c3_tag = solver.new_constraint_tag();
58
59    let variables = (0..n)
60        .map(|i| solver.new_named_bounded_integer(0, n as i32 - 1, format!("q{i}")))
61        .collect::<Vec<_>>();
62
63    let _ = solver
64        .add_constraint(constraints::all_different(variables.clone(), c1_tag))
65        .post();
66
67    let diag1 = variables
68        .iter()
69        .cloned()
70        .enumerate()
71        .map(|(i, var)| var.offset(i as i32))
72        .collect::<Vec<_>>();
73    let diag2 = variables
74        .iter()
75        .cloned()
76        .enumerate()
77        .map(|(i, var)| var.offset(-(i as i32)))
78        .collect::<Vec<_>>();
79
80    let _ = solver
81        .add_constraint(constraints::all_different(diag1, c2_tag))
82        .post();
83    let _ = solver
84        .add_constraint(constraints::all_different(diag2, c3_tag))
85        .post();
86
87    let mut brancher = solver.default_brancher();
88    match solver.satisfy(&mut brancher, &mut Indefinite) {
89        SatisfactionResult::Satisfiable(satisfiable) => {
90            let solution = satisfiable.solution();
91
92            let row_separator = format!("{}+", "+---".repeat(n as usize));
93
94            for row in 0..n {
95                println!("{row_separator}");
96
97                let queen_col = solution.get_integer_value(variables[row as usize]) as u32;
98
99                for col in 0..n {
100                    let string = if queen_col == col { "| * " } else { "|   " };
101
102                    print!("{string}");
103                }
104
105                println!("|");
106            }
107
108            println!("{row_separator}");
109        }
110        SatisfactionResult::Unsatisfiable(_, _) => {
111            println!("{n}-queens is unsatisfiable.");
112        }
113        SatisfactionResult::Unknown(_, _) => {
114            println!("Timeout.");
115        }
116    };
117}
Source

pub fn new_sparse_integer(&mut self, values: impl Into<Vec<i32>>) -> DomainId

Create a new integer variable which has a domain of predefined values. We remove duplicates by converting to a hash set

§Example
let mut solver = Solver::default();

// We can also create an integer variable with a non-continuous domain in the follow way
let mut sparse_integer = solver.new_sparse_integer(vec![0, 3, 5]);
Source

pub fn new_named_sparse_integer( &mut self, values: impl Into<Vec<i32>>, name: impl Into<String>, ) -> DomainId

Create a new named integer variable which has a domain of predefined values.

§Example
let mut solver = Solver::default();

// We can also create such a variable with a name
let named_sparse_integer = solver.new_named_sparse_integer(vec![0, 3, 5], "y");
Source§

impl Solver

Functions for solving with the constraints that have been added to the Solver.

Source

pub fn satisfy<'this, 'brancher, B, T>( &'this mut self, brancher: &'brancher mut B, termination: &mut T, ) -> SatisfactionResult<'this, 'brancher, B>

Solves the current model in the Solver until it finds a solution (or is indicated to terminate by the provided TerminationCondition) and returns a SatisfactionResult which can be used to obtain the found solution or find other solutions.

Examples found in repository?
examples/nqueens.rs (line 88)
25fn main() {
26    let Cli {
27        n,
28        proof: proof_path,
29    } = Cli::parse();
30
31    if n < 2 {
32        println!("Please provide an 'n > 1'");
33        return;
34    }
35
36    let Ok(proof_log) = proof_path
37        .as_ref()
38        .map(|path| ProofLog::cp(path, true))
39        .transpose()
40        .map(|proof| proof.unwrap_or_default())
41    else {
42        eprintln!(
43            "Failed to create proof file at {}",
44            proof_path.unwrap().display()
45        );
46        return;
47    };
48
49    let mut solver = Solver::with_options(SolverOptions {
50        proof_log,
51        ..Default::default()
52    });
53
54    // Create the constraint tags for the three all_different constraints.
55    let c1_tag = solver.new_constraint_tag();
56    let c2_tag = solver.new_constraint_tag();
57    let c3_tag = solver.new_constraint_tag();
58
59    let variables = (0..n)
60        .map(|i| solver.new_named_bounded_integer(0, n as i32 - 1, format!("q{i}")))
61        .collect::<Vec<_>>();
62
63    let _ = solver
64        .add_constraint(constraints::all_different(variables.clone(), c1_tag))
65        .post();
66
67    let diag1 = variables
68        .iter()
69        .cloned()
70        .enumerate()
71        .map(|(i, var)| var.offset(i as i32))
72        .collect::<Vec<_>>();
73    let diag2 = variables
74        .iter()
75        .cloned()
76        .enumerate()
77        .map(|(i, var)| var.offset(-(i as i32)))
78        .collect::<Vec<_>>();
79
80    let _ = solver
81        .add_constraint(constraints::all_different(diag1, c2_tag))
82        .post();
83    let _ = solver
84        .add_constraint(constraints::all_different(diag2, c3_tag))
85        .post();
86
87    let mut brancher = solver.default_brancher();
88    match solver.satisfy(&mut brancher, &mut Indefinite) {
89        SatisfactionResult::Satisfiable(satisfiable) => {
90            let solution = satisfiable.solution();
91
92            let row_separator = format!("{}+", "+---".repeat(n as usize));
93
94            for row in 0..n {
95                println!("{row_separator}");
96
97                let queen_col = solution.get_integer_value(variables[row as usize]) as u32;
98
99                for col in 0..n {
100                    let string = if queen_col == col { "| * " } else { "|   " };
101
102                    print!("{string}");
103                }
104
105                println!("|");
106            }
107
108            println!("{row_separator}");
109        }
110        SatisfactionResult::Unsatisfiable(_, _) => {
111            println!("{n}-queens is unsatisfiable.");
112        }
113        SatisfactionResult::Unknown(_, _) => {
114            println!("Timeout.");
115        }
116    };
117}
More examples
Hide additional examples
examples/bibd.rs (line 149)
75fn main() {
76    env_logger::init();
77
78    let Some(bibd) = Bibd::from_args() else {
79        eprintln!("Usage: {} <v> <k> <l>", std::env::args().next().unwrap());
80        return;
81    };
82
83    println!(
84        "bibd: (v = {}, b = {}, r = {}, k = {}, l = {})",
85        bibd.rows, bibd.columns, bibd.row_sum, bibd.column_sum, bibd.max_dot_product
86    );
87
88    let mut solver = Solver::default();
89
90    // Creates a dummy constraint tag; since this example does not support proof logging the
91    // constraint tag does not matter.
92    let constraint_tag = solver.new_constraint_tag();
93
94    // Create 0-1 integer variables that make up the matrix.
95    let matrix = create_matrix(&mut solver, &bibd);
96
97    // Enforce the row sum.
98    for row in matrix.iter() {
99        let _ = solver
100            .add_constraint(constraints::equals(
101                row.clone(),
102                bibd.row_sum as i32,
103                constraint_tag,
104            ))
105            .post();
106    }
107
108    // Enforce the column sum.
109    for row in transpose(&matrix) {
110        let _ = solver
111            .add_constraint(constraints::equals(
112                row,
113                bibd.column_sum as i32,
114                constraint_tag,
115            ))
116            .post();
117    }
118
119    // Enforce the dot product constraint.
120    // pairwise_product[r1][r2][col] = matrix[r1][col] * matrix[r2][col]
121    let pairwise_product = (0..bibd.rows)
122        .map(|_| create_matrix(&mut solver, &bibd))
123        .collect::<Vec<_>>();
124
125    for r1 in 0..bibd.rows as usize {
126        for r2 in r1 + 1..bibd.rows as usize {
127            for col in 0..bibd.columns as usize {
128                let _ = solver
129                    .add_constraint(constraints::times(
130                        matrix[r1][col],
131                        matrix[r2][col],
132                        pairwise_product[r1][r2][col],
133                        constraint_tag,
134                    ))
135                    .post();
136            }
137
138            let _ = solver
139                .add_constraint(constraints::less_than_or_equals(
140                    pairwise_product[r1][r2].clone(),
141                    bibd.max_dot_product as i32,
142                    constraint_tag,
143                ))
144                .post();
145        }
146    }
147
148    let mut brancher = solver.default_brancher();
149    match solver.satisfy(&mut brancher, &mut Indefinite) {
150        SatisfactionResult::Satisfiable(satisfiable) => {
151            let solution = satisfiable.solution();
152
153            let row_separator = format!("{}+", "+---".repeat(bibd.columns as usize));
154
155            for row in matrix.iter() {
156                let line = row
157                    .iter()
158                    .map(|var| {
159                        if solution.get_integer_value(*var) == 1 {
160                            String::from("| * ")
161                        } else {
162                            String::from("|   ")
163                        }
164                    })
165                    .collect::<String>();
166
167                println!("{row_separator}\n{line}|");
168            }
169
170            println!("{row_separator}");
171        }
172        SatisfactionResult::Unsatisfiable(_, _) => {
173            println!("UNSATISFIABLE")
174        }
175        SatisfactionResult::Unknown(_, _) => {
176            println!("UNKNOWN")
177        }
178    };
179}
examples/disjunctive_scheduling.rs (line 92)
20fn main() {
21    let mut args = std::env::args();
22
23    let n_tasks = args
24        .nth(1)
25        .expect("Please provide a number of tasks")
26        .parse::<usize>()
27        .expect("Not a valid usized");
28    let processing_times = args
29        .take(n_tasks)
30        .map(|arg| arg.parse::<usize>())
31        .collect::<Result<Vec<_>, _>>()
32        .expect("The provided processing times are not valid unsigned integers");
33    assert_eq!(
34        processing_times.len(),
35        n_tasks,
36        "Provided fewer than `n_tasks` processing times."
37    );
38
39    let horizon = processing_times.iter().sum::<usize>();
40
41    let mut solver = Solver::default();
42
43    // Creates a dummy constraint tag; since this example does not support proof logging the
44    // constraint tag does not matter.
45    let constraint_tag = solver.new_constraint_tag();
46
47    let start_variables = (0..n_tasks)
48        .map(|i| solver.new_bounded_integer(0, (horizon - processing_times[i]) as i32))
49        .collect::<Vec<_>>();
50
51    // Literal which indicates precedence (i.e. precedence_literals[x][y] <=> x ends before y
52    // starts)
53    let precedence_literals = (0..n_tasks)
54        .map(|_| {
55            (0..n_tasks)
56                .map(|_| solver.new_literal())
57                .collect::<Vec<_>>()
58        })
59        .collect::<Vec<_>>();
60
61    for x in 0..n_tasks {
62        for y in 0..n_tasks {
63            if x == y {
64                continue;
65            }
66            // precedence_literals[x][y] <=> x ends before y starts
67            let literal = precedence_literals[x][y];
68            // literal <=> (s_x + p_x <= s_y)
69            // equivelent to literal <=> (s_x - s_y <= -p_x)
70            // So the variables are -s_y and s_x, and the rhs is -p_x
71            let variables = vec![start_variables[y].scaled(-1), start_variables[x].scaled(1)];
72            let _ = constraints::less_than_or_equals(
73                variables,
74                -(processing_times[x] as i32),
75                constraint_tag,
76            )
77            .reify(&mut solver, literal);
78
79            // Either x starts before y or y start before x
80            let _ = solver.add_clause(
81                [
82                    literal.get_true_predicate(),
83                    precedence_literals[y][x].get_true_predicate(),
84                ],
85                constraint_tag,
86            );
87        }
88    }
89
90    let mut brancher = solver.default_brancher();
91    if matches!(
92        solver.satisfy(&mut brancher, &mut Indefinite),
93        SatisfactionResult::Unsatisfiable(_, _),
94    ) {
95        panic!("Infeasibility Detected")
96    }
97    match solver.satisfy(&mut brancher, &mut Indefinite) {
98        SatisfactionResult::Satisfiable(satisfiable) => {
99            let solution = satisfiable.solution();
100
101            let mut start_variables_and_processing_times = start_variables
102                .iter()
103                .zip(processing_times)
104                .collect::<Vec<_>>();
105            start_variables_and_processing_times.sort_by(|(s1, _), (s2, _)| {
106                solution
107                    .get_integer_value(**s1)
108                    .cmp(&solution.get_integer_value(**s2))
109            });
110
111            println!(
112                "{}",
113                start_variables_and_processing_times
114                    .iter()
115                    .map(|(var, processing_time)| format!(
116                        "[{}, {}]",
117                        solution.get_integer_value(**var),
118                        solution.get_integer_value(**var) + *processing_time as i32
119                    ))
120                    .collect::<Vec<_>>()
121                    .join(" - ")
122            );
123        }
124        SatisfactionResult::Unsatisfiable(_, _) => panic!("Infeasibility Detected"),
125        SatisfactionResult::Unknown(_, _) => println!("Timeout."),
126    };
127}
Source

pub fn get_solution_iterator<'this, 'brancher, 'termination, B, T>( &'this mut self, brancher: &'brancher mut B, termination: &'termination mut T, ) -> SolutionIterator<'this, 'brancher, 'termination, B, T>

Source

pub fn satisfy_under_assumptions<'this, 'brancher, B, T>( &'this mut self, brancher: &'brancher mut B, termination: &mut T, assumptions: &[Predicate], ) -> SatisfactionResultUnderAssumptions<'this, 'brancher, B>

Solves the current model in the Solver until it finds a solution (or is indicated to terminate by the provided TerminationCondition) and returns a SatisfactionResult which can be used to obtain the found solution or find other solutions.

This method takes as input a list of Predicates which represent so-called assumptions (see [1] for a more detailed explanation). See the [predicates] documentation for how to construct these predicates.

§Bibliography

[1] N. Eén and N. Sörensson, ‘Temporal induction by incremental SAT solving’, Electronic Notes in Theoretical Computer Science, vol. 89, no. 4, pp. 543–560, 2003.

Source

pub fn optimise<B, Callback>( &mut self, brancher: &mut B, termination: &mut impl TerminationCondition, optimisation_procedure: impl OptimisationProcedure<B, Callback>, ) -> OptimisationResult
where B: Brancher, Callback: SolutionCallback<B>,

Solves the model currently in the Solver to optimality where the provided objective_variable is optimised as indicated by the direction (or is indicated to terminate by the provided TerminationCondition). Uses a search strategy based on the provided OptimisationProcedure, currently [LinearSatUnsat] and [LinearUnsatSat] are supported.

It returns an OptimisationResult which can be used to retrieve the optimal solution if it exists.

Source§

impl Solver

Functions for adding new constraints to the solver.

Source

pub fn new_constraint_tag(&mut self) -> ConstraintTag

Creates a new ConstraintTag that can be used to add constraints to the solver.

See the ConstraintTag documentation for information on how the tags are used.

Examples found in repository?
examples/nqueens.rs (line 55)
25fn main() {
26    let Cli {
27        n,
28        proof: proof_path,
29    } = Cli::parse();
30
31    if n < 2 {
32        println!("Please provide an 'n > 1'");
33        return;
34    }
35
36    let Ok(proof_log) = proof_path
37        .as_ref()
38        .map(|path| ProofLog::cp(path, true))
39        .transpose()
40        .map(|proof| proof.unwrap_or_default())
41    else {
42        eprintln!(
43            "Failed to create proof file at {}",
44            proof_path.unwrap().display()
45        );
46        return;
47    };
48
49    let mut solver = Solver::with_options(SolverOptions {
50        proof_log,
51        ..Default::default()
52    });
53
54    // Create the constraint tags for the three all_different constraints.
55    let c1_tag = solver.new_constraint_tag();
56    let c2_tag = solver.new_constraint_tag();
57    let c3_tag = solver.new_constraint_tag();
58
59    let variables = (0..n)
60        .map(|i| solver.new_named_bounded_integer(0, n as i32 - 1, format!("q{i}")))
61        .collect::<Vec<_>>();
62
63    let _ = solver
64        .add_constraint(constraints::all_different(variables.clone(), c1_tag))
65        .post();
66
67    let diag1 = variables
68        .iter()
69        .cloned()
70        .enumerate()
71        .map(|(i, var)| var.offset(i as i32))
72        .collect::<Vec<_>>();
73    let diag2 = variables
74        .iter()
75        .cloned()
76        .enumerate()
77        .map(|(i, var)| var.offset(-(i as i32)))
78        .collect::<Vec<_>>();
79
80    let _ = solver
81        .add_constraint(constraints::all_different(diag1, c2_tag))
82        .post();
83    let _ = solver
84        .add_constraint(constraints::all_different(diag2, c3_tag))
85        .post();
86
87    let mut brancher = solver.default_brancher();
88    match solver.satisfy(&mut brancher, &mut Indefinite) {
89        SatisfactionResult::Satisfiable(satisfiable) => {
90            let solution = satisfiable.solution();
91
92            let row_separator = format!("{}+", "+---".repeat(n as usize));
93
94            for row in 0..n {
95                println!("{row_separator}");
96
97                let queen_col = solution.get_integer_value(variables[row as usize]) as u32;
98
99                for col in 0..n {
100                    let string = if queen_col == col { "| * " } else { "|   " };
101
102                    print!("{string}");
103                }
104
105                println!("|");
106            }
107
108            println!("{row_separator}");
109        }
110        SatisfactionResult::Unsatisfiable(_, _) => {
111            println!("{n}-queens is unsatisfiable.");
112        }
113        SatisfactionResult::Unknown(_, _) => {
114            println!("Timeout.");
115        }
116    };
117}
More examples
Hide additional examples
examples/bibd.rs (line 92)
75fn main() {
76    env_logger::init();
77
78    let Some(bibd) = Bibd::from_args() else {
79        eprintln!("Usage: {} <v> <k> <l>", std::env::args().next().unwrap());
80        return;
81    };
82
83    println!(
84        "bibd: (v = {}, b = {}, r = {}, k = {}, l = {})",
85        bibd.rows, bibd.columns, bibd.row_sum, bibd.column_sum, bibd.max_dot_product
86    );
87
88    let mut solver = Solver::default();
89
90    // Creates a dummy constraint tag; since this example does not support proof logging the
91    // constraint tag does not matter.
92    let constraint_tag = solver.new_constraint_tag();
93
94    // Create 0-1 integer variables that make up the matrix.
95    let matrix = create_matrix(&mut solver, &bibd);
96
97    // Enforce the row sum.
98    for row in matrix.iter() {
99        let _ = solver
100            .add_constraint(constraints::equals(
101                row.clone(),
102                bibd.row_sum as i32,
103                constraint_tag,
104            ))
105            .post();
106    }
107
108    // Enforce the column sum.
109    for row in transpose(&matrix) {
110        let _ = solver
111            .add_constraint(constraints::equals(
112                row,
113                bibd.column_sum as i32,
114                constraint_tag,
115            ))
116            .post();
117    }
118
119    // Enforce the dot product constraint.
120    // pairwise_product[r1][r2][col] = matrix[r1][col] * matrix[r2][col]
121    let pairwise_product = (0..bibd.rows)
122        .map(|_| create_matrix(&mut solver, &bibd))
123        .collect::<Vec<_>>();
124
125    for r1 in 0..bibd.rows as usize {
126        for r2 in r1 + 1..bibd.rows as usize {
127            for col in 0..bibd.columns as usize {
128                let _ = solver
129                    .add_constraint(constraints::times(
130                        matrix[r1][col],
131                        matrix[r2][col],
132                        pairwise_product[r1][r2][col],
133                        constraint_tag,
134                    ))
135                    .post();
136            }
137
138            let _ = solver
139                .add_constraint(constraints::less_than_or_equals(
140                    pairwise_product[r1][r2].clone(),
141                    bibd.max_dot_product as i32,
142                    constraint_tag,
143                ))
144                .post();
145        }
146    }
147
148    let mut brancher = solver.default_brancher();
149    match solver.satisfy(&mut brancher, &mut Indefinite) {
150        SatisfactionResult::Satisfiable(satisfiable) => {
151            let solution = satisfiable.solution();
152
153            let row_separator = format!("{}+", "+---".repeat(bibd.columns as usize));
154
155            for row in matrix.iter() {
156                let line = row
157                    .iter()
158                    .map(|var| {
159                        if solution.get_integer_value(*var) == 1 {
160                            String::from("| * ")
161                        } else {
162                            String::from("|   ")
163                        }
164                    })
165                    .collect::<String>();
166
167                println!("{row_separator}\n{line}|");
168            }
169
170            println!("{row_separator}");
171        }
172        SatisfactionResult::Unsatisfiable(_, _) => {
173            println!("UNSATISFIABLE")
174        }
175        SatisfactionResult::Unknown(_, _) => {
176            println!("UNKNOWN")
177        }
178    };
179}
examples/disjunctive_scheduling.rs (line 45)
20fn main() {
21    let mut args = std::env::args();
22
23    let n_tasks = args
24        .nth(1)
25        .expect("Please provide a number of tasks")
26        .parse::<usize>()
27        .expect("Not a valid usized");
28    let processing_times = args
29        .take(n_tasks)
30        .map(|arg| arg.parse::<usize>())
31        .collect::<Result<Vec<_>, _>>()
32        .expect("The provided processing times are not valid unsigned integers");
33    assert_eq!(
34        processing_times.len(),
35        n_tasks,
36        "Provided fewer than `n_tasks` processing times."
37    );
38
39    let horizon = processing_times.iter().sum::<usize>();
40
41    let mut solver = Solver::default();
42
43    // Creates a dummy constraint tag; since this example does not support proof logging the
44    // constraint tag does not matter.
45    let constraint_tag = solver.new_constraint_tag();
46
47    let start_variables = (0..n_tasks)
48        .map(|i| solver.new_bounded_integer(0, (horizon - processing_times[i]) as i32))
49        .collect::<Vec<_>>();
50
51    // Literal which indicates precedence (i.e. precedence_literals[x][y] <=> x ends before y
52    // starts)
53    let precedence_literals = (0..n_tasks)
54        .map(|_| {
55            (0..n_tasks)
56                .map(|_| solver.new_literal())
57                .collect::<Vec<_>>()
58        })
59        .collect::<Vec<_>>();
60
61    for x in 0..n_tasks {
62        for y in 0..n_tasks {
63            if x == y {
64                continue;
65            }
66            // precedence_literals[x][y] <=> x ends before y starts
67            let literal = precedence_literals[x][y];
68            // literal <=> (s_x + p_x <= s_y)
69            // equivelent to literal <=> (s_x - s_y <= -p_x)
70            // So the variables are -s_y and s_x, and the rhs is -p_x
71            let variables = vec![start_variables[y].scaled(-1), start_variables[x].scaled(1)];
72            let _ = constraints::less_than_or_equals(
73                variables,
74                -(processing_times[x] as i32),
75                constraint_tag,
76            )
77            .reify(&mut solver, literal);
78
79            // Either x starts before y or y start before x
80            let _ = solver.add_clause(
81                [
82                    literal.get_true_predicate(),
83                    precedence_literals[y][x].get_true_predicate(),
84                ],
85                constraint_tag,
86            );
87        }
88    }
89
90    let mut brancher = solver.default_brancher();
91    if matches!(
92        solver.satisfy(&mut brancher, &mut Indefinite),
93        SatisfactionResult::Unsatisfiable(_, _),
94    ) {
95        panic!("Infeasibility Detected")
96    }
97    match solver.satisfy(&mut brancher, &mut Indefinite) {
98        SatisfactionResult::Satisfiable(satisfiable) => {
99            let solution = satisfiable.solution();
100
101            let mut start_variables_and_processing_times = start_variables
102                .iter()
103                .zip(processing_times)
104                .collect::<Vec<_>>();
105            start_variables_and_processing_times.sort_by(|(s1, _), (s2, _)| {
106                solution
107                    .get_integer_value(**s1)
108                    .cmp(&solution.get_integer_value(**s2))
109            });
110
111            println!(
112                "{}",
113                start_variables_and_processing_times
114                    .iter()
115                    .map(|(var, processing_time)| format!(
116                        "[{}, {}]",
117                        solution.get_integer_value(**var),
118                        solution.get_integer_value(**var) + *processing_time as i32
119                    ))
120                    .collect::<Vec<_>>()
121                    .join(" - ")
122            );
123        }
124        SatisfactionResult::Unsatisfiable(_, _) => panic!("Infeasibility Detected"),
125        SatisfactionResult::Unknown(_, _) => println!("Timeout."),
126    };
127}
Source

pub fn add_constraint<Constraint>( &mut self, constraint: Constraint, ) -> ConstraintPoster<'_, Constraint>

Add a constraint to the solver. This returns a ConstraintPoster which enables control on whether to add the constraint as-is, or whether to (half) reify it.

All constraints require a ConstraintTag to be supplied. See its documentation for more information.

If none of the methods on ConstraintPoster are used, the constraint is not actually added to the solver. In this case, a warning is emitted.

§Example
let mut solver = Solver::default();

let a = solver.new_bounded_integer(0, 3);
let b = solver.new_bounded_integer(0, 3);

let constraint_tag = solver.new_constraint_tag();

solver
    .add_constraint(constraints::equals([a, b], 0, constraint_tag))
    .post();
Examples found in repository?
examples/nqueens.rs (line 64)
25fn main() {
26    let Cli {
27        n,
28        proof: proof_path,
29    } = Cli::parse();
30
31    if n < 2 {
32        println!("Please provide an 'n > 1'");
33        return;
34    }
35
36    let Ok(proof_log) = proof_path
37        .as_ref()
38        .map(|path| ProofLog::cp(path, true))
39        .transpose()
40        .map(|proof| proof.unwrap_or_default())
41    else {
42        eprintln!(
43            "Failed to create proof file at {}",
44            proof_path.unwrap().display()
45        );
46        return;
47    };
48
49    let mut solver = Solver::with_options(SolverOptions {
50        proof_log,
51        ..Default::default()
52    });
53
54    // Create the constraint tags for the three all_different constraints.
55    let c1_tag = solver.new_constraint_tag();
56    let c2_tag = solver.new_constraint_tag();
57    let c3_tag = solver.new_constraint_tag();
58
59    let variables = (0..n)
60        .map(|i| solver.new_named_bounded_integer(0, n as i32 - 1, format!("q{i}")))
61        .collect::<Vec<_>>();
62
63    let _ = solver
64        .add_constraint(constraints::all_different(variables.clone(), c1_tag))
65        .post();
66
67    let diag1 = variables
68        .iter()
69        .cloned()
70        .enumerate()
71        .map(|(i, var)| var.offset(i as i32))
72        .collect::<Vec<_>>();
73    let diag2 = variables
74        .iter()
75        .cloned()
76        .enumerate()
77        .map(|(i, var)| var.offset(-(i as i32)))
78        .collect::<Vec<_>>();
79
80    let _ = solver
81        .add_constraint(constraints::all_different(diag1, c2_tag))
82        .post();
83    let _ = solver
84        .add_constraint(constraints::all_different(diag2, c3_tag))
85        .post();
86
87    let mut brancher = solver.default_brancher();
88    match solver.satisfy(&mut brancher, &mut Indefinite) {
89        SatisfactionResult::Satisfiable(satisfiable) => {
90            let solution = satisfiable.solution();
91
92            let row_separator = format!("{}+", "+---".repeat(n as usize));
93
94            for row in 0..n {
95                println!("{row_separator}");
96
97                let queen_col = solution.get_integer_value(variables[row as usize]) as u32;
98
99                for col in 0..n {
100                    let string = if queen_col == col { "| * " } else { "|   " };
101
102                    print!("{string}");
103                }
104
105                println!("|");
106            }
107
108            println!("{row_separator}");
109        }
110        SatisfactionResult::Unsatisfiable(_, _) => {
111            println!("{n}-queens is unsatisfiable.");
112        }
113        SatisfactionResult::Unknown(_, _) => {
114            println!("Timeout.");
115        }
116    };
117}
More examples
Hide additional examples
examples/bibd.rs (lines 100-104)
75fn main() {
76    env_logger::init();
77
78    let Some(bibd) = Bibd::from_args() else {
79        eprintln!("Usage: {} <v> <k> <l>", std::env::args().next().unwrap());
80        return;
81    };
82
83    println!(
84        "bibd: (v = {}, b = {}, r = {}, k = {}, l = {})",
85        bibd.rows, bibd.columns, bibd.row_sum, bibd.column_sum, bibd.max_dot_product
86    );
87
88    let mut solver = Solver::default();
89
90    // Creates a dummy constraint tag; since this example does not support proof logging the
91    // constraint tag does not matter.
92    let constraint_tag = solver.new_constraint_tag();
93
94    // Create 0-1 integer variables that make up the matrix.
95    let matrix = create_matrix(&mut solver, &bibd);
96
97    // Enforce the row sum.
98    for row in matrix.iter() {
99        let _ = solver
100            .add_constraint(constraints::equals(
101                row.clone(),
102                bibd.row_sum as i32,
103                constraint_tag,
104            ))
105            .post();
106    }
107
108    // Enforce the column sum.
109    for row in transpose(&matrix) {
110        let _ = solver
111            .add_constraint(constraints::equals(
112                row,
113                bibd.column_sum as i32,
114                constraint_tag,
115            ))
116            .post();
117    }
118
119    // Enforce the dot product constraint.
120    // pairwise_product[r1][r2][col] = matrix[r1][col] * matrix[r2][col]
121    let pairwise_product = (0..bibd.rows)
122        .map(|_| create_matrix(&mut solver, &bibd))
123        .collect::<Vec<_>>();
124
125    for r1 in 0..bibd.rows as usize {
126        for r2 in r1 + 1..bibd.rows as usize {
127            for col in 0..bibd.columns as usize {
128                let _ = solver
129                    .add_constraint(constraints::times(
130                        matrix[r1][col],
131                        matrix[r2][col],
132                        pairwise_product[r1][r2][col],
133                        constraint_tag,
134                    ))
135                    .post();
136            }
137
138            let _ = solver
139                .add_constraint(constraints::less_than_or_equals(
140                    pairwise_product[r1][r2].clone(),
141                    bibd.max_dot_product as i32,
142                    constraint_tag,
143                ))
144                .post();
145        }
146    }
147
148    let mut brancher = solver.default_brancher();
149    match solver.satisfy(&mut brancher, &mut Indefinite) {
150        SatisfactionResult::Satisfiable(satisfiable) => {
151            let solution = satisfiable.solution();
152
153            let row_separator = format!("{}+", "+---".repeat(bibd.columns as usize));
154
155            for row in matrix.iter() {
156                let line = row
157                    .iter()
158                    .map(|var| {
159                        if solution.get_integer_value(*var) == 1 {
160                            String::from("| * ")
161                        } else {
162                            String::from("|   ")
163                        }
164                    })
165                    .collect::<String>();
166
167                println!("{row_separator}\n{line}|");
168            }
169
170            println!("{row_separator}");
171        }
172        SatisfactionResult::Unsatisfiable(_, _) => {
173            println!("UNSATISFIABLE")
174        }
175        SatisfactionResult::Unknown(_, _) => {
176            println!("UNKNOWN")
177        }
178    };
179}
Source

pub fn add_clause( &mut self, clause: impl IntoIterator<Item = Predicate>, constraint_tag: ConstraintTag, ) -> Result<(), ConstraintOperationError>

Creates a clause from literals and adds it to the current formula.

If the formula becomes trivially unsatisfiable, a ConstraintOperationError will be returned. Subsequent calls to this method will always return an error, and no modification of the solver will take place.

Examples found in repository?
examples/disjunctive_scheduling.rs (lines 80-86)
20fn main() {
21    let mut args = std::env::args();
22
23    let n_tasks = args
24        .nth(1)
25        .expect("Please provide a number of tasks")
26        .parse::<usize>()
27        .expect("Not a valid usized");
28    let processing_times = args
29        .take(n_tasks)
30        .map(|arg| arg.parse::<usize>())
31        .collect::<Result<Vec<_>, _>>()
32        .expect("The provided processing times are not valid unsigned integers");
33    assert_eq!(
34        processing_times.len(),
35        n_tasks,
36        "Provided fewer than `n_tasks` processing times."
37    );
38
39    let horizon = processing_times.iter().sum::<usize>();
40
41    let mut solver = Solver::default();
42
43    // Creates a dummy constraint tag; since this example does not support proof logging the
44    // constraint tag does not matter.
45    let constraint_tag = solver.new_constraint_tag();
46
47    let start_variables = (0..n_tasks)
48        .map(|i| solver.new_bounded_integer(0, (horizon - processing_times[i]) as i32))
49        .collect::<Vec<_>>();
50
51    // Literal which indicates precedence (i.e. precedence_literals[x][y] <=> x ends before y
52    // starts)
53    let precedence_literals = (0..n_tasks)
54        .map(|_| {
55            (0..n_tasks)
56                .map(|_| solver.new_literal())
57                .collect::<Vec<_>>()
58        })
59        .collect::<Vec<_>>();
60
61    for x in 0..n_tasks {
62        for y in 0..n_tasks {
63            if x == y {
64                continue;
65            }
66            // precedence_literals[x][y] <=> x ends before y starts
67            let literal = precedence_literals[x][y];
68            // literal <=> (s_x + p_x <= s_y)
69            // equivelent to literal <=> (s_x - s_y <= -p_x)
70            // So the variables are -s_y and s_x, and the rhs is -p_x
71            let variables = vec![start_variables[y].scaled(-1), start_variables[x].scaled(1)];
72            let _ = constraints::less_than_or_equals(
73                variables,
74                -(processing_times[x] as i32),
75                constraint_tag,
76            )
77            .reify(&mut solver, literal);
78
79            // Either x starts before y or y start before x
80            let _ = solver.add_clause(
81                [
82                    literal.get_true_predicate(),
83                    precedence_literals[y][x].get_true_predicate(),
84                ],
85                constraint_tag,
86            );
87        }
88    }
89
90    let mut brancher = solver.default_brancher();
91    if matches!(
92        solver.satisfy(&mut brancher, &mut Indefinite),
93        SatisfactionResult::Unsatisfiable(_, _),
94    ) {
95        panic!("Infeasibility Detected")
96    }
97    match solver.satisfy(&mut brancher, &mut Indefinite) {
98        SatisfactionResult::Satisfiable(satisfiable) => {
99            let solution = satisfiable.solution();
100
101            let mut start_variables_and_processing_times = start_variables
102                .iter()
103                .zip(processing_times)
104                .collect::<Vec<_>>();
105            start_variables_and_processing_times.sort_by(|(s1, _), (s2, _)| {
106                solution
107                    .get_integer_value(**s1)
108                    .cmp(&solution.get_integer_value(**s2))
109            });
110
111            println!(
112                "{}",
113                start_variables_and_processing_times
114                    .iter()
115                    .map(|(var, processing_time)| format!(
116                        "[{}, {}]",
117                        solution.get_integer_value(**var),
118                        solution.get_integer_value(**var) + *processing_time as i32
119                    ))
120                    .collect::<Vec<_>>()
121                    .join(" - ")
122            );
123        }
124        SatisfactionResult::Unsatisfiable(_, _) => panic!("Infeasibility Detected"),
125        SatisfactionResult::Unknown(_, _) => println!("Timeout."),
126    };
127}
Source§

impl Solver

Default brancher implementation

Source

pub fn default_brancher( &self, ) -> AutonomousSearch<IndependentVariableValueBrancher<DomainId, RandomSelector, RandomSplitter>>

Creates an instance of the DefaultBrancher.

Examples found in repository?
examples/nqueens.rs (line 87)
25fn main() {
26    let Cli {
27        n,
28        proof: proof_path,
29    } = Cli::parse();
30
31    if n < 2 {
32        println!("Please provide an 'n > 1'");
33        return;
34    }
35
36    let Ok(proof_log) = proof_path
37        .as_ref()
38        .map(|path| ProofLog::cp(path, true))
39        .transpose()
40        .map(|proof| proof.unwrap_or_default())
41    else {
42        eprintln!(
43            "Failed to create proof file at {}",
44            proof_path.unwrap().display()
45        );
46        return;
47    };
48
49    let mut solver = Solver::with_options(SolverOptions {
50        proof_log,
51        ..Default::default()
52    });
53
54    // Create the constraint tags for the three all_different constraints.
55    let c1_tag = solver.new_constraint_tag();
56    let c2_tag = solver.new_constraint_tag();
57    let c3_tag = solver.new_constraint_tag();
58
59    let variables = (0..n)
60        .map(|i| solver.new_named_bounded_integer(0, n as i32 - 1, format!("q{i}")))
61        .collect::<Vec<_>>();
62
63    let _ = solver
64        .add_constraint(constraints::all_different(variables.clone(), c1_tag))
65        .post();
66
67    let diag1 = variables
68        .iter()
69        .cloned()
70        .enumerate()
71        .map(|(i, var)| var.offset(i as i32))
72        .collect::<Vec<_>>();
73    let diag2 = variables
74        .iter()
75        .cloned()
76        .enumerate()
77        .map(|(i, var)| var.offset(-(i as i32)))
78        .collect::<Vec<_>>();
79
80    let _ = solver
81        .add_constraint(constraints::all_different(diag1, c2_tag))
82        .post();
83    let _ = solver
84        .add_constraint(constraints::all_different(diag2, c3_tag))
85        .post();
86
87    let mut brancher = solver.default_brancher();
88    match solver.satisfy(&mut brancher, &mut Indefinite) {
89        SatisfactionResult::Satisfiable(satisfiable) => {
90            let solution = satisfiable.solution();
91
92            let row_separator = format!("{}+", "+---".repeat(n as usize));
93
94            for row in 0..n {
95                println!("{row_separator}");
96
97                let queen_col = solution.get_integer_value(variables[row as usize]) as u32;
98
99                for col in 0..n {
100                    let string = if queen_col == col { "| * " } else { "|   " };
101
102                    print!("{string}");
103                }
104
105                println!("|");
106            }
107
108            println!("{row_separator}");
109        }
110        SatisfactionResult::Unsatisfiable(_, _) => {
111            println!("{n}-queens is unsatisfiable.");
112        }
113        SatisfactionResult::Unknown(_, _) => {
114            println!("Timeout.");
115        }
116    };
117}
More examples
Hide additional examples
examples/bibd.rs (line 148)
75fn main() {
76    env_logger::init();
77
78    let Some(bibd) = Bibd::from_args() else {
79        eprintln!("Usage: {} <v> <k> <l>", std::env::args().next().unwrap());
80        return;
81    };
82
83    println!(
84        "bibd: (v = {}, b = {}, r = {}, k = {}, l = {})",
85        bibd.rows, bibd.columns, bibd.row_sum, bibd.column_sum, bibd.max_dot_product
86    );
87
88    let mut solver = Solver::default();
89
90    // Creates a dummy constraint tag; since this example does not support proof logging the
91    // constraint tag does not matter.
92    let constraint_tag = solver.new_constraint_tag();
93
94    // Create 0-1 integer variables that make up the matrix.
95    let matrix = create_matrix(&mut solver, &bibd);
96
97    // Enforce the row sum.
98    for row in matrix.iter() {
99        let _ = solver
100            .add_constraint(constraints::equals(
101                row.clone(),
102                bibd.row_sum as i32,
103                constraint_tag,
104            ))
105            .post();
106    }
107
108    // Enforce the column sum.
109    for row in transpose(&matrix) {
110        let _ = solver
111            .add_constraint(constraints::equals(
112                row,
113                bibd.column_sum as i32,
114                constraint_tag,
115            ))
116            .post();
117    }
118
119    // Enforce the dot product constraint.
120    // pairwise_product[r1][r2][col] = matrix[r1][col] * matrix[r2][col]
121    let pairwise_product = (0..bibd.rows)
122        .map(|_| create_matrix(&mut solver, &bibd))
123        .collect::<Vec<_>>();
124
125    for r1 in 0..bibd.rows as usize {
126        for r2 in r1 + 1..bibd.rows as usize {
127            for col in 0..bibd.columns as usize {
128                let _ = solver
129                    .add_constraint(constraints::times(
130                        matrix[r1][col],
131                        matrix[r2][col],
132                        pairwise_product[r1][r2][col],
133                        constraint_tag,
134                    ))
135                    .post();
136            }
137
138            let _ = solver
139                .add_constraint(constraints::less_than_or_equals(
140                    pairwise_product[r1][r2].clone(),
141                    bibd.max_dot_product as i32,
142                    constraint_tag,
143                ))
144                .post();
145        }
146    }
147
148    let mut brancher = solver.default_brancher();
149    match solver.satisfy(&mut brancher, &mut Indefinite) {
150        SatisfactionResult::Satisfiable(satisfiable) => {
151            let solution = satisfiable.solution();
152
153            let row_separator = format!("{}+", "+---".repeat(bibd.columns as usize));
154
155            for row in matrix.iter() {
156                let line = row
157                    .iter()
158                    .map(|var| {
159                        if solution.get_integer_value(*var) == 1 {
160                            String::from("| * ")
161                        } else {
162                            String::from("|   ")
163                        }
164                    })
165                    .collect::<String>();
166
167                println!("{row_separator}\n{line}|");
168            }
169
170            println!("{row_separator}");
171        }
172        SatisfactionResult::Unsatisfiable(_, _) => {
173            println!("UNSATISFIABLE")
174        }
175        SatisfactionResult::Unknown(_, _) => {
176            println!("UNKNOWN")
177        }
178    };
179}
examples/disjunctive_scheduling.rs (line 90)
20fn main() {
21    let mut args = std::env::args();
22
23    let n_tasks = args
24        .nth(1)
25        .expect("Please provide a number of tasks")
26        .parse::<usize>()
27        .expect("Not a valid usized");
28    let processing_times = args
29        .take(n_tasks)
30        .map(|arg| arg.parse::<usize>())
31        .collect::<Result<Vec<_>, _>>()
32        .expect("The provided processing times are not valid unsigned integers");
33    assert_eq!(
34        processing_times.len(),
35        n_tasks,
36        "Provided fewer than `n_tasks` processing times."
37    );
38
39    let horizon = processing_times.iter().sum::<usize>();
40
41    let mut solver = Solver::default();
42
43    // Creates a dummy constraint tag; since this example does not support proof logging the
44    // constraint tag does not matter.
45    let constraint_tag = solver.new_constraint_tag();
46
47    let start_variables = (0..n_tasks)
48        .map(|i| solver.new_bounded_integer(0, (horizon - processing_times[i]) as i32))
49        .collect::<Vec<_>>();
50
51    // Literal which indicates precedence (i.e. precedence_literals[x][y] <=> x ends before y
52    // starts)
53    let precedence_literals = (0..n_tasks)
54        .map(|_| {
55            (0..n_tasks)
56                .map(|_| solver.new_literal())
57                .collect::<Vec<_>>()
58        })
59        .collect::<Vec<_>>();
60
61    for x in 0..n_tasks {
62        for y in 0..n_tasks {
63            if x == y {
64                continue;
65            }
66            // precedence_literals[x][y] <=> x ends before y starts
67            let literal = precedence_literals[x][y];
68            // literal <=> (s_x + p_x <= s_y)
69            // equivelent to literal <=> (s_x - s_y <= -p_x)
70            // So the variables are -s_y and s_x, and the rhs is -p_x
71            let variables = vec![start_variables[y].scaled(-1), start_variables[x].scaled(1)];
72            let _ = constraints::less_than_or_equals(
73                variables,
74                -(processing_times[x] as i32),
75                constraint_tag,
76            )
77            .reify(&mut solver, literal);
78
79            // Either x starts before y or y start before x
80            let _ = solver.add_clause(
81                [
82                    literal.get_true_predicate(),
83                    precedence_literals[y][x].get_true_predicate(),
84                ],
85                constraint_tag,
86            );
87        }
88    }
89
90    let mut brancher = solver.default_brancher();
91    if matches!(
92        solver.satisfy(&mut brancher, &mut Indefinite),
93        SatisfactionResult::Unsatisfiable(_, _),
94    ) {
95        panic!("Infeasibility Detected")
96    }
97    match solver.satisfy(&mut brancher, &mut Indefinite) {
98        SatisfactionResult::Satisfiable(satisfiable) => {
99            let solution = satisfiable.solution();
100
101            let mut start_variables_and_processing_times = start_variables
102                .iter()
103                .zip(processing_times)
104                .collect::<Vec<_>>();
105            start_variables_and_processing_times.sort_by(|(s1, _), (s2, _)| {
106                solution
107                    .get_integer_value(**s1)
108                    .cmp(&solution.get_integer_value(**s2))
109            });
110
111            println!(
112                "{}",
113                start_variables_and_processing_times
114                    .iter()
115                    .map(|(var, processing_time)| format!(
116                        "[{}, {}]",
117                        solution.get_integer_value(**var),
118                        solution.get_integer_value(**var) + *processing_time as i32
119                    ))
120                    .collect::<Vec<_>>()
121                    .join(" - ")
122            );
123        }
124        SatisfactionResult::Unsatisfiable(_, _) => panic!("Infeasibility Detected"),
125        SatisfactionResult::Unknown(_, _) => println!("Timeout."),
126    };
127}
Source§

impl Solver

Proof logging methods

Source

pub fn conclude_proof_dual_bound(&mut self, bound: Predicate)

Conclude the proof with the optimality claim.

This method will finish the proof. Any new operation will not be logged to the proof.

Trait Implementations§

Source§

impl Debug for Solver

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for Solver

Source§

fn default() -> Solver

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl Freeze for Solver

§

impl !RefUnwindSafe for Solver

§

impl !Send for Solver

§

impl !Sync for Solver

§

impl Unpin for Solver

§

impl !UnwindSafe for Solver

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<'src, T> IntoMaybe<'src, T> for T
where T: 'src,

Source§

type Proj<U: 'src> = U

Source§

fn map_maybe<R>( self, _f: impl FnOnce(&'src T) -> &'src R, g: impl FnOnce(T) -> R, ) -> <T as IntoMaybe<'src, T>>::Proj<R>
where R: 'src,

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V