kibi-core 0.23.0 → 0.26.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kibi-core",
3
- "version": "0.23.0",
3
+ "version": "0.26.1",
4
4
  "private": false,
5
5
  "description": "Core Prolog modules and RDF graph logic for Kibi",
6
6
  "type": "module",
@@ -56,6 +56,9 @@ entity_property(req, exempts_claims, list).
56
56
  % Absent reads as default.
57
57
  entity_property(req, firmness, atom).
58
58
  entity_property(adr, firmness, atom).
59
+ % The weakest evidence level a current requirement accepts: proved, tested,
60
+ % asserted or unverified. Absent: tested when firm, otherwise unverified.
61
+ entity_property(req, required_evidence, atom).
59
62
  % Intended outcome of a scenario: success, rejection or error.
60
63
  entity_property(scenario, expects, atom).
61
64
 
@@ -69,6 +72,8 @@ entity_property(fact, value_string, string).
69
72
  entity_property(fact, value_int, integer).
70
73
  entity_property(fact, value_number, number).
71
74
  entity_property(fact, value_bool, boolean).
75
+ % The exact members of a set fact (value_type set).
76
+ entity_property(fact, value_set, list).
72
77
  entity_property(fact, unit, string).
73
78
  entity_property(fact, scope, string).
74
79
  entity_property(fact, polarity, atom).
@@ -150,6 +155,7 @@ optional_property(req, rationale).
150
155
  optional_property(req, exempts_claims).
151
156
  optional_property(req, firmness).
152
157
  optional_property(adr, firmness).
158
+ optional_property(req, required_evidence).
153
159
  optional_property(test, verification_scope).
154
160
  optional_property(test, verification_perspective).
155
161
  optional_property(test, proof_contract).
@@ -108,6 +108,7 @@ is_fact_only_field(value_string).
108
108
  is_fact_only_field(value_int).
109
109
  is_fact_only_field(value_number).
110
110
  is_fact_only_field(value_bool).
111
+ is_fact_only_field(value_set).
111
112
  is_fact_only_field(unit).
112
113
  is_fact_only_field(scope).
113
114
  is_fact_only_field(polarity).
@@ -156,6 +157,8 @@ validate_fact_shape(property_value, Props) :-
156
157
  valid_operator(Op),
157
158
  memberchk(value_type=VT, Props),
158
159
  valid_value_type(VT),
160
+ ( Op == matches -> VT == string ; true ),
161
+ ( memberchk(Op, [includes, subset_of, excludes]) -> VT == set ; true ),
159
162
  exactly_one_value_field(Props),
160
163
  value_type_matches_field(VT, Props),
161
164
  valid_optional_fact_enums(Props),
@@ -224,10 +227,10 @@ text_atom(Value, Atom) :- atom(Value), !, Atom = Value.
224
227
  text_atom(Value, Atom) :- string(Value), atom_string(Atom, Value).
225
228
 
226
229
  % valid_operator(+Op)
227
- valid_operator(Op) :- memberchk(Op, [eq, neq, lt, lte, gt, gte]), !.
230
+ valid_operator(Op) :- memberchk(Op, [eq, neq, lt, lte, gt, gte, matches, includes, subset_of, excludes]), !.
228
231
 
229
232
  % valid_value_type(+VT)
230
- valid_value_type(ValueType) :- memberchk(ValueType, [string, int, number, bool]), !.
233
+ valid_value_type(ValueType) :- memberchk(ValueType, [string, int, number, bool, set]), !.
231
234
 
232
235
  % valid_polarity_in_props(+Props)
233
236
  % Validates polarity if present; succeeds if no polarity in props
@@ -299,6 +302,7 @@ is_value_field(value_string).
299
302
  is_value_field(value_int).
300
303
  is_value_field(value_number).
301
304
  is_value_field(value_bool).
305
+ is_value_field(value_set).
302
306
 
303
307
  % value_type_matches_field(+ValueType, +Props)
304
308
  % Ensures that value_type matches the actual value field present
@@ -306,6 +310,7 @@ value_type_matches_field(string, Props) :- memberchk(value_string=_, Props), !.
306
310
  value_type_matches_field(int, Props) :- memberchk(value_int=_, Props), !.
307
311
  value_type_matches_field(number, Props) :- memberchk(value_number=_, Props), !.
308
312
  value_type_matches_field(bool, Props) :- memberchk(value_bool=_, Props), !.
313
+ value_type_matches_field(set, Props) :- memberchk(value_set=_, Props), !.
309
314
 
310
315
  % valid_origin_in_props(+Props)
311
316
  % Entity provenance travels as a JSON object string (or a dict). When present
@@ -38,6 +38,7 @@
38
38
  certificate_rule_edges/1,
39
39
  certificate_member_holds/3,
40
40
  certificate_members_satisfiable/2,
41
+ set_texts/2,
41
42
  verify_certificates/2,
42
43
  kb_verify_certificates_file/2
43
44
  ]).
@@ -148,7 +149,7 @@ kind_is(Certificate, Kind) :-
148
149
  % Every comparable context: context(Subject, Property, Scope, Domain, Members)
149
150
  % or unresolved(Subject, Property, Scope, Reason). A member is
150
151
  % m(FactId, Op, Value, ValidFrom, ValidTo) with the operator already negated
151
- % for a forbid fact; Domain is int, number, bool or string.
152
+ % for a forbid fact; Domain is int, number, bool, string or set.
152
153
  certificate_property_contexts(Contexts) :-
153
154
  findall(Fact-tuple(Subject, Property, Op, Type, Value, Unit, Scope, Polarity, From, To),
154
155
  ( kb:current_req(Req),
@@ -202,6 +203,8 @@ context_domain(Members, Domain, Reason) :-
202
203
  -> Domain = bool, context_scalar_reason(Members, Reason)
203
204
  ; Types == [string]
204
205
  -> Domain = string, context_scalar_reason(Members, Reason)
206
+ ; Types == [set]
207
+ -> Domain = set, context_set_reason(Members, Reason)
205
208
  ; Types = [_, _|_]
206
209
  -> Domain = none, Reason = "the facts use different value types"
207
210
  ; Domain = none, Reason = "the value type is not supported"
@@ -225,6 +228,25 @@ context_scalar_reason(Members, Reason) :-
225
228
  ; Reason = none
226
229
  ).
227
230
 
231
+ % Sets also compare with includes (every member of the bound has the
232
+ % property), subset_of (every member with the property is in the bound) and
233
+ % excludes (no member of the bound has it), but only as requirements: a
234
+ % forbidden one has no single negation among the set operators. neq beside
235
+ % subset_of or excludes is left unresolved: the finite candidates below decide
236
+ % those only without neq.
237
+ context_set_reason(Members, Reason) :-
238
+ ( member(_-tuple(_, _, Op, _, _, _, _, Polarity, _, _), Members),
239
+ ( \+ memberchk(Op, [eq, neq, includes, subset_of, excludes])
240
+ ; memberchk(Op, [includes, subset_of, excludes]), Polarity == forbid
241
+ )
242
+ -> Reason = "only eq, neq and a required includes, subset_of or excludes compare set values"
243
+ ; member(_-tuple(_, _, neq, _, _, _, _, _, _, _), Members),
244
+ member(_-tuple(_, _, Bounding, _, _, _, _, _, _, _), Members),
245
+ memberchk(Bounding, [subset_of, excludes])
246
+ -> Reason = "neq beside subset_of or excludes on one set property is not compared"
247
+ ; Reason = none
248
+ ).
249
+
228
250
  context_member(_Domain, Fact-tuple(_, _, Op0, _, Value, _, _, Polarity, From, To), m(Fact, Op, Value, From, To)) :-
229
251
  ( Polarity == forbid
230
252
  -> numeric_constraint_negation(c(Op0, _, Value), c(Op, _, _))
@@ -247,6 +269,26 @@ certificate_member_holds(Domain, m(_, Op, Bound, _, _), Value) :-
247
269
  ; Op == neq -> ValueText \== BoundText
248
270
  ).
249
271
 
272
+ % A set value is the exact set of members with the property: eq holds for
273
+ % the same members in any order, neq for any other set, includes for any set
274
+ % that contains every member of the bound.
275
+ certificate_member_holds(set, m(_, Op, Bound, _, _), Value) :-
276
+ set_texts(Value, ValueTexts),
277
+ set_texts(Bound, BoundTexts),
278
+ ( Op == eq -> ValueTexts == BoundTexts
279
+ ; Op == neq -> ValueTexts \== BoundTexts
280
+ ; Op == includes -> subtract(BoundTexts, ValueTexts, [])
281
+ ; Op == subset_of -> subtract(ValueTexts, BoundTexts, [])
282
+ ; Op == excludes -> \+ ( member(Text, ValueTexts), memberchk(Text, BoundTexts) )
283
+ ).
284
+
285
+ %% set_texts(+Value, -Texts)
286
+ % The sorted, unique member texts of a set value.
287
+ set_texts(Value, Texts) :-
288
+ is_list(Value),
289
+ maplist(text_of, Value, Texts0),
290
+ sort(Texts0, Texts).
291
+
250
292
  numeric_domain(int).
251
293
  numeric_domain(number).
252
294
 
@@ -282,6 +324,25 @@ scalar_candidates(string, Members, Candidates) :-
282
324
  atomic_list_concat(["kibi:fresh"|Texts], '|', FreshAtom),
283
325
  atom_string(FreshAtom, Fresh),
284
326
  append(Texts, [Fresh], Candidates).
327
+ scalar_candidates(set, Members, Candidates) :-
328
+ findall(Texts, (member(m(_, _, Bound, _, _), Members), set_texts(Bound, Texts)), Sets0),
329
+ sort(Sets0, Sets),
330
+ findall(Text, (member(Set, Sets), member(Text, Set)), Texts0),
331
+ sort(Texts0, Texts),
332
+ atomic_list_concat(["kibi:fresh"|Texts], '|', FreshAtom),
333
+ atom_string(FreshAtom, Fresh),
334
+ % The union of every bound satisfies any set of includes facts alone; the
335
+ % union of the includes bounds is the least set every includes fact
336
+ % allows, so with subset_of and excludes (and no eq or neq) it is
337
+ % satisfiable exactly when that union is.
338
+ includes_union(Members, Included),
339
+ append(Sets, [Texts, Included, [Fresh]], Candidates).
340
+
341
+ includes_union(Members, Union) :-
342
+ findall(Text,
343
+ (member(m(_, includes, Bound, _, _), Members), set_texts(Bound, Set), member(Text, Set)),
344
+ Texts),
345
+ sort(Texts, Union).
285
346
 
286
347
  property_theorem(Contexts, Numbered, Theorem, Used, Rejected) :-
287
348
  findall(Index-Certificate,
@@ -27,7 +27,8 @@
27
27
  certificate_decision_groups/1,
28
28
  certificate_rule_edges/1,
29
29
  certificate_member_holds/3,
30
- certificate_members_satisfiable/2
30
+ certificate_members_satisfiable/2,
31
+ set_texts/2
31
32
  ]).
32
33
 
33
34
  %% kb_write_certificates(+Path, -JsonString)
@@ -121,6 +122,21 @@ model_candidates(string, Members, Candidates) :-
121
122
  fresh_text(Bounds, 0, Fresh),
122
123
  append(Bounds, [Fresh], Candidates).
123
124
 
125
+ model_candidates(set, Members, Candidates) :-
126
+ findall(Set, (member(m(_, _, Bound, _, _), Members), set_texts(Bound, Set)), Sets0),
127
+ sort(Sets0, Sets),
128
+ findall(Text, (member(Set, Sets), member(Text, Set)), Texts0),
129
+ sort(Texts0, Texts),
130
+ fresh_text(Texts, 0, Fresh),
131
+ % The union of every bound satisfies any set of includes facts alone;
132
+ % the union of the includes bounds alone is the least set they allow,
133
+ % which subset_of and excludes facts accept when anything does.
134
+ findall(Text,
135
+ (member(m(_, includes, Bound, _, _), Members), set_texts(Bound, Set), member(Text, Set)),
136
+ Included0),
137
+ sort(Included0, Included),
138
+ append(Sets, [Texts, Included, [Fresh]], Candidates).
139
+
124
140
  fresh_text(Bounds, Index, Fresh) :-
125
141
  format(string(Candidate), "kibi:model:~w", [Index]),
126
142
  ( member(Bound, Bounds), text_value(Bound, Candidate)
package/src/checks.pl CHANGED
@@ -2377,7 +2377,7 @@ property_value_shape_error(Props, "Property value fact missing required field: o
2377
2377
  property_value_shape_error(Props, "Property value fact missing required field: value_type") :-
2378
2378
  memberchk(operator=_, Props),
2379
2379
  \+ memberchk(value_type=_, Props).
2380
- property_value_shape_error(Props, "Property value fact missing value field (value_string, value_int, value_number, or value_bool)") :-
2380
+ property_value_shape_error(Props, "Property value fact missing value field (value_string, value_int, value_number, value_bool, or value_set)") :-
2381
2381
  memberchk(value_type=_, Props),
2382
2382
  \+ has_value_field(Props).
2383
2383
  property_value_shape_error(Props, "Property value fact has multiple value fields (should have exactly one)") :-
@@ -2394,17 +2394,20 @@ is_value_field(value_string).
2394
2394
  is_value_field(value_int).
2395
2395
  is_value_field(value_number).
2396
2396
  is_value_field(value_bool).
2397
+ is_value_field(value_set).
2397
2398
 
2398
2399
  has_value_field(Props) :- memberchk(value_string=_, Props), !.
2399
2400
  has_value_field(Props) :- memberchk(value_int=_, Props), !.
2400
2401
  has_value_field(Props) :- memberchk(value_number=_, Props), !.
2401
2402
  has_value_field(Props) :- memberchk(value_bool=_, Props), !.
2403
+ has_value_field(Props) :- memberchk(value_set=_, Props), !.
2402
2404
 
2403
2405
  % value_type_matches_field(+ValueType, +Props)
2404
2406
  value_type_matches_field(string, Props) :- memberchk(value_string=_, Props), !.
2405
2407
  value_type_matches_field(int, Props) :- memberchk(value_int=_, Props), !.
2406
2408
  value_type_matches_field(number, Props) :- memberchk(value_number=_, Props), !.
2407
2409
  value_type_matches_field(bool, Props) :- memberchk(value_bool=_, Props), !.
2410
+ value_type_matches_field(set, Props) :- memberchk(value_set=_, Props), !.
2408
2411
 
2409
2412
  predicate_schema_shape_error(Props, "Predicate schema fact missing required field: predicate_name") :-
2410
2413
  \+ memberchk(predicate_name=_, Props).