cadical 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +26 -0
- data/README.md +101 -0
- data/ext/cadical/cadical/LICENSE +28 -0
- data/ext/cadical/cadical/README.md +65 -0
- data/ext/cadical/cadical/VERSION +1 -0
- data/ext/cadical/cadical/src/analyze.cpp +1360 -0
- data/ext/cadical/cadical/src/arena.cpp +30 -0
- data/ext/cadical/cadical/src/arena.hpp +105 -0
- data/ext/cadical/cadical/src/assume.cpp +616 -0
- data/ext/cadical/cadical/src/averages.cpp +34 -0
- data/ext/cadical/cadical/src/averages.hpp +37 -0
- data/ext/cadical/cadical/src/backbone.cpp +631 -0
- data/ext/cadical/cadical/src/backtrack.cpp +177 -0
- data/ext/cadical/cadical/src/backward.cpp +231 -0
- data/ext/cadical/cadical/src/bins.cpp +22 -0
- data/ext/cadical/cadical/src/bins.hpp +22 -0
- data/ext/cadical/cadical/src/block.cpp +824 -0
- data/ext/cadical/cadical/src/block.hpp +37 -0
- data/ext/cadical/cadical/src/cadical.cpp +1026 -0
- data/ext/cadical/cadical/src/cadical.hpp +1419 -0
- data/ext/cadical/cadical/src/ccadical.cpp +208 -0
- data/ext/cadical/cadical/src/ccadical.h +74 -0
- data/ext/cadical/cadical/src/checker.cpp +649 -0
- data/ext/cadical/cadical/src/checker.hpp +173 -0
- data/ext/cadical/cadical/src/clause.cpp +689 -0
- data/ext/cadical/cadical/src/clause.hpp +190 -0
- data/ext/cadical/cadical/src/collect.cpp +545 -0
- data/ext/cadical/cadical/src/compact.cpp +550 -0
- data/ext/cadical/cadical/src/condition.cpp +940 -0
- data/ext/cadical/cadical/src/config.cpp +101 -0
- data/ext/cadical/cadical/src/config.hpp +20 -0
- data/ext/cadical/cadical/src/congruence.cpp +7880 -0
- data/ext/cadical/cadical/src/congruence.hpp +757 -0
- data/ext/cadical/cadical/src/constrain.cpp +64 -0
- data/ext/cadical/cadical/src/contract.cpp +27 -0
- data/ext/cadical/cadical/src/contract.hpp +139 -0
- data/ext/cadical/cadical/src/cover.cpp +704 -0
- data/ext/cadical/cadical/src/cover.hpp +34 -0
- data/ext/cadical/cadical/src/decide.cpp +347 -0
- data/ext/cadical/cadical/src/decompose.cpp +738 -0
- data/ext/cadical/cadical/src/decompose.hpp +23 -0
- data/ext/cadical/cadical/src/deduplicate.cpp +275 -0
- data/ext/cadical/cadical/src/definition.cpp +283 -0
- data/ext/cadical/cadical/src/delay.hpp +38 -0
- data/ext/cadical/cadical/src/drattracer.cpp +153 -0
- data/ext/cadical/cadical/src/drattracer.hpp +55 -0
- data/ext/cadical/cadical/src/elim.cpp +1171 -0
- data/ext/cadical/cadical/src/elim.hpp +53 -0
- data/ext/cadical/cadical/src/elimfast.cpp +570 -0
- data/ext/cadical/cadical/src/ema.cpp +95 -0
- data/ext/cadical/cadical/src/ema.hpp +68 -0
- data/ext/cadical/cadical/src/extend.cpp +289 -0
- data/ext/cadical/cadical/src/external.cpp +1034 -0
- data/ext/cadical/cadical/src/external.hpp +487 -0
- data/ext/cadical/cadical/src/external_propagate.cpp +1316 -0
- data/ext/cadical/cadical/src/factor.cpp +1011 -0
- data/ext/cadical/cadical/src/factor.hpp +54 -0
- data/ext/cadical/cadical/src/file.cpp +506 -0
- data/ext/cadical/cadical/src/file.hpp +210 -0
- data/ext/cadical/cadical/src/flags.cpp +135 -0
- data/ext/cadical/cadical/src/flags.hpp +93 -0
- data/ext/cadical/cadical/src/flip.cpp +269 -0
- data/ext/cadical/cadical/src/format.cpp +89 -0
- data/ext/cadical/cadical/src/format.hpp +36 -0
- data/ext/cadical/cadical/src/frattracer.cpp +277 -0
- data/ext/cadical/cadical/src/frattracer.hpp +67 -0
- data/ext/cadical/cadical/src/gates.cpp +766 -0
- data/ext/cadical/cadical/src/heap.hpp +212 -0
- data/ext/cadical/cadical/src/idruptracer.cpp +566 -0
- data/ext/cadical/cadical/src/idruptracer.hpp +112 -0
- data/ext/cadical/cadical/src/instantiate.cpp +365 -0
- data/ext/cadical/cadical/src/instantiate.hpp +45 -0
- data/ext/cadical/cadical/src/internal.cpp +1265 -0
- data/ext/cadical/cadical/src/internal.hpp +1941 -0
- data/ext/cadical/cadical/src/inttypes.hpp +34 -0
- data/ext/cadical/cadical/src/ipasir.cpp +47 -0
- data/ext/cadical/cadical/src/ipasir.h +37 -0
- data/ext/cadical/cadical/src/kitten.c +2599 -0
- data/ext/cadical/cadical/src/kitten.h +97 -0
- data/ext/cadical/cadical/src/level.hpp +33 -0
- data/ext/cadical/cadical/src/lidruptracer.cpp +657 -0
- data/ext/cadical/cadical/src/lidruptracer.hpp +122 -0
- data/ext/cadical/cadical/src/limit.cpp +146 -0
- data/ext/cadical/cadical/src/limit.hpp +168 -0
- data/ext/cadical/cadical/src/logging.cpp +214 -0
- data/ext/cadical/cadical/src/logging.hpp +98 -0
- data/ext/cadical/cadical/src/lookahead.cpp +520 -0
- data/ext/cadical/cadical/src/lratchecker.cpp +833 -0
- data/ext/cadical/cadical/src/lratchecker.hpp +168 -0
- data/ext/cadical/cadical/src/lrattracer.cpp +200 -0
- data/ext/cadical/cadical/src/lrattracer.hpp +61 -0
- data/ext/cadical/cadical/src/lucky.cpp +506 -0
- data/ext/cadical/cadical/src/message.cpp +218 -0
- data/ext/cadical/cadical/src/message.hpp +65 -0
- data/ext/cadical/cadical/src/minimize.cpp +224 -0
- data/ext/cadical/cadical/src/mobical.cpp +5588 -0
- data/ext/cadical/cadical/src/occs.cpp +52 -0
- data/ext/cadical/cadical/src/occs.hpp +36 -0
- data/ext/cadical/cadical/src/options.cpp +359 -0
- data/ext/cadical/cadical/src/options.hpp +442 -0
- data/ext/cadical/cadical/src/parse.cpp +471 -0
- data/ext/cadical/cadical/src/parse.hpp +75 -0
- data/ext/cadical/cadical/src/phases.cpp +56 -0
- data/ext/cadical/cadical/src/phases.hpp +19 -0
- data/ext/cadical/cadical/src/probe.cpp +990 -0
- data/ext/cadical/cadical/src/profile.cpp +107 -0
- data/ext/cadical/cadical/src/profile.hpp +283 -0
- data/ext/cadical/cadical/src/proof.cpp +691 -0
- data/ext/cadical/cadical/src/proof.hpp +125 -0
- data/ext/cadical/cadical/src/propagate.cpp +584 -0
- data/ext/cadical/cadical/src/queue.cpp +90 -0
- data/ext/cadical/cadical/src/queue.hpp +74 -0
- data/ext/cadical/cadical/src/radix.hpp +180 -0
- data/ext/cadical/cadical/src/random.cpp +207 -0
- data/ext/cadical/cadical/src/random.h +44 -0
- data/ext/cadical/cadical/src/random.hpp +98 -0
- data/ext/cadical/cadical/src/range.hpp +125 -0
- data/ext/cadical/cadical/src/reap.cpp +127 -0
- data/ext/cadical/cadical/src/reap.hpp +28 -0
- data/ext/cadical/cadical/src/reduce.cpp +278 -0
- data/ext/cadical/cadical/src/reluctant.hpp +82 -0
- data/ext/cadical/cadical/src/rephase.cpp +402 -0
- data/ext/cadical/cadical/src/report.cpp +313 -0
- data/ext/cadical/cadical/src/resources.cpp +160 -0
- data/ext/cadical/cadical/src/resources.hpp +16 -0
- data/ext/cadical/cadical/src/restart.cpp +178 -0
- data/ext/cadical/cadical/src/restore.cpp +267 -0
- data/ext/cadical/cadical/src/score.cpp +51 -0
- data/ext/cadical/cadical/src/score.hpp +16 -0
- data/ext/cadical/cadical/src/shrink.cpp +507 -0
- data/ext/cadical/cadical/src/signal.cpp +136 -0
- data/ext/cadical/cadical/src/signal.hpp +33 -0
- data/ext/cadical/cadical/src/solution.cpp +50 -0
- data/ext/cadical/cadical/src/solver.cpp +1871 -0
- data/ext/cadical/cadical/src/stable.cpp +31 -0
- data/ext/cadical/cadical/src/stack.h +110 -0
- data/ext/cadical/cadical/src/stats.cpp +920 -0
- data/ext/cadical/cadical/src/stats.hpp +418 -0
- data/ext/cadical/cadical/src/subsume.cpp +645 -0
- data/ext/cadical/cadical/src/sweep.cpp +1960 -0
- data/ext/cadical/cadical/src/sweep.hpp +61 -0
- data/ext/cadical/cadical/src/terminal.cpp +38 -0
- data/ext/cadical/cadical/src/terminal.hpp +96 -0
- data/ext/cadical/cadical/src/ternary.cpp +450 -0
- data/ext/cadical/cadical/src/testing.hpp +24 -0
- data/ext/cadical/cadical/src/tier.cpp +189 -0
- data/ext/cadical/cadical/src/tracer.hpp +186 -0
- data/ext/cadical/cadical/src/transred.cpp +253 -0
- data/ext/cadical/cadical/src/unstable.cpp +29 -0
- data/ext/cadical/cadical/src/util.cpp +129 -0
- data/ext/cadical/cadical/src/util.hpp +181 -0
- data/ext/cadical/cadical/src/var.cpp +39 -0
- data/ext/cadical/cadical/src/var.hpp +22 -0
- data/ext/cadical/cadical/src/veripbtracer.cpp +427 -0
- data/ext/cadical/cadical/src/veripbtracer.hpp +106 -0
- data/ext/cadical/cadical/src/version.cpp +107 -0
- data/ext/cadical/cadical/src/version.hpp +13 -0
- data/ext/cadical/cadical/src/vivify.cpp +1893 -0
- data/ext/cadical/cadical/src/vivify.hpp +50 -0
- data/ext/cadical/cadical/src/walk.cpp +1088 -0
- data/ext/cadical/cadical/src/walk.hpp +91 -0
- data/ext/cadical/cadical/src/walk_full_occs.cpp +966 -0
- data/ext/cadical/cadical/src/warmup.cpp +403 -0
- data/ext/cadical/cadical/src/watch.cpp +126 -0
- data/ext/cadical/cadical/src/watch.hpp +77 -0
- data/ext/cadical/cadical_ext.c +778 -0
- data/ext/cadical/extconf.rb +26 -0
- data/lib/cadical/version.rb +3 -0
- data/lib/cadical.rb +12 -0
- metadata +218 -0
|
@@ -0,0 +1,778 @@
|
|
|
1
|
+
/* Ruby C extension wrapping the CaDiCaL SAT solver's C API.
|
|
2
|
+
*
|
|
3
|
+
* Decide whether a set of boolean clauses can all be true at once, and if so produce a
|
|
4
|
+
* satisfying assignment. */
|
|
5
|
+
|
|
6
|
+
#include <ruby.h>
|
|
7
|
+
#include <ruby/thread.h>
|
|
8
|
+
#include <stdint.h>
|
|
9
|
+
#include <stdio.h>
|
|
10
|
+
#include <string.h>
|
|
11
|
+
|
|
12
|
+
#include "ccadical.h"
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* Document-class: CaDiCaL
|
|
16
|
+
*
|
|
17
|
+
* A Ruby binding for the {CaDiCaL}[https://github.com/arminbiere/cadical] SAT solver.
|
|
18
|
+
*
|
|
19
|
+
* Decide whether a set of boolean clauses can all be true at once, and if so produce a
|
|
20
|
+
* satisfying assignment.
|
|
21
|
+
*
|
|
22
|
+
* This is a thin, general-purpose binding over CaDiCaL's C API (ccadical_*).
|
|
23
|
+
*
|
|
24
|
+
* Typical use:
|
|
25
|
+
*
|
|
26
|
+
* s = CaDiCaL.new
|
|
27
|
+
* x = s.new_var
|
|
28
|
+
* y = s.new_var
|
|
29
|
+
* s.add(x, y) # (x \/ y)
|
|
30
|
+
* s.add(-x, y) # (!x \/ y)
|
|
31
|
+
* s.solve # => :sat
|
|
32
|
+
* s.true?(y) # => true
|
|
33
|
+
*
|
|
34
|
+
* - Make variables with #new_var (each is just an Integer).
|
|
35
|
+
* - A literal is a variable or its negation: +v+ means "variable +v+ is true", +-v+ means false.
|
|
36
|
+
* - A clause is a list of literals; add it as a constraint with #add.
|
|
37
|
+
* - #solve reports whether the clauses can all be satisfied: +:sat+ if so, +:unsat+ if not.
|
|
38
|
+
* - After +:sat+, read a variable's assigned truth value with #true?.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
static ID id_sat, id_unsat, id_unknown, id_call;
|
|
42
|
+
|
|
43
|
+
/* ccadical_solve returns the IPASIR status codes: 10 = SATISFIABLE, 20 = UNSATISFIABLE,
|
|
44
|
+
* 0 = UNKNOWN. The C API does not give them names, so the constants live here. */
|
|
45
|
+
static VALUE result_sym(int r) {
|
|
46
|
+
return ID2SYM(r == 10 ? id_sat : r == 20 ? id_unsat : id_unknown);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* --- wrapper struct --- */
|
|
50
|
+
|
|
51
|
+
/* The wrapper struct for a CCaDiCaL handle. */
|
|
52
|
+
typedef struct {
|
|
53
|
+
CCaDiCaL *solver;
|
|
54
|
+
VALUE on_terminate; /* proc or Qnil */
|
|
55
|
+
VALUE on_learn; /* proc or Qnil */
|
|
56
|
+
VALUE pending_exc; /* exception from a callback, to re-raise after solve; or Qnil */
|
|
57
|
+
FILE *proof_file; /* open proof-trace file we own, or NULL */
|
|
58
|
+
int state; /* current solver state (mirrors cadical.hpp's State enum). get_cad
|
|
59
|
+
* checks each method's require() contract against it; the mutators
|
|
60
|
+
* update it. Turns CaDiCaL's fatal "wrong state" aborts into exceptions. */
|
|
61
|
+
} cadical_t;
|
|
62
|
+
|
|
63
|
+
static void cad_mark(void *p) {
|
|
64
|
+
cadical_t *c = p;
|
|
65
|
+
rb_gc_mark(c->on_terminate);
|
|
66
|
+
rb_gc_mark(c->on_learn);
|
|
67
|
+
rb_gc_mark(c->pending_exc);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static void cad_free(void *p) {
|
|
71
|
+
cadical_t *c = p;
|
|
72
|
+
if (c->proof_file) fclose(c->proof_file);
|
|
73
|
+
if (c->solver) ccadical_release(c->solver);
|
|
74
|
+
xfree(c);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static size_t cad_memsize(const void *p) {
|
|
78
|
+
(void)p;
|
|
79
|
+
return sizeof(cadical_t);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static const rb_data_type_t cadical_type = {
|
|
83
|
+
"CaDiCaL",
|
|
84
|
+
{cad_mark, cad_free, cad_memsize},
|
|
85
|
+
NULL,
|
|
86
|
+
NULL,
|
|
87
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/* Solver states we can observe from Ruby, a subset of cadical.hpp's State enum with the same
|
|
91
|
+
* bit values. We always terminate clauses, so the transient ADDING state is never left set
|
|
92
|
+
* between calls; SOLVING / INITIALIZING / DELETING are likewise unobservable. Hence for us
|
|
93
|
+
* VALID == READY, and every method's require() contract is one of the masks below. */
|
|
94
|
+
enum {
|
|
95
|
+
CONFIGURING = 2, STEADY = 4, SATISFIED = 32, UNSATISFIED = 64, INCONCLUSIVE = 256
|
|
96
|
+
};
|
|
97
|
+
#define SOLVED (SATISFIED | UNSATISFIED | INCONCLUSIVE)
|
|
98
|
+
#define READY (CONFIGURING | STEADY | SOLVED)
|
|
99
|
+
|
|
100
|
+
static const char *state_name(int s) {
|
|
101
|
+
switch (s) {
|
|
102
|
+
case CONFIGURING: return "configuring";
|
|
103
|
+
case STEADY: return "steady";
|
|
104
|
+
case SATISFIED: return "satisfied";
|
|
105
|
+
case UNSATISFIED: return "unsatisfied";
|
|
106
|
+
case INCONCLUSIVE: return "inconclusive";
|
|
107
|
+
default: return "invalid";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* Fetch the solver, checking it is live and in one of the +allowed+ states (the method's
|
|
112
|
+
* require() contract). Raising here turns CaDiCaL's fatal state aborts into Ruby exceptions. */
|
|
113
|
+
static cadical_t *get_cad(VALUE self, int allowed) {
|
|
114
|
+
cadical_t *c;
|
|
115
|
+
TypedData_Get_Struct(self, cadical_t, &cadical_type, c);
|
|
116
|
+
if (!c->solver) rb_raise(rb_eRuntimeError, "CaDiCaL already released");
|
|
117
|
+
if (!(c->state & allowed))
|
|
118
|
+
rb_raise(rb_eRuntimeError, "CaDiCaL: operation not allowed in the '%s' state", state_name(c->state));
|
|
119
|
+
return c;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* Record the state a call transitions to (cadical.hpp's 'ensure' postcondition). */
|
|
123
|
+
static void ensure(cadical_t *c, int state) { c->state = state; }
|
|
124
|
+
|
|
125
|
+
static VALUE cad_alloc(VALUE klass) {
|
|
126
|
+
cadical_t *c;
|
|
127
|
+
VALUE obj = TypedData_Make_Struct(klass, cadical_t, &cadical_type, c);
|
|
128
|
+
c->solver = ccadical_init();
|
|
129
|
+
c->on_terminate = Qnil;
|
|
130
|
+
c->on_learn = Qnil;
|
|
131
|
+
c->pending_exc = Qnil;
|
|
132
|
+
c->proof_file = NULL;
|
|
133
|
+
c->state = CONFIGURING;
|
|
134
|
+
return obj;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/*
|
|
138
|
+
* call-seq:
|
|
139
|
+
* solver.release -> nil
|
|
140
|
+
*
|
|
141
|
+
* Release the solver and its resources now (instead of at GC); the object is unusable
|
|
142
|
+
* afterwards.
|
|
143
|
+
*/
|
|
144
|
+
static VALUE cad_release(VALUE self) {
|
|
145
|
+
cadical_t *c = get_cad(self, READY);
|
|
146
|
+
if (c->proof_file) {
|
|
147
|
+
fclose(c->proof_file);
|
|
148
|
+
c->proof_file = NULL;
|
|
149
|
+
}
|
|
150
|
+
ccadical_release(c->solver);
|
|
151
|
+
c->solver = NULL;
|
|
152
|
+
return Qnil;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* --- clauses / assumptions / constraints --- */
|
|
156
|
+
|
|
157
|
+
/*
|
|
158
|
+
* call-seq:
|
|
159
|
+
* solver.add(*lits) -> self
|
|
160
|
+
*
|
|
161
|
+
* Add a permanent clause: the disjunction of the given literals.
|
|
162
|
+
*
|
|
163
|
+
* Unlike the C API you need not add the terminating 0, it is appended for you. With no
|
|
164
|
+
* literals it adds the empty clause (an unsatisfiable formula).
|
|
165
|
+
*/
|
|
166
|
+
static VALUE cad_add(int argc, VALUE *argv, VALUE self) {
|
|
167
|
+
cadical_t *c = get_cad(self, READY);
|
|
168
|
+
int i;
|
|
169
|
+
for (i = 0; i < argc; i++) ccadical_add(c->solver, NUM2INT(argv[i]));
|
|
170
|
+
ccadical_add(c->solver, 0);
|
|
171
|
+
ensure(c, STEADY); /* adding a clause leaves the SATISFIED / UNSATISFIED state */
|
|
172
|
+
return self;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/*
|
|
176
|
+
* call-seq:
|
|
177
|
+
* solver.assume(lit) -> self
|
|
178
|
+
*
|
|
179
|
+
* Assume valid non-zero literal +lit+ for next call to #solve. These assumptions are reset
|
|
180
|
+
* after the call to #solve as well as after returning from #simplify.
|
|
181
|
+
*
|
|
182
|
+
* See #failed.
|
|
183
|
+
*/
|
|
184
|
+
static VALUE cad_assume(VALUE self, VALUE lit) {
|
|
185
|
+
cadical_t *c = get_cad(self, READY);
|
|
186
|
+
ccadical_assume(c->solver, NUM2INT(lit));
|
|
187
|
+
ensure(c, STEADY);
|
|
188
|
+
return self;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/*
|
|
192
|
+
* call-seq:
|
|
193
|
+
* solver.constrain(*lits) -> self
|
|
194
|
+
*
|
|
195
|
+
* Same functionality as #add, but the clause only exists for the next call to #solve (same
|
|
196
|
+
* lifetime as assumptions). Only one constraint may exist at a time. A new constraint
|
|
197
|
+
* replaces the old.
|
|
198
|
+
*
|
|
199
|
+
* See #constraint_failed?.
|
|
200
|
+
*/
|
|
201
|
+
static VALUE cad_constrain(int argc, VALUE *argv, VALUE self) {
|
|
202
|
+
cadical_t *c = get_cad(self, READY);
|
|
203
|
+
int i;
|
|
204
|
+
for (i = 0; i < argc; i++) ccadical_constrain(c->solver, NUM2INT(argv[i]));
|
|
205
|
+
ccadical_constrain(c->solver, 0);
|
|
206
|
+
ensure(c, STEADY);
|
|
207
|
+
return self;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* --- solving --- */
|
|
211
|
+
|
|
212
|
+
/* Re-raise an exception deferred from a callback during the last solve/simplify. */
|
|
213
|
+
static void reraise_pending(cadical_t *c) {
|
|
214
|
+
if (!NIL_P(c->pending_exc)) {
|
|
215
|
+
VALUE e = c->pending_exc;
|
|
216
|
+
c->pending_exc = Qnil;
|
|
217
|
+
rb_exc_raise(e);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* #solve / #simplify run the (possibly long) search with the GVL released, so other Ruby
|
|
222
|
+
* threads keep running. The terminate/learn callbacks re-acquire the GVL (see below). */
|
|
223
|
+
struct solve_call {
|
|
224
|
+
CCaDiCaL *solver;
|
|
225
|
+
int result;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
static void *do_solve(void *p) {
|
|
229
|
+
struct solve_call *a = p;
|
|
230
|
+
a->result = ccadical_solve(a->solver);
|
|
231
|
+
return NULL;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
static void *do_simplify(void *p) {
|
|
235
|
+
struct solve_call *a = p;
|
|
236
|
+
a->result = ccadical_simplify(a->solver);
|
|
237
|
+
return NULL;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/* Unblock a GVL-released search if the calling thread is interrupted (e.g. Thread#kill):
|
|
241
|
+
* ccadical_terminate stops the search asynchronously, as the C API allows. */
|
|
242
|
+
static void unblock_solve(void *p) {
|
|
243
|
+
struct solve_call *a = p;
|
|
244
|
+
ccadical_terminate(a->solver);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/*
|
|
248
|
+
* call-seq:
|
|
249
|
+
* solver.solve -> :sat or :unsat or :unknown
|
|
250
|
+
*
|
|
251
|
+
* Try to solve the current formula. Returns +:sat+ (satisfiable), +:unsat+ (unsatisfiable),
|
|
252
|
+
* or +:unknown+ (limit reached or interrupted through #terminate).
|
|
253
|
+
*/
|
|
254
|
+
static VALUE cad_solve(VALUE self) {
|
|
255
|
+
cadical_t *c = get_cad(self, READY);
|
|
256
|
+
struct solve_call a = { c->solver, 0 };
|
|
257
|
+
rb_thread_call_without_gvl(do_solve, &a, unblock_solve, &a);
|
|
258
|
+
ensure(c, a.result == 10 ? SATISFIED : a.result == 20 ? UNSATISFIED : INCONCLUSIVE);
|
|
259
|
+
reraise_pending(c);
|
|
260
|
+
return result_sym(a.result);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/*
|
|
264
|
+
* call-seq:
|
|
265
|
+
* solver.simplify -> :sat or :unsat or :unknown
|
|
266
|
+
*
|
|
267
|
+
* This function executes preprocessing rounds. It is similar to #solve except that no CDCL nor local search, nor
|
|
268
|
+
* lucky phases are executed. The result values are also the same: +:sat+, +:unsat+, or
|
|
269
|
+
* +:unknown+.
|
|
270
|
+
*
|
|
271
|
+
* As #solve it resets current assumptions and limits before returning.
|
|
272
|
+
*/
|
|
273
|
+
static VALUE cad_simplify(VALUE self) {
|
|
274
|
+
cadical_t *c = get_cad(self, READY);
|
|
275
|
+
struct solve_call a = { c->solver, 0 };
|
|
276
|
+
rb_thread_call_without_gvl(do_simplify, &a, unblock_solve, &a);
|
|
277
|
+
ensure(c, a.result == 10 ? SATISFIED : a.result == 20 ? UNSATISFIED : INCONCLUSIVE);
|
|
278
|
+
reraise_pending(c);
|
|
279
|
+
return result_sym(a.result);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/*
|
|
283
|
+
* call-seq:
|
|
284
|
+
* solver.val(lit) -> integer
|
|
285
|
+
*
|
|
286
|
+
* Get the value of a valid non-zero literal +lit+. This follows the IPASIR semantics which
|
|
287
|
+
* says to return +lit+ if +lit+ is assigned to true and +-lit+ if +lit+ is assigned to false.
|
|
288
|
+
* This has the consequence that the returned literal is always assigned to true and thus might
|
|
289
|
+
* be a bit confusing. To avoid the headache of these semantics (which we unfortunately should
|
|
290
|
+
* follow to be compatible with IPASIR) the user can simply use positive variable indices
|
|
291
|
+
* instead of literals. Then the returned integer is negative if the variable is assigned to
|
|
292
|
+
* false and positive if it is assigned to true.
|
|
293
|
+
*
|
|
294
|
+
* Raises RuntimeError unless the last #solve returned +:sat+.
|
|
295
|
+
*/
|
|
296
|
+
static VALUE cad_val(VALUE self, VALUE lit) {
|
|
297
|
+
return INT2NUM(ccadical_val(get_cad(self, SATISFIED)->solver, NUM2INT(lit)));
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/*
|
|
301
|
+
* call-seq:
|
|
302
|
+
* solver.true?(lit) -> true or false
|
|
303
|
+
*
|
|
304
|
+
* Determine whether +lit+ is assigned to true (i.e. <tt>val(lit) == lit</tt>).
|
|
305
|
+
*
|
|
306
|
+
* Raises RuntimeError unless the last #solve returned +:sat+.
|
|
307
|
+
*/
|
|
308
|
+
static VALUE cad_is_true(VALUE self, VALUE lit) {
|
|
309
|
+
int l = NUM2INT(lit);
|
|
310
|
+
return ccadical_val(get_cad(self, SATISFIED)->solver, l) == l ? Qtrue : Qfalse;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/*
|
|
314
|
+
* call-seq:
|
|
315
|
+
* solver.failed(lit) -> true or false
|
|
316
|
+
*
|
|
317
|
+
* Determine whether the valid non-zero literal +lit+ is in the core. Returns true if the
|
|
318
|
+
* literal is in the core and false otherwise. Note that the core does not have to be minimal.
|
|
319
|
+
*
|
|
320
|
+
* Raises RuntimeError unless the last #solve returned +:unsat+.
|
|
321
|
+
*/
|
|
322
|
+
static VALUE cad_failed(VALUE self, VALUE lit) {
|
|
323
|
+
return ccadical_failed(get_cad(self, UNSATISFIED)->solver, NUM2INT(lit)) ? Qtrue : Qfalse;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/*
|
|
327
|
+
* call-seq:
|
|
328
|
+
* solver.constraint_failed? -> true or false
|
|
329
|
+
*
|
|
330
|
+
* Determine whether the constraint was used to prove the unsatisfiability. Note that the
|
|
331
|
+
* formula might still be unsatisfiable without the constraint.
|
|
332
|
+
*
|
|
333
|
+
* Raises RuntimeError unless the last #solve returned +:unsat+.
|
|
334
|
+
*/
|
|
335
|
+
static VALUE cad_constraint_failed(VALUE self) {
|
|
336
|
+
return ccadical_constraint_failed(get_cad(self, UNSATISFIED)->solver) ? Qtrue : Qfalse;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/*
|
|
340
|
+
* call-seq:
|
|
341
|
+
* solver.fixed(lit) -> 1, -1, or 0
|
|
342
|
+
*
|
|
343
|
+
* Root level assigned variables can be queried with this function. It returns +1+ if the
|
|
344
|
+
* literal is implied by the formula, +-1+ if its negation is implied, or +0+ if this is
|
|
345
|
+
* unclear at this point.
|
|
346
|
+
*/
|
|
347
|
+
static VALUE cad_fixed(VALUE self, VALUE lit) {
|
|
348
|
+
return INT2NUM(ccadical_fixed(get_cad(self, READY)->solver, NUM2INT(lit)));
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/* --- callbacks (set_terminate / set_learn) --- */
|
|
352
|
+
|
|
353
|
+
static VALUE call_proc0(VALUE proc) { return rb_funcall(proc, id_call, 0); }
|
|
354
|
+
|
|
355
|
+
static void *terminate_body(void *state) {
|
|
356
|
+
cadical_t *c = state;
|
|
357
|
+
int err = 0;
|
|
358
|
+
VALUE r = rb_protect(call_proc0, c->on_terminate, &err);
|
|
359
|
+
if (err) {
|
|
360
|
+
c->pending_exc = rb_errinfo();
|
|
361
|
+
rb_set_errinfo(Qnil);
|
|
362
|
+
return (void *)1; /* abort the solve so we return and can re-raise */
|
|
363
|
+
}
|
|
364
|
+
return (void *)(intptr_t)(RTEST(r) ? 1 : 0);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
static int terminate_trampoline(void *state) {
|
|
368
|
+
cadical_t *c = state;
|
|
369
|
+
if (NIL_P(c->on_terminate)) return 0; /* no callback: cheap, no GVL needed */
|
|
370
|
+
if (!NIL_P(c->pending_exc)) return 1; /* already failed: keep aborting */
|
|
371
|
+
return (int)(intptr_t)rb_thread_call_with_gvl(terminate_body, c);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
struct learn_call {
|
|
375
|
+
cadical_t *c;
|
|
376
|
+
int *clause;
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
static VALUE learn_invoke(VALUE arg) {
|
|
380
|
+
struct learn_call *lc = (struct learn_call *)arg;
|
|
381
|
+
VALUE ary = rb_ary_new();
|
|
382
|
+
int *p;
|
|
383
|
+
for (p = lc->clause; *p; p++) rb_ary_push(ary, INT2NUM(*p));
|
|
384
|
+
return rb_funcall(lc->c->on_learn, id_call, 1, ary);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
static void *learn_body(void *arg) {
|
|
388
|
+
struct learn_call *lc = arg;
|
|
389
|
+
int err = 0;
|
|
390
|
+
rb_protect(learn_invoke, (VALUE)lc, &err);
|
|
391
|
+
if (err) {
|
|
392
|
+
lc->c->pending_exc = rb_errinfo();
|
|
393
|
+
rb_set_errinfo(Qnil);
|
|
394
|
+
/* Unlike terminate, the learn callback returns void and cannot signal an abort, so ask
|
|
395
|
+
* the solver to stop asynchronously; #solve then returns and re-raises the exception. */
|
|
396
|
+
ccadical_terminate(lc->c->solver);
|
|
397
|
+
}
|
|
398
|
+
return NULL;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
static void learn_trampoline(void *state, int *clause) {
|
|
402
|
+
cadical_t *c = state;
|
|
403
|
+
struct learn_call lc;
|
|
404
|
+
if (NIL_P(c->on_learn) || !NIL_P(c->pending_exc)) return;
|
|
405
|
+
lc.c = c;
|
|
406
|
+
lc.clause = clause;
|
|
407
|
+
rb_thread_call_with_gvl(learn_body, &lc);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/*
|
|
411
|
+
* call-seq:
|
|
412
|
+
* solver.set_terminate { -> bool } -> self
|
|
413
|
+
* solver.set_terminate -> self
|
|
414
|
+
*
|
|
415
|
+
* Add a callback which is checked regularly for termination: return a truthy value from the
|
|
416
|
+
* block to abort the search (#solve then returns +:unknown+). There can only be one such
|
|
417
|
+
* callback; installing a second replaces the first, and calling with no block clears it.
|
|
418
|
+
*/
|
|
419
|
+
static VALUE cad_set_terminate(VALUE self) {
|
|
420
|
+
cadical_t *c = get_cad(self, READY);
|
|
421
|
+
if (rb_block_given_p()) {
|
|
422
|
+
c->on_terminate = rb_block_proc();
|
|
423
|
+
ccadical_set_terminate(c->solver, c, terminate_trampoline);
|
|
424
|
+
} else {
|
|
425
|
+
c->on_terminate = Qnil;
|
|
426
|
+
ccadical_set_terminate(c->solver, NULL, NULL);
|
|
427
|
+
}
|
|
428
|
+
return self;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/*
|
|
432
|
+
* call-seq:
|
|
433
|
+
* solver.set_learn(max_length) { |clause| ... } -> self
|
|
434
|
+
* solver.set_learn(max_length) -> self
|
|
435
|
+
*
|
|
436
|
+
* Add a callback which allows to export learned clauses: the block is invoked with each
|
|
437
|
+
* learned clause of at most +max_length+ literals, as an Array of literals.
|
|
438
|
+
* Installing a second callback replaces the first, and calling with no block clears it.
|
|
439
|
+
*/
|
|
440
|
+
static VALUE cad_set_learn(VALUE self, VALUE max_length) {
|
|
441
|
+
cadical_t *c = get_cad(self, READY);
|
|
442
|
+
if (rb_block_given_p()) {
|
|
443
|
+
c->on_learn = rb_block_proc();
|
|
444
|
+
ccadical_set_learn(c->solver, c, NUM2INT(max_length), learn_trampoline);
|
|
445
|
+
} else {
|
|
446
|
+
c->on_learn = Qnil;
|
|
447
|
+
ccadical_set_learn(c->solver, NULL, NUM2INT(max_length), NULL);
|
|
448
|
+
}
|
|
449
|
+
return self;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/*
|
|
453
|
+
* call-seq:
|
|
454
|
+
* solver.terminate -> self
|
|
455
|
+
*
|
|
456
|
+
* Force termination of #solve asynchronously.
|
|
457
|
+
*/
|
|
458
|
+
static VALUE cad_terminate(VALUE self) {
|
|
459
|
+
ccadical_terminate(get_cad(self, READY)->solver);
|
|
460
|
+
return self;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/* --- options / limits / stats --- */
|
|
464
|
+
|
|
465
|
+
/*
|
|
466
|
+
* call-seq:
|
|
467
|
+
* solver.set_option(name, value) -> self
|
|
468
|
+
*
|
|
469
|
+
* Set the option +name+ to +value+. If the option value is out of range the actual value is
|
|
470
|
+
* computed as the closest (minimum or maximum) value possible. Options can only be set right
|
|
471
|
+
* after initialization; the binding raises RuntimeError otherwise.
|
|
472
|
+
*/
|
|
473
|
+
static VALUE cad_set_option(VALUE self, VALUE name, VALUE val) {
|
|
474
|
+
ccadical_set_option(get_cad(self, CONFIGURING)->solver, StringValueCStr(name), NUM2INT(val));
|
|
475
|
+
return self;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/*
|
|
479
|
+
* call-seq:
|
|
480
|
+
* solver.get_option(name) -> integer
|
|
481
|
+
*
|
|
482
|
+
* Get the current value of the option +name+. If +name+ is invalid then zero is returned.
|
|
483
|
+
*/
|
|
484
|
+
static VALUE cad_get_option(VALUE self, VALUE name) {
|
|
485
|
+
return INT2NUM(ccadical_get_option(get_cad(self, READY)->solver, StringValueCStr(name)));
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/*
|
|
489
|
+
* call-seq:
|
|
490
|
+
* solver.limit(name, value) -> self
|
|
491
|
+
*
|
|
492
|
+
* Specify search limits, where currently +name+ can be "conflicts", "decisions",
|
|
493
|
+
* "preprocessing", or "localsearch". The first two limits are unbounded by default. Thus using
|
|
494
|
+
* a negative limit for conflicts or decisions switches back to the default of unlimited search
|
|
495
|
+
* (for that particular limit). The preprocessing limit determines the number of preprocessing
|
|
496
|
+
* rounds, which is zero by default. Similarly, the local search limit determines the number of
|
|
497
|
+
* local search rounds (zero by default).
|
|
498
|
+
*
|
|
499
|
+
* These limits are only valid for the next #solve or #simplify call and reset to their default
|
|
500
|
+
* after #solve returns (as well as overwritten and reset during calls to #simplify).
|
|
501
|
+
*
|
|
502
|
+
* An unknown +name+ raises ArgumentError.
|
|
503
|
+
*/
|
|
504
|
+
static VALUE cad_limit(VALUE self, VALUE name, VALUE limit) {
|
|
505
|
+
const char *n = StringValueCStr(name);
|
|
506
|
+
cadical_t *c = get_cad(self, READY);
|
|
507
|
+
if (strcmp(n, "conflicts") && strcmp(n, "decisions") &&
|
|
508
|
+
strcmp(n, "preprocessing") && strcmp(n, "localsearch"))
|
|
509
|
+
rb_raise(rb_eArgError, "unknown CaDiCaL limit: %s", n);
|
|
510
|
+
ccadical_limit(c->solver, n, NUM2INT(limit));
|
|
511
|
+
return self;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/*
|
|
515
|
+
* call-seq:
|
|
516
|
+
* solver.active -> integer
|
|
517
|
+
*
|
|
518
|
+
* Returns the number of currently active variables. A variable becomes active if a clause is
|
|
519
|
+
* added with it, and inactive again if it is eliminated or fixed at the root level.
|
|
520
|
+
*/
|
|
521
|
+
static VALUE cad_active(VALUE self) {
|
|
522
|
+
return LL2NUM(ccadical_active(get_cad(self, READY)->solver));
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/*
|
|
526
|
+
* call-seq:
|
|
527
|
+
* solver.irredundant -> integer
|
|
528
|
+
*
|
|
529
|
+
* Returns the number of currently active irredundant clauses. A clause becomes inactive if it
|
|
530
|
+
* is satisfied, subsumed, or eliminated.
|
|
531
|
+
*/
|
|
532
|
+
static VALUE cad_irredundant(VALUE self) {
|
|
533
|
+
return LL2NUM(ccadical_irredundant(get_cad(self, READY)->solver));
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/* --- variables / phases / freezing --- */
|
|
537
|
+
|
|
538
|
+
/*
|
|
539
|
+
* call-seq:
|
|
540
|
+
* solver.declare_one_more_variable -> integer
|
|
541
|
+
*
|
|
542
|
+
* Returns the next fresh variable that was not used internally.
|
|
543
|
+
*/
|
|
544
|
+
static VALUE cad_declare_one_more_variable(VALUE self) {
|
|
545
|
+
cadical_t *c = get_cad(self, READY);
|
|
546
|
+
int v = ccadical_declare_one_more_variable(c->solver);
|
|
547
|
+
ensure(c, STEADY);
|
|
548
|
+
return INT2NUM(v);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/*
|
|
552
|
+
* call-seq:
|
|
553
|
+
* solver.declare_more_variables(n) -> integer
|
|
554
|
+
*
|
|
555
|
+
* Increase the maximum variable index by +n+ new variables. Returns the new maximum variable
|
|
556
|
+
* index, which is the highest variable name of the consecutive range of newly declared
|
|
557
|
+
* variables.
|
|
558
|
+
*/
|
|
559
|
+
static VALUE cad_declare_more_variables(VALUE self, VALUE n) {
|
|
560
|
+
cadical_t *c = get_cad(self, READY);
|
|
561
|
+
int v = ccadical_declare_more_variables(c->solver, NUM2INT(n));
|
|
562
|
+
ensure(c, STEADY);
|
|
563
|
+
return INT2NUM(v);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/*
|
|
567
|
+
* call-seq:
|
|
568
|
+
* solver.vars -> integer
|
|
569
|
+
*
|
|
570
|
+
* The library internally keeps a maximum variable index, which this returns.
|
|
571
|
+
*/
|
|
572
|
+
static VALUE cad_vars(VALUE self) {
|
|
573
|
+
return INT2NUM(ccadical_vars(get_cad(self, READY)->solver));
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/*
|
|
577
|
+
* call-seq:
|
|
578
|
+
* solver.phase(lit) -> self
|
|
579
|
+
*
|
|
580
|
+
* Force the default decision phase of a variable to a certain value (the sign of +lit+).
|
|
581
|
+
*/
|
|
582
|
+
static VALUE cad_phase(VALUE self, VALUE lit) {
|
|
583
|
+
ccadical_phase(get_cad(self, READY)->solver, NUM2INT(lit));
|
|
584
|
+
return self;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/*
|
|
588
|
+
* call-seq:
|
|
589
|
+
* solver.unphase(lit) -> self
|
|
590
|
+
*
|
|
591
|
+
* Reset the default decision phase of +lit+'s variable forced by #phase.
|
|
592
|
+
*/
|
|
593
|
+
static VALUE cad_unphase(VALUE self, VALUE lit) {
|
|
594
|
+
ccadical_unphase(get_cad(self, READY)->solver, NUM2INT(lit));
|
|
595
|
+
return self;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/* CaDiCaL's freeze/melt/frozen for literals, exposed as #freeze_lit / #melt_lit / #frozen_lit?.
|
|
599
|
+
* The +_lit+ suffix keeps #freeze_lit and #frozen_lit? from shadowing Ruby's Object#freeze /
|
|
600
|
+
* #frozen? (which take no argument and mean object immutability); #melt_lit follows suit. */
|
|
601
|
+
|
|
602
|
+
/*
|
|
603
|
+
* call-seq:
|
|
604
|
+
* solver.freeze_lit(lit) -> self
|
|
605
|
+
*
|
|
606
|
+
* #freeze_lit, #melt_lit and #frozen_lit? are reference-counting functions which avoid restoring
|
|
607
|
+
* clauses but require substantial user guidance. This was the only way to use inprocessing in
|
|
608
|
+
* incremental SAT solving in Lingeling (and before in MiniSAT's 'freeze' / 'thaw') and which did
|
|
609
|
+
* not use automatic clause restoring. In general this is slower than restoring clauses and should
|
|
610
|
+
* not be used.
|
|
611
|
+
*
|
|
612
|
+
* In essence the user freezes variables which potentially are still needed in clauses added or
|
|
613
|
+
* assumptions used after the next #solve call. As in Lingeling you can freeze a variable multiple
|
|
614
|
+
* times, but then have to #melt_lit it the same number of times again in order to enable variable
|
|
615
|
+
* eliminating on it etc. The arguments can be literals (negative indices) but conceptually
|
|
616
|
+
* variables are frozen.
|
|
617
|
+
*
|
|
618
|
+
* In the old way of doing things without restore you should not use a variable incrementally (in
|
|
619
|
+
* #add or #assume), which was used before and potentially could have been eliminated in a previous
|
|
620
|
+
* #solve call. This can lead to spurious satisfying assignment. In order to check this API
|
|
621
|
+
* contract one can use the "checkfrozen" option (via #set_option). This has the drawback that
|
|
622
|
+
* restoring clauses implicitly would fail with a fatal error message even if in principle the
|
|
623
|
+
* solver could just restore clauses. Thus this option is disabled by default.
|
|
624
|
+
*
|
|
625
|
+
* See the CaDiCaL SAT'19 paper [FazekasBiereScholl-SAT'19] for more details.
|
|
626
|
+
*/
|
|
627
|
+
static VALUE cad_freeze_lit(VALUE self, VALUE lit) {
|
|
628
|
+
ccadical_freeze(get_cad(self, READY)->solver, NUM2INT(lit));
|
|
629
|
+
return self;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/*
|
|
633
|
+
* call-seq:
|
|
634
|
+
* solver.melt_lit(lit) -> self
|
|
635
|
+
*
|
|
636
|
+
* Melt (thaw) the variable of +lit+, reversing one #freeze_lit: once its freeze count reaches
|
|
637
|
+
* zero, simplification may eliminate the variable again. The variable must currently be frozen.
|
|
638
|
+
*/
|
|
639
|
+
static VALUE cad_melt_lit(VALUE self, VALUE lit) {
|
|
640
|
+
ccadical_melt(get_cad(self, READY)->solver, NUM2INT(lit));
|
|
641
|
+
return self;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/*
|
|
645
|
+
* call-seq:
|
|
646
|
+
* solver.frozen_lit?(lit) -> true or false
|
|
647
|
+
*
|
|
648
|
+
* Returns whether the variable of +lit+ is currently frozen (see #freeze_lit).
|
|
649
|
+
*/
|
|
650
|
+
static VALUE cad_frozen_lit(VALUE self, VALUE lit) {
|
|
651
|
+
return ccadical_frozen(get_cad(self, READY)->solver, NUM2INT(lit)) ? Qtrue : Qfalse;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/* --- proof tracing --- */
|
|
655
|
+
|
|
656
|
+
/*
|
|
657
|
+
* call-seq:
|
|
658
|
+
* solver.trace_proof(path) -> true or false
|
|
659
|
+
*
|
|
660
|
+
* Enable clausal proof tracing to the file at +path+; returns true if it was successfully
|
|
661
|
+
* opened for writing. Must be called before any #add or #solve, else it raises RuntimeError.
|
|
662
|
+
*/
|
|
663
|
+
static VALUE cad_trace_proof(VALUE self, VALUE path) {
|
|
664
|
+
cadical_t *c = get_cad(self, CONFIGURING);
|
|
665
|
+
const char *p = StringValueCStr(path);
|
|
666
|
+
FILE *f = fopen(p, "w");
|
|
667
|
+
int ok;
|
|
668
|
+
if (!f) rb_sys_fail(p);
|
|
669
|
+
ok = ccadical_trace_proof(c->solver, f, p);
|
|
670
|
+
if (!ok) {
|
|
671
|
+
fclose(f);
|
|
672
|
+
return Qfalse;
|
|
673
|
+
}
|
|
674
|
+
if (c->proof_file) fclose(c->proof_file);
|
|
675
|
+
c->proof_file = f;
|
|
676
|
+
return Qtrue;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/*
|
|
680
|
+
* call-seq:
|
|
681
|
+
* solver.close_proof -> self
|
|
682
|
+
*
|
|
683
|
+
* Close the proof trace early. Before actually closing, CaDiCaL checks whether a proof step (a
|
|
684
|
+
* clause addition or deletion) has been traced since the last flush and, if so, flushes it
|
|
685
|
+
* first. The file opened by #trace_proof is closed as well.
|
|
686
|
+
*
|
|
687
|
+
* Raises RuntimeError unless a proof trace is currently open (CaDiCaL would otherwise abort the
|
|
688
|
+
* process).
|
|
689
|
+
*/
|
|
690
|
+
static VALUE cad_close_proof(VALUE self) {
|
|
691
|
+
cadical_t *c = get_cad(self, READY);
|
|
692
|
+
if (!c->proof_file) rb_raise(rb_eRuntimeError, "no proof trace is open (see #trace_proof)");
|
|
693
|
+
ccadical_close_proof(c->solver);
|
|
694
|
+
fclose(c->proof_file);
|
|
695
|
+
c->proof_file = NULL;
|
|
696
|
+
return self;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/*
|
|
700
|
+
* call-seq:
|
|
701
|
+
* solver.conclude -> self
|
|
702
|
+
*
|
|
703
|
+
* Triggers the conclusion of incremental proofs. If the solver is satisfied it gives the model
|
|
704
|
+
* to the proof tracer; if it is unsatisfied it learns the failing core (for failed assumptions
|
|
705
|
+
* the core negated as a single clause, for a failing constraint possibly several) and concludes
|
|
706
|
+
* with the ids of the newly learnt clauses or the id of the global conflict; if the result was
|
|
707
|
+
* unknown it collects the currently entailed literals and adds them to the proof.
|
|
708
|
+
*
|
|
709
|
+
* Raises RuntimeError unless a #solve or #simplify has completed.
|
|
710
|
+
*/
|
|
711
|
+
static VALUE cad_conclude(VALUE self) {
|
|
712
|
+
ccadical_conclude(get_cad(self, SOLVED)->solver);
|
|
713
|
+
return self;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/* --- class-level --- */
|
|
717
|
+
|
|
718
|
+
/*
|
|
719
|
+
* call-seq:
|
|
720
|
+
* CaDiCaL.signature -> string
|
|
721
|
+
*
|
|
722
|
+
* The name of this library.
|
|
723
|
+
*/
|
|
724
|
+
static VALUE cad_signature(VALUE klass) {
|
|
725
|
+
(void)klass;
|
|
726
|
+
return rb_str_new_cstr(ccadical_signature());
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
void Init_cadical(void) {
|
|
730
|
+
VALUE c;
|
|
731
|
+
|
|
732
|
+
id_sat = rb_intern("sat");
|
|
733
|
+
id_unsat = rb_intern("unsat");
|
|
734
|
+
id_unknown = rb_intern("unknown");
|
|
735
|
+
id_call = rb_intern("call");
|
|
736
|
+
|
|
737
|
+
c = rb_define_class("CaDiCaL", rb_cObject);
|
|
738
|
+
rb_define_alloc_func(c, cad_alloc);
|
|
739
|
+
|
|
740
|
+
rb_define_singleton_method(c, "signature", cad_signature, 0);
|
|
741
|
+
|
|
742
|
+
rb_define_method(c, "release", cad_release, 0);
|
|
743
|
+
|
|
744
|
+
rb_define_method(c, "add", cad_add, -1);
|
|
745
|
+
rb_define_method(c, "assume", cad_assume, 1);
|
|
746
|
+
rb_define_method(c, "constrain", cad_constrain, -1);
|
|
747
|
+
|
|
748
|
+
rb_define_method(c, "solve", cad_solve, 0);
|
|
749
|
+
rb_define_method(c, "simplify", cad_simplify, 0);
|
|
750
|
+
rb_define_method(c, "val", cad_val, 1);
|
|
751
|
+
rb_define_method(c, "true?", cad_is_true, 1);
|
|
752
|
+
rb_define_method(c, "failed", cad_failed, 1);
|
|
753
|
+
rb_define_method(c, "constraint_failed?", cad_constraint_failed, 0);
|
|
754
|
+
rb_define_method(c, "fixed", cad_fixed, 1);
|
|
755
|
+
|
|
756
|
+
rb_define_method(c, "set_terminate", cad_set_terminate, 0);
|
|
757
|
+
rb_define_method(c, "set_learn", cad_set_learn, 1);
|
|
758
|
+
rb_define_method(c, "terminate", cad_terminate, 0);
|
|
759
|
+
|
|
760
|
+
rb_define_method(c, "set_option", cad_set_option, 2);
|
|
761
|
+
rb_define_method(c, "get_option", cad_get_option, 1);
|
|
762
|
+
rb_define_method(c, "limit", cad_limit, 2);
|
|
763
|
+
rb_define_method(c, "active", cad_active, 0);
|
|
764
|
+
rb_define_method(c, "irredundant", cad_irredundant, 0);
|
|
765
|
+
|
|
766
|
+
rb_define_method(c, "declare_one_more_variable", cad_declare_one_more_variable, 0);
|
|
767
|
+
rb_define_method(c, "declare_more_variables", cad_declare_more_variables, 1);
|
|
768
|
+
rb_define_method(c, "vars", cad_vars, 0);
|
|
769
|
+
rb_define_method(c, "phase", cad_phase, 1);
|
|
770
|
+
rb_define_method(c, "unphase", cad_unphase, 1);
|
|
771
|
+
rb_define_method(c, "freeze_lit", cad_freeze_lit, 1);
|
|
772
|
+
rb_define_method(c, "melt_lit", cad_melt_lit, 1);
|
|
773
|
+
rb_define_method(c, "frozen_lit?", cad_frozen_lit, 1);
|
|
774
|
+
|
|
775
|
+
rb_define_method(c, "trace_proof", cad_trace_proof, 1);
|
|
776
|
+
rb_define_method(c, "close_proof", cad_close_proof, 0);
|
|
777
|
+
rb_define_method(c, "conclude", cad_conclude, 0);
|
|
778
|
+
}
|