CIRCT 23.0.0git
Loading...
Searching...
No Matches
SATSolver.cpp
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 file defines incremental SAT solvers with an IPASIR-style interface.
10//
11//===----------------------------------------------------------------------===//
12
14#include "llvm/ADT/ArrayRef.h"
15#include "llvm/ADT/SmallVector.h"
16#include "llvm/Support/ErrorHandling.h"
17#include "llvm/Support/SMTAPI.h"
18
19#ifdef CIRCT_CADICAL_ENABLED
20#include "third_party/cadical/cadical.hpp"
21#endif
22
23#include <cassert>
24#include <cstdlib>
25#include <string>
26
27namespace circt {
28
29namespace {
30
31//===----------------------------------------------------------------------===//
32// CaDiCaL Backend
33//===----------------------------------------------------------------------===//
34
35#ifdef CIRCT_CADICAL_ENABLED
36
37const char *
38toCadicalConfigName(CadicalSATSolverOptions::CadicalSolverConfig config) {
39 switch (config) {
41 return "default";
43 return "plain";
45 return "sat";
47 return "unsat";
48 }
49 llvm_unreachable("unknown CaDiCaL configuration");
50}
51
52class CadicalSATSolver : public IncrementalSATSolver {
53public:
54 explicit CadicalSATSolver(const CadicalSATSolverOptions &options) {
55 if (options.config !=
56 CadicalSATSolverOptions::CadicalSolverConfig::Default) {
57 bool configured = solver.configure(toCadicalConfigName(options.config));
58 assert(configured && "invalid CaDiCaL configuration");
59 (void)configured;
60 }
61 }
62 void add(int lit) override { solver.add(lit); }
63 void assume(int lit) override {
64 if (lit != 0)
65 solver.assume(lit);
66 }
67 Result solve() override {
68 if (conflictLimit >= 0)
69 solver.limit("conflicts", conflictLimit);
70 switch (solver.solve()) {
71 case CaDiCaL::SATISFIABLE:
72 return kSAT;
73 case CaDiCaL::UNSATISFIABLE:
74 return kUNSAT;
75 default:
76 return kUNKNOWN;
77 }
78 }
79 int val(int v) const override {
80 if (v <= 0 || v > maxVariable)
81 return 0;
82 return solver.val(v);
83 }
84 void setConflictLimit(int limit) override { conflictLimit = limit; }
85 void reserveVars(int maxVar) override {
86 if (maxVar <= maxVariable)
87 return;
88 solver.resize(maxVar);
89 maxVariable = maxVar;
90 }
91 void addClause(llvm::ArrayRef<int> lits) override {
92 if (lits.empty()) {
93 solver.add(0);
94 return;
95 }
96 solver.clause(lits.data(), lits.size());
97 }
98
99private:
100 mutable CaDiCaL::Solver solver;
101 int maxVariable = 0;
102 int conflictLimit = -1;
103};
104
105#endif // CIRCT_CADICAL_ENABLED
106
107//===----------------------------------------------------------------------===//
108// Z3 Backend
109//===----------------------------------------------------------------------===//
110
111#if LLVM_WITH_Z3
112
113class Z3SATSolver : public IncrementalSATSolver {
114public:
115 Z3SATSolver();
116 ~Z3SATSolver() override;
117
118 void add(int lit) override;
119 void assume(int lit) override;
120 Result solve() override;
121 Result solve(llvm::ArrayRef<int> assumptions) override;
122 int val(int v) const override;
123 void reserveVars(int maxVar) override;
124
125private:
126 void clearSolveScope();
127 int newVariable();
128 llvm::SMTExprRef literalToExpr(int lit);
129 void addClauseInternal(llvm::ArrayRef<int> lits);
130
131 llvm::SMTSolverRef solver;
132 llvm::SmallVector<llvm::SMTExprRef> variables;
133 llvm::SmallVector<int> assumptions;
134 llvm::SmallVector<int> clauseBuffer;
135 int maxVariable = 0;
136 Result lastResult = kUNKNOWN;
137 bool solveScopeActive = false;
138};
139
140Z3SATSolver::Z3SATSolver() : solver(llvm::CreateZ3Solver()) {}
141
142Z3SATSolver::~Z3SATSolver() { clearSolveScope(); }
143
144void Z3SATSolver::add(int lit) {
145 clearSolveScope();
146 if (lit == 0) {
147 addClauseInternal(clauseBuffer);
148 clauseBuffer.clear();
149 return;
150 }
151
152 reserveVars(std::abs(lit));
153 clauseBuffer.push_back(lit);
154}
155
156void Z3SATSolver::assume(int lit) {
157 clearSolveScope();
158 if (lit == 0)
159 return;
160 assumptions.push_back(lit);
161}
162
163IncrementalSATSolver::Result Z3SATSolver::solve() {
164 auto localAssumptions = assumptions;
165 assumptions.clear();
166 return solve(localAssumptions);
167}
168
169IncrementalSATSolver::Result
170Z3SATSolver::solve(llvm::ArrayRef<int> assumptions) {
171 clearSolveScope();
172 solver->push();
173 solveScopeActive = true;
174 for (int lit : assumptions)
175 solver->addConstraint(literalToExpr(lit));
176 auto result = solver->check();
177 if (!result)
178 return lastResult = kUNKNOWN;
179 if (*result)
180 return lastResult = kSAT;
181 return lastResult = kUNSAT;
182}
183
184int Z3SATSolver::val(int v) const {
185 if (lastResult != kSAT || v <= 0 || v > maxVariable)
186 return 0;
187 llvm::APSInt value(llvm::APInt(1, 0), true);
188 // Z3 returns an interpretation for all variables, even those not involved
189 // in the problem. If the variable is not involved, return 0 to indicate
190 // "undefined" rather than a potentially misleading true/false value.
191 if (!solver->getInterpretation(variables[v - 1], value))
192 return 0;
193 return value != 0 ? v : -v;
194}
195
196void Z3SATSolver::reserveVars(int maxVar) {
197 if (maxVar <= maxVariable)
198 return;
199 while (static_cast<int>(variables.size()) < maxVar)
200 newVariable();
201 maxVariable = maxVar;
202}
203
204void Z3SATSolver::clearSolveScope() {
205 if (!solveScopeActive)
206 return;
207 solver->pop();
208 solveScopeActive = false;
209 lastResult = kUNKNOWN;
210}
211
212int Z3SATSolver::newVariable() {
213 int varIndex = static_cast<int>(variables.size()) + 1;
214 std::string name = "v" + std::to_string(varIndex);
215 variables.push_back(solver->mkSymbol(name.c_str(), solver->getBoolSort()));
216 return varIndex;
217}
218
219llvm::SMTExprRef Z3SATSolver::literalToExpr(int lit) {
220 int absLit = std::abs(lit);
221 // Ensure variable exists for this literal.
222 reserveVars(absLit);
223 auto *variable = variables[absLit - 1];
224 return lit > 0 ? variable : solver->mkNot(variable);
225}
226
227void Z3SATSolver::addClauseInternal(llvm::ArrayRef<int> lits) {
228 if (lits.empty()) {
229 solver->addConstraint(solver->mkBoolean(false));
230 return;
231 }
232
233 llvm::SMTExprRef clause = nullptr;
234 for (int lit : lits) {
235 if (lit == 0)
236 continue;
237 auto *expr = literalToExpr(lit);
238 clause = clause ? solver->mkOr(clause, expr) : expr;
239 }
240
241 if (!clause) {
242 solver->addConstraint(solver->mkBoolean(false));
243 return;
244 }
245 solver->addConstraint(clause);
246}
247
248#endif // LLVM_WITH_Z3
249
250} // namespace
251
252std::unique_ptr<IncrementalSATSolver> createZ3SATSolver() {
253#if LLVM_WITH_Z3
254 return std::make_unique<Z3SATSolver>();
255#else
256 return {};
257#endif
258}
259
260std::unique_ptr<IncrementalSATSolver>
262#ifdef CIRCT_CADICAL_ENABLED
263 return std::make_unique<CadicalSATSolver>(options);
264#else
265 return {};
266#endif
267}
268
270 return static_cast<bool>(createCadicalSATSolver()) ||
271 static_cast<bool>(createZ3SATSolver());
272}
273
274} // namespace circt
assert(baseType &&"element must be base type")
static void solve(PassGlobals &globals, Term *lhs, Term *rhs)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
bool hasIncrementalSATSolverBackend()
Return true when at least one incremental SAT backend is available.
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.