Repository navigation
Expand file tree
/
Copy pathSidekick_core.ml
More file actions
1562 lines (1215 loc) · 50.7 KB
/
Copy pathSidekick_core.ml
File metadata and controls
1562 lines (1215 loc) · 50.7 KB
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
(** {1 Main Signatures}
Theories and concrete solvers rely on an environment that defines
several important types:
- sorts
- terms (to represent logic expressions and formulas)
- a congruence closure instance
- a bridge to some SAT solver
In this module we define most of the main signatures used
throughout Sidekick.
*)
module Fmt = CCFormat
(** View terms through the lens of the Congruence Closure *)
module CC_view = struct
(** A view of a term fron the point of view of the congruence closure.
- ['f] is the type of function symbols
- ['t] is the type of terms
- ['ts] is the type of sequences of terms (arguments of function application)
*)
type ('f, 't, 'ts) t =
| Bool of bool
| App_fun of 'f * 'ts
| App_ho of 't * 't
| If of 't * 't * 't
| Eq of 't * 't
| Not of 't
| Opaque of 't
(* do not enter *)
(** Map function over a view, one level deep.
Each function maps over a different type, e.g. [f_t] maps over terms *)
let map_view ~f_f ~f_t ~f_ts (v : _ t) : _ t =
match v with
| Bool b -> Bool b
| App_fun (f, args) -> App_fun (f_f f, f_ts args)
| App_ho (f, a) -> App_ho (f_t f, f_t a)
| Not t -> Not (f_t t)
| If (a, b, c) -> If (f_t a, f_t b, f_t c)
| Eq (a, b) -> Eq (f_t a, f_t b)
| Opaque t -> Opaque (f_t t)
(** Iterate over a view, one level deep. *)
let iter_view ~f_f ~f_t ~f_ts (v : _ t) : unit =
match v with
| Bool _ -> ()
| App_fun (f, args) ->
f_f f;
f_ts args
| App_ho (f, a) ->
f_t f;
f_t a
| Not t -> f_t t
| If (a, b, c) ->
f_t a;
f_t b;
f_t c
| Eq (a, b) ->
f_t a;
f_t b
| Opaque t -> f_t t
end
(** Main representation of Terms and Types *)
module type TERM = sig
(** A function symbol, like "f" or "plus" or "is_human" or "socrates" *)
module Fun : sig
type t
val equal : t -> t -> bool
val hash : t -> int
val pp : t Fmt.printer
end
(** Types
Types should be comparable (ideally, in O(1)), and have
at least a boolean type available. *)
module Ty : sig
type t
val equal : t -> t -> bool
val hash : t -> int
val pp : t Fmt.printer
type store
val bool : store -> t
val is_bool : t -> bool
end
(** Term structure.
Terms should be {b hashconsed}, with perfect sharing.
This allows, for example, {!Term.Tbl} and {!Term.iter_dag} to be efficient.
*)
module Term : sig
type t
val equal : t -> t -> bool
val compare : t -> t -> int
val hash : t -> int
val pp : t Fmt.printer
type store
(** A store used to create new terms. It is where the hashconsing
table should live, along with other all-terms related store. *)
val ty : t -> Ty.t
val bool : store -> bool -> t
(** build true/false *)
val as_bool : t -> bool option
(** [as_bool t] is [Some true] if [t] is the term [true], and similarly
for [false]. For other terms it is [None]. *)
val abs : store -> t -> t * bool
(** [abs t] returns an "absolute value" for the term, along with the
sign of [t].
The idea is that we want to turn [not a] into [(a, false)],
or [(a != b)] into [(a=b, false)]. For terms without a negation this
should return [(t, true)].
The store is passed in case a new term needs to be created. *)
val map_shallow : store -> (t -> t) -> t -> t
(** Map function on immediate subterms. This should not be recursive. *)
val iter_shallow : store -> (t -> unit) -> t -> unit
(** Iterate function on immediate subterms. This should not be recursive. *)
val iter_dag : t -> (t -> unit) -> unit
(** [iter_dag t f] calls [f] once on each subterm of [t], [t] included.
It must {b not} traverse [t] as a tree, but rather as a
perfectly shared DAG.
For example, in:
{[
let x = 2 in
let y = f x x in
let z = g y x in
z = z
]}
the DAG has the following nodes:
{[ n1: 2
n2: f n1 n1
n3: g n2 n1
n4: = n3 n3
]}
*)
module Tbl : CCHashtbl.S with type key = t
end
end
(** Signature for SAT-solver proof emission. *)
module type SAT_PROOF = sig
type t
(** The stored proof (possibly nil, possibly on disk, possibly in memory) *)
type proof_step
(** identifier for a proof *)
module Step_vec : Vec_sig.BASE with type elt = proof_step
(** A vector of steps *)
type lit
(** A boolean literal for the proof trace *)
type proof_rule = t -> proof_step
(** A proof proof_rule constructor, used to obtain proofs from theories *)
val enabled : t -> bool
(** Returns true if proof production is enabled *)
val emit_input_clause : lit Iter.t -> proof_rule
(** Emit an input clause. *)
val emit_redundant_clause : lit Iter.t -> hyps:proof_step Iter.t -> proof_rule
(** Emit a clause deduced by the SAT solver, redundant wrt previous clauses.
The clause must be RUP wrt [hyps]. *)
val emit_unsat_core : lit Iter.t -> proof_rule
(** Produce a proof of the empty clause given this subset of the assumptions.
FIXME: probably needs the list of proof_step that disprove the lits? *)
val emit_unsat : proof_step -> t -> unit
(** Signal "unsat" result at the given proof *)
val del_clause : proof_step -> lit Iter.t -> t -> unit
(** Forget a clause. Only useful for performance considerations. *)
end
(** Proofs of unsatisfiability. *)
module type PROOF = sig
type t
(** The abstract representation of a proof. A proof always proves
a clause to be {b valid} (true in every possible interpretation
of the problem's assertions, and the theories) *)
type proof_step
(** Identifier for a proof proof_rule (like a unique ID for a clause previously
added/proved) *)
type term
type lit
type proof_rule = t -> proof_step
include
SAT_PROOF
with type t := t
and type lit := lit
and type proof_step := proof_step
and type proof_rule := proof_rule
val lemma_cc : lit Iter.t -> proof_rule
(** [lemma_cc proof lits] asserts that [lits] form a tautology for the theory
of uninterpreted functions. *)
val define_term : term -> term -> proof_rule
(** [define_term cst u proof] defines the new constant [cst] as being equal
to [u].
The result is a proof of the clause [cst = u] *)
val proof_p1 : proof_step -> proof_step -> proof_rule
(** [proof_p1 p1 p2], where [p1] proves the unit clause [t=u] (t:bool)
and [p2] proves [C \/ t], is the rule that produces [C \/ u],
i.e unit paramodulation. *)
val proof_r1 : proof_step -> proof_step -> proof_rule
(** [proof_r1 p1 p2], where [p1] proves the unit clause [|- t] (t:bool)
and [p2] proves [C \/ ¬t], is the rule that produces [C \/ u],
i.e unit resolution. *)
val proof_res : pivot:term -> proof_step -> proof_step -> proof_rule
(** [proof_res ~pivot p1 p2], where [p1] proves the clause [|- C \/ l]
and [p2] proves [D \/ ¬l], where [l] is either [pivot] or [¬pivot],
is the rule that produces [C \/ D], i.e boolean resolution. *)
val with_defs : proof_step -> proof_step Iter.t -> proof_rule
(** [with_defs pr defs] specifies that [pr] is valid only in
a context where the definitions [defs] are present. *)
val lemma_true : term -> proof_rule
(** [lemma_true (true) p] asserts the clause [(true)] *)
val lemma_preprocess : term -> term -> using:proof_step Iter.t -> proof_rule
(** [lemma_preprocess t u ~using p] asserts that [t = u] is a tautology
and that [t] has been preprocessed into [u].
The theorem [/\_{eqn in using} eqn |- t=u] is proved using congruence
closure, and then resolved against the clauses [using] to obtain
a unit equality.
From now on, [t] and [u] will be used interchangeably.
@return a proof_rule ID for the clause [(t=u)]. *)
val lemma_rw_clause :
proof_step -> res:lit Iter.t -> using:proof_step Iter.t -> proof_rule
(** [lemma_rw_clause prc ~res ~using], where [prc] is the proof of [|- c],
uses the equations [|- p_i = q_i] from [using]
to rewrite some literals of [c] into [res]. This is used to preprocess
literals of a clause (using {!lemma_preprocess} individually). *)
end
(** Literals
Literals are a pair of a boolean-sorted term, and a sign.
Positive literals are the same as their term, and negative literals
are the negation of their term.
The SAT solver deals only in literals and clauses (sets of literals).
Everything else belongs in the SMT solver. *)
module type LIT = sig
module T : TERM
(** Literals depend on terms *)
type t
(** A literal *)
val term : t -> T.Term.t
(** Get the (positive) term *)
val sign : t -> bool
(** Get the sign. A negated literal has sign [false]. *)
val neg : t -> t
(** Take negation of literal. [sign (neg lit) = not (sign lit)]. *)
val abs : t -> t
(** [abs lit] is like [lit] but always positive, i.e. [sign (abs lit) = true] *)
val signed_term : t -> T.Term.t * bool
(** Return the atom and the sign *)
val atom : ?sign:bool -> T.Term.store -> T.Term.t -> t
(** [atom store t] makes a literal out of a term, possibly normalizing
its sign in the process.
@param sign if provided, and [sign=false], negate the resulting lit. *)
val norm_sign : t -> t * bool
(** [norm_sign (+t)] is [+t, true],
and [norm_sign (-t)] is [+t, false].
In both cases the term is positive, and the boolean reflects the initial sign. *)
val equal : t -> t -> bool
val hash : t -> int
val pp : t Fmt.printer
end
(** Actions provided to the congruence closure.
The congruence closure must be able to propagate literals when
it detects that they are true or false; it must also
be able to create conflicts when the set of (dis)equalities
is inconsistent *)
module type CC_ACTIONS = sig
module T : TERM
module Lit : LIT with module T = T
type proof
type proof_step
module P :
PROOF
with type lit = Lit.t
and type t = proof
and type term = T.Term.t
and type proof_step = proof_step
type t
(** An action handle. It is used by the congruence closure
to perform the actions below. How it performs the actions
is not specified and is solver-specific. *)
val proof : t -> proof
val raise_conflict : t -> Lit.t list -> proof_step -> 'a
(** [raise_conflict acts c pr] declares that [c] is a tautology of
the theory of congruence. This does not return (it should raise an
exception).
@param pr the proof of [c] being a tautology *)
val raise_semantic_conflict :
t -> Lit.t list -> (bool * T.Term.t * T.Term.t) list -> 'a
(** [raise_semantic_conflict acts lits same_val] declares that
the conjunction of all [lits] (literals true in current trail) and tuples
[{=,≠}, t_i, u_i] implies false.
The [{=,≠}, t_i, u_i] are pairs of terms with the same value (if [=] / true)
or distinct value (if [≠] / false)) in the current model.
This does not return. It should raise an exception.
*)
val propagate : t -> Lit.t -> reason:(unit -> Lit.t list * proof_step) -> unit
(** [propagate acts lit ~reason pr] declares that [reason() => lit]
is a tautology.
- [reason()] should return a list of literals that are currently true.
- [lit] should be a literal of interest (see {!CC_S.set_as_lit}).
This function might never be called, a congruence closure has the right
to not propagate and only trigger conflicts. *)
end
(** Arguments to a congruence closure's implementation *)
module type CC_ARG = sig
module T : TERM
module Lit : LIT with module T = T
type proof
type proof_step
module P :
PROOF
with type lit = Lit.t
and type t = proof
and type term = T.Term.t
and type proof_step = proof_step
module Actions :
CC_ACTIONS
with module T = T
and module Lit = Lit
and type proof = proof
and type proof_step = proof_step
val cc_view : T.Term.t -> (T.Fun.t, T.Term.t, T.Term.t Iter.t) CC_view.t
(** View the term through the lens of the congruence closure *)
val mk_lit_eq : ?sign:bool -> T.Term.store -> T.Term.t -> T.Term.t -> Lit.t
(** [mk_lit_eq store t u] makes the literal [t=u] *)
end
(** Main congruence closure signature.
The congruence closure handles the theory QF_UF (uninterpreted
function symbols).
It is also responsible for {i theory combination}, and provides
a general framework for equality reasoning that other
theories piggyback on.
For example, the theory of datatypes relies on the congruence closure
to do most of the work, and "only" adds injectivity/disjointness/acyclicity
lemmas when needed.
Similarly, a theory of arrays would hook into the congruence closure and
assert (dis)equalities as needed.
*)
module type CC_S = sig
(** first, some aliases. *)
module T : TERM
module Lit : LIT with module T = T
type proof
type proof_step
module P :
PROOF
with type lit = Lit.t
and type t = proof
and type proof_step = proof_step
module Actions :
CC_ACTIONS
with module T = T
and module Lit = Lit
and type proof = proof
and type proof_step = proof_step
type term_store = T.Term.store
type term = T.Term.t
type value = term
type fun_ = T.Fun.t
type lit = Lit.t
type actions = Actions.t
type t
(** The congruence closure object.
It contains a fair amount of state and is mutable
and backtrackable. *)
(** Equivalence classes.
An equivalence class is a set of terms that are currently equal
in the partial model built by the solver.
The class is represented by a collection of nodes, one of which is
distinguished and is called the "representative".
All information pertaining to the whole equivalence class is stored
in this representative's node.
When two classes become equal (are "merged"), one of the two
representatives is picked as the representative of the new class.
The new class contains the union of the two old classes' nodes.
We also allow theories to store additional information in the
representative. This information can be used when two classes are
merged, to detect conflicts and solve equations à la Shostak.
*)
module N : sig
type t
(** An equivalent class, containing terms that are proved
to be equal.
A value of type [t] points to a particular term, but see
{!find} to get the representative of the class. *)
val term : t -> term
(** Term contained in this equivalence class.
If [is_root n], then [term n] is the class' representative term. *)
val equal : t -> t -> bool
(** Are two classes {b physically} equal? To check for
logical equality, use [CC.N.equal (CC.find cc n1) (CC.find cc n2)]
which checks for equality of representatives. *)
val hash : t -> int
(** An opaque hash of this node. *)
val pp : t Fmt.printer
(** Unspecified printing of the node, for example its term,
a unique ID, etc. *)
val is_root : t -> bool
(** Is the node a root (ie the representative of its class)?
See {!find} to get the root. *)
val iter_class : t -> t Iter.t
(** Traverse the congruence class.
Precondition: [is_root n] (see {!find} below) *)
val iter_parents : t -> t Iter.t
(** Traverse the parents of the class.
Precondition: [is_root n] (see {!find} below) *)
type bitfield
(** A field in the bitfield of this node. This should only be
allocated when a theory is initialized.
Bitfields are accessed using preallocated keys.
See {!CC_S.allocate_bitfield}.
All fields are initially 0, are backtracked automatically,
and are merged automatically when classes are merged. *)
end
(** Explanations
Explanations are specialized proofs, created by the congruence closure
when asked to justify why 2 terms are equal. *)
module Expl : sig
type t
val pp : t Fmt.printer
val mk_merge : N.t -> N.t -> t
(** Explanation: the nodes were explicitly merged *)
val mk_merge_t : term -> term -> t
(** Explanation: the terms were explicitly merged *)
val mk_lit : lit -> t
(** Explanation: we merged [t] and [u] because of literal [t=u],
or we merged [t] and [true] because of literal [t],
or [t] and [false] because of literal [¬t] *)
val mk_same_value : N.t -> N.t -> t
val mk_list : t list -> t
(** Conjunction of explanations *)
val mk_theory :
term -> term -> (term * term * t list) list -> proof_step -> t
(** [mk_theory t u expl_sets pr] builds a theory explanation for
why [|- t=u]. It depends on sub-explanations [expl_sets] which
are tuples [ (t_i, u_i, expls_i) ] where [expls_i] are
explanations that justify [t_i = u_i] in the current congruence closure.
The proof [pr] is the theory lemma, of the form
[ (t_i = u_i)_i |- t=u ].
It is resolved against each [expls_i |- t_i=u_i] obtained from
[expl_sets], on pivot [t_i=u_i], to obtain a proof of [Gamma |- t=u]
where [Gamma] is a subset of the literals asserted into the congruence
closure.
For example for the lemma [a=b] deduced by injectivity
from [Some a=Some b] in the theory of datatypes,
the arguments would be
[a, b, [Some a, Some b, mk_merge_t (Some a)(Some b)], pr]
where [pr] is the injectivity lemma [Some a=Some b |- a=b].
*)
end
(** Resolved explanations.
The congruence closure keeps explanations for why terms are in the same
class. However these are represented in a compact, cheap form.
To use these explanations we need to {b resolve} them into a
resolved explanation, typically a list of
literals that are true in the current trail and are responsible for
merges.
However, we can also have merged classes because they have the same value
in the current model. *)
module Resolved_expl : sig
type t = {
lits: lit list;
same_value: (N.t * N.t) list;
pr: proof -> proof_step;
}
val is_semantic : t -> bool
(** [is_semantic expl] is [true] if there's at least one
pair in [expl.same_value]. *)
val pp : t Fmt.printer
end
type node = N.t
(** A node of the congruence closure *)
type repr = N.t
(** Node that is currently a representative *)
type explanation = Expl.t
(** {3 Accessors} *)
val term_store : t -> term_store
val proof : t -> proof
val find : t -> node -> repr
(** Current representative *)
val add_term : t -> term -> node
(** Add the term to the congruence closure, if not present already.
Will be backtracked. *)
val mem_term : t -> term -> bool
(** Returns [true] if the term is explicitly present in the congruence closure *)
(** {3 Events}
Events triggered by the congruence closure, to which
other plugins can subscribe. *)
type ev_on_pre_merge = t -> actions -> N.t -> N.t -> Expl.t -> unit
(** [ev_on_pre_merge cc acts n1 n2 expl] is called right before [n1]
and [n2] are merged with explanation [expl]. *)
type ev_on_post_merge = t -> actions -> N.t -> N.t -> unit
(** [ev_on_post_merge cc acts n1 n2] is called right after [n1]
and [n2] were merged. [find cc n1] and [find cc n2] will return
the same node. *)
type ev_on_new_term = t -> N.t -> term -> unit
(** [ev_on_new_term cc n t] is called whenever a new term [t]
is added to the congruence closure. Its node is [n]. *)
type ev_on_conflict = t -> th:bool -> lit list -> unit
(** [ev_on_conflict acts ~th c] is called when the congruence
closure triggers a conflict by asserting the tautology [c].
@param th true if the explanation for this conflict involves
at least one "theory" explanation; i.e. some of the equations
participating in the conflict are purely syntactic theories
like injectivity of constructors. *)
type ev_on_propagate = t -> lit -> (unit -> lit list * proof_step) -> unit
(** [ev_on_propagate cc lit reason] is called whenever [reason() => lit]
is a propagated lemma. See {!CC_ACTIONS.propagate}. *)
type ev_on_is_subterm = N.t -> term -> unit
(** [ev_on_is_subterm n t] is called when [n] is a subterm of
another node for the first time. [t] is the term corresponding to
the node [n]. This can be useful for theory combination. *)
val create :
?stat:Stat.t ->
?on_pre_merge:ev_on_pre_merge list ->
?on_post_merge:ev_on_post_merge list ->
?on_new_term:ev_on_new_term list ->
?on_conflict:ev_on_conflict list ->
?on_propagate:ev_on_propagate list ->
?on_is_subterm:ev_on_is_subterm list ->
?size:[ `Small | `Big ] ->
term_store ->
proof ->
t
(** Create a new congruence closure.
@param term_store used to be able to create new terms. All terms
interacting with this congruence closure must belong in this term state
as well. *)
val allocate_bitfield : descr:string -> t -> N.bitfield
(** Allocate a new node field (see {!N.bitfield}).
This field descriptor is henceforth reserved for all nodes
in this congruence closure, and can be set using {!set_bitfield}
for each node individually.
This can be used to efficiently store some metadata on nodes
(e.g. "is there a numeric value in the class"
or "is there a constructor term in the class").
There may be restrictions on how many distinct fields are allocated
for a given congruence closure (e.g. at most {!Sys.int_size} fields).
*)
val get_bitfield : t -> N.bitfield -> N.t -> bool
(** Access the bit field of the given node *)
val set_bitfield : t -> N.bitfield -> bool -> N.t -> unit
(** Set the bitfield for the node. This will be backtracked.
See {!N.bitfield}. *)
(* TODO: remove? this is managed by the solver anyway? *)
val on_pre_merge : t -> ev_on_pre_merge -> unit
(** Add a function to be called when two classes are merged *)
val on_post_merge : t -> ev_on_post_merge -> unit
(** Add a function to be called when two classes are merged *)
val on_new_term : t -> ev_on_new_term -> unit
(** Add a function to be called when a new node is created *)
val on_conflict : t -> ev_on_conflict -> unit
(** Called when the congruence closure finds a conflict *)
val on_propagate : t -> ev_on_propagate -> unit
(** Called when the congruence closure propagates a literal *)
val on_is_subterm : t -> ev_on_is_subterm -> unit
(** Called on terms that are subterms of function symbols *)
val set_as_lit : t -> N.t -> lit -> unit
(** map the given node to a literal. *)
val find_t : t -> term -> repr
(** Current representative of the term.
@raise Not_found if the term is not already {!add}-ed. *)
val add_seq : t -> term Iter.t -> unit
(** Add a sequence of terms to the congruence closure *)
val all_classes : t -> repr Iter.t
(** All current classes. This is costly, only use if there is no other solution *)
val assert_lit : t -> lit -> unit
(** Given a literal, assume it in the congruence closure and propagate
its consequences. Will be backtracked.
Useful for the theory combination or the SAT solver's functor *)
val assert_lits : t -> lit Iter.t -> unit
(** Addition of many literals *)
val explain_eq : t -> N.t -> N.t -> Resolved_expl.t
(** Explain why the two nodes are equal.
Fails if they are not, in an unspecified way. *)
val raise_conflict_from_expl : t -> actions -> Expl.t -> 'a
(** Raise a conflict with the given explanation.
It must be a theory tautology that [expl ==> absurd].
To be used in theories.
This fails in an unspecified way if the explanation, once resolved,
satisfies {!Resolved_expl.is_semantic}. *)
val n_true : t -> N.t
(** Node for [true] *)
val n_false : t -> N.t
(** Node for [false] *)
val n_bool : t -> bool -> N.t
(** Node for either true or false *)
val merge : t -> N.t -> N.t -> Expl.t -> unit
(** Merge these two nodes given this explanation.
It must be a theory tautology that [expl ==> n1 = n2].
To be used in theories. *)
val merge_t : t -> term -> term -> Expl.t -> unit
(** Shortcut for adding + merging *)
val set_model_value : t -> term -> value -> unit
(** Set the value of a term in the model. *)
val with_model_mode : t -> (unit -> 'a) -> 'a
(** Enter model combination mode. *)
val get_model_for_each_class : t -> (repr * N.t Iter.t * value) Iter.t
(** In model combination mode, obtain classes with their values. *)
val check : t -> actions -> unit
(** Perform all pending operations done via {!assert_eq}, {!assert_lit}, etc.
Will use the {!actions} to propagate literals, declare conflicts, etc. *)
val push_level : t -> unit
(** Push backtracking level *)
val pop_levels : t -> int -> unit
(** Restore to state [n] calls to [push_level] earlier. Used during backtracking. *)
val get_model : t -> N.t Iter.t Iter.t
(** get all the equivalence classes so they can be merged in the model *)
(**/**)
module Debug_ : sig
val pp : t Fmt.printer
end
(**/**)
end
(** Registry to extract values *)
module type REGISTRY = sig
type t
type 'a key
val create_key : unit -> 'a key
(** Call this statically, typically at program initialization, for
each distinct key. *)
val create : unit -> t
val get : t -> 'a key -> 'a option
val set : t -> 'a key -> 'a -> unit
end
(** A view of the solver from a theory's point of view.
Theories should interact with the solver via this module, to assert
new lemmas, propagate literals, access the congruence closure, etc. *)
module type SOLVER_INTERNAL = sig
module T : TERM
module Lit : LIT with module T = T
type ty = T.Ty.t
type term = T.Term.t
type value = T.Term.t
type term_store = T.Term.store
type ty_store = T.Ty.store
type clause_pool
type proof
type proof_step
(** {3 Proofs} *)
module P :
PROOF
with type lit = Lit.t
and type term = term
and type t = proof
and type proof_step = proof_step
type t
(** {3 Main type for a solver} *)
type solver = t
val tst : t -> term_store
val ty_st : t -> ty_store
val stats : t -> Stat.t
val proof : t -> proof
(** Access the proof object *)
(** {3 Registry} *)
module Registry : REGISTRY
val registry : t -> Registry.t
(** A solver contains a registry so that theories can share data *)
(** {3 Actions for the theories} *)
type theory_actions
(** Handle that the theories can use to perform actions. *)
type lit = Lit.t
(** {3 Congruence Closure} *)
(** Congruence closure instance *)
module CC :
CC_S
with module T = T
and module Lit = Lit
and type proof = proof
and type proof_step = proof_step
and type P.t = proof
and type P.lit = lit
and type Actions.t = theory_actions
val cc : t -> CC.t
(** Congruence closure for this solver *)
(** {3 Simplifiers} *)
(** Simplify terms *)
module Simplify : sig
type t
val tst : t -> term_store
val ty_st : t -> ty_store
val clear : t -> unit
(** Reset internal cache, etc. *)
val proof : t -> proof
(** Access proof *)
type hook = t -> term -> (term * proof_step Iter.t) option
(** Given a term, try to simplify it. Return [None] if it didn't change.
A simple example could be a hook that takes a term [t],
and if [t] is [app "+" (const x) (const y)] where [x] and [y] are number,
returns [Some (const (x+y))], and [None] otherwise.
The simplifier will take care of simplifying the resulting term further,
caching (so that work is not duplicated in subterms), etc.
*)
val normalize : t -> term -> (term * proof_step) option
(** Normalize a term using all the hooks. This performs
a fixpoint, i.e. it only stops when no hook applies anywhere inside
the term. *)
val normalize_t : t -> term -> term * proof_step option
(** Normalize a term using all the hooks, along with a proof that the
simplification is correct.
returns [t, ø] if no simplification occurred. *)
end
type simplify_hook = Simplify.hook
val add_simplifier : t -> Simplify.hook -> unit
(** Add a simplifier hook for preprocessing. *)
val simplify_t : t -> term -> (term * proof_step) option
(** Simplify input term, returns [Some u] if some
simplification occurred. *)
val simp_t : t -> term -> term * proof_step option
(** [simp_t si t] returns [u] even if no simplification occurred
(in which case [t == u] syntactically).
It emits [|- t=u].
(see {!simplifier}) *)
(** {3 Preprocessors}
These preprocessors turn mixed, raw literals (possibly simplified) into
literals suitable for reasoning.
Typically some clauses are also added to the solver. *)
module type PREPROCESS_ACTS = sig
val proof : proof
val mk_lit : ?sign:bool -> term -> lit
(** [mk_lit t] creates a new literal for a boolean term [t]. *)
val add_clause : lit list -> proof_step -> unit
(** pushes a new clause into the SAT solver. *)
val add_lit : ?default_pol:bool -> lit -> unit
(** Ensure the literal will be decided/handled by the SAT solver. *)
end
type preprocess_actions = (module PREPROCESS_ACTS)
(** Actions available to the preprocessor *)
type preprocess_hook = t -> preprocess_actions -> term -> unit
(** Given a term, preprocess it.
The idea is to add literals and clauses to help define the meaning of
the term, if needed. For example for boolean formulas, clauses
for their Tseitin encoding can be added, with the formula acting
as its own proxy symbol.
@param preprocess_actions actions available during preprocessing.
*)
val on_preprocess : t -> preprocess_hook -> unit
(** Add a hook that will be called when terms are preprocessed *)
(** {3 hooks for the theory} *)
val raise_conflict : t -> theory_actions -> lit list -> proof_step -> 'a
(** Give a conflict clause to the solver *)
val push_decision : t -> theory_actions -> lit -> unit
(** Ask the SAT solver to decide the given literal in an extension of the
current trail. This is useful for theory combination.
If the SAT solver backtracks, this (potential) decision is removed
and forgotten. *)
val propagate :
t -> theory_actions -> lit -> reason:(unit -> lit list * proof_step) -> unit
(** Propagate a boolean using a unit clause.
[expl => lit] must be a theory lemma, that is, a T-tautology *)
val propagate_l : t -> theory_actions -> lit -> lit list -> proof_step -> unit
(** Propagate a boolean using a unit clause.
[expl => lit] must be a theory lemma, that is, a T-tautology *)
val add_clause_temp : t -> theory_actions -> lit list -> proof_step -> unit
(** Add local clause to the SAT solver. This clause will be
removed when the solver backtracks. *)
val add_clause_permanent :
t -> theory_actions -> lit list -> proof_step -> unit
(** Add toplevel clause to the SAT solver. This clause will
not be backtracked. *)
val mk_lit : t -> theory_actions -> ?sign:bool -> term -> lit
(** Create a literal. This automatically preprocesses the term. *)
val add_lit : t -> theory_actions -> ?default_pol:bool -> lit -> unit
(** Add the given literal to the SAT solver, so it gets assigned
a boolean value.
@param default_pol default polarity for the corresponding atom *)
val add_lit_t : t -> theory_actions -> ?sign:bool -> term -> unit
(** Add the given (signed) bool term to the SAT solver, so it gets assigned
a boolean value *)
val cc_raise_conflict_expl : t -> theory_actions -> CC.Expl.t -> 'a
(** Raise a conflict with the given congruence closure explanation.
it must be a theory tautology that [expl ==> absurd].