package logtk

  1. Overview
  2. Docs
Core types and algorithms for logic

Install

Dune Dependency

Authors

Maintainers

Sources

1.5.1.tar.gz
md5=cc320f66f10555c54822da624419e003
sha512=f8d5f7a5ae790bf0388d74261673803cf375f91f92f7b413b70db1ce5841ef55343a208f98727c8551d66f1840ab892f1c0c943a34861d14d79ce469b235a2f2

doc/src/logtk/Literals.ml.html

Source file Literals.ml

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

(* This file is free software, part of Zipperposition. See file "license" for more details. *)

(** {1 Array of literals} *)

module BV = CCBV
module T = Term
module S = Subst
module Lit = Literal

type term = Term.t

type t = Literal.t array

let prof_maxlits = Util.mk_profiler "lits.maxlits"

let equal lits1 lits2 =
  let rec check i =
    if i = Array.length lits1 then true else
      Lit.equal lits1.(i) lits2.(i) && check (i+1)
  in
  if Array.length lits1 <> Array.length lits2
  then false
  else check 0

let equal_com lits1 lits2 =
  let rec check i =
    if i = Array.length lits1 then true else
      Lit.equal_com lits1.(i) lits2.(i) && check (i+1)
  in
  if Array.length lits1 <> Array.length lits2
  then false
  else check 0

let compare lits1 lits2 = CCArray.compare Lit.compare lits1 lits2

let compare_multiset ~ord (l1:t) (l2:t) : Comparison.t =
  let module M = Multiset.Make(Literal) in
  M.compare_partial_l (Literal.Comp.compare ~ord)
    (Array.to_list l1) (Array.to_list l2)

let hash lits = Hash.array Lit.hash lits

let variant ?(subst=S.empty) (a1,sc1) (a2,sc2) =
  Unif.unif_array_com ~size:`Same (subst,[]) (a1,sc1) (a2,sc2)
    ~op:(fun (subst,t1) x y k ->
      Lit.variant ~subst x y (fun (s,t2) -> k (s,t1@t2)))
  |> Iter.filter (fun (s,_) -> Subst.is_renaming s)

let are_variant a1 a2 =
  not (Iter.is_empty (variant (Scoped.make a1 0) (Scoped.make a2 1)))

let matching ?(subst=S.empty) ~pattern:(a1,sc1) (a2,sc2) =
  Unif.unif_array_com ~size:`Same (subst,[]) (a1,sc1) (a2,sc2)
    ~op:(fun (subst,t1) x y k ->
      Lit.matching ~subst ~pattern:x y
        (fun (s,t2) -> k (s,t1@t2)))

let matches a1 a2 =
  not (Iter.is_empty (matching ~pattern:(Scoped.make a1 0) (Scoped.make a2 1)))

let weight lits =
  Array.fold_left (fun w lit -> w + Lit.weight lit) 0 lits

let depth lits =
  Array.fold_left (fun d lit -> max d (Lit.depth lit)) 0 lits

let vars lits =
  Iter.of_array lits
  |> Iter.flat_map Lit.Seq.vars
  |> T.VarSet.of_seq
  |> T.VarSet.to_list

let is_ground lits =
  CCArray.for_all Lit.is_ground lits

let to_form lits =
  let lits = Array.map Lit.Conv.to_form lits in
  Array.to_list lits

(** Apply the substitution to the array of literals, with scope *)
let apply_subst renaming subst (lits,sc) =
  Array.map
    (fun lit -> Lit.apply_subst renaming subst (lit,sc))
    lits

let of_unif_subst renaming s =
  Literal.of_unif_subst renaming s |> Array.of_list

let map f lits =
  Array.map (fun lit -> Lit.map f lit) lits

(** bitvector of literals that are positive *)
let pos lits =
  let bv = BV.create ~size:(Array.length lits) false in
  for i = 0 to Array.length lits - 1 do
    if Lit.is_pos lits.(i) then BV.set bv i
  done;
  bv

(** bitvector of literals that are positive *)
let neg lits =
  let bv = BV.create ~size:(Array.length lits) false in
  for i = 0 to Array.length lits - 1 do
    if Lit.is_neg lits.(i) then BV.set bv i
  done;
  bv

(** Multiset of literals, with their index *)
module MLI = Multiset.Make(struct
    type t = Lit.t * int
    let compare (l1,i1)(l2,i2) =
      if i1=i2 then Lit.compare l1 l2 else Pervasives.compare i1 i2
  end)

let _compare_lit_with_idx ~ord (lit1,i1) (lit2,i2) =
  if i1=i2
  then Comparison.Eq (* ignore collisions *)
  else (
    let c = Lit.Comp.compare ~ord lit1 lit2 in
    (* two occurrences of one lit should be incomparable (and therefore maximal) *)
    if c = Comparison.Eq then Comparison.Incomparable else c
  )

let _to_multiset_with_idx lits =
  CCArray.foldi
    (fun acc i x -> MLI.add acc (x,i))
    MLI.empty lits

(* TODO: optimize! quite a bottleneck on pb47.p with NoSelection *)
let maxlits_l ~ord lits =
  Util.enter_prof prof_maxlits;
  let m = _to_multiset_with_idx lits in
  let max = MLI.max_seq (_compare_lit_with_idx ~ord) m
            |> Iter.map fst
            |> Iter.to_list
  in
  Util.exit_prof prof_maxlits;
  max

let maxlits ~ord lits =
  Util.enter_prof prof_maxlits;
  let m = _to_multiset_with_idx lits in
  let max = MLI.max_seq (_compare_lit_with_idx ~ord) m
            |> Iter.map (fun (x,_) -> snd x)
            |> Iter.to_list
            |> BV.of_list
  in
  Util.exit_prof prof_maxlits;
  max

let is_max ~ord lits =
  (*
  let max = maxlits_l ~ord lits in
  fun i -> List.exists (fun (_,j) -> i=j) max
  *)
  let m = _to_multiset_with_idx lits in
  fun i ->
    let lit = lits.(i) in
    MLI.is_max (_compare_lit_with_idx ~ord) (lit,i) m

let is_trivial lits =
  (* check if a pair of lits is trivial *)
  let rec check_multi lits i =
    if i = Array.length lits then false
    else
      let triv = match lits.(i) with
        | Lit.Prop (p, sign) ->
          CCArray.exists
            (function
              | Lit.Prop (p', sign') when sign = not sign' ->
                T.equal p p'  (* p  \/  ~p *)
              | _ -> false)
            lits
        | Lit.Equation (l, r, true) when T.equal l r -> true
        | Lit.Equation (l, r, sign) ->
          CCArray.exists
            (function
              | Lit.Equation (l', r', sign') when sign = not sign' ->
                (T.equal l l' && T.equal r r') || (T.equal l r' && T.equal l' r)
              | _ -> false)
            lits
        | lit -> Lit.is_trivial lit
      in
      triv || check_multi lits (i+1)
  in
  CCArray.exists Lit.is_trivial lits || check_multi lits 0

let is_absurd lits =
  CCArray.for_all Lit.is_absurd lits

let apply_subst renaming subst (lits,sc) =
  CCArray.map (fun l -> Lit.apply_subst renaming subst (l,sc)) lits

module Seq = struct
  let vars lits =
    Iter.of_array lits |> Iter.flat_map Lit.Seq.vars
  let terms a =
    Iter.of_array a |> Iter.flat_map Lit.Seq.terms
  let to_form a = Iter.of_array a |> Iter.map Lit.Conv.to_form
end

(** {3 High Order combinators} *)

module Pos = struct
  let _fail_lits lits pos =
    let msg =
      CCFormat.sprintf "@[invalid position @[%a@]@ in lits [@[%a@]]@]"
        Position.pp pos (CCFormat.array Lit.pp) lits
    in invalid_arg msg

  let _fail_pos pos =
    let msg =
      CCFormat.sprintf
        "@[<2>invalid literal-array position@ @[%a@]@]" Position.pp pos in
    invalid_arg msg

  let at lits pos = match pos with
    | Position.Arg (idx, pos') when idx >= 0 && idx < Array.length lits ->
      Lit.Pos.at lits.(idx) pos'
    | _ -> _fail_lits lits pos

  let lit_at lits pos = match pos with
    | Position.Arg (i, pos') when i >= 0 && i < Array.length lits ->
      lits.(i), pos'
    | _ -> _fail_lits lits pos

  let replace lits ~at ~by = match at with
    | Position.Arg (idx, pos') when idx >= 0 && idx < Array.length lits ->
      lits.(idx) <- Lit.Pos.replace lits.(idx) ~at:pos' ~by
    | _ -> _fail_lits lits at

  let idx = function
    | Position.Arg(i, _) -> i
    | p -> _fail_pos p

  let tail = function
    | Position.Arg (_, pos') -> pos'
    | p -> _fail_pos p

  let cut = function
    | Position.Arg (i, pos') -> i, pos'
    | p -> _fail_pos p
end

module Conv = struct
  let of_forms ?hooks forms =
    let forms = Array.of_list forms in
    Array.map (Lit.Conv.of_form ?hooks) forms

  let to_forms ?hooks lits =
    Array.to_list (Array.map (Lit.Conv.to_form ?hooks) lits)

  let to_s_form ?allow_free_db ?(ctx=T.Conv.create()) ?hooks lits =
    Array.to_list lits
    |> List.map (Literal.Conv.to_s_form ?hooks ?allow_free_db ~ctx)
    |> TypedSTerm.Form.or_
end

module View = struct
  let get_eqn lits pos = match pos with
    | Position.Arg (idx, pos') when idx < Array.length lits ->
      Lit.View.get_eqn lits.(idx) pos'
    | _ -> None

  let get_arith lits pos = match pos with
    | Position.Arg (idx, pos') when idx < Array.length lits ->
      Lit.View.focus_arith lits.(idx) pos'
    | _ -> None

  let get_rat lits pos = match pos with
    | Position.Arg (idx, pos') when idx < Array.length lits ->
      Lit.View.focus_rat lits.(idx) pos'
    | _ -> None

  let _unwrap2 ~msg f x y = match f x y with
    | Some z -> z
    | None -> invalid_arg msg

  let get_eqn_exn =
    _unwrap2 ~msg:"get_eqn: improper position" get_eqn

  let get_arith_exn =
    _unwrap2 ~msg:"get_arith: improper position" get_arith

  let get_rat_exn =
    _unwrap2 ~msg:"get_rat: improper position" get_rat
end

let fold_lits ~eligible lits k =
  let rec aux i =
    if i = Array.length lits then ()
    else if not (eligible i lits.(i)) then aux (i+1)
    else (
      k (lits.(i), i);
      aux (i+1)
    )
  in
  aux 0

let fold_eqn ?(both=true) ?sign ~ord ~eligible lits k =
  let sign_ok s = match sign with
    | None -> true
    | Some sign -> sign = s
  in
  let rec aux i =
    if i = Array.length lits then ()
    else if not (eligible i lits.(i)) then aux (i+1)
    else (
      begin match lits.(i) with
        | Lit.Equation (l,r,sign) when sign_ok sign ->
          begin match Ordering.compare ord l r with
            | Comparison.Gt ->
              k (l, r, sign, Position.(arg i @@ left @@ stop))
            | Comparison.Lt ->
              k (r, l, sign, Position.(arg i @@ right @@ stop))
            | Comparison.Eq
            | Comparison.Incomparable ->
              if both
              then (
                (* visit both sides of the equation *)
                k (r, l, sign, Position.(arg i @@ right @@ stop));
                k (l, r, sign, Position.(arg i @@ left @@ stop))
              ) else
                (* only one side *)
                k (l, r, sign, Position.(arg i @@ left @@ stop))
          end
        | Lit.Prop (p, sign) when sign_ok sign ->
          k (p, T.true_, sign, Position.(arg i @@ left @@ stop))
        | Lit.Prop _
        | Lit.Equation _
        | Lit.Int _
        | Lit.Rat _
        | Lit.True
        | Lit.False -> ()
      end;
      aux (i+1)
    )
  in
  aux 0

let fold_arith ~eligible lits k =
  let rec aux i =
    if i = Array.length lits then ()
    else if not (eligible i lits.(i)) then aux (i+1)
    else (
      begin match Lit.View.get_arith lits.(i) with
        | None -> ()
        | Some x ->
          let pos = Position.(arg i stop) in
          k (x, pos)
      end;
      aux (i+1)
    )
  in aux 0

let fold_arith_terms ~eligible ~which ~ord lits k =
  let module M = Monome in let module MF = Monome.Focus in
  fold_arith ~eligible lits
    (fun (a_lit, pos) ->
       (* do we use the given term? *)
       let do_term =
         match which with
           | `All -> (fun _ -> true)
           | `Max ->
             let max_terms = Int_lit.max_terms ~ord a_lit in
             fun t -> CCList.mem ~eq:T.equal t max_terms
       in
       Int_lit.Focus.fold_terms ~pos a_lit
         (fun (foc_lit, pos) ->
            let t = Int_lit.Focus.term foc_lit in
            if do_term t then k (t, foc_lit, pos))
    )

let fold_rat ~eligible lits k =
  let rec aux i =
    if i = Array.length lits then ()
    else if not (eligible i lits.(i)) then aux (i+1)
    else (
      begin match Lit.View.get_rat lits.(i) with
        | None -> ()
        | Some x ->
          let pos = Position.(arg i stop) in
          k (x, pos)
      end;
      aux (i+1)
    )
  in aux 0

let fold_rat_terms ~eligible ~which ~ord lits k =
  let module M = Monome in let module MF = Monome.Focus in
  fold_rat ~eligible lits
    (fun (a_lit, pos) ->
       (* do we use the given term? *)
       let do_term =
         match which with
           | `All -> (fun _ -> true)
           | `Max ->
             let max_terms = Rat_lit.max_terms ~ord a_lit in
             fun t -> CCList.mem ~eq:T.equal t max_terms
       in
       Rat_lit.Focus.fold_terms ~pos a_lit
         (fun (foc_lit, pos) ->
            let t = Rat_lit.Focus.term foc_lit in
            if do_term t then k (t, foc_lit, pos))
    )

let fold_terms ?(vars=false) ?ty_args ~(which : [< `All|`Max])
    ~ord ~subterms ~eligible lits k =
  let rec aux i =
    if i = Array.length lits then ()
    else if not (eligible i lits.(i))
    then aux (i+1) (* ignore lit *)
    else (
      Lit.fold_terms
        ~position:Position.(arg i stop) ?ty_args ~vars ~which ~ord ~subterms
        lits.(i) k;
      aux (i+1)
    )
  in
  aux 0

let symbols ?(init=ID.Set.empty) lits =
  Iter.of_array lits
  |> Iter.flat_map Lit.Seq.symbols
  |> ID.Set.add_seq init

(** {3 IO} *)

let pp_gen ~false_ ~l ~r ~sep ~pp_lit out lits = match lits with
  | [||] -> CCFormat.string out false_
  | [| l |] -> pp_lit out l
  | _ ->
    Format.fprintf out "%s@[<hv>%a@]%s" l CCFormat.(array ~sep pp_lit) lits r

let pp out lits =
  let pp_lit = CCFormat.hovbox Lit.pp in
  pp_gen ~l:"[" ~r:"]" ~false_:"⊥" ~sep:(CCFormat.return "@ ∨ ") ~pp_lit out lits

let pp_vars_gen ~pp_var ~pp_lits out lits =
  let pp_vars out = function
    | [] -> ()
    | l -> Format.fprintf out "forall @[%a@].@ " (Util.pp_list ~sep:" " pp_var) l
  in
  let vars_ = Seq.vars lits |> T.VarSet.of_seq |> T.VarSet.to_list in
  Format.fprintf out "@[<2>%a%a@]" pp_vars vars_ pp_lits lits

let pp_vars out lits = pp_vars_gen ~pp_var:Type.pp_typed_var ~pp_lits:pp out lits

let pp_tstp out lits =
  let pp_lit = CCFormat.hovbox Lit.pp_tstp in
  pp_gen ~l:"(" ~r:")" ~false_:"$false" ~sep:(CCFormat.return "@ | ") ~pp_lit out lits

(* print quantified literals *)
let pp_tstp_closed out lits =
  let pp_vars out = function
    | [] -> ()
    | l ->
      Format.fprintf out "![@[%a@]]:@ "
        (Util.pp_list ~sep:", " Type.TPTP.pp_typed_var) l
  in
  Format.fprintf out "@[<2>%a%a@]" pp_vars (vars lits) pp_tstp lits

let pp_zf out lits =
  let pp_lit = CCFormat.hovbox Lit.pp_zf in
  pp_gen ~l:"(" ~r:")" ~false_:"false" ~sep:(CCFormat.return "@ || ") ~pp_lit out lits

let pp_zf_closed out lits =
  pp_vars_gen ~pp_var:Type.ZF.pp_typed_var ~pp_lits:pp_zf out lits

let to_string a = CCFormat.to_string pp a

(** {2 Special kinds of array} *)

(** Recognized whether the clause is a Range-Restricted Horn clause *)
let is_RR_horn_clause lits =
  let bv = pos lits in
  match BV.to_list bv with
    | [i] ->
      (* single positive lit, check variables restrictions, ie all vars
          occur in the head *)
      let hd_vars = Lit.vars lits.(i) in
      List.length hd_vars = List.length (vars lits)
    | _ -> false

(** Recognizes Horn clauses (at most one positive literal) *)
let is_horn lits =
  let bv = pos lits in
  BV.cardinal bv <= 1

let is_pos_eq lits =
  match lits with
    | [| Lit.Equation (l,r,true) |] -> Some (l,r)
    | [| Lit.Prop(p,true) |] -> Some (p, T.true_)
    | [| Lit.True |] -> Some (T.true_, T.true_)
    | _ -> None

(** {2 Shielded Variables} *)

let is_shielded var (lits:t) : bool =
  let var_eq = HVar.equal Type.equal in
  let rec shielded_by_term ~root t = match T.view t with
    | T.Var v' when var_eq v' var -> not root
    | _ when Type.Seq.vars (T.ty t) |> Iter.exists (var_eq var) ->
      true (* shielded by type *)
    | T.Var _
    | T.DB _
    | T.Const _ -> false
    | T.AppBuiltin (_, l) ->
      List.exists (shielded_by_term ~root:false) l
    | T.App (f, l) ->
      shielded_by_term ~root f ||
      List.exists (shielded_by_term ~root:false) l
    | T.Fun (_, bod) -> shielded_by_term ~root:false bod
  in
  (* is there a term, directly under a literal, that shields the variable? *)
  begin
    lits
    |> Seq.terms
    |> Iter.exists (shielded_by_term ~root:true)
  end

let unshielded_vars ?(filter=fun _->true) lits: _ list =
  vars lits
  |> List.filter
    (fun var ->
       filter var &&
       not (is_shielded var lits))
OCaml

Innovation. Community. Security.