Skip to content

Commit eddbf13

Browse files
committed
refactor sudoku solver; make it compile; use new term repr
1 parent 92edae3 commit eddbf13

File tree

2 files changed

+103
-78
lines changed

2 files changed

+103
-78
lines changed

examples/sudoku/sudoku_solve.ml

+100-78
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
(** simple sudoku solver *)
22

3-
module Fmt = CCFormat
4-
module Vec = Sidekick_util.Vec
5-
module Log = Sidekick_util.Log
6-
module Profile = Sidekick_util.Profile
3+
open Sidekick_util
74

85
let errorf msg = Fmt.kasprintf failwith msg
96

@@ -147,74 +144,73 @@ module Solver : sig
147144
val create : Grid.t -> t
148145
val solve : t -> Grid.t option
149146
end = struct
150-
open Sidekick_sat.Solver_intf
147+
open Sidekick_core
151148

152-
(* formulas *)
153-
module F = struct
154-
type t = bool * int * int * Cell.t
149+
type Const.view += Cell_is of { x: int; y: int; value: Cell.t }
155150

156-
let equal (sign1, x1, y1, c1) (sign2, x2, y2, c2) =
157-
sign1 = sign2 && x1 = x2 && y1 = y2 && Cell.equal c1 c2
151+
let ops =
152+
(module struct
153+
let pp out = function
154+
| Cell_is { x; y; value } ->
155+
Fmt.fprintf out "(%d:%d=%a)" x y Cell.pp value
156+
| _ -> ()
158157

159-
let hash (sign, x, y, c) =
160-
CCHash.(combine4 (bool sign) (int x) (int y) (Cell.hash c))
158+
let hash = function
159+
| Cell_is { x; y; value } ->
160+
Hash.(combine3 (int x) (int y) (Cell.hash value))
161+
| _ -> assert false
161162

162-
let pp out (sign, x, y, c) =
163-
Fmt.fprintf out "[@[(%d,%d) %s %a@]]" x y
164-
(if sign then
165-
"="
166-
else
167-
"!=")
168-
Cell.pp c
169-
170-
let neg (sign, x, y, c) = not sign, x, y, c
163+
let equal a b =
164+
match a, b with
165+
| Cell_is a, Cell_is b ->
166+
a.x = b.x && a.y = b.y && Cell.equal a.value b.value
167+
| _ -> false
168+
end : Const.DYN_OPS)
171169

172-
let norm_sign ((sign, _, _, _) as f) =
173-
if sign then
174-
f, true
175-
else
176-
neg f, false
177-
178-
let make sign x y (c : Cell.t) : t = sign, x, y, c
179-
end
170+
module Sat = Sidekick_sat
180171

181-
module Theory = struct
182-
include Sidekick_sat.Proof_dummy.Make (F)
172+
let mk_cell tst x y value : Term.t =
173+
Term.const tst
174+
@@ Const.make (Cell_is { x; y; value }) ops ~ty:(Term.bool tst)
183175

184-
type proof = unit
185-
type proof_step = unit
176+
let mk_cell_lit ?sign tst x y value : Lit.t =
177+
Lit.atom ?sign @@ mk_cell tst x y value
186178

187-
module Lit = F
179+
module Theory : sig
180+
type t
188181

189-
type lit = Lit.t
190-
type t = { grid: Grid.t B_ref.t }
182+
val grid : t -> Grid.t
183+
val create : Term.store -> Grid.t -> t
184+
val to_plugin : t -> Sat.plugin
185+
end = struct
186+
type t = { tst: Term.store; grid: Grid.t B_ref.t }
191187

192-
let create g : t = { grid = B_ref.create g }
193188
let[@inline] grid self : Grid.t = B_ref.get self.grid
194189
let[@inline] set_grid self g : unit = B_ref.set self.grid g
195190
let push_level self = B_ref.push_level self.grid
196191
let pop_levels self n = B_ref.pop_levels self.grid n
197-
let pp_c_ = Fmt.(list ~sep:(return "@ ∨ ")) F.pp
192+
let pp_c_ = Fmt.(list ~sep:(return "@ ∨ ")) Lit.pp
198193

199194
let[@inline] logs_conflict kind c : unit =
200195
Log.debugf 4 (fun k -> k "(@[conflict.%s@ %a@])" kind pp_c_ c)
201196

202197
(* check that all cells are full *)
203-
let check_full_ (self : t) (acts : (Lit.t, proof, proof_step) acts) : unit =
204-
Profile.with_ "check-full" @@ fun () ->
198+
let check_full_ (self : t) (acts : Sat.acts) : unit =
199+
(*let@ () = Profile.with_ "check-full" in*)
205200
let (module A) = acts in
206201
Grid.all_cells (grid self) (fun (x, y, c) ->
207202
if Cell.is_empty c then (
208203
let c =
209-
CCList.init 9 (fun c -> F.make true x y (Cell.make (c + 1)))
204+
CCList.init 9 (fun c ->
205+
mk_cell_lit self.tst x y (Cell.make (c + 1)))
210206
in
211207
Log.debugf 4 (fun k -> k "(@[add-clause@ %a@])" pp_c_ c);
212-
A.add_clause ~keep:true c ()
208+
A.add_clause ~keep:true c Proof_trace.dummy_step_id
213209
))
214210

215211
(* check constraints *)
216-
let check_ (self : t) (acts : (Lit.t, proof, proof_step) acts) : unit =
217-
Profile.with_ "check-constraints" @@ fun () ->
212+
let check_ (self : t) (acts : Sat.acts) : unit =
213+
(*let@ () = Profile.with_ "check-constraints" in*)
218214
Log.debugf 4 (fun k ->
219215
k "(@[sudoku.check@ @[:g %a@]@])" Grid.pp (B_ref.get self.grid));
220216
let (module A) = acts in
@@ -229,77 +225,103 @@ end = struct
229225
pairs (fun ((x1, y1, c1), (x2, y2, c2)) ->
230226
if Cell.equal c1 c2 then (
231227
assert (x1 <> x2 || y1 <> y2);
232-
let c = [ F.make false x1 y1 c1; F.make false x2 y2 c2 ] in
228+
let c =
229+
[
230+
mk_cell_lit self.tst ~sign:false x1 y1 c1;
231+
mk_cell_lit self.tst ~sign:false x2 y2 c2;
232+
]
233+
in
233234
logs_conflict ("all-diff." ^ kind) c;
234-
A.raise_conflict c ()
235+
A.raise_conflict c Proof_trace.dummy_step_id
235236
))
236237
in
237238
all_diff "rows" Grid.rows;
238239
all_diff "cols" Grid.cols;
239240
all_diff "squares" Grid.squares;
240241
()
241242

242-
let trail_ (acts : (Lit.t, proof, proof_step) acts) =
243+
let trail_ (acts : Sat.acts) =
243244
let (module A) = acts in
244245
A.iter_assumptions
245246

246247
(* update current grid with the given slice *)
247-
let add_slice (self : t) (acts : (Lit.t, proof, proof_step) acts) : unit =
248+
let add_slice (self : t) (acts : Sat.acts) : unit =
248249
let (module A) = acts in
249-
trail_ acts (function
250-
| false, _, _, _ -> ()
251-
| true, x, y, c ->
252-
assert (Cell.is_full c);
253-
let grid = grid self in
254-
let c' = Grid.get grid x y in
255-
if Cell.is_empty c' then
256-
set_grid self (Grid.set grid x y c)
257-
else if Cell.neq c c' then (
258-
(* conflict: at most one value *)
259-
let c = [ F.make false x y c; F.make false x y c' ] in
260-
logs_conflict "at-most-one" c;
261-
A.raise_conflict c ()
262-
))
250+
trail_ acts (fun lit ->
251+
match Lit.sign lit, Term.view (Lit.term lit) with
252+
| true, E_const { Const.c_view = Cell_is { x; y; value = c }; _ } ->
253+
assert (Cell.is_full c);
254+
let grid = grid self in
255+
let c' = Grid.get grid x y in
256+
if Cell.is_empty c' then
257+
set_grid self (Grid.set grid x y c)
258+
else if Cell.neq c c' then (
259+
(* conflict: at most one value *)
260+
let c =
261+
[
262+
mk_cell_lit self.tst ~sign:false x y c;
263+
mk_cell_lit self.tst ~sign:false x y c';
264+
]
265+
in
266+
logs_conflict "at-most-one" c;
267+
A.raise_conflict c Proof_trace.dummy_step_id
268+
)
269+
| _ -> ())
263270

264271
let partial_check (self : t) acts : unit =
265-
Profile.with_ "partial-check" @@ fun () ->
272+
(* let@ () = Profile.with_ "partial-check" in*)
266273
Log.debugf 4 (fun k ->
267-
k "(@[sudoku.partial-check@ :trail [@[%a@]]@])" (Fmt.list F.pp)
268-
(trail_ acts |> Iter.to_list));
274+
k "(@[sudoku.partial-check@ :trail [@[%a@]]@])" (Fmt.iter Lit.pp)
275+
(trail_ acts));
269276
add_slice self acts;
270277
check_ self acts
271278

272279
let final_check (self : t) acts : unit =
273-
Profile.with_ "final-check" @@ fun () ->
280+
(*let@ () = Profile.with_ "final-check" in*)
274281
Log.debugf 4 (fun k -> k "(@[sudoku.final-check@])");
275282
check_full_ self acts;
276283
check_ self acts
277-
end
278284

279-
module S = Sidekick_sat.Make_cdcl_t (Theory)
285+
let create tst g : t = { tst; grid = B_ref.create g }
286+
287+
let to_plugin (self : t) : Sat.plugin =
288+
Sat.mk_plugin_cdcl_t
289+
~push_level:(fun () -> push_level self)
290+
~pop_levels:(fun n -> pop_levels self n)
291+
~partial_check:(partial_check self) ~final_check:(final_check self) ()
292+
end
280293

281-
type t = { grid0: Grid.t; solver: S.t }
294+
type t = { grid0: Grid.t; tst: Term.store; theory: Theory.t; solver: Sat.t }
282295

283296
let solve (self : t) : _ option =
284-
Profile.with_ "sudoku.solve" @@ fun () ->
297+
let@ () = Profile.with_ "sudoku.solve" in
285298
let assumptions =
286299
Grid.all_cells self.grid0
287300
|> Iter.filter (fun (_, _, c) -> Cell.is_full c)
288-
|> Iter.map (fun (x, y, c) -> F.make true x y c)
301+
|> Iter.map (fun (x, y, c) -> mk_cell_lit self.tst x y c)
289302
|> Iter.to_rev_list
290303
in
291304
Log.debugf 2 (fun k ->
292-
k "(@[sudoku.solve@ :assumptions %a@])" (Fmt.Dump.list F.pp) assumptions);
305+
k "(@[sudoku.solve@ :assumptions %a@])" (Fmt.Dump.list Lit.pp)
306+
assumptions);
293307
let r =
294-
match S.solve self.solver ~assumptions with
295-
| S.Sat _ -> Some (Theory.grid (S.theory self.solver))
296-
| S.Unsat _ -> None
308+
match Sat.solve self.solver ~assumptions with
309+
| Sat.Sat _ -> Some (Theory.grid self.theory)
310+
| Sat.Unsat _ -> None
297311
in
298312
(* TODO: print some stats *)
299313
r
300314

301315
let create g : t =
302-
{ solver = S.create ~proof:() (Theory.create g); grid0 = g }
316+
let tst = Term.Store.create () in
317+
let theory = Theory.create tst g in
318+
let plugin : Sat.plugin = Theory.to_plugin theory in
319+
{
320+
tst;
321+
solver = Sat.create ~proof:Proof_trace.dummy plugin;
322+
theory;
323+
grid0 = g;
324+
}
303325
end
304326

305327
let solve_grid (g : Grid.t) : Grid.t option =
@@ -320,7 +342,7 @@ let chrono ~pp_time : (module CHRONO) =
320342
(module M)
321343

322344
let solve_file ~pp_time file =
323-
Profile.with_ "solve-file" @@ fun () ->
345+
let@ () = Profile.with_ "solve-file" in
324346
let open (val chrono ~pp_time) in
325347
Format.printf "solve grids in file %S@." file;
326348

@@ -360,7 +382,7 @@ let solve_file ~pp_time file =
360382
()
361383

362384
let () =
363-
Sidekick_tef.with_setup @@ fun () ->
385+
let@ () = Sidekick_tef.with_setup in
364386
Fmt.set_color_default true;
365387
let files = ref [] in
366388
let debug = ref 0 in

sudoku_solve.sh

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
OPTS="--profile=release --display=quiet"
3+
exec dune exec $OPTS examples/sudoku/sudoku_solve.exe -- $@

0 commit comments

Comments
 (0)