-
Notifications
You must be signed in to change notification settings - Fork 36
/
sat_dependency_provider.rs
153 lines (136 loc) · 5.43 KB
/
sat_dependency_provider.rs
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// SPDX-License-Identifier: MPL-2.0
use pubgrub::{
Dependencies, DependencyProvider, Map, OfflineDependencyProvider, Package, PubGrubError,
SelectedDependencies, VersionSet,
};
use varisat::ExtendFormula;
fn sat_at_most_one(solver: &mut impl ExtendFormula, vars: &[varisat::Var]) {
if vars.len() <= 1 {
return;
} else if vars.len() == 2 {
solver.add_clause(&[vars[0].negative(), vars[1].negative()]);
return;
} else if vars.len() == 3 {
solver.add_clause(&[vars[0].negative(), vars[1].negative()]);
solver.add_clause(&[vars[0].negative(), vars[2].negative()]);
solver.add_clause(&[vars[1].negative(), vars[2].negative()]);
return;
}
// use the "Binary Encoding" from
// https://www.it.uu.se/research/group/astra/ModRef10/papers/Alan%20M.%20Frisch%20and%20Paul%20A.%20Giannoros.%20SAT%20Encodings%20of%20the%20At-Most-k%20Constraint%20-%20ModRef%202010.pdf
let len_bits = vars.len().ilog2() as usize + 1;
let bits: Vec<varisat::Var> = solver.new_var_iter(len_bits).collect();
for (i, p) in vars.iter().enumerate() {
for (j, &bit) in bits.iter().enumerate() {
solver.add_clause(&[p.negative(), bit.lit(((1 << j) & i) > 0)]);
}
}
}
/// Resolution can be reduced to the SAT problem. So this is an alternative implementation
/// of the resolver that uses a SAT library for the hard work. This is intended to be easy to read,
/// as compared to the real resolver. This will find a valid resolution if one exists.
///
/// The SAT library does not optimize for the newer version,
/// so the selected packages may not match the real resolver.
pub struct SatResolve<P: Package, VS: VersionSet> {
solver: varisat::Solver<'static>,
all_versions_by_p: Map<P, Vec<(VS::V, varisat::Var)>>,
}
impl<P: Package, VS: VersionSet> SatResolve<P, VS> {
pub fn new(dp: &OfflineDependencyProvider<P, VS>) -> Self {
let mut cnf = varisat::CnfFormula::new();
let mut all_versions = vec![];
let mut all_versions_by_p: Map<P, Vec<(VS::V, varisat::Var)>> = Map::default();
for p in dp.packages() {
let mut versions_for_p = vec![];
for v in dp.versions(p).unwrap() {
let new_var = cnf.new_var();
all_versions.push((p.clone(), v.clone(), new_var));
versions_for_p.push(new_var);
all_versions_by_p
.entry(p.clone())
.or_default()
.push((v.clone(), new_var));
}
// no two versions of the same package
sat_at_most_one(&mut cnf, &versions_for_p);
}
// active packages need each of there `deps` to be satisfied
for (p, v, var) in &all_versions {
let deps = match dp.get_dependencies(p, v).unwrap() {
Dependencies::Unavailable(_) => panic!(),
Dependencies::Available(d) => d,
};
for (p1, range) in &deps {
let empty_vec = vec![];
let mut matches: Vec<varisat::Lit> = all_versions_by_p
.get(p1)
.unwrap_or(&empty_vec)
.iter()
.filter(|(v1, _)| range.contains(v1))
.map(|(_, var1)| var1.positive())
.collect();
// ^ the `dep` is satisfied or
matches.push(var.negative());
// ^ `p` is not active
cnf.add_clause(&matches);
}
}
let mut solver = varisat::Solver::new();
solver.add_formula(&cnf);
// We dont need to `solve` now. We know that "use nothing" will satisfy all the clauses so far.
// But things run faster if we let it spend some time figuring out how the constraints interact before we add assumptions.
solver
.solve()
.expect("docs say it can't error in default config");
Self {
solver,
all_versions_by_p,
}
}
pub fn resolve(&mut self, name: &P, ver: &VS::V) -> bool {
if let Some(vers) = self.all_versions_by_p.get(name) {
if let Some((_, var)) = vers.iter().find(|(v, _)| v == ver) {
self.solver.assume(&[var.positive()]);
self.solver
.solve()
.expect("docs say it can't error in default config")
} else {
false
}
} else {
false
}
}
pub fn is_valid_solution<DP: DependencyProvider<P = P, VS = VS, V = VS::V>>(
&mut self,
pids: &SelectedDependencies<DP>,
) -> bool {
let mut assumption = vec![];
for (p, vs) in &self.all_versions_by_p {
let pid_for_p = pids.get(p);
for (v, var) in vs {
assumption.push(var.lit(pid_for_p == Some(v)))
}
}
self.solver.assume(&assumption);
self.solver
.solve()
.expect("docs say it can't error in default config")
}
pub fn check_resolve<DP: DependencyProvider<P = P, VS = VS, V = VS::V>>(
&mut self,
res: &Result<SelectedDependencies<DP>, PubGrubError<DP>>,
p: &P,
v: &VS::V,
) {
match res {
Ok(s) => {
assert!(self.is_valid_solution::<DP>(s));
}
Err(_) => {
assert!(!self.resolve(p, v));
}
}
}
}