eyeling 1.33.6 → 1.33.8
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/README.md +2 -2
- package/docs/eyelang-guide.md +7 -10
- package/docs/eyelang-language-reference.md +12 -10
- package/examples/eyelang/annotation.pl +9 -9
- package/examples/eyelang/context-association.pl +3 -3
- package/examples/eyelang/delfour.pl +7 -7
- package/examples/eyelang/dijkstra-risk-path.pl +2 -2
- package/examples/eyelang/dijkstra.pl +2 -2
- package/examples/eyelang/family-cousins.pl +1 -1
- package/examples/eyelang/gps.pl +4 -4
- package/examples/eyelang/odrl-dpv-healthcare-risk-ranked.pl +2 -2
- package/examples/eyelang/odrl-dpv-risk-ranked.pl +2 -2
- package/examples/eyelang/proof/annotation.pl +12 -12
- package/lib/eyelang/builtins/context.js +42 -0
- package/lib/eyelang/builtins/registry.js +2 -3
- package/package.json +1 -1
- package/test/eyelang/conformance/README.md +1 -1
- package/test/eyelang/conformance/cases/extension/006_holds_parts.pl +4 -0
- package/test/eyelang/conformance/cases/extension/026_nested_holds_parts.pl +4 -0
- package/test/eyelang/conformance/cases/extension/027_formula_member.query +1 -0
- package/test/eyelang/conformance/cases/extension/027_holds_member.pl +3 -0
- package/test/eyelang/conformance/expected/extension/006_holds_parts.out +3 -0
- package/test/eyelang/conformance/expected/extension/026_nested_holds_parts.out +3 -0
- package/test/eyelang/conformance/expected/extension/027_holds_member.out +2 -0
- package/examples/eyelang/output/project-portfolio-optimization.pl +0 -6
- package/examples/eyelang/output/resilient-city-orchestration.pl +0 -67
- package/examples/eyelang/project-portfolio-optimization.pl +0 -101
- package/examples/eyelang/resilient-city-orchestration.pl +0 -303
- package/lib/eyelang/builtins/formula.js +0 -26
- package/lib/eyelang/builtins/portfolio.js +0 -73
- package/test/eyelang/conformance/cases/extension/006_formula_terms.pl +0 -4
- package/test/eyelang/conformance/cases/extension/026_nested_formula_terms.pl +0 -4
- package/test/eyelang/conformance/cases/extension/046_bounded_subset.pl +0 -4
- package/test/eyelang/conformance/expected/extension/006_formula_terms.out +0 -2
- package/test/eyelang/conformance/expected/extension/026_nested_formula_terms.out +0 -3
- package/test/eyelang/conformance/expected/extension/046_bounded_subset.out +0 -5
package/README.md
CHANGED
|
@@ -828,8 +828,8 @@ The eyelang engine has its own built-in registry under `lib/eyelang/builtins/`.
|
|
|
828
828
|
| Lists | `append/3`, `nth0/3`, `set_nth0/4`, `rest/2`, `member/2`, `select/3`, `not_member/2`, `reverse/2`, `length/2`, `sort/2` |
|
|
829
829
|
| Aggregation | `findall/3`, `countall/2`, `sumall/3`, `aggregate_min/5`, `aggregate_max/5` |
|
|
830
830
|
| Control | `not/1`, `once/1` |
|
|
831
|
-
|
|
|
832
|
-
| Search and optimization helpers | `n_queens/2`, `weighted_hamiltonian_cycle/4`, `weighted_hamiltonian_path/4`, `hamiltonian_cycle/3`, `fixed_length_cycle/4`, `bounded_path/5`, `
|
|
831
|
+
| Context terms | `holds/2`, `holds/3` |
|
|
832
|
+
| Search and optimization helpers | `n_queens/2`, `weighted_hamiltonian_cycle/4`, `weighted_hamiltonian_path/4`, `hamiltonian_cycle/3`, `fixed_length_cycle/4`, `bounded_path/5`, `cnf_model/3`, `qm_prime_implicants/4`, `qm_minimal_cover/4`, `alphametic_sum/5` |
|
|
833
833
|
| Numeric extension helpers | `extended_gcd/5`, `collatz_trajectory/2`, `kaprekar_steps/2`, `goldbach_pair/3` |
|
|
834
834
|
| Matrix helpers | `matrix_sum/2`, `matrix_multiply/2`, `cholesky_decomposition/2`, `determinant/2`, `matrix_inv_triang/2`, `matrix_inversion/2` |
|
|
835
835
|
|
package/docs/eyelang-guide.md
CHANGED
|
@@ -16,7 +16,7 @@ For the normative language definition, including lexical syntax, terms, clauses,
|
|
|
16
16
|
3. [Default output](#default-output)
|
|
17
17
|
4. [Writing programs](#writing-programs)
|
|
18
18
|
5. [Aggregation helpers](#aggregation-helpers)
|
|
19
|
-
6. [
|
|
19
|
+
6. [Context data](#context-data)
|
|
20
20
|
7. [Example catalog](#example-catalog)
|
|
21
21
|
8. [Golden outputs, tests, and conformance](#golden-outputs-tests-and-conformance)
|
|
22
22
|
9. [Development and release](#development-and-release)
|
|
@@ -250,7 +250,7 @@ The playground has matching `--stats` and `--proof` checkboxes, so browser runs
|
|
|
250
250
|
|
|
251
251
|
eyelang builtins are registered by name and arity in small modules under [`lib/eyelang/builtins`](../lib/eyelang/builtins). This keeps the runtime portable to Node.js and the browser while giving each builtin family a clear boundary. Builtins are enabled by normal predicate calls.
|
|
252
252
|
|
|
253
|
-
The core builtin families cover unification, arithmetic, comparison, dates, strings, lists, aggregation,
|
|
253
|
+
The core builtin families cover unification, arithmetic, comparison, dates, strings, lists, aggregation, context terms, and search control. Additional reusable finite-search helpers are available only where bundled examples need them to avoid large amounts of repetitive generate-and-test code. These helpers are deliberately general relations rather than shortcuts tied to a particular example name. For example:
|
|
254
254
|
|
|
255
255
|
```prolog
|
|
256
256
|
answer(Queens) :-
|
|
@@ -287,17 +287,16 @@ best_cycle(Cycle, Cost) :-
|
|
|
287
287
|
aggregate_min([Cost, Cycle], Cycle, candidate_cycle(Cities, Cycle, Cost), [Cost, Cycle], Cycle).
|
|
288
288
|
```
|
|
289
289
|
|
|
290
|
-
##
|
|
290
|
+
## Context data
|
|
291
291
|
|
|
292
|
-
Comma terms can be data as well as conjunctions. eyelang provides
|
|
293
|
-
|
|
294
|
-
`formula_binary(Formula, S, P, O)` enumerates binary terms and exposes their functor as an atom constant:
|
|
292
|
+
Comma terms can be data as well as conjunctions. eyelang provides two context utilities:
|
|
295
293
|
|
|
296
294
|
```prolog
|
|
297
|
-
|
|
295
|
+
holds((name(alice, "Alice"), knows(alice, bob)), name(S, O)).
|
|
296
|
+
holds((ready, name(alice, "Alice"), route(alice, bob, 7)), Name, Args).
|
|
298
297
|
```
|
|
299
298
|
|
|
300
|
-
|
|
299
|
+
Use `holds/2` when you want to match the member term directly, for example `name(S, O)`, `route(A, B, Cost)`, or `edge(A, arc(B, Cost))`. Use `holds/3` when you need the predicate name and argument list as data: it exposes any-arity member as atom constant `Name` plus a proper list `Args`, so zero-, binary-, and ternary members appear as `ready/0`, `name/2`, and `route/3` shapes without a special binary predicate. These utilities are useful for quoted context data, but they do not make those context members true in the ambient program.
|
|
301
300
|
|
|
302
301
|
|
|
303
302
|
## Example catalog
|
|
@@ -416,12 +415,10 @@ The repository includes examples for recursion, graph reachability, finite searc
|
|
|
416
415
|
| [`peasant.pl`](../examples/eyelang/peasant.pl) | Performs peasant multiplication and exponentiation. | [`output/peasant.pl`](../examples/eyelang/output/peasant.pl) |
|
|
417
416
|
| [`pendulum-period.pl`](../examples/eyelang/pendulum-period.pl) | Computes simple pendulum periods. | [`output/pendulum-period.pl`](../examples/eyelang/output/pendulum-period.pl) |
|
|
418
417
|
| [`polynomial.pl`](../examples/eyelang/polynomial.pl) | Finds complex integer polynomial roots. | [`output/polynomial.pl`](../examples/eyelang/output/polynomial.pl) |
|
|
419
|
-
| [`project-portfolio-optimization.pl`](../examples/eyelang/project-portfolio-optimization.pl) | Optimizes a constrained project portfolio with pruning and aggregate builtins. | [`output/project-portfolio-optimization.pl`](../examples/eyelang/output/project-portfolio-optimization.pl) |
|
|
420
418
|
| [`proof-contrapositive.pl`](../examples/eyelang/proof-contrapositive.pl) | Models proof by contrapositive. | [`output/proof-contrapositive.pl`](../examples/eyelang/output/proof-contrapositive.pl) |
|
|
421
419
|
| [`quadratic-formula.pl`](../examples/eyelang/quadratic-formula.pl) | Solves sample quadratic equations. | [`output/quadratic-formula.pl`](../examples/eyelang/output/quadratic-formula.pl) |
|
|
422
420
|
| [`quine-mccluskey.pl`](../examples/eyelang/quine-mccluskey.pl) | Minimizes Boolean terms with Quine-McCluskey. | [`output/quine-mccluskey.pl`](../examples/eyelang/output/quine-mccluskey.pl) |
|
|
423
421
|
| [`radioactive-decay.pl`](../examples/eyelang/radioactive-decay.pl) | Computes radioactive decay over time. | [`output/radioactive-decay.pl`](../examples/eyelang/output/radioactive-decay.pl) |
|
|
424
|
-
| [`resilient-city-orchestration.pl`](../examples/eyelang/resilient-city-orchestration.pl) | Orchestrates storm-response missions from signals, policy, routes, teams, and portfolio optimization. | [`output/resilient-city-orchestration.pl`](../examples/eyelang/output/resilient-city-orchestration.pl) |
|
|
425
422
|
| [`riemann-hypothesis.pl`](../examples/eyelang/riemann-hypothesis.pl) | Checks a finite catalogue of non-trivial zeta zeros against the Riemann-hypothesis condition. | [`output/riemann-hypothesis.pl`](../examples/eyelang/output/riemann-hypothesis.pl) |
|
|
426
423
|
| [`sat-dpll.pl`](../examples/eyelang/sat-dpll.pl) | Solves a finite SAT instance. | [`output/sat-dpll.pl`](../examples/eyelang/output/sat-dpll.pl) |
|
|
427
424
|
| [`security-incident-correlation.pl`](../examples/eyelang/security-incident-correlation.pl) | Correlates security incidents across signals. | [`output/security-incident-correlation.pl`](../examples/eyelang/output/security-incident-correlation.pl) |
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
- [9.6 Strings and atom constants](#96-strings-and-atom-constants)
|
|
40
40
|
- [9.7 Lists](#97-lists)
|
|
41
41
|
- [9.8 Aggregation and ordering](#98-aggregation-and-ordering)
|
|
42
|
-
- [9.9
|
|
42
|
+
- [9.9 Context terms](#99-context-terms)
|
|
43
43
|
- [9.10 Search control](#910-search-control)
|
|
44
44
|
- [10. Extension built-ins](#10-extension-built-ins)
|
|
45
45
|
- [11. Declarations](#11-declarations)
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
|
|
68
68
|
eyelang is a compact Prolog-like definite-clause language for rule-based programs over ordinary terms, lists, arithmetic, strings, and finite search. An eyelang program is a finite sequence of facts and Horn clauses. The underlying declarative semantics of the pure language is **Herbrand semantics**: constants, compound terms, and lists denote themselves, and predicates denote sets of ground atomic formulas over those terms. Evaluation is goal-directed: goals are solved by unification against facts, rules, and a fixed set of built-in predicates.
|
|
69
69
|
|
|
70
|
-
eyelang is intentionally smaller than ISO Prolog. It supports enough Prolog syntax to express Horn-clause reasoning, list processing, arithmetic examples, finite search, and
|
|
70
|
+
eyelang is intentionally smaller than ISO Prolog. It supports enough Prolog syntax to express Horn-clause reasoning, list processing, arithmetic examples, finite search, and context data, without operators, cut, modules, dynamic predicates, DCGs, or a complete ISO standard library.
|
|
71
71
|
|
|
72
72
|
## 1. Terminology and normative language
|
|
73
73
|
|
|
@@ -96,7 +96,7 @@ eyelang is designed to be:
|
|
|
96
96
|
- small enough to embed and audit;
|
|
97
97
|
- deterministic in textual output order after duplicate suppression;
|
|
98
98
|
- useful for relation-style `p(S, O)` output through ordinary predicate names;
|
|
99
|
-
- practical for examples involving recursion, lists, arithmetic, strings, aggregation, finite search, and
|
|
99
|
+
- practical for examples involving recursion, lists, arithmetic, strings, aggregation, finite search, and context-valued data.
|
|
100
100
|
|
|
101
101
|
Non-goals include complete ISO Prolog compatibility, operator declarations, module systems, dynamic database updates, cut-based control, and full bottom-up closure semantics.
|
|
102
102
|
|
|
@@ -252,7 +252,7 @@ Parenthesized comma terms may be goals or data:
|
|
|
252
252
|
(name(alice, "Alice"), knows(alice, bob))
|
|
253
253
|
```
|
|
254
254
|
|
|
255
|
-
When a comma term appears as a goal, it is evaluated as conjunction. When it appears as data, it remains a term. `
|
|
255
|
+
When a comma term appears as a goal, it is evaluated as conjunction. When it appears as data, it remains a term. `holds/2` enumerates member terms inside such contexts, and `holds/3` exposes each member as a predicate name plus an argument list for any arity.
|
|
256
256
|
|
|
257
257
|
## 6. Clauses and predicates
|
|
258
258
|
|
|
@@ -429,21 +429,23 @@ Comparisons interpret numeric-looking terms numerically. Other scalar terms are
|
|
|
429
429
|
| `aggregate_max(Key, Template, Goal, BestKey, BestTemplate)` | Selects the solution of `Goal` with the largest resolved `Key`, returning that key and the corresponding resolved `Template`. Fails when `Goal` has no solutions. |
|
|
430
430
|
| `sort(Input, Output)` | Sorts and deduplicates a proper list. |
|
|
431
431
|
|
|
432
|
-
### 9.9
|
|
432
|
+
### 9.9 Context terms
|
|
433
433
|
|
|
434
|
-
|
|
434
|
+
Context terms are data representations of atomic formulas and comma conjunctions.
|
|
435
435
|
|
|
436
436
|
| Built-in | Meaning |
|
|
437
437
|
|---|---|
|
|
438
|
-
| `
|
|
438
|
+
| `holds(Context, Term)` | Enumerates member terms inside a context term and unifies each member with `Term`. |
|
|
439
|
+
| `holds(Context, Name, Args)` | Enumerates context members of any arity, exposing each member as atom constant `Name` plus a proper argument list `Args`. |
|
|
439
440
|
|
|
440
441
|
Example:
|
|
441
442
|
|
|
442
443
|
```prolog
|
|
443
|
-
|
|
444
|
+
holds((name(alice, "Alice"), knows(alice, bob)), name(S, O)).
|
|
445
|
+
holds((ready, name(alice, "Alice"), route(alice, bob, 7)), Name, Args).
|
|
444
446
|
```
|
|
445
447
|
|
|
446
|
-
|
|
448
|
+
The first goal can yield `holds((name(alice, "Alice"), knows(alice, bob)), name(alice, "Alice")).` The second can yield `holds((ready, name(alice, "Alice"), route(alice, bob, 7)), ready, []).`, `holds((ready, name(alice, "Alice"), route(alice, bob, 7)), name, [alice, "Alice"]).`, and `holds((ready, name(alice, "Alice"), route(alice, bob, 7)), route, [alice, bob, 7]).`
|
|
447
449
|
|
|
448
450
|
### 9.10 Search control
|
|
449
451
|
|
|
@@ -466,7 +468,7 @@ An extension built-in SHOULD obey the same surface-language discipline as standa
|
|
|
466
468
|
- it SHOULD document its intended modes, especially which arguments must be ground before it runs deterministically;
|
|
467
469
|
- it MUST NOT change the meaning of ordinary facts, rules, unification, or standard built-ins.
|
|
468
470
|
|
|
469
|
-
For example, an implementation may include extension modules for
|
|
471
|
+
For example, an implementation may include extension modules for number-theory algorithms, graph search, matrix operations, or alphametic puzzles. Those modules may be valuable and may make example programs much faster, but their predicate names, arities, algorithms, and modes are implementation-defined unless they are separately standardized.
|
|
470
472
|
|
|
471
473
|
An implementation that provides explanation output SHOULD make extension built-ins explainable at least as opaque successful or failed built-in calls, so that proof traces do not incorrectly report "no clauses" for a host-provided relation.
|
|
472
474
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
% Annotation with quoted formula data.
|
|
2
2
|
%
|
|
3
3
|
% The program keeps the annotation as data and derives visible relations from it.
|
|
4
|
-
%
|
|
4
|
+
% Context members become default output only when explicit rules project them.
|
|
5
5
|
|
|
6
6
|
% Output declarations: materialize/2 selects the relations written to this example's golden output.
|
|
7
7
|
materialize(name, 2).
|
|
@@ -18,17 +18,17 @@ annotation(t, (
|
|
|
18
18
|
|
|
19
19
|
% Derivation rules: each rule below contributes one logical step toward the displayed results.
|
|
20
20
|
name(S, O) :-
|
|
21
|
-
annotation(_T,
|
|
22
|
-
|
|
21
|
+
annotation(_T, Context),
|
|
22
|
+
holds(Context, name(S, O)).
|
|
23
23
|
|
|
24
24
|
log_nameOf(T, name(S, O)) :-
|
|
25
|
-
annotation(T,
|
|
26
|
-
|
|
25
|
+
annotation(T, Context),
|
|
26
|
+
holds(Context, name(S, O)).
|
|
27
27
|
|
|
28
28
|
statedBy(S, O) :-
|
|
29
|
-
annotation(_T,
|
|
30
|
-
|
|
29
|
+
annotation(_T, Context),
|
|
30
|
+
holds(Context, statedBy(S, O)).
|
|
31
31
|
|
|
32
32
|
recorded(S, O) :-
|
|
33
|
-
annotation(_T,
|
|
34
|
-
|
|
33
|
+
annotation(_T, Context),
|
|
34
|
+
holds(Context, recorded(S, O)).
|
|
@@ -39,9 +39,9 @@ log_nameOf(g3, (
|
|
|
39
39
|
% A tiny projection shows how a program can inspect a quoted context without
|
|
40
40
|
% making the entire context globally true.
|
|
41
41
|
% Derivation rules: each rule below contributes one logical step toward the displayed results.
|
|
42
|
-
context_statement(
|
|
43
|
-
log_nameOf(
|
|
44
|
-
|
|
42
|
+
context_statement(ContextName, Subject, Predicate, Object) :-
|
|
43
|
+
log_nameOf(ContextName, Context),
|
|
44
|
+
holds(Context, Predicate, [Subject, Object]).
|
|
45
45
|
|
|
46
46
|
dataGraph(association, skolem_g0) :-
|
|
47
47
|
context_statement(skolem_g0, bob, foaf_name, "Bob").
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
% checklist facts as relation materialization.
|
|
5
5
|
%
|
|
6
6
|
% Static input is kept as scoped data: the case, insight, policy, envelope, and
|
|
7
|
-
% signature are
|
|
7
|
+
% signature are context terms, while the product catalog is a list of records.
|
|
8
8
|
% Rules project only the fields they need, avoiding global permission/prohibition
|
|
9
9
|
% facts that could contradict another policy formula in the same program.
|
|
10
10
|
|
|
@@ -41,7 +41,7 @@ materialize(marketingProhibited, 2).
|
|
|
41
41
|
materialize(filesWrittenExpected, 2).
|
|
42
42
|
|
|
43
43
|
% Program structure: facts set up the scenario, and rules derive the materialized conclusions.
|
|
44
|
-
%
|
|
44
|
+
% Context-valued facts keep each input graph scoped and easy to project.
|
|
45
45
|
case_graph(delfourCaseGraph, (
|
|
46
46
|
caseName(case, "delfour"),
|
|
47
47
|
requestPurpose(case, "shopping_assist"),
|
|
@@ -102,11 +102,11 @@ signature_graph(delfourSignatureGraph, (
|
|
|
102
102
|
reason_text(reasonText, "Household requires low-sugar guidance (diabetes in POD). A neutral Insight is scoped to device 'self-scanner', event 'pick_up_scanner', retailer 'Delfour', and expires soon; the policy confines use to shopping assistance.").
|
|
103
103
|
|
|
104
104
|
% Derivation rules: each rule below contributes one logical step toward the displayed results.
|
|
105
|
-
case_statement(S, P, O) :- case_graph(delfourCaseGraph,
|
|
106
|
-
insight_statement(S, P, O) :- insight_graph(delfourInsightGraph,
|
|
107
|
-
policy_statement(S, P, O) :- policy_graph(delfourPolicyGraph,
|
|
108
|
-
envelope_statement(S, P, O) :- envelope_graph(delfourEnvelopeGraph,
|
|
109
|
-
signature_statement(S, P, O) :- signature_graph(delfourSignatureGraph,
|
|
105
|
+
case_statement(S, P, O) :- case_graph(delfourCaseGraph, Context), holds(Context, P, [S, O]).
|
|
106
|
+
insight_statement(S, P, O) :- insight_graph(delfourInsightGraph, Context), holds(Context, P, [S, O]).
|
|
107
|
+
policy_statement(S, P, O) :- policy_graph(delfourPolicyGraph, Context), holds(Context, P, [S, O]).
|
|
108
|
+
envelope_statement(S, P, O) :- envelope_graph(delfourEnvelopeGraph, Context), holds(Context, P, [S, O]).
|
|
109
|
+
signature_statement(S, P, O) :- signature_graph(delfourSignatureGraph, Context), holds(Context, P, [S, O]).
|
|
110
110
|
|
|
111
111
|
case_name(case, Name) :- case_statement(case, caseName, Name).
|
|
112
112
|
request_purpose(case, Purpose) :- case_statement(case, requestPurpose, Purpose).
|
|
@@ -32,8 +32,8 @@ route_network(riskNetwork, (
|
|
|
32
32
|
|
|
33
33
|
% Derivation rules: each rule below contributes one logical step toward the displayed results.
|
|
34
34
|
route_segment(From, To, Raw, Risk) :-
|
|
35
|
-
route_network(riskNetwork,
|
|
36
|
-
|
|
35
|
+
route_network(riskNetwork, Context),
|
|
36
|
+
holds(Context, segment(From, segment(To, Raw, Risk))).
|
|
37
37
|
|
|
38
38
|
candidate(pathB, [depotA, depotB, labD]).
|
|
39
39
|
candidate(pathC, [depotA, depotC, labD]).
|
|
@@ -24,8 +24,8 @@ weighted_graph(dijkstraGraph, (
|
|
|
24
24
|
|
|
25
25
|
% Derivation rules: each rule below contributes one logical step toward the displayed results.
|
|
26
26
|
base_link(A, B, Cost) :-
|
|
27
|
-
weighted_graph(dijkstraGraph,
|
|
28
|
-
|
|
27
|
+
weighted_graph(dijkstraGraph, Context),
|
|
28
|
+
holds(Context, edge(A, arc(B, Cost))).
|
|
29
29
|
|
|
30
30
|
link(A, B, Cost) :- base_link(A, B, Cost).
|
|
31
31
|
link(B, A, Cost) :- base_link(A, B, Cost).
|
|
@@ -33,7 +33,7 @@ family_graph(familyGraph, (
|
|
|
33
33
|
)).
|
|
34
34
|
|
|
35
35
|
% Derivation rules: each rule below contributes one logical step toward the displayed results.
|
|
36
|
-
family_statement(S, P, O) :- family_graph(familyGraph,
|
|
36
|
+
family_statement(S, P, O) :- family_graph(familyGraph, Context), holds(Context, P, [S, O]).
|
|
37
37
|
|
|
38
38
|
parent(Parent, Child) :- family_statement(Parent, parent, Child).
|
|
39
39
|
branch(Person, Branch) :- family_statement(Person, seedBranch, Branch).
|
package/examples/eyelang/gps.pl
CHANGED
|
@@ -39,12 +39,12 @@ map_graph(mapBE, (
|
|
|
39
39
|
|
|
40
40
|
% Derivation rules: each rule below contributes one logical step toward the displayed results.
|
|
41
41
|
case_statement(S, P, O) :-
|
|
42
|
-
case_graph(caseGraph,
|
|
43
|
-
|
|
42
|
+
case_graph(caseGraph, Context),
|
|
43
|
+
holds(Context, P, [S, O]).
|
|
44
44
|
|
|
45
45
|
map_description(From, To, Action, Duration, Cost, Belief, Comfort) :-
|
|
46
|
-
map_graph(mapBE,
|
|
47
|
-
|
|
46
|
+
map_graph(mapBE, Context),
|
|
47
|
+
holds(Context, gps_description(mapBE, description(From, true, To, Action, Duration, Cost, Belief, Comfort))).
|
|
48
48
|
|
|
49
49
|
path(From, To, [Action], Duration, Cost, Belief, Comfort) :-
|
|
50
50
|
map_description(From, To, Action, Duration, Cost, Belief, Comfort).
|
|
@@ -114,8 +114,8 @@ policy_graph(policyGraphHC1, (
|
|
|
114
114
|
|
|
115
115
|
% Derivation rules: each rule below contributes one logical step toward the displayed results.
|
|
116
116
|
policy_statement(GraphName, Subject, Predicate, Object) :-
|
|
117
|
-
policy_graph(GraphName,
|
|
118
|
-
|
|
117
|
+
policy_graph(GraphName, Context),
|
|
118
|
+
holds(Context, Predicate, [Subject, Object]).
|
|
119
119
|
|
|
120
120
|
permission(Graph, Permission) :- policy_statement(Graph, policyHC1, odrl_permission, Permission).
|
|
121
121
|
clause(Graph, Permission, Clause) :- policy_statement(Graph, Permission, clause, Clause).
|
|
@@ -98,8 +98,8 @@ policy_graph(policyGraph1, (
|
|
|
98
98
|
|
|
99
99
|
% Derivation rules: each rule below contributes one logical step toward the displayed results.
|
|
100
100
|
policy_statement(Subject, Predicate, Object) :-
|
|
101
|
-
policy_graph(_Graph,
|
|
102
|
-
|
|
101
|
+
policy_graph(_Graph, Context),
|
|
102
|
+
holds(Context, Predicate, [Subject, Object]).
|
|
103
103
|
|
|
104
104
|
policy(Policy, Agreement) :- policy_statement(Policy, odrl_appliesTo, Agreement).
|
|
105
105
|
permission(Policy, Rule) :- policy_statement(Policy, odrl_permission, Rule).
|
|
@@ -4,15 +4,15 @@ why(
|
|
|
4
4
|
proof(
|
|
5
5
|
goal(name(a, "Alice")),
|
|
6
6
|
by(rule("annotation.pl", clause(6))),
|
|
7
|
-
bindings([binding("S", a), binding("O", "Alice"), binding("_T", t), binding("
|
|
7
|
+
bindings([binding("S", a), binding("O", "Alice"), binding("_T", t), binding("Context", (name(a, "Alice"), statedBy(t, bob), recorded(t, "2021-07-07")))]),
|
|
8
8
|
uses([
|
|
9
9
|
proof(
|
|
10
10
|
goal(annotation(t, (name(a, "Alice"), statedBy(t, bob), recorded(t, "2021-07-07")))),
|
|
11
11
|
by(fact("annotation.pl", clause(5)))
|
|
12
12
|
),
|
|
13
13
|
proof(
|
|
14
|
-
goal(
|
|
15
|
-
by(builtin(
|
|
14
|
+
goal(holds((name(a, "Alice"), statedBy(t, bob), recorded(t, "2021-07-07")), name(a, "Alice"))),
|
|
15
|
+
by(builtin(holds, 2))
|
|
16
16
|
)
|
|
17
17
|
])
|
|
18
18
|
)
|
|
@@ -24,15 +24,15 @@ why(
|
|
|
24
24
|
proof(
|
|
25
25
|
goal(log_nameOf(t, name(a, "Alice"))),
|
|
26
26
|
by(rule("annotation.pl", clause(7))),
|
|
27
|
-
bindings([binding("T", t), binding("S", a), binding("O", "Alice"), binding("
|
|
27
|
+
bindings([binding("T", t), binding("S", a), binding("O", "Alice"), binding("Context", (name(a, "Alice"), statedBy(t, bob), recorded(t, "2021-07-07")))]),
|
|
28
28
|
uses([
|
|
29
29
|
proof(
|
|
30
30
|
goal(annotation(t, (name(a, "Alice"), statedBy(t, bob), recorded(t, "2021-07-07")))),
|
|
31
31
|
by(fact("annotation.pl", clause(5)))
|
|
32
32
|
),
|
|
33
33
|
proof(
|
|
34
|
-
goal(
|
|
35
|
-
by(builtin(
|
|
34
|
+
goal(holds((name(a, "Alice"), statedBy(t, bob), recorded(t, "2021-07-07")), name(a, "Alice"))),
|
|
35
|
+
by(builtin(holds, 2))
|
|
36
36
|
)
|
|
37
37
|
])
|
|
38
38
|
)
|
|
@@ -44,15 +44,15 @@ why(
|
|
|
44
44
|
proof(
|
|
45
45
|
goal(statedBy(t, bob)),
|
|
46
46
|
by(rule("annotation.pl", clause(8))),
|
|
47
|
-
bindings([binding("S", t), binding("O", bob), binding("_T", t), binding("
|
|
47
|
+
bindings([binding("S", t), binding("O", bob), binding("_T", t), binding("Context", (name(a, "Alice"), statedBy(t, bob), recorded(t, "2021-07-07")))]),
|
|
48
48
|
uses([
|
|
49
49
|
proof(
|
|
50
50
|
goal(annotation(t, (name(a, "Alice"), statedBy(t, bob), recorded(t, "2021-07-07")))),
|
|
51
51
|
by(fact("annotation.pl", clause(5)))
|
|
52
52
|
),
|
|
53
53
|
proof(
|
|
54
|
-
goal(
|
|
55
|
-
by(builtin(
|
|
54
|
+
goal(holds((name(a, "Alice"), statedBy(t, bob), recorded(t, "2021-07-07")), statedBy(t, bob))),
|
|
55
|
+
by(builtin(holds, 2))
|
|
56
56
|
)
|
|
57
57
|
])
|
|
58
58
|
)
|
|
@@ -64,15 +64,15 @@ why(
|
|
|
64
64
|
proof(
|
|
65
65
|
goal(recorded(t, "2021-07-07")),
|
|
66
66
|
by(rule("annotation.pl", clause(9))),
|
|
67
|
-
bindings([binding("S", t), binding("O", "2021-07-07"), binding("_T", t), binding("
|
|
67
|
+
bindings([binding("S", t), binding("O", "2021-07-07"), binding("_T", t), binding("Context", (name(a, "Alice"), statedBy(t, bob), recorded(t, "2021-07-07")))]),
|
|
68
68
|
uses([
|
|
69
69
|
proof(
|
|
70
70
|
goal(annotation(t, (name(a, "Alice"), statedBy(t, bob), recorded(t, "2021-07-07")))),
|
|
71
71
|
by(fact("annotation.pl", clause(5)))
|
|
72
72
|
),
|
|
73
73
|
proof(
|
|
74
|
-
goal(
|
|
75
|
-
by(builtin(
|
|
74
|
+
goal(holds((name(a, "Alice"), statedBy(t, bob), recorded(t, "2021-07-07")), recorded(t, "2021-07-07"))),
|
|
75
|
+
by(builtin(holds, 2))
|
|
76
76
|
)
|
|
77
77
|
])
|
|
78
78
|
)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Context builtins that treat comma conjunctions as first-class data terms.
|
|
2
|
+
// These are used by examples that construct or inspect rule bodies programmatically.
|
|
3
|
+
import { atom, deref, isConjunction, listFromItems, unify } from '../term.js';
|
|
4
|
+
|
|
5
|
+
export const contextBuiltins = {
|
|
6
|
+
register(registry) {
|
|
7
|
+
registry.add('holds', 2, holdsTerm);
|
|
8
|
+
registry.add('holds', 3, holdsParts);
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function* emitContextTerm(context, term, env) {
|
|
13
|
+
context = deref(context, env);
|
|
14
|
+
if (isConjunction(context)) {
|
|
15
|
+
yield* emitContextTerm(context.args[0], term, env);
|
|
16
|
+
yield* emitContextTerm(context.args[1], term, env);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const next = env.clone();
|
|
20
|
+
if (unify(term, context, next)) yield next;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function* emitContextParts(context, name, args, env) {
|
|
24
|
+
context = deref(context, env);
|
|
25
|
+
if (isConjunction(context)) {
|
|
26
|
+
yield* emitContextParts(context.args[0], name, args, env);
|
|
27
|
+
yield* emitContextParts(context.args[1], name, args, env);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (context.type !== 'atom' && context.type !== 'compound') return;
|
|
31
|
+
const next = env.clone();
|
|
32
|
+
const argList = context.type === 'compound' ? listFromItems(context.args) : listFromItems([]);
|
|
33
|
+
if (unify(name, atom(context.name), next) && unify(args, argList, next)) yield next;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function* holdsTerm({ goal, env }) {
|
|
37
|
+
yield* emitContextTerm(goal.args[0], goal.args[1], env);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function* holdsParts({ goal, env }) {
|
|
41
|
+
yield* emitContextParts(goal.args[0], goal.args[1], goal.args[2], env);
|
|
42
|
+
}
|
|
@@ -5,9 +5,8 @@ import { coreBuiltins } from './core.js';
|
|
|
5
5
|
import { stringBuiltins } from './strings.js';
|
|
6
6
|
import { listBuiltins } from './lists.js';
|
|
7
7
|
import { aggregationBuiltins } from './aggregation.js';
|
|
8
|
-
import {
|
|
8
|
+
import { contextBuiltins } from './context.js';
|
|
9
9
|
import { controlBuiltins } from './control.js';
|
|
10
|
-
import { portfolioBuiltins } from './portfolio.js';
|
|
11
10
|
import { searchBuiltins } from './search.js';
|
|
12
11
|
import { numberTheoryBuiltins } from './number-theory.js';
|
|
13
12
|
import { matrixBuiltins } from './matrix.js';
|
|
@@ -38,7 +37,7 @@ export class BuiltinRegistry {
|
|
|
38
37
|
|
|
39
38
|
export function createDefaultRegistry() {
|
|
40
39
|
const registry = new BuiltinRegistry();
|
|
41
|
-
for (const mod of [coreBuiltins, arithmeticBuiltins, stringBuiltins, listBuiltins, aggregationBuiltins,
|
|
40
|
+
for (const mod of [coreBuiltins, arithmeticBuiltins, stringBuiltins, listBuiltins, aggregationBuiltins, contextBuiltins, controlBuiltins, searchBuiltins, numberTheoryBuiltins, matrixBuiltins, alphameticBuiltins]) {
|
|
42
41
|
mod.register(registry);
|
|
43
42
|
}
|
|
44
43
|
return registry;
|
package/package.json
CHANGED
|
@@ -36,7 +36,7 @@ The runner executes materialized programs in-process through the public JavaScri
|
|
|
36
36
|
|
|
37
37
|
`core` covers the portable core language profile from the [eyelang language reference](../../../docs/eyelang-language-reference.md): lexical syntax, facts, definite clauses, first-order terms, lists, conjunction, structured unification through user predicates, left-to-right goal-directed proof search, materialized output, and read-back printing.
|
|
38
38
|
|
|
39
|
-
`extension` covers the standard built-in and host behavior exercised by the current reference implementation: arithmetic, comparison, strings, list relations, aggregation,
|
|
39
|
+
`extension` covers the standard built-in and host behavior exercised by the current reference implementation: arithmetic, comparison, strings, list relations, aggregation, context-term helpers, number-theory helpers, finite-search helpers used by the example corpus, matrix helpers, `memoize/2`, `materialize/2`, and default derived output.
|
|
40
40
|
|
|
41
41
|
The profile name `extension` is a test-suite grouping name. It does not mean that these cases are outside the eyelang language reference; most of them correspond to the standard built-in profile and standard host profile in the [eyelang language reference](../../../docs/eyelang-language-reference.md).
|
|
42
42
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
answer(Label, Term)
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
bestPortfolio(portfolio2026, result(137, 75, 28, [solar, hvac, training, quality, recycling, cloud, security])).
|
|
2
|
-
lowRiskTarget(portfolio2026, result(126, 74, 23, [solar, battery, hvac, training, quality, cloud])).
|
|
3
|
-
feasibleCount(portfolio2026, 1390).
|
|
4
|
-
projectCount(portfolio2026, 12).
|
|
5
|
-
totalAvailableValue(portfolio2026, 268).
|
|
6
|
-
note(portfolio2026, "aggregate builtins combine with pruning and memoization to avoid sorted candidate bags").
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
incidentSummary(civic_storm, summary("CivicStorm Alpha", "protect people, medicine, and power without using prohibited surveillance", budget(105), riskCap(60))).
|
|
2
|
-
activeSignal(civic_storm, river_surge).
|
|
3
|
-
activeSignal(civic_storm, hospital_power_risk).
|
|
4
|
-
activeSignal(civic_storm, medicine_cold_chain_risk).
|
|
5
|
-
activeSignal(civic_storm, grid_stress).
|
|
6
|
-
criticalNeed(civic_storm, evacuate_hospital).
|
|
7
|
-
criticalNeed(civic_storm, deliver_medicine).
|
|
8
|
-
criticalNeed(civic_storm, stabilize_substation).
|
|
9
|
-
criticalNeed(civic_storm, open_shelter).
|
|
10
|
-
criticalNeed(civic_storm, map_flood_front).
|
|
11
|
-
policyClearance(medical_transport, deploy).
|
|
12
|
-
policyClearance(cold_chain, deploy).
|
|
13
|
-
policyClearance(grid_repair, deploy).
|
|
14
|
-
policyClearance(shelter_ops, deploy).
|
|
15
|
-
policyClearance(drone_mapping, deploy).
|
|
16
|
-
policyClearance(public_info, deploy).
|
|
17
|
-
usableRoute(hospital_quay, route([base, north_gate, hospital_quay], minutes(22), risk(9))).
|
|
18
|
-
usableRoute(clinic_east, route([base, cold_depot, clinic_east], minutes(19), risk(5))).
|
|
19
|
-
usableRoute(substation_east, route([base, north_gate, substation_east], minutes(31), risk(10))).
|
|
20
|
-
usableRoute(high_school, route([base, civic_center, high_school], minutes(17), risk(4))).
|
|
21
|
-
usableRoute(riverside, route([base, civic_center, riverside], minutes(21), risk(6))).
|
|
22
|
-
usableRoute(civic_square, route([base, civic_center, civic_square], minutes(15), risk(3))).
|
|
23
|
-
eligibleAssignment(evacuate_hospital, assignment(alpha_ambulance, [base, north_gate, hospital_quay])).
|
|
24
|
-
eligibleAssignment(deliver_insulin, assignment(beta_coldchain, [base, cold_depot, clinic_east])).
|
|
25
|
-
eligibleAssignment(stabilize_substation, assignment(delta_grid, [base, north_gate, substation_east])).
|
|
26
|
-
eligibleAssignment(open_school_shelter, assignment(gamma_shelter, [base, civic_center, high_school])).
|
|
27
|
-
eligibleAssignment(map_flood_front, assignment(echo_drone, [base, civic_center, riverside])).
|
|
28
|
-
eligibleAssignment(public_dashboard, assignment(foxtrot_comms, [base, civic_center, civic_square])).
|
|
29
|
-
candidateMission(evacuate_hospital, score(95, 34, 14, alpha_ambulance, [base, north_gate, hospital_quay])).
|
|
30
|
-
candidateMission(deliver_insulin, score(70, 16, 9, beta_coldchain, [base, cold_depot, clinic_east])).
|
|
31
|
-
candidateMission(stabilize_substation, score(85, 25, 18, delta_grid, [base, north_gate, substation_east])).
|
|
32
|
-
candidateMission(open_school_shelter, score(60, 14, 6, gamma_shelter, [base, civic_center, high_school])).
|
|
33
|
-
candidateMission(map_flood_front, score(45, 8, 8, echo_drone, [base, civic_center, riverside])).
|
|
34
|
-
candidateMission(public_dashboard, score(28, 12, 4, foxtrot_comms, [base, civic_center, civic_square])).
|
|
35
|
-
blockedMission(face_scan_crowds, policy_prohibition).
|
|
36
|
-
blockedMission(resupply_west_bank, route_risk_too_high).
|
|
37
|
-
bestResponse(civic_storm, response(value(355), cost(97), risk(55), missions([evacuate_hospital, deliver_insulin, stabilize_substation, open_school_shelter, map_flood_front]))).
|
|
38
|
-
selectedMission(civic_storm, evacuate_hospital).
|
|
39
|
-
selectedMission(civic_storm, deliver_insulin).
|
|
40
|
-
selectedMission(civic_storm, stabilize_substation).
|
|
41
|
-
selectedMission(civic_storm, open_school_shelter).
|
|
42
|
-
selectedMission(civic_storm, map_flood_front).
|
|
43
|
-
selectedTeam(evacuate_hospital, alpha_ambulance).
|
|
44
|
-
selectedTeam(deliver_insulin, beta_coldchain).
|
|
45
|
-
selectedTeam(stabilize_substation, delta_grid).
|
|
46
|
-
selectedTeam(open_school_shelter, gamma_shelter).
|
|
47
|
-
selectedTeam(map_flood_front, echo_drone).
|
|
48
|
-
selectedRoute(evacuate_hospital, [base, north_gate, hospital_quay]).
|
|
49
|
-
selectedRoute(deliver_insulin, [base, cold_depot, clinic_east]).
|
|
50
|
-
selectedRoute(stabilize_substation, [base, north_gate, substation_east]).
|
|
51
|
-
selectedRoute(open_school_shelter, [base, civic_center, high_school]).
|
|
52
|
-
selectedRoute(map_flood_front, [base, civic_center, riverside]).
|
|
53
|
-
coveredNeed(civic_storm, evacuate_hospital).
|
|
54
|
-
coveredNeed(civic_storm, deliver_medicine).
|
|
55
|
-
coveredNeed(civic_storm, stabilize_substation).
|
|
56
|
-
coveredNeed(civic_storm, open_shelter).
|
|
57
|
-
coveredNeed(civic_storm, map_flood_front).
|
|
58
|
-
unmetNeed(civic_storm, none).
|
|
59
|
-
readinessCheck(civic_storm, all_critical_needs_covered).
|
|
60
|
-
readinessCheck(civic_storm, budget_and_risk_caps_respected).
|
|
61
|
-
readinessCheck(civic_storm, prohibited_surveillance_rejected).
|
|
62
|
-
readinessCheck(civic_storm, dangerous_west_bank_route_rejected).
|
|
63
|
-
auditTrail(civic_storm, "raw sensor thresholds derive operational needs before search").
|
|
64
|
-
auditTrail(civic_storm, "policy formula data is projected into deployable skill clearances").
|
|
65
|
-
auditTrail(civic_storm, "bounded route search and team capabilities create candidate missions").
|
|
66
|
-
auditTrail(civic_storm, "bounded_subset and aggregate_max choose the highest-value safe portfolio").
|
|
67
|
-
recommendation(civic_storm, "activate the selected mission portfolio; do not deploy face recognition; defer west-bank resupply until route risk drops").
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
% Capital project portfolio optimization.
|
|
2
|
-
%
|
|
3
|
-
% This is a practical finite-search benchmark: enumerate project portfolios
|
|
4
|
-
% under budget and risk caps, pruning infeasible branches as cost and risk
|
|
5
|
-
% accumulate, then use aggregate builtins to select useful optima without
|
|
6
|
-
% building and sorting a large bag of candidate portfolios.
|
|
7
|
-
|
|
8
|
-
% Output declarations: materialize/2 selects the relations written to this example's golden output.
|
|
9
|
-
materialize(bestPortfolio, 2).
|
|
10
|
-
materialize(lowRiskTarget, 2).
|
|
11
|
-
materialize(feasibleCount, 2).
|
|
12
|
-
materialize(projectCount, 2).
|
|
13
|
-
materialize(totalAvailableValue, 2).
|
|
14
|
-
materialize(note, 2).
|
|
15
|
-
|
|
16
|
-
% Program structure: facts set up the scenario, and rules derive the materialized conclusions.
|
|
17
|
-
budget(portfolio2026, 75).
|
|
18
|
-
riskCap(portfolio2026, 28).
|
|
19
|
-
targetValue(portfolio2026, 125).
|
|
20
|
-
|
|
21
|
-
allProjectData([
|
|
22
|
-
p(solar, 22, 15, 4),
|
|
23
|
-
p(battery, 28, 20, 5),
|
|
24
|
-
p(hvac, 20, 13, 3),
|
|
25
|
-
p(analytics, 18, 8, 7),
|
|
26
|
-
p(training, 16, 6, 2),
|
|
27
|
-
p(robotics, 35, 30, 9),
|
|
28
|
-
p(iot, 24, 12, 8),
|
|
29
|
-
p(quality, 19, 9, 4),
|
|
30
|
-
p(resilience, 26, 18, 6),
|
|
31
|
-
p(recycling, 14, 5, 3),
|
|
32
|
-
p(cloud, 21, 11, 5),
|
|
33
|
-
p(security, 25, 16, 7)
|
|
34
|
-
]).
|
|
35
|
-
|
|
36
|
-
% Derivation rules: each rule below contributes one logical step toward the displayed results.
|
|
37
|
-
project(Name, Value, Cost, Risk) :-
|
|
38
|
-
allProjectData(Projects),
|
|
39
|
-
member(p(Name, Value, Cost, Risk), Projects).
|
|
40
|
-
|
|
41
|
-
% Cache this finite relation so bestPortfolio/2, lowRiskTarget/2, and
|
|
42
|
-
% feasibleCount/2 do not re-enumerate the same search space independently.
|
|
43
|
-
memoize(feasiblePortfolio, 4).
|
|
44
|
-
|
|
45
|
-
choosePortfolio([], _BudgetLeft, _RiskLeft, [], 0, 0, 0).
|
|
46
|
-
choosePortfolio([p(Name, ProjectValue, ProjectCost, ProjectRisk) | Rest], BudgetLeft, RiskLeft, [Name | Chosen], Value, Cost, Risk) :-
|
|
47
|
-
le(ProjectCost, BudgetLeft),
|
|
48
|
-
le(ProjectRisk, RiskLeft),
|
|
49
|
-
sub(BudgetLeft, ProjectCost, NextBudget),
|
|
50
|
-
sub(RiskLeft, ProjectRisk, NextRisk),
|
|
51
|
-
choosePortfolio(Rest, NextBudget, NextRisk, Chosen, RestValue, RestCost, RestRisk),
|
|
52
|
-
add(ProjectValue, RestValue, Value),
|
|
53
|
-
add(ProjectCost, RestCost, Cost),
|
|
54
|
-
add(ProjectRisk, RestRisk, Risk).
|
|
55
|
-
choosePortfolio([_Project | Rest], BudgetLeft, RiskLeft, Chosen, Value, Cost, Risk) :-
|
|
56
|
-
choosePortfolio(Rest, BudgetLeft, RiskLeft, Chosen, Value, Cost, Risk).
|
|
57
|
-
|
|
58
|
-
feasiblePortfolio(Selected, Value, Cost, Risk) :-
|
|
59
|
-
budget(portfolio2026, Budget),
|
|
60
|
-
riskCap(portfolio2026, Cap),
|
|
61
|
-
allProjectData(Projects),
|
|
62
|
-
bounded_subset(Projects, Budget, Cap, Selected, Value, Cost, Risk),
|
|
63
|
-
gt(Value, 0).
|
|
64
|
-
|
|
65
|
-
scoredPortfolio(Selected, Value, Cost, Risk, NegCost, NegRisk) :-
|
|
66
|
-
feasiblePortfolio(Selected, Value, Cost, Risk),
|
|
67
|
-
neg(Cost, NegCost),
|
|
68
|
-
neg(Risk, NegRisk).
|
|
69
|
-
|
|
70
|
-
targetPortfolio(Target, Selected, Value, Cost, Risk) :-
|
|
71
|
-
feasiblePortfolio(Selected, Value, Cost, Risk),
|
|
72
|
-
ge(Value, Target).
|
|
73
|
-
|
|
74
|
-
bestPortfolio(portfolio2026, result(Value, Cost, Risk, Selected)) :-
|
|
75
|
-
aggregate_max(
|
|
76
|
-
[Value, NegCost, NegRisk, Selected],
|
|
77
|
-
result(Value, Cost, Risk, Selected),
|
|
78
|
-
scoredPortfolio(Selected, Value, Cost, Risk, NegCost, NegRisk),
|
|
79
|
-
_Key,
|
|
80
|
-
result(Value, Cost, Risk, Selected)).
|
|
81
|
-
|
|
82
|
-
lowRiskTarget(portfolio2026, result(Value, Cost, Risk, Selected)) :-
|
|
83
|
-
targetValue(portfolio2026, Target),
|
|
84
|
-
aggregate_min(
|
|
85
|
-
[Risk, Cost, Selected],
|
|
86
|
-
result(Value, Cost, Risk, Selected),
|
|
87
|
-
targetPortfolio(Target, Selected, Value, Cost, Risk),
|
|
88
|
-
_Key,
|
|
89
|
-
result(Value, Cost, Risk, Selected)).
|
|
90
|
-
|
|
91
|
-
feasibleCount(portfolio2026, Count) :-
|
|
92
|
-
countall(feasiblePortfolio(_Selected, _Value, _Cost, _Risk), Count).
|
|
93
|
-
|
|
94
|
-
projectCount(portfolio2026, Count) :-
|
|
95
|
-
countall(project(_Name, _Value, _Cost, _Risk), Count).
|
|
96
|
-
|
|
97
|
-
totalAvailableValue(portfolio2026, Total) :-
|
|
98
|
-
sumall(Value, project(_Name, Value, _Cost, _Risk), Total).
|
|
99
|
-
|
|
100
|
-
note(portfolio2026, "aggregate builtins combine with pruning and memoization to avoid sorted candidate bags") :-
|
|
101
|
-
eq(ok, ok).
|
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
% Resilient-city orchestration scenario.
|
|
2
|
-
%
|
|
3
|
-
% This example is intentionally richer than a single puzzle. It combines raw
|
|
4
|
-
% incident signals, policy-as-data, route search, team capabilities, negation,
|
|
5
|
-
% aggregation, finite optimization, and materialized audit output. The result is
|
|
6
|
-
% a compact declarative emergency plan: facts describe the world; rules decide
|
|
7
|
-
% which missions are lawful, reachable, and worth funding under cost/risk caps.
|
|
8
|
-
|
|
9
|
-
% Output declarations: materialize/2 selects the relations written to this example's golden output.
|
|
10
|
-
materialize(incidentSummary, 2).
|
|
11
|
-
materialize(activeSignal, 2).
|
|
12
|
-
materialize(criticalNeed, 2).
|
|
13
|
-
materialize(policyClearance, 2).
|
|
14
|
-
materialize(usableRoute, 2).
|
|
15
|
-
materialize(eligibleAssignment, 2).
|
|
16
|
-
materialize(candidateMission, 2).
|
|
17
|
-
materialize(blockedMission, 2).
|
|
18
|
-
materialize(bestResponse, 2).
|
|
19
|
-
materialize(selectedMission, 2).
|
|
20
|
-
materialize(selectedTeam, 2).
|
|
21
|
-
materialize(selectedRoute, 2).
|
|
22
|
-
materialize(coveredNeed, 2).
|
|
23
|
-
materialize(unmetNeed, 2).
|
|
24
|
-
materialize(readinessCheck, 2).
|
|
25
|
-
materialize(auditTrail, 2).
|
|
26
|
-
materialize(recommendation, 2).
|
|
27
|
-
|
|
28
|
-
% Program structure: facts set up the storm response, and rules derive the final plan.
|
|
29
|
-
incident(civic_storm).
|
|
30
|
-
incident_label(civic_storm, "CivicStorm Alpha").
|
|
31
|
-
incident_goal(civic_storm, "protect people, medicine, and power without using prohibited surveillance").
|
|
32
|
-
budget_cap(civic_storm, 105).
|
|
33
|
-
risk_cap(civic_storm, 60).
|
|
34
|
-
route_risk_cap(civic_storm, 20).
|
|
35
|
-
route_minutes_cap(civic_storm, 45).
|
|
36
|
-
|
|
37
|
-
% Sensor readings are deliberately low-level; rules below turn them into needs.
|
|
38
|
-
reading(river_gauge_cm, 318).
|
|
39
|
-
threshold(river_gauge_cm, 280).
|
|
40
|
-
reading(hospital_generator_hours, 5).
|
|
41
|
-
threshold_low(hospital_generator_hours, 8).
|
|
42
|
-
reading(clinic_refrigerator_hours, 4).
|
|
43
|
-
threshold_low(clinic_refrigerator_hours, 6).
|
|
44
|
-
reading(substation_load_percent, 94).
|
|
45
|
-
threshold(substation_load_percent, 85).
|
|
46
|
-
|
|
47
|
-
% The deployable skills are stored as quoted formula data and projected by
|
|
48
|
-
% formula_binary/4, showing how policy documents can be reasoned over as data.
|
|
49
|
-
policy_bundle(response_policy, (
|
|
50
|
-
permission(medical_transport, deploy),
|
|
51
|
-
permission(cold_chain, deploy),
|
|
52
|
-
permission(grid_repair, deploy),
|
|
53
|
-
permission(shelter_ops, deploy),
|
|
54
|
-
permission(drone_mapping, deploy),
|
|
55
|
-
permission(public_info, deploy),
|
|
56
|
-
prohibition(public_face_recognition, deploy)
|
|
57
|
-
)).
|
|
58
|
-
|
|
59
|
-
% Teams can only serve missions whose skill they possess and whose route is usable.
|
|
60
|
-
team(alpha_ambulance).
|
|
61
|
-
teamSkill(alpha_ambulance, medical_transport).
|
|
62
|
-
teamStatus(alpha_ambulance, ready).
|
|
63
|
-
team(beta_coldchain).
|
|
64
|
-
teamSkill(beta_coldchain, cold_chain).
|
|
65
|
-
teamStatus(beta_coldchain, ready).
|
|
66
|
-
team(delta_grid).
|
|
67
|
-
teamSkill(delta_grid, grid_repair).
|
|
68
|
-
teamStatus(delta_grid, ready).
|
|
69
|
-
team(gamma_shelter).
|
|
70
|
-
teamSkill(gamma_shelter, shelter_ops).
|
|
71
|
-
teamStatus(gamma_shelter, ready).
|
|
72
|
-
team(echo_drone).
|
|
73
|
-
teamSkill(echo_drone, drone_mapping).
|
|
74
|
-
teamStatus(echo_drone, ready).
|
|
75
|
-
team(foxtrot_comms).
|
|
76
|
-
teamSkill(foxtrot_comms, public_info).
|
|
77
|
-
teamStatus(foxtrot_comms, ready).
|
|
78
|
-
team(omega_vision).
|
|
79
|
-
teamSkill(omega_vision, public_face_recognition).
|
|
80
|
-
teamStatus(omega_vision, ready).
|
|
81
|
-
|
|
82
|
-
% The road graph is directed. bounded_path/5 discovers simple bounded routes.
|
|
83
|
-
road(base, north_gate).
|
|
84
|
-
road(north_gate, hospital_quay).
|
|
85
|
-
road(base, cold_depot).
|
|
86
|
-
road(cold_depot, clinic_east).
|
|
87
|
-
road(north_gate, substation_east).
|
|
88
|
-
road(base, civic_center).
|
|
89
|
-
road(civic_center, high_school).
|
|
90
|
-
road(civic_center, riverside).
|
|
91
|
-
road(civic_center, civic_square).
|
|
92
|
-
road(base, west_bridge).
|
|
93
|
-
road(west_bridge, west_bank).
|
|
94
|
-
|
|
95
|
-
zone(hospital_quay).
|
|
96
|
-
zone(clinic_east).
|
|
97
|
-
zone(substation_east).
|
|
98
|
-
zone(high_school).
|
|
99
|
-
zone(riverside).
|
|
100
|
-
zone(civic_square).
|
|
101
|
-
zone(west_bank).
|
|
102
|
-
|
|
103
|
-
routeRisk(hospital_quay, 9).
|
|
104
|
-
routeRisk(clinic_east, 5).
|
|
105
|
-
routeRisk(substation_east, 10).
|
|
106
|
-
routeRisk(high_school, 4).
|
|
107
|
-
routeRisk(riverside, 6).
|
|
108
|
-
routeRisk(civic_square, 3).
|
|
109
|
-
routeRisk(west_bank, 24).
|
|
110
|
-
routeMinutes(hospital_quay, 22).
|
|
111
|
-
routeMinutes(clinic_east, 19).
|
|
112
|
-
routeMinutes(substation_east, 31).
|
|
113
|
-
routeMinutes(high_school, 17).
|
|
114
|
-
routeMinutes(riverside, 21).
|
|
115
|
-
routeMinutes(civic_square, 15).
|
|
116
|
-
routeMinutes(west_bank, 42).
|
|
117
|
-
|
|
118
|
-
% Mission facts carry the operational need, location, required skill, value,
|
|
119
|
-
% budget cost, and base risk. Route risk is added by candidateMission/2.
|
|
120
|
-
missionBase(evacuate_hospital, evacuate_hospital, hospital_quay, medical_transport, 95, 34, 5).
|
|
121
|
-
missionBase(deliver_insulin, deliver_medicine, clinic_east, cold_chain, 70, 16, 4).
|
|
122
|
-
missionBase(stabilize_substation, stabilize_substation, substation_east, grid_repair, 85, 25, 8).
|
|
123
|
-
missionBase(open_school_shelter, open_shelter, high_school, shelter_ops, 60, 14, 2).
|
|
124
|
-
missionBase(map_flood_front, map_flood_front, riverside, drone_mapping, 45, 8, 2).
|
|
125
|
-
missionBase(public_dashboard, public_information, civic_square, public_info, 28, 12, 1).
|
|
126
|
-
missionBase(face_scan_crowds, situational_awareness, civic_square, public_face_recognition, 90, 20, 2).
|
|
127
|
-
missionBase(resupply_west_bank, shelter_supply, west_bank, shelter_ops, 50, 20, 5).
|
|
128
|
-
|
|
129
|
-
% Memoized derived predicates keep the example readable while avoiding repeated finite searches.
|
|
130
|
-
memoize(usableRoute, 2).
|
|
131
|
-
memoize(candidateMission, 2).
|
|
132
|
-
memoize(bestResponse, 2).
|
|
133
|
-
|
|
134
|
-
% Derivation rules: each rule below contributes one logical step toward the displayed results.
|
|
135
|
-
incidentSummary(Incident, summary(Label, Goal, budget(Budget), riskCap(RiskCap))) :-
|
|
136
|
-
incident_label(Incident, Label),
|
|
137
|
-
incident_goal(Incident, Goal),
|
|
138
|
-
budget_cap(Incident, Budget),
|
|
139
|
-
risk_cap(Incident, RiskCap).
|
|
140
|
-
|
|
141
|
-
activeSignal(civic_storm, river_surge) :-
|
|
142
|
-
reading(river_gauge_cm, Value),
|
|
143
|
-
threshold(river_gauge_cm, Threshold),
|
|
144
|
-
ge(Value, Threshold).
|
|
145
|
-
activeSignal(civic_storm, hospital_power_risk) :-
|
|
146
|
-
reading(hospital_generator_hours, Value),
|
|
147
|
-
threshold_low(hospital_generator_hours, Threshold),
|
|
148
|
-
le(Value, Threshold).
|
|
149
|
-
activeSignal(civic_storm, medicine_cold_chain_risk) :-
|
|
150
|
-
reading(clinic_refrigerator_hours, Value),
|
|
151
|
-
threshold_low(clinic_refrigerator_hours, Threshold),
|
|
152
|
-
le(Value, Threshold).
|
|
153
|
-
activeSignal(civic_storm, grid_stress) :-
|
|
154
|
-
reading(substation_load_percent, Value),
|
|
155
|
-
threshold(substation_load_percent, Threshold),
|
|
156
|
-
ge(Value, Threshold).
|
|
157
|
-
|
|
158
|
-
criticalNeed(civic_storm, evacuate_hospital) :-
|
|
159
|
-
activeSignal(civic_storm, river_surge),
|
|
160
|
-
activeSignal(civic_storm, hospital_power_risk).
|
|
161
|
-
criticalNeed(civic_storm, deliver_medicine) :-
|
|
162
|
-
activeSignal(civic_storm, medicine_cold_chain_risk).
|
|
163
|
-
criticalNeed(civic_storm, stabilize_substation) :-
|
|
164
|
-
activeSignal(civic_storm, grid_stress).
|
|
165
|
-
criticalNeed(civic_storm, open_shelter) :-
|
|
166
|
-
activeSignal(civic_storm, river_surge).
|
|
167
|
-
criticalNeed(civic_storm, map_flood_front) :-
|
|
168
|
-
activeSignal(civic_storm, river_surge).
|
|
169
|
-
|
|
170
|
-
policyClearance(Skill, deploy) :-
|
|
171
|
-
policy_bundle(response_policy, Formula),
|
|
172
|
-
formula_binary(Formula, Skill, permission, deploy),
|
|
173
|
-
not(policyBlocked(Skill, deploy)).
|
|
174
|
-
|
|
175
|
-
policyBlocked(Skill, deploy) :-
|
|
176
|
-
policy_bundle(response_policy, Formula),
|
|
177
|
-
formula_binary(Formula, Skill, prohibition, deploy).
|
|
178
|
-
|
|
179
|
-
usableRoute(Zone, route(Path, minutes(Minutes), risk(RouteRisk))) :-
|
|
180
|
-
zone(Zone),
|
|
181
|
-
bounded_path(road, base, Zone, 3, Path),
|
|
182
|
-
routeMinutes(Zone, Minutes),
|
|
183
|
-
routeRisk(Zone, RouteRisk),
|
|
184
|
-
route_minutes_cap(civic_storm, MinutesCap),
|
|
185
|
-
route_risk_cap(civic_storm, RiskCap),
|
|
186
|
-
le(Minutes, MinutesCap),
|
|
187
|
-
le(RouteRisk, RiskCap).
|
|
188
|
-
|
|
189
|
-
eligibleAssignment(Mission, assignment(Team, Path)) :-
|
|
190
|
-
missionBase(Mission, _Need, Zone, Skill, _Value, _Cost, _BaseRisk),
|
|
191
|
-
policyClearance(Skill, deploy),
|
|
192
|
-
teamSkill(Team, Skill),
|
|
193
|
-
teamStatus(Team, ready),
|
|
194
|
-
usableRoute(Zone, route(Path, minutes(_Minutes), risk(_RouteRisk))).
|
|
195
|
-
|
|
196
|
-
candidateMission(Mission, score(Value, Cost, TotalRisk, Team, Path)) :-
|
|
197
|
-
missionBase(Mission, _Need, Zone, Skill, Value, Cost, BaseRisk),
|
|
198
|
-
policyClearance(Skill, deploy),
|
|
199
|
-
teamSkill(Team, Skill),
|
|
200
|
-
teamStatus(Team, ready),
|
|
201
|
-
usableRoute(Zone, route(Path, minutes(_Minutes), risk(RouteRisk))),
|
|
202
|
-
add(BaseRisk, RouteRisk, TotalRisk).
|
|
203
|
-
|
|
204
|
-
blockedMission(Mission, policy_prohibition) :-
|
|
205
|
-
missionBase(Mission, _Need, _Zone, Skill, _Value, _Cost, _BaseRisk),
|
|
206
|
-
policyBlocked(Skill, deploy).
|
|
207
|
-
blockedMission(Mission, route_risk_too_high) :-
|
|
208
|
-
missionBase(Mission, _Need, Zone, _Skill, _Value, _Cost, _BaseRisk),
|
|
209
|
-
routeRisk(Zone, RouteRisk),
|
|
210
|
-
route_risk_cap(civic_storm, Cap),
|
|
211
|
-
gt(RouteRisk, Cap).
|
|
212
|
-
blockedMission(Mission, no_eligible_assignment) :-
|
|
213
|
-
missionBase(Mission, _Need, _Zone, _Skill, _Value, _Cost, _BaseRisk),
|
|
214
|
-
not(candidateMission(Mission, _Score)),
|
|
215
|
-
not(blockedMission(Mission, policy_prohibition)),
|
|
216
|
-
not(blockedMission(Mission, route_risk_too_high)).
|
|
217
|
-
|
|
218
|
-
missionItem(p(Mission, Value, Cost, Risk)) :-
|
|
219
|
-
candidateMission(Mission, score(Value, Cost, Risk, _Team, _Path)).
|
|
220
|
-
|
|
221
|
-
missionItems(Items) :-
|
|
222
|
-
findall(Item, missionItem(Item), Items).
|
|
223
|
-
|
|
224
|
-
missionAddressesNeed(Mission, Need) :-
|
|
225
|
-
missionBase(Mission, Need, _Zone, _Skill, _Value, _Cost, _BaseRisk).
|
|
226
|
-
|
|
227
|
-
planCoversNeed(Plan, Need) :-
|
|
228
|
-
member(Mission, Plan),
|
|
229
|
-
missionAddressesNeed(Mission, Need).
|
|
230
|
-
|
|
231
|
-
uncoveredNeedInPlan(Plan, Need) :-
|
|
232
|
-
criticalNeed(civic_storm, Need),
|
|
233
|
-
not(planCoversNeed(Plan, Need)).
|
|
234
|
-
|
|
235
|
-
coversAllCriticalNeeds(Plan) :-
|
|
236
|
-
not(uncoveredNeedInPlan(Plan, _Need)).
|
|
237
|
-
|
|
238
|
-
candidateResponse(Plan, Value, Cost, Risk, NegCost, NegRisk) :-
|
|
239
|
-
budget_cap(civic_storm, Budget),
|
|
240
|
-
risk_cap(civic_storm, RiskCap),
|
|
241
|
-
missionItems(Items),
|
|
242
|
-
bounded_subset(Items, Budget, RiskCap, Plan, Value, Cost, Risk),
|
|
243
|
-
coversAllCriticalNeeds(Plan),
|
|
244
|
-
neg(Cost, NegCost),
|
|
245
|
-
neg(Risk, NegRisk).
|
|
246
|
-
|
|
247
|
-
bestResponse(civic_storm, response(value(Value), cost(Cost), risk(Risk), missions(Plan))) :-
|
|
248
|
-
aggregate_max(
|
|
249
|
-
[Value, NegCost, NegRisk, Plan],
|
|
250
|
-
response(value(Value), cost(Cost), risk(Risk), missions(Plan)),
|
|
251
|
-
candidateResponse(Plan, Value, Cost, Risk, NegCost, NegRisk),
|
|
252
|
-
_Key,
|
|
253
|
-
response(value(Value), cost(Cost), risk(Risk), missions(Plan))).
|
|
254
|
-
|
|
255
|
-
selectedMission(civic_storm, Mission) :-
|
|
256
|
-
bestResponse(civic_storm, response(value(_Value), cost(_Cost), risk(_Risk), missions(Plan))),
|
|
257
|
-
member(Mission, Plan).
|
|
258
|
-
|
|
259
|
-
selectedTeam(Mission, Team) :-
|
|
260
|
-
selectedMission(civic_storm, Mission),
|
|
261
|
-
candidateMission(Mission, score(_Value, _Cost, _Risk, Team, _Path)).
|
|
262
|
-
|
|
263
|
-
selectedRoute(Mission, Path) :-
|
|
264
|
-
selectedMission(civic_storm, Mission),
|
|
265
|
-
candidateMission(Mission, score(_Value, _Cost, _Risk, _Team, Path)).
|
|
266
|
-
|
|
267
|
-
coveredNeed(civic_storm, Need) :-
|
|
268
|
-
selectedMission(civic_storm, Mission),
|
|
269
|
-
missionAddressesNeed(Mission, Need).
|
|
270
|
-
|
|
271
|
-
unmetNeed(civic_storm, Need) :-
|
|
272
|
-
bestResponse(civic_storm, response(value(_Value), cost(_Cost), risk(_Risk), missions(Plan))),
|
|
273
|
-
uncoveredNeedInPlan(Plan, Need).
|
|
274
|
-
unmetNeed(civic_storm, none) :-
|
|
275
|
-
bestResponse(civic_storm, response(value(_Value), cost(_Cost), risk(_Risk), missions(Plan))),
|
|
276
|
-
coversAllCriticalNeeds(Plan).
|
|
277
|
-
|
|
278
|
-
readinessCheck(civic_storm, all_critical_needs_covered) :-
|
|
279
|
-
unmetNeed(civic_storm, none).
|
|
280
|
-
readinessCheck(civic_storm, budget_and_risk_caps_respected) :-
|
|
281
|
-
bestResponse(civic_storm, response(value(_Value), cost(Cost), risk(Risk), missions(_Plan))),
|
|
282
|
-
budget_cap(civic_storm, Budget),
|
|
283
|
-
risk_cap(civic_storm, Cap),
|
|
284
|
-
le(Cost, Budget),
|
|
285
|
-
le(Risk, Cap).
|
|
286
|
-
readinessCheck(civic_storm, prohibited_surveillance_rejected) :-
|
|
287
|
-
blockedMission(face_scan_crowds, policy_prohibition).
|
|
288
|
-
readinessCheck(civic_storm, dangerous_west_bank_route_rejected) :-
|
|
289
|
-
blockedMission(resupply_west_bank, route_risk_too_high).
|
|
290
|
-
|
|
291
|
-
auditTrail(civic_storm, "raw sensor thresholds derive operational needs before search") :-
|
|
292
|
-
eq(ok, ok).
|
|
293
|
-
auditTrail(civic_storm, "policy formula data is projected into deployable skill clearances") :-
|
|
294
|
-
eq(ok, ok).
|
|
295
|
-
auditTrail(civic_storm, "bounded route search and team capabilities create candidate missions") :-
|
|
296
|
-
eq(ok, ok).
|
|
297
|
-
auditTrail(civic_storm, "bounded_subset and aggregate_max choose the highest-value safe portfolio") :-
|
|
298
|
-
eq(ok, ok).
|
|
299
|
-
|
|
300
|
-
recommendation(civic_storm, "activate the selected mission portfolio; do not deploy face recognition; defer west-bank resupply until route risk drops") :-
|
|
301
|
-
readinessCheck(civic_storm, all_critical_needs_covered),
|
|
302
|
-
readinessCheck(civic_storm, prohibited_surveillance_rejected),
|
|
303
|
-
readinessCheck(civic_storm, dangerous_west_bank_route_rejected).
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
// Formula builtins that treat conjunctions as first-class data terms.
|
|
2
|
-
// These are used by examples that construct or inspect rule bodies programmatically.
|
|
3
|
-
import { atom, deref, isConjunction, unify } from '../term.js';
|
|
4
|
-
|
|
5
|
-
export const formulaBuiltins = {
|
|
6
|
-
register(registry) {
|
|
7
|
-
registry.add('formula_binary', 4, formulaBinary);
|
|
8
|
-
}
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
function* emitFormulaBinary(formula, subject, predicate, object, env) {
|
|
13
|
-
formula = deref(formula, env);
|
|
14
|
-
if (isConjunction(formula)) {
|
|
15
|
-
yield* emitFormulaBinary(formula.args[0], subject, predicate, object, env);
|
|
16
|
-
yield* emitFormulaBinary(formula.args[1], subject, predicate, object, env);
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
if (formula.type !== 'compound' || formula.arity !== 2) return;
|
|
20
|
-
const next = env.clone();
|
|
21
|
-
if (unify(subject, formula.args[0], next) && unify(predicate, atom(formula.name), next) && unify(object, formula.args[1], next)) yield next;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function* formulaBinary({ goal, env }) {
|
|
25
|
-
yield* emitFormulaBinary(goal.args[0], goal.args[1], goal.args[2], goal.args[3], env);
|
|
26
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
// Reusable bounded-subset optimizer for finite knapsack-style examples.
|
|
2
|
-
// Items are p(Name, Value, Cost, Risk) terms; the builtin enumerates every
|
|
3
|
-
// subset whose total cost/risk stays within the supplied caps.
|
|
4
|
-
import { deref, listFromItems, numberTerm, properListItems, unify } from '../term.js';
|
|
5
|
-
|
|
6
|
-
export const portfolioBuiltins = {
|
|
7
|
-
register(registry) {
|
|
8
|
-
registry.add('bounded_subset', 7, boundedSubset, {
|
|
9
|
-
fallbackWhenNotReady: true,
|
|
10
|
-
ready: boundedSubsetReady,
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
function boundedSubsetReady(goal, env) {
|
|
16
|
-
return parseItems(goal.args[0], env) !== null && intArg(goal.args[1], env) !== null && intArg(goal.args[2], env) !== null;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function* boundedSubset({ goal, env }) {
|
|
20
|
-
const items = parseItems(goal.args[0], env);
|
|
21
|
-
const budget = intArg(goal.args[1], env);
|
|
22
|
-
const riskCap = intArg(goal.args[2], env);
|
|
23
|
-
if (!items || budget == null || riskCap == null) return;
|
|
24
|
-
for (const answer of enumerate(items, 0, budget, riskCap)) {
|
|
25
|
-
const next = env.clone();
|
|
26
|
-
if (unify(goal.args[3], listFromItems(answer.names), next) &&
|
|
27
|
-
unify(goal.args[4], numberTerm(answer.value), next) &&
|
|
28
|
-
unify(goal.args[5], numberTerm(answer.cost), next) &&
|
|
29
|
-
unify(goal.args[6], numberTerm(answer.risk), next)) {
|
|
30
|
-
yield next;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function* enumerate(items, index, budget, riskLeft) {
|
|
36
|
-
if (index >= items.length) {
|
|
37
|
-
yield { names: [], value: 0, cost: 0, risk: 0 };
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
const item = items[index];
|
|
41
|
-
if (item.cost <= budget && item.risk <= riskLeft) {
|
|
42
|
-
for (const rest of enumerate(items, index + 1, budget - item.cost, riskLeft - item.risk)) {
|
|
43
|
-
yield {
|
|
44
|
-
names: [item.name, ...rest.names],
|
|
45
|
-
value: item.value + rest.value,
|
|
46
|
-
cost: item.cost + rest.cost,
|
|
47
|
-
risk: item.risk + rest.risk,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
yield* enumerate(items, index + 1, budget, riskLeft);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function parseItems(term, env) {
|
|
55
|
-
const list = properListItems(term, env);
|
|
56
|
-
if (!list) return null;
|
|
57
|
-
const items = [];
|
|
58
|
-
for (const itemTerm of list) {
|
|
59
|
-
const item = deref(itemTerm, env);
|
|
60
|
-
if (item.type !== 'compound' || item.name !== 'p' || item.arity !== 4) return null;
|
|
61
|
-
const value = intArg(item.args[1], env), cost = intArg(item.args[2], env), risk = intArg(item.args[3], env);
|
|
62
|
-
if (value == null || cost == null || risk == null) return null;
|
|
63
|
-
items.push({ name: item.args[0], value, cost, risk });
|
|
64
|
-
}
|
|
65
|
-
return items;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function intArg(term, env) {
|
|
69
|
-
const value = deref(term, env);
|
|
70
|
-
if (value.type !== 'number' || !/^-?\d+$/.test(value.name)) return null;
|
|
71
|
-
const n = Number(value.name);
|
|
72
|
-
return Number.isSafeInteger(n) ? n : null;
|
|
73
|
-
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
% Reference 9.5: bounded_subset/7 enumerates subsets within budget and risk caps.
|
|
2
|
-
projects([p(a, 5, 3, 1), p(b, 4, 2, 2), p(c, 7, 5, 3)]).
|
|
3
|
-
answer(selection, result(Names, Value, Cost, Risk)) :- projects(Ps), bounded_subset(Ps, 5, 3, Names, Value, Cost, Risk).
|
|
4
|
-
materialize(answer, 2).
|