kibi-core 0.24.0 → 0.27.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.
package/src/logic_ir.pl CHANGED
@@ -8,7 +8,6 @@
8
8
  logic_rule_safety/2,
9
9
  logic_rule_render/2,
10
10
  logic_rule_semantic_key/2,
11
- logic_derive/2,
12
11
  logic_rule_conflict/3,
13
12
  logic_rule_conflict/4,
14
13
  logic_rule_conflict_witness/3,
@@ -22,23 +21,59 @@
22
21
  ]).
23
22
 
24
23
  :- use_module(library(http/json)).
25
- :- use_module(library(aggregate)).
26
24
  :- use_module(library(clpfd)).
27
25
  :- use_module('intervals.pl', [numeric_constraints_satisfiable/2, numeric_constraint_entailed/3, numeric_constraint_holds/3]).
28
- :- use_module('kb.pl', [kb_entity/3, predicate_fact/5, predicate_schema_keys/4]).
26
+ :- use_module('kb.pl', [predicate_schema_keys/4]).
29
27
 
30
28
  %% logic_rule_from_props(+Props, -Rule)
31
29
  % Props is an entity property list from a fact_kind=rule fact.
32
30
  logic_rule_from_props(Props, Rule) :-
33
31
  memberchk(rule_ir=Raw, Props),
34
32
  raw_json_atom(Raw, Json),
35
- catch(atom_json_dict(Json, Dict, [value_string_as(atom)]), _, fail),
36
- json_rule(Dict, Rule).
33
+ logic_rule_from_json(Json, Rule).
34
+
35
+ % A rule body is decoded once per text: the feasibility analysis of every
36
+ % scenario and every requirement's proof ladder read the same rule facts.
37
+ % Decoding is a pure function of the text, so the memo never goes stale; it
38
+ % is bounded so a long-lived engine cannot grow it without limit.
39
+ :- dynamic logic_rule_memo/2.
40
+ :- dynamic logic_rule_memo_size/1.
41
+
42
+ % mutalog-disable-next-line literal: the bound is a tuning value; any size gives the same answers
43
+ logic_rule_memo_limit(16384).
44
+
45
+ logic_rule_from_json(Json, Rule) :-
46
+ ( logic_rule_memo(Json, Stored)
47
+ -> Stored = rule(Rule)
48
+ ; ( catch(atom_json_dict(Json, Dict, [value_string_as(atom)]), _, fail),
49
+ json_rule(Dict, Rule0)
50
+ -> Stored = rule(Rule0)
51
+ ; Stored = invalid
52
+ ),
53
+ logic_rule_remember(Json, Stored),
54
+ Stored = rule(Rule)
55
+ ).
56
+
57
+ logic_rule_remember(Json, Stored) :-
58
+ ( logic_rule_memo_size(Size)
59
+ -> true
60
+ ; Size = 0
61
+ ),
62
+ logic_rule_memo_limit(Limit),
63
+ ( Size >= Limit
64
+ -> retractall(logic_rule_memo(_, _)),
65
+ retractall(logic_rule_memo_size(_)),
66
+ Next = 1
67
+ ; retractall(logic_rule_memo_size(_)),
68
+ Next is Size + 1
69
+ ),
70
+ assertz(logic_rule_memo(Json, Stored)),
71
+ assertz(logic_rule_memo_size(Next)).
37
72
 
38
- raw_json_atom(^^(Value, _), Atom) :- !, raw_json_atom(Value, Atom).
39
- raw_json_atom(literal(type(_, Value)), Atom) :- !, raw_json_atom(Value, Atom).
40
- raw_json_atom(Value, Atom) :- atom(Value), !, Atom = Value.
41
- raw_json_atom(Value, Atom) :- string(Value), !, atom_string(Atom, Value).
73
+ % Anything that is not JSON text is rejected by atom_json_dict/3.
74
+ raw_json_atom(^^(Value, _), Text) :- !, raw_json_atom(Value, Text).
75
+ raw_json_atom(literal(type(_, Value)), Text) :- !, raw_json_atom(Value, Text).
76
+ raw_json_atom(Text, Text).
42
77
 
43
78
  json_rule(Dict, rule(Kind, Modality, Head, Body, Exceptions, Scope, From, To, SchemaId, Variables)) :-
44
79
  json_rule_keys(Dict),
@@ -71,7 +106,7 @@ json_quantifier(Value, Value) :- memberchk(Value, [forall, exists]), !.
71
106
  json_quantifier(Value, Quantifier) :- string(Value), atom_string(Atom, Value), json_quantifier(Atom, Quantifier).
72
107
 
73
108
  json_modality(Value, Value) :- memberchk(Value, [assert, deny, oblige, permit, forbid]), !.
74
- json_modality(Value, Value) :- string(Value), atom_string(Atom, Value), json_modality(Atom, Value).
109
+ json_modality(Value, Modality) :- string(Value), atom_string(Atom, Value), json_modality(Atom, Modality).
75
110
 
76
111
  json_atom(Dict, atom(Namespace, Name, Args, Polarity, ClosedWorld)) :-
77
112
  is_dict(Dict),
@@ -89,7 +124,7 @@ json_atom(Dict, atom(Namespace, Name, Args, Polarity, ClosedWorld)) :-
89
124
  ( get_dict(closedWorld, Dict, RawClosedWorld) -> memberchk(RawClosedWorld, [true, false]), ClosedWorld = RawClosedWorld ; ClosedWorld = false ).
90
125
 
91
126
  json_atom_name(Value, Atom) :- atom(Value), !, Atom = Value.
92
- json_atom_name(Value, Atom) :- string(Value), !, atom_string(Atom, Value).
127
+ json_atom_name(Value, Atom) :- string(Value), atom_string(Atom, Value).
93
128
 
94
129
  valid_variable_name(Name) :-
95
130
  atom(Name),
@@ -119,11 +154,7 @@ valid_predicate_name(Name) :-
119
154
  char_type(First, lower),
120
155
  forall(member(Char, Rest), allowed_predicate_char(Char)).
121
156
 
122
- allowed_predicate_char(Char) :- char_type(Char, alnum), !.
123
- allowed_predicate_char('_').
124
- allowed_predicate_char('-').
125
- allowed_predicate_char('/').
126
- allowed_predicate_char('.').
157
+ allowed_predicate_char(Char) :- char_type(Char, alnum) -> true ; memberchk(Char, ['_', '-', '/', '.']).
127
158
 
128
159
  json_text(Value, Expected) :- atom(Value), !, Value = Expected.
129
160
  json_text(Value, Expected) :- string(Value), atom_string(Expected, Value).
@@ -145,7 +176,7 @@ json_term(Dict, number(Value, Unit)) :-
145
176
  ( get_dict(unit, Dict, RawUnit) -> json_atom_name(RawUnit, Unit), valid_type_name(Unit) ; Unit = none ), !.
146
177
  json_term(Dict, duration(Value, Unit)) :-
147
178
  is_dict(Dict), dict_keys_allowed(Dict, [kind, value, unit]), get_dict(kind, Dict, Kind), json_text(Kind, duration),
148
- get_dict(value, Dict, Value), number(Value), Value =:= Value, Value >= 0,
179
+ get_dict(value, Dict, Value), number(Value), Value >= 0,
149
180
  get_dict(unit, Dict, RawUnit), json_atom_name(RawUnit, Unit), memberchk(Unit, [ms, s, m, h, d, w]), !.
150
181
  json_term(Dict, timestamp(Value)) :-
151
182
  is_dict(Dict), dict_keys_allowed(Dict, [kind, value]), get_dict(kind, Dict, Kind), json_text(Kind, timestamp),
@@ -153,15 +184,15 @@ json_term(Dict, timestamp(Value)) :-
153
184
  json_term(Dict, interval(Start, End)) :-
154
185
  is_dict(Dict), dict_keys_allowed(Dict, [kind, start, end]), get_dict(kind, Dict, Kind), json_text(Kind, interval),
155
186
  get_dict(start, Dict, RawStart), json_timestamp(RawStart, Start),
156
- get_dict(end, Dict, RawEnd), json_timestamp(RawEnd, End), Start @=< End, !.
187
+ get_dict(end, Dict, RawEnd), json_timestamp(RawEnd, End), Start @=< End.
157
188
 
158
- json_expression(Dict, Atom) :- is_dict(Dict), dict_keys_allowed(Dict, [kind, namespace, name, args, polarity, closedWorld]), get_dict(kind, Dict, Kind), json_text(Kind, atom), !, json_atom(Dict, Atom).
189
+ json_expression(Dict, Atom) :- is_dict(Dict), get_dict(kind, Dict, Kind), json_text(Kind, atom), !, json_atom(Dict, Atom).
159
190
  json_expression(Dict, all(Items)) :- is_dict(Dict), dict_keys_allowed(Dict, [kind, items]), get_dict(kind, Dict, Kind), json_text(Kind, all), !, get_dict(items, Dict, Raw), is_list(Raw), Raw \= [], length(Raw, Count), Count =< 32, maplist(json_expression, Raw, Items).
160
191
  json_expression(Dict, any(Items)) :- is_dict(Dict), dict_keys_allowed(Dict, [kind, items]), get_dict(kind, Dict, Kind), json_text(Kind, any), !, get_dict(items, Dict, Raw), is_list(Raw), Raw \= [], length(Raw, Count), Count =< 32, maplist(json_expression, Raw, Items).
161
192
  json_expression(Dict, not(Atom)) :- is_dict(Dict), dict_keys_allowed(Dict, [kind, item]), get_dict(kind, Dict, Kind), json_text(Kind, not), !, get_dict(item, Dict, Raw), json_atom(Raw, Atom).
162
193
  json_expression(Dict, compare(Op, Left, Right)) :- is_dict(Dict), dict_keys_allowed(Dict, [kind, operator, left, right]), get_dict(kind, Dict, Kind), json_text(Kind, compare), !, get_dict(operator, Dict, OpRaw), json_atom_name(OpRaw, Op), valid_compare_operator(Op), get_dict(left, Dict, LeftRaw), get_dict(right, Dict, RightRaw), json_term(LeftRaw, Left), json_term(RightRaw, Right).
163
194
  json_expression(Dict, count(Atom, Op, Value)) :- is_dict(Dict), dict_keys_allowed(Dict, [kind, atom, operator, value]), get_dict(kind, Dict, Kind), json_text(Kind, count), !, get_dict(atom, Dict, AtomRaw), json_atom(AtomRaw, Atom), get_dict(operator, Dict, OpRaw), json_atom_name(OpRaw, Op), valid_compare_operator(Op), get_dict(value, Dict, Value), integer(Value), Value >= 0.
164
- json_expression(Dict, temporal(Relation, Left, Right)) :- is_dict(Dict), dict_keys_allowed(Dict, [kind, relation, left, right]), get_dict(kind, Dict, Kind), json_text(Kind, temporal), !, get_dict(relation, Dict, RelationRaw), json_atom_name(RelationRaw, Relation), valid_temporal_relation(Relation), get_dict(left, Dict, LeftRaw), get_dict(right, Dict, RightRaw), json_term(LeftRaw, Left), json_term(RightRaw, Right).
195
+ json_expression(Dict, temporal(Relation, Left, Right)) :- is_dict(Dict), dict_keys_allowed(Dict, [kind, relation, left, right]), get_dict(kind, Dict, Kind), json_text(Kind, temporal), get_dict(relation, Dict, RelationRaw), json_atom_name(RelationRaw, Relation), valid_temporal_relation(Relation), get_dict(left, Dict, LeftRaw), get_dict(right, Dict, RightRaw), json_term(LeftRaw, Left), json_term(RightRaw, Right).
165
196
 
166
197
  valid_compare_operator(Op) :- memberchk(Op, [eq, neq, lt, lte, gt, gte]).
167
198
  valid_temporal_relation(Relation) :- memberchk(Relation, [before, after, during, overlaps, starts, finishes]).
@@ -215,19 +246,27 @@ rule_limit_error(Head, Body, Exceptions, expression_depth_exceeded) :-
215
246
 
216
247
  expression_atom_count(none, 0).
217
248
  expression_atom_count(atom(_, _, _, _, _), 1).
249
+ % mutalog-disable-next-line cut_insert: maplist/3 over a det predicate
218
250
  expression_atom_count(all(Items), Count) :- maplist(expression_atom_count, Items, Counts), sum_list(Counts, ChildCount), Count is ChildCount.
251
+ % mutalog-disable-next-line cut_insert: maplist/3 over a det predicate
219
252
  expression_atom_count(any(Items), Count) :- maplist(expression_atom_count, Items, Counts), sum_list(Counts, ChildCount), Count is ChildCount.
253
+ % mutalog-disable-next-line cut_insert: a det call on a subterm
220
254
  expression_atom_count(not(Atom), Count) :- expression_atom_count(Atom, ChildCount), Count is ChildCount.
221
255
  expression_atom_count(compare(_, _, _), 0).
256
+ % mutalog-disable-next-line cut_insert: a det call on a subterm
222
257
  expression_atom_count(count(Atom, _, _), Count) :- expression_atom_count(Atom, Count).
223
258
  expression_atom_count(temporal(_, _, _), 0).
224
259
 
225
260
  expression_depth(none, 0).
226
261
  expression_depth(atom(_, _, _, _, _), 1).
262
+ % mutalog-disable-next-line cut_insert: maplist/3 over a det predicate
227
263
  expression_depth(all(Items), Depth) :- maplist(expression_depth, Items, Depths), max_list([0|Depths], ChildDepth), Depth is ChildDepth + 1.
264
+ % mutalog-disable-next-line cut_insert: maplist/3 over a det predicate
228
265
  expression_depth(any(Items), Depth) :- maplist(expression_depth, Items, Depths), max_list([0|Depths], ChildDepth), Depth is ChildDepth + 1.
266
+ % mutalog-disable-next-line cut_insert: a det call on a subterm
229
267
  expression_depth(not(Atom), Depth) :- expression_depth(Atom, ChildDepth), Depth is ChildDepth + 1.
230
268
  expression_depth(compare(_, _, _), 1).
269
+ % mutalog-disable-next-line cut_insert: a det call on a subterm
231
270
  expression_depth(count(Atom, _, _), Depth) :- expression_depth(Atom, ChildDepth), Depth is ChildDepth + 1.
232
271
  expression_depth(temporal(_, _, _), 1).
233
272
 
@@ -238,7 +277,6 @@ expression_term(not(Atom), Term) :- expression_term(Atom, Term).
238
277
  expression_term(compare(_, Left, Right), Term) :- member(Term, [Left, Right]).
239
278
  expression_term(count(Atom, _, _), Term) :- expression_term(Atom, Term).
240
279
  expression_term(temporal(_, Left, Right), Term) :- member(Term, [Left, Right]).
241
- expression_term(Term, Term) :- Term = interval(_, _).
242
280
 
243
281
  variable_safety_error(Head, Body, _Variables, head_variable_not_range_restricted(Name)) :-
244
282
  head_variable(Name, Head),
@@ -269,7 +307,6 @@ positive_variable(Name, atom(_, _, Args, positive, _)) :- member(Arg, Args), ter
269
307
  positive_variable(Name, all(Items)) :- member(Item, Items), positive_variable(Name, Item).
270
308
  positive_variable(Name, any(Items)) :- member(Item, Items), positive_variable(Name, Item).
271
309
  positive_variable(Name, count(Atom, _, _)) :- positive_variable(Name, Atom).
272
- positive_variable(_, _) :- fail.
273
310
 
274
311
  term_variable(var(Name, _), Name).
275
312
  expression_variable(atom(_, _, Args, _, _), Name) :- member(Arg, Args), term_variable(Arg, Name).
@@ -281,99 +318,78 @@ expression_variable(count(Atom, _, _), Name) :- expression_variable(Atom, Name).
281
318
  expression_variable(temporal(_, Left, Right), Name) :- (term_variable(Left, Name) ; term_variable(Right, Name)).
282
319
 
283
320
  rule_shape_error(atom, none, _Body, missing_head).
321
+ % mutalog-disable-next-line cut_insert: a test, and no later clause matches the same term
284
322
  rule_shape_error(atom, _Head, Body, body_for_atom) :- Body \= none.
285
323
  rule_shape_error(rule, none, _Body, missing_head).
286
324
  rule_shape_error(rule, _Head, none, missing_body).
287
325
  rule_shape_error(constraint, _Head, none, missing_body).
288
326
  rule_shape_error(constraint, Head, _Body, head_for_constraint) :- Head \= none.
289
- rule_shape_error(_, _, _, _) :- fail.
290
327
 
291
- expression_safety_error(none, _) :- fail.
292
328
  expression_safety_error(not(atom(_, _, _, _, false)), negation_requires_closed_world).
329
+ % mutalog-disable-next-line cut_insert: a test, and no later clause matches the same term
293
330
  expression_safety_error(compare(Op, _Left, _Right), invalid_compare_operator) :- \+ valid_compare_operator(Op).
331
+ % mutalog-disable-next-line cut_insert: a test, and no later clause matches the same term
294
332
  expression_safety_error(count(_Atom, Op, _Value), invalid_compare_operator) :- \+ valid_compare_operator(Op).
295
333
  expression_safety_error(count(_Atom, _Op, Value), invalid_count_value) :- \+ integer(Value) ; Value < 0.
334
+ % mutalog-disable-next-line cut_insert: a test, and no later clause matches the same term
296
335
  expression_safety_error(temporal(Relation, _Left, _Right), invalid_temporal_relation) :- \+ valid_temporal_relation(Relation).
297
336
  expression_safety_error(all(Items), Error) :- member(Item, Items), expression_safety_error(Item, Error).
298
337
  expression_safety_error(any(Items), Error) :- member(Item, Items), expression_safety_error(Item, Error).
299
338
  expression_safety_error(not(Atom), Error) :- expression_safety_error(Atom, Error).
300
- expression_safety_error(_, _) :- fail.
301
339
 
302
340
  %% logic_rule_render(+Props, -Text)
303
341
  logic_rule_render(Props, Text) :-
304
342
  logic_rule_from_props(Props, Rule),
305
343
  render_rule(Rule, Text).
306
344
 
307
- render_rule(rule(_Kind, Modality, Head, none, _Exceptions, _Scope, _From, _To, _Schema, _Variables), Text) :-
308
- render_head(Modality, Head, HeadText), format(string(Text), '~w.', [HeadText]).
309
345
  render_rule(rule(_Kind, Modality, Head, Body, _Exceptions, _Scope, _From, _To, _Schema, _Variables), Text) :-
310
- render_head(Modality, Head, HeadText), render_expression(Body, BodyText), format(string(Text), '~w :- ~w.', [HeadText, BodyText]).
346
+ render_head(Modality, Head, HeadText),
347
+ ( Body == none
348
+ -> format(string(Text), '~w.', [HeadText])
349
+ ; render_expression(Body, BodyText), format(string(Text), '~w :- ~w.', [HeadText, BodyText])
350
+ ).
311
351
 
312
- render_head(Modality, none, Text) :- format(string(Text), '~w', [Modality]).
313
- render_head(Modality, Atom, Text) :- render_atom(Atom, AtomText), format(string(Text), '~w(~w)', [Modality, AtomText]).
352
+ render_head(Modality, Head, Text) :-
353
+ ( Head == none
354
+ -> format(string(Text), '~w', [Modality])
355
+ ; render_atom(Head, AtomText), format(string(Text), '~w(~w)', [Modality, AtomText])
356
+ ).
314
357
  render_atom(atom(Namespace, Name, Args, Polarity, _), Text) :-
315
358
  ( Namespace == default -> Full = Name ; format(atom(Full), '~w:~w', [Namespace, Name]) ),
316
359
  maplist(render_term, Args, Rendered), atomic_list_concat(Rendered, ',', ArgsText),
317
360
  ( Polarity == negative -> format(string(Text), 'negative(~w(~w))', [Full, ArgsText]) ; format(string(Text), '~w(~w)', [Full, ArgsText]) ).
318
361
  render_term(var(Name, _), Name).
319
362
  render_term(const(Value, _), Value).
320
- render_term(number(Value, none), Text) :- format(string(Text), '~w', [Value]).
321
- render_term(number(Value, Unit), Text) :- format(string(Text), '~w~w', [Value, Unit]).
363
+ render_term(number(Value, Unit), Text) :-
364
+ ( Unit == none -> format(string(Text), '~w', [Value]) ; format(string(Text), '~w~w', [Value, Unit]) ).
365
+ % mutalog-disable-next-line cut_insert: format/3 is det
322
366
  render_term(duration(Value, Unit), Text) :- format(string(Text), 'duration(~w,~w)', [Value, Unit]).
323
367
  render_term(timestamp(Value), Value).
324
368
  render_term(interval(Start, End), Text) :- format(string(Text), 'interval(~w,~w)', [Start, End]).
325
- render_expression(Atom, Text) :- Atom = atom(_, _, _, _, _), !, render_atom(Atom, Text).
369
+ % mutalog-disable-next-line cut_insert: rendering is det
370
+ render_expression(atom(Namespace, Name, Args, Polarity, ClosedWorld), Text) :- render_atom(atom(Namespace, Name, Args, Polarity, ClosedWorld), Text).
371
+ % mutalog-disable-next-line cut_insert: maplist/3 over a det predicate
326
372
  render_expression(all(Items), Text) :- maplist(render_expression, Items, Parts), atomic_list_concat(Parts, ', ', Text).
373
+ % mutalog-disable-next-line cut_insert: maplist/3 over a det predicate
327
374
  render_expression(any(Items), Text) :- maplist(render_expression, Items, Parts), atomic_list_concat(Parts, ' ; ', Inner), format(string(Text), '(~w)', [Inner]).
375
+ % mutalog-disable-next-line cut_insert: rendering is det
328
376
  render_expression(not(Atom), Text) :- render_atom(Atom, AtomText), format(string(Text), 'not(~w)', [AtomText]).
377
+ % mutalog-disable-next-line cut_insert: rendering is det
329
378
  render_expression(compare(Op, Left, Right), Text) :- render_term(Left, L), render_term(Right, R), format(string(Text), '~w ~w ~w', [L, Op, R]).
379
+ % mutalog-disable-next-line cut_insert: rendering is det
330
380
  render_expression(count(Atom, Op, Value), Text) :- render_atom(Atom, AtomText), format(string(Text), 'count(~w,~w,~w)', [AtomText, Op, Value]).
331
381
  render_expression(temporal(Relation, Left, Right), Text) :- render_term(Left, L), render_term(Right, R), format(string(Text), 'temporal(~w,~w,~w)', [Relation, L, R]).
332
382
 
333
383
  logic_rule_semantic_key(Props, Key) :-
334
384
  memberchk(semantic_key=Key, Props), !.
335
385
  logic_rule_semantic_key(Props, Key) :-
336
- memberchk(rule_hash=Hash, Props), !, Key = Hash.
386
+ memberchk(rule_hash=Key, Props).
337
387
 
338
388
  logic_atom_signature(atom(Namespace, Name, Args, Polarity, _), signature(Namespace, Name, Args, Polarity)).
339
389
 
340
- %% logic_derive(+Goal, -Proof)
341
- % Goal is atom(Namespace, Name, Args, Polarity), with ground arguments. Rules
342
- % are interpreted by a finite, data-driven evaluator; no stored source is
343
- % consulted and recursive rule chains are bounded.
344
- logic_derive(Goal, Proof) :-
345
- ground(Goal),
346
- derive_goal(Goal, [], 0, Proof).
347
-
348
- logic_max_depth(16).
349
-
350
- derive_goal(Goal, _Seen, _Depth, direct(FactId)) :-
351
- Goal = atom(Namespace, Name, Args, Polarity, _),
352
- predicate_fact(FactId, Namespace, Name, Args, Polarity), !.
353
- derive_goal(Goal, Seen, Depth, rule(FactId, BodyProof)) :-
354
- logic_max_depth(MaxDepth),
355
- Depth < MaxDepth,
356
- logic_rule_fact(FactId, StoredRule),
357
- materialize_rule(StoredRule, Rule),
358
- Rule = rule(rule, _Modality, Head, Body, Exceptions, _Scope, _From, _To, _Schema, _Variables),
359
- Goal = Head,
360
- atom_signature_for_seen(Head, Signature),
361
- \+ memberchk(Signature, Seen),
362
- NextDepth is Depth + 1,
363
- prove_expression(Body, [Signature|Seen], NextDepth, BodyProof),
364
- exceptions_do_not_hold(Exceptions, [Signature|Seen], NextDepth).
365
-
366
- logic_rule_fact(FactId, Rule) :-
367
- kb_entity(FactId, fact, Props),
368
- memberchk(fact_kind=Kind, Props), normalize_atom(Kind, rule),
369
- logic_rule_from_props(Props, Rule).
370
-
371
390
  % The JSON decoder keeps variable names as data terms for validation and
372
391
  % rendering. Evaluation reifies those names into fresh Prolog variables so
373
392
  % ordinary unification can be used without ever evaluating caller source.
374
- materialize_rule(Rule, Materialized) :-
375
- materialize_rule_environment(Rule, Materialized, _Environment).
376
-
377
393
  % Environment maps each declared variable name to its fresh Prolog variable.
378
394
  materialize_rule_environment(
379
395
  rule(Kind, Modality, Head, Body, Exceptions, Scope, From, To, Schema, Variables),
@@ -389,18 +405,23 @@ variable_environment([], []).
389
405
  variable_environment([variable(Name, _Type, _Quantifier)|Rest], [Name-_Variable|Environment]) :-
390
406
  variable_environment(Rest, Environment).
391
407
 
392
- materialize_head(none, _Variables, none).
393
- materialize_head(Head, Variables, MaterialHead) :- materialize_atom(Head, Variables, MaterialHead).
408
+ materialize_head(Head, Variables, MaterialHead) :-
409
+ ( Head == none -> MaterialHead = none ; materialize_atom(Head, Variables, MaterialHead) ).
394
410
 
395
411
  materialize_expression(none, _Variables, none).
396
- materialize_expression(Expression, Variables, Materialized) :-
397
- Expression = atom(_, _, _, _, _), !,
398
- materialize_atom(Expression, Variables, Materialized).
412
+ materialize_expression(atom(Namespace, Name, Args, Polarity, ClosedWorld), Variables, Materialized) :-
413
+ % mutalog-disable-next-line cut_insert: materializing is det
414
+ materialize_atom(atom(Namespace, Name, Args, Polarity, ClosedWorld), Variables, Materialized).
415
+ % mutalog-disable-next-line cut_insert: maplist/3 over a det predicate
399
416
  materialize_expression(all(Items), Variables, all(MaterialItems)) :- maplist(materialize_expression_with(Variables), Items, MaterialItems).
417
+ % mutalog-disable-next-line cut_insert: maplist/3 over a det predicate
400
418
  materialize_expression(any(Items), Variables, any(MaterialItems)) :- maplist(materialize_expression_with(Variables), Items, MaterialItems).
419
+ % mutalog-disable-next-line cut_insert: materializing is det
401
420
  materialize_expression(not(Atom), Variables, not(MaterialAtom)) :- materialize_atom(Atom, Variables, MaterialAtom).
402
421
  materialize_expression(compare(Op, Left, Right), Variables, compare(Op, MaterialLeft, MaterialRight)) :-
422
+ % mutalog-disable-next-line cut_insert: materializing is det
403
423
  materialize_term(Left, Variables, MaterialLeft), materialize_term(Right, Variables, MaterialRight).
424
+ % mutalog-disable-next-line cut_insert: materializing is det
404
425
  materialize_expression(count(Atom, Op, Value), Variables, count(MaterialAtom, Op, Value)) :- materialize_atom(Atom, Variables, MaterialAtom).
405
426
  materialize_expression(temporal(Relation, Left, Right), Variables, temporal(Relation, MaterialLeft, MaterialRight)) :-
406
427
  materialize_term(Left, Variables, MaterialLeft), materialize_term(Right, Variables, MaterialRight).
@@ -408,7 +429,8 @@ materialize_expression_with(Variables, Expression, Materialized) :- materialize_
408
429
 
409
430
  materialize_atom(atom(Namespace, Name, Args, Polarity, ClosedWorld), Variables, atom(Namespace, Name, MaterialArgs, Polarity, ClosedWorld)) :-
410
431
  maplist(materialize_term_with(Variables), Args, MaterialArgs).
411
- materialize_term(var(Name, _Type), Variables, Variable) :- memberchk(Name-Variable, Variables), !.
432
+ % mutalog-disable-next-line cut_insert: memberchk/2 is det
433
+ materialize_term(var(Name, _Type), Variables, Variable) :- memberchk(Name-Variable, Variables).
412
434
  materialize_term(const(Value, Type), _Variables, const(Value, Type)).
413
435
  materialize_term(number(Value, Unit), _Variables, number(Value, Unit)).
414
436
  materialize_term(duration(Value, Unit), _Variables, duration(Value, Unit)).
@@ -416,47 +438,27 @@ materialize_term(timestamp(Value), _Variables, timestamp(Value)).
416
438
  materialize_term(interval(Start, End), _Variables, interval(Start, End)).
417
439
  materialize_term_with(Variables, Term, Materialized) :- materialize_term(Term, Variables, Materialized).
418
440
 
419
- prove_expression(Atom, Seen, Depth, Proof) :-
420
- Atom = atom(_, _, _, _, _),
421
- derive_goal(Atom, Seen, Depth, Proof).
422
- prove_expression(all(Items), Seen, Depth, all(Proofs)) :- maplist(prove_expression_seen(Seen, Depth), Items, Proofs).
423
- prove_expression(any(Items), Seen, Depth, Proof) :- member(Item, Items), prove_expression(Item, Seen, Depth, Proof), !.
424
- prove_expression(not(Atom), Seen, Depth, negated(Atom)) :-
425
- Atom = atom(Namespace, Name, Args, Polarity, _),
426
- \+ derive_goal(atom(Namespace, Name, Args, Polarity, _), Seen, Depth, _).
427
- prove_expression(compare(Op, Left, Right), _Seen, _Depth, comparison(Op, Left, Right)) :- compare_terms(Op, Left, Right).
428
- prove_expression(count(Atom, Op, Value), _Seen, _Depth, count(Atom, Op, Value)) :-
429
- Atom = atom(Namespace, Name, Args, Polarity, _),
430
- aggregate_all(count, predicate_fact(_, Namespace, Name, Args, Polarity), Count),
431
- compare_numbers(Op, Count, Value).
432
- prove_expression(temporal(Relation, Left, Right), _Seen, _Depth, temporal(Relation, Left, Right)) :- temporal_holds(Relation, Left, Right).
433
- prove_expression_seen(Seen, Depth, Item, Proof) :- prove_expression(Item, Seen, Depth, Proof).
434
-
435
- exceptions_do_not_hold([], _Seen, _Depth).
436
- exceptions_do_not_hold([Exception|Rest], Seen, Depth) :-
437
- \+ prove_expression(Exception, Seen, Depth, _),
438
- exceptions_do_not_hold(Rest, Seen, Depth).
439
-
440
- atom_signature_for_seen(atom(Namespace, Name, Args, Polarity, _), signature(Namespace, Name, Args, Polarity)).
441
-
441
+ % mutalog-disable-next-line cut_insert: term_value/2 is det
442
442
  compare_terms(eq, Left, Right) :- term_value(Left, L), term_value(Right, R), L = R.
443
+ % mutalog-disable-next-line cut_insert: term_value/2 is det
443
444
  compare_terms(neq, Left, Right) :- term_value(Left, L), term_value(Right, R), L \= R.
445
+ % mutalog-disable-next-line cut_insert: term_value/2 is det
444
446
  compare_terms(lt, Left, Right) :- term_value(Left, L), term_value(Right, R), L < R.
447
+ % mutalog-disable-next-line cut_insert: term_value/2 is det
445
448
  compare_terms(lte, Left, Right) :- term_value(Left, L), term_value(Right, R), L =< R.
449
+ % mutalog-disable-next-line cut_insert: term_value/2 is det
446
450
  compare_terms(gt, Left, Right) :- term_value(Left, L), term_value(Right, R), L > R.
447
451
  compare_terms(gte, Left, Right) :- term_value(Left, L), term_value(Right, R), L >= R.
448
452
 
449
- term_value(number(Value, Unit), Normalized) :- normalize_numeric_unit(Value, Unit, Normalized).
453
+ % mutalog-disable-next-line cut_insert: duration_milliseconds/3 is det
454
+ term_value(number(Value, Unit), Normalized) :- duration_milliseconds(Value, Unit, Normalized).
455
+ % mutalog-disable-next-line cut_insert: duration_milliseconds/3 is det
450
456
  term_value(duration(Value, Unit), Normalized) :- duration_milliseconds(Value, Unit, Normalized).
451
457
  term_value(const(Value, _), Number) :- atom_number(Value, Number), !.
452
458
  term_value(const(Value, _), Value).
453
459
  term_value(Value, Value) :- number(Value).
454
460
 
455
- normalize_numeric_unit(Value, none, Value) :- !.
456
- normalize_numeric_unit(Value, Unit, Normalized) :-
457
- duration_milliseconds(Value, Unit, Normalized).
458
-
459
- duration_milliseconds(Value, ms, Value) :- !.
461
+ % Other units, none and ms among them, keep their value.
460
462
  duration_milliseconds(Value, s, Normalized) :- Normalized is Value * 1000, !.
461
463
  duration_milliseconds(Value, m, Normalized) :- Normalized is Value * 60000, !.
462
464
  duration_milliseconds(Value, h, Normalized) :- Normalized is Value * 3600000, !.
@@ -464,40 +466,6 @@ duration_milliseconds(Value, d, Normalized) :- Normalized is Value * 86400000, !
464
466
  duration_milliseconds(Value, w, Normalized) :- Normalized is Value * 604800000, !.
465
467
  duration_milliseconds(Value, _Unit, Value).
466
468
 
467
- compare_numbers(eq, L, R) :- L =:= R.
468
- compare_numbers(neq, L, R) :- L =\= R.
469
- compare_numbers(lt, L, R) :- L < R.
470
- compare_numbers(lte, L, R) :- L =< R.
471
- compare_numbers(gt, L, R) :- L > R.
472
- compare_numbers(gte, L, R) :- L >= R.
473
-
474
- temporal_holds(before, Left, Right) :-
475
- temporal_end(Left, LeftEnd),
476
- temporal_start(Right, RightStart),
477
- LeftEnd @< RightStart.
478
- temporal_holds(after, Left, Right) :-
479
- temporal_start(Left, LeftStart),
480
- temporal_end(Right, RightEnd),
481
- LeftStart @> RightEnd.
482
- temporal_holds(during, timestamp(Point), interval(Start, End)) :-
483
- Point @>= Start,
484
- Point @=< End.
485
- temporal_holds(during, interval(InnerStart, InnerEnd), interval(Start, End)) :-
486
- InnerStart @>= Start,
487
- InnerEnd @=< End.
488
- temporal_holds(overlaps, interval(LeftStart, LeftEnd), interval(RightStart, RightEnd)) :-
489
- LeftStart @< RightEnd,
490
- RightStart @< LeftEnd.
491
- temporal_holds(starts, interval(LeftStart, _), interval(RightStart, _)) :-
492
- LeftStart == RightStart.
493
- temporal_holds(finishes, interval(_, LeftEnd), interval(_, RightEnd)) :-
494
- LeftEnd == RightEnd.
495
-
496
- temporal_start(timestamp(Value), Value).
497
- temporal_start(interval(Start, _), Start).
498
- temporal_end(timestamp(Value), Value).
499
- temporal_end(interval(_, End), End).
500
-
501
469
  %% logic_rule_conflict(+RuleA, +RuleB, -Status)
502
470
  % implements REQ-kibi-truthful-consistency
503
471
  % Three-valued comparison of two opposing rules.
@@ -577,7 +545,6 @@ logic_rule_missing_keys(RuleA, RuleB, Functional, Missing) :-
577
545
  ( Single \== []
578
546
  -> Missing = Single
579
547
  ; value_last_declarations(Undeclared, Declarations),
580
- Declarations \== [],
581
548
  append(Declarations, Functional, Assumed),
582
549
  logic_rule_conflict(RuleA, RuleB, Assumed, AssumedStatus),
583
550
  AssumedStatus \== unresolved
@@ -705,8 +672,9 @@ body_pair_status(PartsA, PartsB, ExceptionsA, ExceptionsB, Functional, Declared,
705
672
  rule_context_compatible(scope(AuthA, ScopeA, _), FromA, ToA, scope(AuthB, ScopeB, _), FromB, ToB) :-
706
673
  (AuthA == '' ; AuthB == '' ; AuthA == AuthB),
707
674
  (ScopeA == '' ; ScopeB == '' ; ScopeA == ScopeB),
708
- (FromA == '' ; ToB == '' ; FromA @=< ToB),
709
- (FromB == '' ; ToA == '' ; FromB @=< ToA).
675
+ % An open start ('') sorts before every timestamp.
676
+ (ToB == '' ; FromA @=< ToB),
677
+ (ToA == '' ; FromB @=< ToA).
710
678
 
711
679
  conflicting_modality(assert, deny).
712
680
  conflicting_modality(deny, assert).
@@ -761,8 +729,7 @@ every_declaration_integer(Declared, Variable) :-
761
729
  some_declaration_integer(Declared, Variable) :-
762
730
  member(Other-Type, Declared),
763
731
  Other == Variable,
764
- integer_type(Type),
765
- !.
732
+ integer_type(Type).
766
733
 
767
734
  integer_type(int).
768
735
  integer_type(integer).
@@ -942,7 +909,6 @@ comparison_entailed(Premises, Integers, compare(Op, Left, Right)) :-
942
909
  exception_entailed(Exceptions, OtherParts, Declared) :-
943
910
  member(Exception, Exceptions),
944
911
  expression_parts(Exception, ExceptionParts),
945
- ExceptionParts = parts(_, _, []),
946
912
  \+ \+ parts_subsume(ExceptionParts, OtherParts, Declared), !.
947
913
 
948
914
  %% translate_comparisons(+Comparisons, -Constraints, -Status)
@@ -987,13 +953,14 @@ reflexive_operator(gte).
987
953
  comparison_operand(Term, var(Term)) :- var(Term), !.
988
954
  comparison_operand(Term, num(Value)) :-
989
955
  ir_ground(Term),
990
- catch(term_value(Term, Value), _, fail),
991
- number(Value), !.
956
+ catch((term_value(Term, Value), number(Value)), _, fail), !.
992
957
  comparison_operand(Term, ground(Term)) :- ir_ground(Term), !.
993
958
  comparison_operand(Term, other(Term)).
994
959
 
995
960
  % IR terms keep variables as var(Name, Type) data until materialized.
996
- ir_ground(Term) :- ground(Term), \+ sub_term(var(_, _), Term).
961
+ % An unbound Prolog variable unifies with var(_, _), so this also rejects
962
+ % terms that are not ground.
963
+ ir_ground(Term) :- \+ sub_term(var(_, _), Term).
997
964
 
998
965
  flipped_operator(eq, eq).
999
966
  flipped_operator(neq, neq).
@@ -1087,6 +1054,7 @@ logic_rule_property_form(Rule0, property_form(Modality, Head, ScopeName, From, T
1087
1054
  ).
1088
1055
 
1089
1056
  expression_property_key(atom(Namespace, Name, Args, _, _), Namespace-Name) :-
1057
+ % mutalog-disable-next-line cut_insert: a test, and no later clause matches the same term
1090
1058
  Namespace \== default,
1091
1059
  Args \== [].
1092
1060
  expression_property_key(all(Items), Key) :-
@@ -1096,13 +1064,13 @@ expression_property_key(any(Items), Key) :-
1096
1064
  member(Item, Items),
1097
1065
  expression_property_key(Item, Key).
1098
1066
  expression_property_key(not(Atom), Key) :-
1067
+ % mutalog-disable-next-line cut_insert: an atom has at most one property key
1099
1068
  expression_property_key(Atom, Key).
1100
1069
  expression_property_key(count(Atom, _, _), Key) :-
1101
1070
  expression_property_key(Atom, Key).
1102
1071
 
1103
1072
  property_read(atom(Namespace, Name, Args, positive, _), read(Namespace-Name, Value, Instance)) :-
1104
1073
  Namespace \== default,
1105
- Args \== [],
1106
1074
  append(Instance, [Value], Args).
1107
1075
 
1108
1076
  % Bindings maps each value variable name to its property and declared type.
@@ -1123,16 +1091,19 @@ property_value_bindings(Reads, Head, Bindings, ConstantConditions) :-
1123
1091
  ValueA == ValueB
1124
1092
  ).
1125
1093
 
1126
- read_binding(IdentityNames, read(Key, var(Name, Type), _), acc(Bindings0, Conditions), acc(Bindings, Conditions)) :-
1127
- !,
1128
- \+ memberchk(Name, IdentityNames),
1129
- ( memberchk(Name-OtherKey-_, Bindings0)
1130
- -> OtherKey == Key,
1131
- Bindings = Bindings0
1132
- ; Bindings = [Name-Key-Type|Bindings0]
1094
+ read_binding(IdentityNames, read(Key, Value, _), acc(Bindings0, Conditions0), acc(Bindings, Conditions)) :-
1095
+ ( Value = var(Name, Type)
1096
+ -> \+ memberchk(Name, IdentityNames),
1097
+ Conditions = Conditions0,
1098
+ ( memberchk(Name-OtherKey-_, Bindings0)
1099
+ -> OtherKey == Key,
1100
+ Bindings = Bindings0
1101
+ ; Bindings = [Name-Key-Type|Bindings0]
1102
+ )
1103
+ ; constant_term(Value),
1104
+ Bindings = Bindings0,
1105
+ Conditions = [cond(Key, eq, Value, value)|Conditions0]
1133
1106
  ).
1134
- read_binding(_, read(Key, Value, _), acc(Bindings, Conditions), acc(Bindings, [cond(Key, eq, Value, value)|Conditions])) :-
1135
- constant_term(Value).
1136
1107
 
1137
1108
  constant_term(number(_, _)).
1138
1109
  constant_term(duration(_, _)).
@@ -1196,6 +1167,7 @@ logic_predicate_signature(atom(Namespace, Name, Args, _Polarity, _ClosedWorld),
1196
1167
  length(Args, Arity).
1197
1168
 
1198
1169
  expression_dependency(Atom, Atom, positive) :-
1170
+ % mutalog-disable-next-line cut_insert: a test, and no later clause matches the same term
1199
1171
  Atom = atom(_, _, _, _, _).
1200
1172
  expression_dependency(not(Atom), Atom, negative).
1201
1173
  expression_dependency(all(Items), Atom, Sign) :- member(Item, Items), expression_dependency(Item, Atom, Sign).
@@ -1203,9 +1175,9 @@ expression_dependency(any(Items), Atom, Sign) :- member(Item, Items), expression
1203
1175
  expression_dependency(count(Atom, _Operator, _Value), Atom, positive).
1204
1176
 
1205
1177
  dependency_path(Current, Target, _Edges, Seen) :-
1178
+ % mutalog-disable-next-line cut_insert: a test, and no later clause matches the same term
1206
1179
  Current == Target,
1207
- \+ memberchk(Current, Seen),
1208
- !.
1180
+ \+ memberchk(Current, Seen).
1209
1181
  dependency_path(Current, Target, Edges, Seen) :-
1210
1182
  \+ memberchk(Current, Seen),
1211
1183
  member(edge(Current, Next, _Sign), Edges),
@@ -1247,6 +1219,3 @@ logic_rule_conflict_witness(RuleA, RuleB, Functional, Witness) :-
1247
1219
  rule_schema_b: SchemaB
1248
1220
  }.
1249
1221
 
1250
-
1251
- normalize_atom(Value, Atom) :- atom(Value), !, Atom = Value.
1252
- normalize_atom(Value, Atom) :- string(Value), atom_string(Atom, Value).