CIRCT 23.0.0git
Loading...
Searching...
No Matches
SATSolver.h
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This header defines an abstract incremental SAT interface
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef CIRCT_SUPPORT_SATSOLVER_H
14#define CIRCT_SUPPORT_SATSOLVER_H
15
16#include "llvm/ADT/ArrayRef.h"
17#include "llvm/ADT/STLFunctionalExtras.h"
18#include "llvm/ADT/SmallVector.h"
19#include <memory>
20
21namespace circt {
22template <typename T>
23struct HeapScore;
24
25/// A max-heap of ids into caller-owned storage.
26///
27/// The heap stores only integer ids and keeps a reverse map from each id to its
28/// heap slot. That lets callers update an existing entry without creating
29/// duplicates. Scores come from a stateless policy type. If a caller changes an
30/// element in `storage` in a way that raises its score, it must call
31/// `increase(id)` to restore heap order.
32///
33/// This data structure design is inspired by MiniSat's `minisat/mtl/Heap.h`.
34template <typename T, typename ScoreFn = HeapScore<T>>
36public:
37 // Create an empty heap referencing caller-owned storage. Caller must keep
38 // storage alive; heap is empty until insert() is used.
39 explicit IndexedMaxHeap(llvm::SmallVectorImpl<T> &storage)
40 : storage(storage) {}
41
42 void resize(unsigned size) {
43 if (positions.size() < size)
44 positions.resize(size, kInvalidIndex);
45 }
46
47 bool empty() const { return heap.empty(); }
48
49 bool contains(unsigned id) const {
50 return id < positions.size() && positions[id] != kInvalidIndex;
51 }
52
53 /// Remove all heap entries while keeping the underlying storage untouched.
54 void clear() {
55 heap.clear();
56 std::fill(positions.begin(), positions.end(), kInvalidIndex);
57 }
58
59 /// Insert `id` if it is not already present.
60 ///
61 /// This heap treats ids as stable identities; duplicates are ignored instead
62 /// of creating multiple entries for the same object.
63 void insert(unsigned id) {
64 if (contains(id))
65 return;
66 resize(id + 1);
67 positions[id] = heap.size();
68 heap.push_back(id);
69 siftUp(positions[id]);
70 }
71
72 /// Restore heap order after the score for an existing `id` increased.
73 ///
74 /// This only moves the entry upward. Callers should use this when the score
75 /// policy is monotonic in the "better" direction, such as SAT variable
76 /// activity bumps. Mutating a stored element without calling `increase(id)`
77 /// leaves the heap order stale.
78 void increase(unsigned id) {
79 if (contains(id))
80 siftUp(positions[id]);
81 }
82
83 /// Remove and return the highest-scoring id.
84 unsigned pop() {
85 assert(!empty() && "cannot pop from empty heap");
86 unsigned top = heap.front();
88 if (heap.size() == 1) {
89 heap.pop_back();
90 return top;
91 }
92
93 // Move the last entry to the root and sift down to restore heap order.
94 heap.front() = heap.back();
95 positions[heap.front()] = 0;
96 heap.pop_back();
97 siftDown(0);
98 return top;
99 }
100
101private:
102 static constexpr unsigned kInvalidIndex = ~0u;
103
104 /// Compute the ordering score for one heap entry.
105 double score(unsigned id) const { return ScoreFn{}(storage[id]); }
106
107 /// Swap two heap slots and keep the reverse index in sync.
108 void swapHeapEntries(unsigned lhs, unsigned rhs) {
109 std::swap(heap[lhs], heap[rhs]);
110 positions[heap[lhs]] = lhs;
111 positions[heap[rhs]] = rhs;
112 }
113
114 /// Bubble one entry toward the root until the heap property is restored.
115 void siftUp(unsigned index) {
116 unsigned elem = heap[index];
117 double elemScore = score(elem);
118 while (index > 0) {
119 unsigned parent = (index - 1) / 2;
120 if (score(heap[parent]) >= elemScore)
121 break;
122 swapHeapEntries(index, parent);
123 index = parent;
124 }
125 assert(heap[index] == elem && "siftUp must preserve the promoted element");
126 }
127
128 /// Push one entry toward the leaves until the heap property is restored.
129 void siftDown(unsigned index) {
130 unsigned elem = heap[index];
131 double elemScore = score(elem);
132 unsigned heapSize = heap.size();
133 while (true) {
134 unsigned child = 2 * index + 1;
135 if (child >= heapSize)
136 break;
137 if (child + 1 < heapSize && score(heap[child + 1]) > score(heap[child]))
138 ++child;
139 if (elemScore >= score(heap[child]))
140 break;
141 swapHeapEntries(index, child);
142 index = child;
143 }
144 assert(heap[index] == elem && "siftDown must preserve the demoted element");
145 }
146
147 /// The caller-owned objects indexed by heap ids.
148 llvm::SmallVectorImpl<T> &storage;
149
150 /// Binary heap of ids ordered by descending score.
151 llvm::SmallVector<unsigned, 0> heap;
152
153 /// Reverse map from id to heap slot, or `InvalidIndex` if absent.
154 llvm::SmallVector<unsigned, 0> positions;
155};
156
157/// Abstract interface for incremental SAT solvers with an IPASIR-style API.
159public:
160 enum Result : int { kSAT = 10, kUNSAT = 20, kUNKNOWN = 0 };
161
162 virtual ~IncrementalSATSolver() = default;
163
164 /// Add one literal to the clause currently under construction. A `0`
165 /// literal terminates the clause and submits it to the solver.
166 virtual void add(int lit) = 0;
167 /// Add an assumption literal for the next `solve()` call only.
168 virtual void assume(int lit) = 0;
169 /// Solve under the previously added clauses and current assumptions.
170 virtual Result solve() = 0;
171 virtual Result solve(llvm::ArrayRef<int> assumptions) {
172 for (int lit : assumptions)
173 assume(lit);
174 return solve();
175 };
176 /// Return the satisfying assignment for variable `v` from the last SAT
177 /// result. The sign of the returned literal encodes the Boolean value.
178 virtual int val(int v) const = 0;
179
180 // These helpers are not part of the standard IPASIR interface.
181 /// Set the per-`solve()` conflict budget. Negative values restore the
182 /// backend default of no explicit conflict limit. The backend may choose to
183 /// ignore this if it does not support conflict limits.
184 virtual void setConflictLimit(int limit) {}
185 /// Reserve storage for variables in the range `[1, maxVar]`.
186 virtual void reserveVars(int maxVar) {}
187 /// Add a complete clause in one call.
188 virtual void addClause(llvm::ArrayRef<int> lits) {
189 for (int lit : lits)
190 add(lit);
191 add(0);
192 }
193 /// Add a fresh variable for safe incremental SAT solving.
194 virtual int newVar() = 0;
195};
196
197/// Emit clauses encoding `outVar <=> and(inputLits)`.
198void addAndClauses(int outVar, llvm::ArrayRef<int> inputLits,
199 llvm::function_ref<void(llvm::ArrayRef<int>)> addClause);
200
201/// Emit clauses encoding `outVar <=> or(inputLits)`.
202void addOrClauses(int outVar, llvm::ArrayRef<int> inputLits,
203 llvm::function_ref<void(llvm::ArrayRef<int>)> addClause);
204
205/// Emit clauses encoding `outVar <=> (lhsLit xor rhsLit)`.
206void addXorClauses(int outVar, int lhsLit, int rhsLit,
207 llvm::function_ref<void(llvm::ArrayRef<int>)> addClause);
208
209/// Emit clauses encoding `outVar <=> parity(inputLits)`.
210void addParityClauses(int outVar, llvm::ArrayRef<int> inputLits,
211 llvm::function_ref<void(llvm::ArrayRef<int>)> addClause,
212 llvm::function_ref<int()> newVar);
213
214/// Emit clauses encoding that at most one literal in `inputLits` can be true.
215/// Unlike the Tseitin-style gate helpers above, this helper does not
216/// take an `outVar`; it only emits the cardinality constraint itself.
218 llvm::ArrayRef<int> inputLits,
219 llvm::function_ref<void(llvm::ArrayRef<int>)> addClause,
220 llvm::function_ref<int()> newVar);
221
222/// Emit clauses encoding that exactly one literal in `inputLits` is true.
224 llvm::ArrayRef<int> inputLits,
225 llvm::function_ref<void(llvm::ArrayRef<int>)> addClause,
226 llvm::function_ref<int()> newVar);
227
228/// Construct a Z3-backed incremental IPASIR-style SAT solver.
229std::unique_ptr<IncrementalSATSolver> createZ3SATSolver();
232 Default, // Default.
233 Plain, // Disable preprocessing.
234 Sat, // Target satisfiable instances.
235 Unsat, // Target unsatisfiable instances.
236 };
238};
239/// Construct a CaDiCaL-backed incremental IPASIR-style SAT solver.
240std::unique_ptr<IncrementalSATSolver>
242/// Return true when at least one incremental SAT backend is available.
244
245} // namespace circt
246
247#endif // CIRCT_SUPPORT_SATSOLVER_H
assert(baseType &&"element must be base type")
Abstract interface for incremental SAT solvers with an IPASIR-style API.
Definition SATSolver.h:158
virtual Result solve(llvm::ArrayRef< int > assumptions)
Definition SATSolver.h:171
virtual void reserveVars(int maxVar)
Reserve storage for variables in the range [1, maxVar].
Definition SATSolver.h:186
virtual void addClause(llvm::ArrayRef< int > lits)
Add a complete clause in one call.
Definition SATSolver.h:188
virtual ~IncrementalSATSolver()=default
virtual void add(int lit)=0
Add one literal to the clause currently under construction.
virtual void setConflictLimit(int limit)
Set the per-solve() conflict budget.
Definition SATSolver.h:184
virtual int newVar()=0
Add a fresh variable for safe incremental SAT solving.
virtual int val(int v) const =0
Return the satisfying assignment for variable v from the last SAT result.
virtual Result solve()=0
Solve under the previously added clauses and current assumptions.
virtual void assume(int lit)=0
Add an assumption literal for the next solve() call only.
A max-heap of ids into caller-owned storage.
Definition SATSolver.h:35
void increase(unsigned id)
Restore heap order after the score for an existing id increased.
Definition SATSolver.h:78
static constexpr unsigned kInvalidIndex
Definition SATSolver.h:102
llvm::SmallVector< unsigned, 0 > heap
Binary heap of ids ordered by descending score.
Definition SATSolver.h:151
double score(unsigned id) const
Compute the ordering score for one heap entry.
Definition SATSolver.h:105
bool empty() const
Definition SATSolver.h:47
void siftDown(unsigned index)
Push one entry toward the leaves until the heap property is restored.
Definition SATSolver.h:129
void swapHeapEntries(unsigned lhs, unsigned rhs)
Swap two heap slots and keep the reverse index in sync.
Definition SATSolver.h:108
llvm::SmallVectorImpl< T > & storage
The caller-owned objects indexed by heap ids.
Definition SATSolver.h:148
IndexedMaxHeap(llvm::SmallVectorImpl< T > &storage)
Definition SATSolver.h:39
llvm::SmallVector< unsigned, 0 > positions
Reverse map from id to heap slot, or InvalidIndex if absent.
Definition SATSolver.h:154
void insert(unsigned id)
Insert id if it is not already present.
Definition SATSolver.h:63
bool contains(unsigned id) const
Definition SATSolver.h:49
unsigned pop()
Remove and return the highest-scoring id.
Definition SATSolver.h:84
void siftUp(unsigned index)
Bubble one entry toward the root until the heap property is restored.
Definition SATSolver.h:115
void clear()
Remove all heap entries while keeping the underlying storage untouched.
Definition SATSolver.h:54
void resize(unsigned size)
Definition SATSolver.h:42
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
bool hasIncrementalSATSolverBackend()
Return true when at least one incremental SAT backend is available.
void addExactlyOneClauses(llvm::ArrayRef< int > inputLits, llvm::function_ref< void(llvm::ArrayRef< int >)> addClause, llvm::function_ref< int()> newVar)
Emit clauses encoding that exactly one literal in inputLits is true.
void addAndClauses(int outVar, llvm::ArrayRef< int > inputLits, llvm::function_ref< void(llvm::ArrayRef< int >)> addClause)
Emit clauses encoding outVar <=> and(inputLits).
void addXorClauses(int outVar, int lhsLit, int rhsLit, llvm::function_ref< void(llvm::ArrayRef< int >)> addClause)
Emit clauses encoding outVar <=> (lhsLit xor rhsLit).
std::unique_ptr< IncrementalSATSolver > createZ3SATSolver()
Construct a Z3-backed incremental IPASIR-style SAT solver.
std::unique_ptr< IncrementalSATSolver > createCadicalSATSolver(const CadicalSATSolverOptions &options={})
Construct a CaDiCaL-backed incremental IPASIR-style SAT solver.
void addOrClauses(int outVar, llvm::ArrayRef< int > inputLits, llvm::function_ref< void(llvm::ArrayRef< int >)> addClause)
Emit clauses encoding outVar <=> or(inputLits).
void addParityClauses(int outVar, llvm::ArrayRef< int > inputLits, llvm::function_ref< void(llvm::ArrayRef< int >)> addClause, llvm::function_ref< int()> newVar)
Emit clauses encoding outVar <=> parity(inputLits).
void addAtMostOneClauses(llvm::ArrayRef< int > inputLits, llvm::function_ref< void(llvm::ArrayRef< int >)> addClause, llvm::function_ref< int()> newVar)
Emit clauses encoding that at most one literal in inputLits can be true.
CadicalSolverConfig config
Definition SATSolver.h:237