eyeling 1.15.2 → 1.15.4
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/HANDBOOK.md +389 -1
- package/examples/floating-point-first-ema-tracker.n3 +193 -0
- package/examples/floating-point-first-rc-discharge.n3 +190 -0
- package/examples/floating-point-first-servo-envelope.n3 +195 -0
- package/examples/floating-point-first-thermal-cooling.n3 +190 -0
- package/examples/output/floating-point-first-ema-tracker.n3 +53 -0
- package/examples/output/floating-point-first-rc-discharge.n3 +41 -0
- package/examples/output/floating-point-first-servo-envelope.n3 +29 -0
- package/examples/output/floating-point-first-thermal-cooling.n3 +41 -0
- package/examples/output/tabling-query-cache-stress.n3 +26 -0
- package/examples/tabling-query-cache-stress.n3 +175 -0
- package/eyeling.js +109 -0
- package/lib/engine.js +109 -0
- package/package.json +1 -1
- package/test/check.test.js +12 -12
package/HANDBOOK.md
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
- [Appendix B — Notation3: when facts can carry their own logic](#app-b)
|
|
30
30
|
- [Appendix C — N3 beyond Prolog: logic that survives the open web](#app-c)
|
|
31
31
|
- [Appendix D — LLM + Eyeling: A Repeatable Logic Toolchain](#app-d)
|
|
32
|
+
- [Appendix E — How Eyeling reaches 100% on `notation3tests`](#app-e)
|
|
32
33
|
|
|
33
34
|
---
|
|
34
35
|
|
|
@@ -560,6 +561,47 @@ Implementation notes:
|
|
|
560
561
|
- Keys are _structural_. Atoms use stable IDs; lists use element keys; variables use their identity (so two different variables are **not** conflated). This keeps the cycle check conservative and avoids accidental pruning.
|
|
561
562
|
- This is not full tabling: it does not memoize answers, it only guards against immediate cycles (the common “A depends on A” loops).
|
|
562
563
|
|
|
564
|
+
### 8.4.1 Minimal completed-goal tabling
|
|
565
|
+
|
|
566
|
+
Eyeling has a **very small, deliberately conservative answer table** for backward goals.
|
|
567
|
+
|
|
568
|
+
What is cached:
|
|
569
|
+
|
|
570
|
+
- only **completed** answer sets
|
|
571
|
+
- keyed by the **fully substituted goal list**
|
|
572
|
+
- only when the proof is entered from a “top-level” call shape (no active per-branch `visited` context)
|
|
573
|
+
- only when the engine is not in a result-limiting mode such as `maxResults`
|
|
574
|
+
|
|
575
|
+
What is **not** cached:
|
|
576
|
+
|
|
577
|
+
- pending / in-progress goals
|
|
578
|
+
- recursive dependency states
|
|
579
|
+
- partial answer streams
|
|
580
|
+
- branch-local states inside an active recursive proof
|
|
581
|
+
|
|
582
|
+
This matters because exposing **pending** answers without dependency propagation would change the meaning of recursive programs. Eyeling therefore caches only results that are already complete and replays them only when the surrounding proof context is equivalent.
|
|
583
|
+
|
|
584
|
+
The cache is invalidated whenever any of the following changes:
|
|
585
|
+
|
|
586
|
+
- the number of known facts
|
|
587
|
+
- the number of backward rules
|
|
588
|
+
- the scoped-closure level
|
|
589
|
+
- whether a frozen scoped snapshot is active
|
|
590
|
+
|
|
591
|
+
So this is **not SLG tabling** and not a general recursion engine. It is best understood as a reuse optimization for repeated backward proofs in a stable proof environment.
|
|
592
|
+
|
|
593
|
+
Typical win cases:
|
|
594
|
+
|
|
595
|
+
- many repeated `log:query` directives with the **same premise**
|
|
596
|
+
- repeated forward-rule body proofs that ask the same completed backward question
|
|
597
|
+
- “query-like” workloads where the expensive part is a repeated backward proof and the fact store does not change between calls
|
|
598
|
+
|
|
599
|
+
Typical non-win cases:
|
|
600
|
+
|
|
601
|
+
- first-time proofs
|
|
602
|
+
- recursive subgoals whose value depends on future answers
|
|
603
|
+
- workloads where the fact set changes between almost every call
|
|
604
|
+
|
|
563
605
|
### 8.5 Backward rules: indexed by head predicate
|
|
564
606
|
|
|
565
607
|
Backward rules are indexed in `backRules.__byHeadPred`. When proving a goal with IRI predicate `p`, Eyeling retrieves:
|
|
@@ -705,7 +747,7 @@ Forward chaining runs inside an _outer loop_ that alternates:
|
|
|
705
747
|
|
|
706
748
|
This produces deterministic behavior for scoped operations: they observe a stable snapshot, not a moving target.
|
|
707
749
|
|
|
708
|
-
**Implementation note (performance):** the two-phase scheme is only needed when the program actually uses scoped built-ins. If no rule contains `log:collectAllIn`, `log:forAllIn`, `log:includes`, or `log:notIncludes`, Eyeling
|
|
750
|
+
**Implementation note (performance):** the two-phase scheme is only needed when the program actually uses scoped built-ins. If no rule contains `log:collectAllIn`, `log:forAllIn`, `log:includes`, or `log:notIncludes`, Eyeling **skips Phase B entirely** and runs only a single saturation. This avoids re-running the forward fixpoint and can prevent a “query-like” forward rule (one whose body contains an expensive backward proof search) from being executed twice.
|
|
709
751
|
|
|
710
752
|
**Implementation note (performance):** in Phase A there is no snapshot, so scoped built-ins (and priority-gated scoped queries) are guaranteed to “delay” by failing.
|
|
711
753
|
Instead of proving the entire forward-rule body only to fail at the end, Eyeling precomputes whether a forward rule depends on scoped built-ins and skips it until a snapshot exists and the requested closure level is reached. This can avoid very expensive proof searches in programs that combine recursion with `log:*In` built-ins.
|
|
@@ -1378,6 +1420,8 @@ Each enumerated rule is standardized apart (fresh variable names) before unifica
|
|
|
1378
1420
|
|
|
1379
1421
|
This is “forward-rule-like” in spirit (premise ⇒ conclusion), but the instantiated conclusion triples are **not added back into the fact store**; they are just what Eyeling prints.
|
|
1380
1422
|
|
|
1423
|
+
**Implementation note (performance):** repeated top-level `log:query` directives with the **same premise formula** are a good fit for Eyeling’s minimal completed-goal tabling (§8.4.1). The first query still performs the full backward proof; later identical premises can reuse the completed answer set as long as the saturated closure and scoped-query context are unchanged.
|
|
1424
|
+
|
|
1381
1425
|
**Important details:**
|
|
1382
1426
|
|
|
1383
1427
|
- Only **top-level** `{...} log:query {...}.` directives are recognized. Inside quoted formulas (or inside rule bodies/heads) it is just an ordinary triple.
|
|
@@ -2160,3 +2204,347 @@ A simple structure that keeps the LLM honest:
|
|
|
2160
2204
|
- “If something is unknown, emit a placeholder fact (`:needsFact`) rather than guessing.”
|
|
2161
2205
|
|
|
2162
2206
|
The point isn’t that the LLM is “right”; it’s that **Eyeling makes the result checkable**, and the artifact becomes a maintainable program rather than a one-off generation.
|
|
2207
|
+
|
|
2208
|
+
---
|
|
2209
|
+
|
|
2210
|
+
<a id="app-e"></a>
|
|
2211
|
+
|
|
2212
|
+
## Appendix E — How Eyeling reaches 100% on `notation3tests`
|
|
2213
|
+
|
|
2214
|
+
### E.1 The goal
|
|
2215
|
+
|
|
2216
|
+
Eyeling does not treat [notation3tests](https://codeberg.org/phochste/notation3tests/) as a side check.
|
|
2217
|
+
|
|
2218
|
+
It treats the suite as an **external semantic contract**.
|
|
2219
|
+
|
|
2220
|
+
That means:
|
|
2221
|
+
|
|
2222
|
+
- the target is public
|
|
2223
|
+
- the target is reproducible
|
|
2224
|
+
- the target is outside the local codebase
|
|
2225
|
+
- success means interoperability, not self-consistency
|
|
2226
|
+
|
|
2227
|
+
---
|
|
2228
|
+
|
|
2229
|
+
### E.2 The test loop
|
|
2230
|
+
|
|
2231
|
+
The workflow is simple and strict:
|
|
2232
|
+
|
|
2233
|
+
- clone the external [notation3tests](https://codeberg.org/phochste/notation3tests/) suite
|
|
2234
|
+
- package the current Eyeling tree
|
|
2235
|
+
- install that package into the suite
|
|
2236
|
+
- run the suite’s Eyeling target
|
|
2237
|
+
- fix semantics, not cosmetics
|
|
2238
|
+
|
|
2239
|
+
This keeps the suite honest and keeps Eyeling honest.
|
|
2240
|
+
|
|
2241
|
+
---
|
|
2242
|
+
|
|
2243
|
+
### E.3 The prompt packet
|
|
2244
|
+
|
|
2245
|
+
A typical conformance-fix prompt is not open-ended.
|
|
2246
|
+
|
|
2247
|
+
It usually includes a small, repeatable packet:
|
|
2248
|
+
|
|
2249
|
+
- the Eyeling source as an attached zip `https://github.com/eyereasoner/eyeling/archive/refs/heads/main.zip`
|
|
2250
|
+
- pointers to the failing tests
|
|
2251
|
+
- the exact failing output, or the exact command needed to reproduce it
|
|
2252
|
+
- a pointer to the N3 spec `https://w3c.github.io/N3/spec/`
|
|
2253
|
+
- a pointer to the builtin definitions `https://w3c.github.io/N3/spec/builtins.html`
|
|
2254
|
+
- a direct request to fix the issue in the engine
|
|
2255
|
+
- a direct request to update `HANDBOOK.md`
|
|
2256
|
+
|
|
2257
|
+
The request is usually phrased in a narrow way:
|
|
2258
|
+
|
|
2259
|
+
- fix this specific failing conformance case
|
|
2260
|
+
- preserve existing passing behavior
|
|
2261
|
+
- make the smallest coherent patch
|
|
2262
|
+
- add or update a regression test if needed
|
|
2263
|
+
- update the handbook so the semantic rule is documented, not just implemented
|
|
2264
|
+
- do not stop at making the test green; align the implementation with the spec and explain the semantic reason in `HANDBOOK.md`
|
|
2265
|
+
|
|
2266
|
+
The model is not asked to “improve the reasoner” in general.
|
|
2267
|
+
|
|
2268
|
+
It is asked to repair one semantic gap against: the code, the failing test, the spec, and the handbook.
|
|
2269
|
+
|
|
2270
|
+
---
|
|
2271
|
+
|
|
2272
|
+
### E.4 The core idea
|
|
2273
|
+
|
|
2274
|
+
Eyeling reaches 100% by making the engine match the semantics that the suite exercises.
|
|
2275
|
+
|
|
2276
|
+
That means getting these right:
|
|
2277
|
+
|
|
2278
|
+
- N3 syntax
|
|
2279
|
+
- rule forms
|
|
2280
|
+
- quoted formulas
|
|
2281
|
+
- variable and blank-node behavior
|
|
2282
|
+
- builtin relations
|
|
2283
|
+
- closure and duplicate control
|
|
2284
|
+
|
|
2285
|
+
The result is not “test gaming.”
|
|
2286
|
+
|
|
2287
|
+
The result is semantic alignment.
|
|
2288
|
+
|
|
2289
|
+
---
|
|
2290
|
+
|
|
2291
|
+
### E.5 One rule core, many surfaces
|
|
2292
|
+
|
|
2293
|
+
The suite uses different surface forms for the same logical ideas.
|
|
2294
|
+
|
|
2295
|
+
Eyeling accepts and normalizes them into one internal rule model:
|
|
2296
|
+
|
|
2297
|
+
- `{ P } => { C } .`
|
|
2298
|
+
- `{ H } <= { B } .`
|
|
2299
|
+
- top-level `log:implies`
|
|
2300
|
+
- top-level `log:impliedBy`
|
|
2301
|
+
|
|
2302
|
+
That matters because conformance depends on recognizing equivalence across syntax, not just parsing one preferred style.
|
|
2303
|
+
|
|
2304
|
+
---
|
|
2305
|
+
|
|
2306
|
+
### E.6 Normalize first, reason second
|
|
2307
|
+
|
|
2308
|
+
A large share of conformance work happens **before** execution.
|
|
2309
|
+
|
|
2310
|
+
Eyeling normalizes the tricky parts early:
|
|
2311
|
+
|
|
2312
|
+
- body blanks become variables
|
|
2313
|
+
- head blanks stay existential
|
|
2314
|
+
- RDF collection encodings become list terms
|
|
2315
|
+
- rule syntax variants become one rule representation
|
|
2316
|
+
|
|
2317
|
+
This removes ambiguity before the engine starts proving anything.
|
|
2318
|
+
|
|
2319
|
+
---
|
|
2320
|
+
|
|
2321
|
+
### E.7 Body blanks vs. head blanks
|
|
2322
|
+
|
|
2323
|
+
This is one of the decisive details.
|
|
2324
|
+
|
|
2325
|
+
In Eyeling:
|
|
2326
|
+
|
|
2327
|
+
- blanks in rule bodies act like placeholders
|
|
2328
|
+
- blanks in rule heads act like fresh existentials
|
|
2329
|
+
|
|
2330
|
+
That split is essential.
|
|
2331
|
+
|
|
2332
|
+
Without it:
|
|
2333
|
+
|
|
2334
|
+
- rule matching goes wrong
|
|
2335
|
+
- proofs become unstable
|
|
2336
|
+
- existential output becomes noisy
|
|
2337
|
+
- conformance drops
|
|
2338
|
+
|
|
2339
|
+
---
|
|
2340
|
+
|
|
2341
|
+
### E.8 Builtins must behave like relations
|
|
2342
|
+
|
|
2343
|
+
Eyeling does not treat builtins as one-way helper functions.
|
|
2344
|
+
|
|
2345
|
+
It treats them as **relations inside proof search**.
|
|
2346
|
+
|
|
2347
|
+
That means a builtin can:
|
|
2348
|
+
|
|
2349
|
+
- succeed
|
|
2350
|
+
- fail
|
|
2351
|
+
- bind variables
|
|
2352
|
+
- stay satisfiable without yet binding anything
|
|
2353
|
+
|
|
2354
|
+
This is critical for the suite, because many builtin cases are really tests of search behavior, not just value computation.
|
|
2355
|
+
|
|
2356
|
+
---
|
|
2357
|
+
|
|
2358
|
+
### E.9 Delay builtins when needed
|
|
2359
|
+
|
|
2360
|
+
Some builtins only become useful after neighboring goals bind enough variables.
|
|
2361
|
+
|
|
2362
|
+
Eyeling handles that by deferring non-informative builtins inside conjunctions.
|
|
2363
|
+
|
|
2364
|
+
So instead of failing too early, the engine:
|
|
2365
|
+
|
|
2366
|
+
- rotates the builtin later
|
|
2367
|
+
- keeps proving the remaining goals
|
|
2368
|
+
- retries once more information exists
|
|
2369
|
+
|
|
2370
|
+
This preserves logical behavior while staying operationally efficient.
|
|
2371
|
+
|
|
2372
|
+
---
|
|
2373
|
+
|
|
2374
|
+
### E.10 Formulas are first-class terms
|
|
2375
|
+
|
|
2376
|
+
Quoted formulas are not treated as strings.
|
|
2377
|
+
|
|
2378
|
+
They are treated as structured logical objects.
|
|
2379
|
+
|
|
2380
|
+
That gives Eyeling the machinery it needs for:
|
|
2381
|
+
|
|
2382
|
+
- formula matching
|
|
2383
|
+
- nested reasoning
|
|
2384
|
+
- `log:includes`
|
|
2385
|
+
- `log:conclusion`
|
|
2386
|
+
- formula comparison by alpha-equivalence
|
|
2387
|
+
|
|
2388
|
+
This is a major reason the higher-level N3 tests pass cleanly.
|
|
2389
|
+
|
|
2390
|
+
---
|
|
2391
|
+
|
|
2392
|
+
### E.11 Alpha-equivalence matters
|
|
2393
|
+
|
|
2394
|
+
Two formulas that differ only in internal names must still count as the same formula when their structure matches.
|
|
2395
|
+
|
|
2396
|
+
Eyeling therefore compares formulas by structure, not by accidental naming.
|
|
2397
|
+
|
|
2398
|
+
That removes a common source of false mismatches in:
|
|
2399
|
+
|
|
2400
|
+
- quoted formulas
|
|
2401
|
+
- nested graphs
|
|
2402
|
+
- rule introspection
|
|
2403
|
+
- scoped reasoning
|
|
2404
|
+
|
|
2405
|
+
---
|
|
2406
|
+
|
|
2407
|
+
### E.12 Lists must have one meaning
|
|
2408
|
+
|
|
2409
|
+
The suite exercises list behavior in more than one spelling.
|
|
2410
|
+
|
|
2411
|
+
Eyeling unifies them:
|
|
2412
|
+
|
|
2413
|
+
- concrete N3 lists
|
|
2414
|
+
- RDF `first/rest` collection encodings
|
|
2415
|
+
|
|
2416
|
+
By materializing anonymous RDF collections into list terms, Eyeling gives both forms one semantic path through the engine.
|
|
2417
|
+
|
|
2418
|
+
That keeps list reasoning consistent across the whole suite.
|
|
2419
|
+
|
|
2420
|
+
---
|
|
2421
|
+
|
|
2422
|
+
### E.13 Existentials must be stable
|
|
2423
|
+
|
|
2424
|
+
A rule head with blanks must not generate endless fresh variants of the same logical result.
|
|
2425
|
+
|
|
2426
|
+
Eyeling stabilizes this by skolemizing head blanks per firing instance.
|
|
2427
|
+
|
|
2428
|
+
So one logical firing yields:
|
|
2429
|
+
|
|
2430
|
+
- one stable witness
|
|
2431
|
+
- one stable derived shape
|
|
2432
|
+
- one meaningful duplicate check
|
|
2433
|
+
|
|
2434
|
+
This is what lets closure reach a real fixpoint.
|
|
2435
|
+
|
|
2436
|
+
---
|
|
2437
|
+
|
|
2438
|
+
### E.14 Duplicate suppression is semantic, not cosmetic
|
|
2439
|
+
|
|
2440
|
+
The engine does not merely try to avoid repeated printing.
|
|
2441
|
+
|
|
2442
|
+
It tries to avoid repeated derivation of the same fact.
|
|
2443
|
+
|
|
2444
|
+
That requires:
|
|
2445
|
+
|
|
2446
|
+
- stable term ids
|
|
2447
|
+
- indexed fact storage
|
|
2448
|
+
- reliable duplicate keys
|
|
2449
|
+
- stable existential handling
|
|
2450
|
+
|
|
2451
|
+
Without that, a reasoner can look busy forever and still fail conformance.
|
|
2452
|
+
|
|
2453
|
+
---
|
|
2454
|
+
|
|
2455
|
+
### E.15 Closure must really close
|
|
2456
|
+
|
|
2457
|
+
Full conformance depends on real saturation behavior.
|
|
2458
|
+
|
|
2459
|
+
Eyeling therefore treats closure as:
|
|
2460
|
+
|
|
2461
|
+
- repeated rule firing
|
|
2462
|
+
- repeated proof over indexed facts
|
|
2463
|
+
- duplicate-aware insertion
|
|
2464
|
+
- termination at fixpoint
|
|
2465
|
+
|
|
2466
|
+
This is what turns the engine from a parser plus demos into a conformance-grade reasoner.
|
|
2467
|
+
|
|
2468
|
+
---
|
|
2469
|
+
|
|
2470
|
+
### E.16 Performance choices support correctness
|
|
2471
|
+
|
|
2472
|
+
Several implementation choices are operational, but they directly protect conformance:
|
|
2473
|
+
|
|
2474
|
+
- predicate-based indexing
|
|
2475
|
+
- subject/object refinement
|
|
2476
|
+
- smallest-bucket candidate selection
|
|
2477
|
+
- fast duplicate keys
|
|
2478
|
+
- skipping already-known ground heads
|
|
2479
|
+
|
|
2480
|
+
These choices reduce accidental nontermination and prevent operational noise from becoming semantic failure.
|
|
2481
|
+
|
|
2482
|
+
---
|
|
2483
|
+
|
|
2484
|
+
### E.17 The suite stays external
|
|
2485
|
+
|
|
2486
|
+
This is a key discipline.
|
|
2487
|
+
|
|
2488
|
+
Eyeling does not define success by a private in-repo imitation of [notation3tests](https://codeberg.org/phochste/notation3tests/).
|
|
2489
|
+
|
|
2490
|
+
It runs against the external suite.
|
|
2491
|
+
|
|
2492
|
+
That means:
|
|
2493
|
+
|
|
2494
|
+
- the compliance test suite is shared
|
|
2495
|
+
- the contract is public
|
|
2496
|
+
- the result is independently meaningful
|
|
2497
|
+
|
|
2498
|
+
A green run says something real.
|
|
2499
|
+
|
|
2500
|
+
---
|
|
2501
|
+
|
|
2502
|
+
### E.18 Every failure becomes an invariant
|
|
2503
|
+
|
|
2504
|
+
Eyeling reaches 100% because failures are not patched superficially.
|
|
2505
|
+
|
|
2506
|
+
Each failure is turned into an engine rule.
|
|
2507
|
+
|
|
2508
|
+
Examples:
|
|
2509
|
+
|
|
2510
|
+
- parser failure → broader syntax support
|
|
2511
|
+
- list failure → one unified list model
|
|
2512
|
+
- formula failure → alpha-equivalence discipline
|
|
2513
|
+
- builtin failure → relational evaluation
|
|
2514
|
+
- closure failure → stable existential handling
|
|
2515
|
+
|
|
2516
|
+
That is how the suite shapes the engine.
|
|
2517
|
+
|
|
2518
|
+
---
|
|
2519
|
+
|
|
2520
|
+
### E.19 Why 100% happens
|
|
2521
|
+
|
|
2522
|
+
Eyeling gets to 100% because all the key layers line up:
|
|
2523
|
+
|
|
2524
|
+
- the parser accepts the full rule surface
|
|
2525
|
+
- normalization removes semantic ambiguity
|
|
2526
|
+
- formulas are real terms
|
|
2527
|
+
- builtins participate in proof search
|
|
2528
|
+
- existential output is stable
|
|
2529
|
+
- closure reaches a true fixpoint
|
|
2530
|
+
- the public suite remains the judge
|
|
2531
|
+
|
|
2532
|
+
Once those pieces are in place, 100% is the visible result of a coherent design.
|
|
2533
|
+
|
|
2534
|
+
---
|
|
2535
|
+
|
|
2536
|
+
### E.20 Final takeaway
|
|
2537
|
+
|
|
2538
|
+
Eyeling reaches full [notation3tests](https://codeberg.org/phochste/notation3tests/) conformance by making “pass the suite” and “implement N3 correctly enough to interoperate” the same task.
|
|
2539
|
+
|
|
2540
|
+
That is the method:
|
|
2541
|
+
|
|
2542
|
+
- external suite
|
|
2543
|
+
- one semantic core
|
|
2544
|
+
- early normalization
|
|
2545
|
+
- relational builtins
|
|
2546
|
+
- formula-aware reasoning
|
|
2547
|
+
- stable existential output
|
|
2548
|
+
- duplicate-safe fixpoint closure
|
|
2549
|
+
|
|
2550
|
+
That is why the result is 100%.
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# ====================================================================================
|
|
2
|
+
# Floating-point-first EMA tracking envelope
|
|
3
|
+
#
|
|
4
|
+
# Why this example exists:
|
|
5
|
+
# It is another floating-point-first certificate in the same family as the servo,
|
|
6
|
+
# RC-discharge, and thermal-cooling examples, but now phrased as an exponential
|
|
7
|
+
# moving average (EMA) tracker.
|
|
8
|
+
#
|
|
9
|
+
# Physical story:
|
|
10
|
+
# Let e(k) be the absolute tracking error of an EMA estimator relative to a constant
|
|
11
|
+
# target. Then
|
|
12
|
+
#
|
|
13
|
+
# e(k+1) = a * e(k)
|
|
14
|
+
#
|
|
15
|
+
# where a is the complement of the EMA gain. In this example we choose
|
|
16
|
+
#
|
|
17
|
+
# a = exp(-Ts/tau) = exp(-1/6)
|
|
18
|
+
#
|
|
19
|
+
# with Ts = 0.5 s and tau = 3 s.
|
|
20
|
+
#
|
|
21
|
+
# Since exp(-1/6) is transcendental, we certify it by a double interval:
|
|
22
|
+
#
|
|
23
|
+
# 8.464817248e-1 <= exp(-1/6) <= 8.464817249e-1
|
|
24
|
+
#
|
|
25
|
+
# and propagate a floating-point envelope for the tracking error.
|
|
26
|
+
#
|
|
27
|
+
# What is certified:
|
|
28
|
+
# * the double interval is nonempty and strictly below 1
|
|
29
|
+
# * the tracking-error envelope shrinks at every sample
|
|
30
|
+
# * from an initial error of 8.0e0, the estimator is guaranteed within 2.5e-1
|
|
31
|
+
# by sample 21
|
|
32
|
+
# * with Ts = 5.0e-1 s, that means guaranteed settling by 1.05e1 s
|
|
33
|
+
# ====================================================================================
|
|
34
|
+
|
|
35
|
+
@prefix : <http://example.org/floating-ema#>.
|
|
36
|
+
@prefix math: <http://www.w3.org/2000/10/swap/math#>.
|
|
37
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#>.
|
|
38
|
+
|
|
39
|
+
# ----------
|
|
40
|
+
# Parameters
|
|
41
|
+
# ----------
|
|
42
|
+
|
|
43
|
+
:ema a :SampledEMAEnvelope;
|
|
44
|
+
:samplePeriod 5.0e-1;
|
|
45
|
+
:timeConstant 3.0e0;
|
|
46
|
+
:exactDecaySymbol "exp(-1/6)";
|
|
47
|
+
:decayLower 8.464817248e-1;
|
|
48
|
+
:decayUpper 8.464817249e-1;
|
|
49
|
+
:initialAbsTrackingError 8.0e0;
|
|
50
|
+
:tolerance 2.5e-1;
|
|
51
|
+
:maxStep 24.
|
|
52
|
+
|
|
53
|
+
# ----------------------------------------------------------------
|
|
54
|
+
# The double interval is a finite certificate for a transcendental
|
|
55
|
+
# ----------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
{
|
|
58
|
+
:ema :decayLower ?lo.
|
|
59
|
+
:ema :decayUpper ?hi.
|
|
60
|
+
?lo math:lessThan ?hi.
|
|
61
|
+
?hi math:lessThan 1.0e0.
|
|
62
|
+
?lo math:greaterThan 0.0e0.
|
|
63
|
+
}
|
|
64
|
+
=>
|
|
65
|
+
{
|
|
66
|
+
:ema :decayCertificate :CertifiedDoubleInterval.
|
|
67
|
+
:ema :contractiveDecay true.
|
|
68
|
+
}.
|
|
69
|
+
|
|
70
|
+
# ----------------
|
|
71
|
+
# Initial envelope
|
|
72
|
+
# ----------------
|
|
73
|
+
|
|
74
|
+
{
|
|
75
|
+
:ema :initialAbsTrackingError ?e0.
|
|
76
|
+
}
|
|
77
|
+
=>
|
|
78
|
+
{
|
|
79
|
+
:ema :trackingErrorEnvelopeAt (0 ?e0 ?e0).
|
|
80
|
+
}.
|
|
81
|
+
|
|
82
|
+
# -------------------------------------
|
|
83
|
+
# Envelope propagation
|
|
84
|
+
# lower(k+1) = decayLower * lower(k)
|
|
85
|
+
# upper(k+1) = decayUpper * upper(k)
|
|
86
|
+
# -------------------------------------
|
|
87
|
+
|
|
88
|
+
{
|
|
89
|
+
:ema :maxStep ?max;
|
|
90
|
+
:decayLower ?aLo;
|
|
91
|
+
:decayUpper ?aHi.
|
|
92
|
+
:ema :trackingErrorEnvelopeAt (?k ?lo ?hi).
|
|
93
|
+
?k math:lessThan ?max.
|
|
94
|
+
( ?k 1 ) math:sum ?k1.
|
|
95
|
+
( ?aLo ?lo ) math:product ?lo1.
|
|
96
|
+
( ?aHi ?hi ) math:product ?hi1.
|
|
97
|
+
}
|
|
98
|
+
=>
|
|
99
|
+
{
|
|
100
|
+
:ema :trackingErrorEnvelopeAt (?k1 ?lo1 ?hi1).
|
|
101
|
+
}.
|
|
102
|
+
|
|
103
|
+
# -----------------------------
|
|
104
|
+
# The envelope shrinks strictly
|
|
105
|
+
# -----------------------------
|
|
106
|
+
|
|
107
|
+
{
|
|
108
|
+
:ema :trackingErrorEnvelopeAt (?k ?lo ?hi).
|
|
109
|
+
( ?k 1 ) math:sum ?k1.
|
|
110
|
+
:ema :trackingErrorEnvelopeAt (?k1 ?lo1 ?hi1).
|
|
111
|
+
?hi1 math:lessThan ?hi.
|
|
112
|
+
}
|
|
113
|
+
=>
|
|
114
|
+
{
|
|
115
|
+
:ema :strictContractionAt ?k.
|
|
116
|
+
}.
|
|
117
|
+
|
|
118
|
+
# --------------------------------
|
|
119
|
+
# Settlement in the tolerance band
|
|
120
|
+
# --------------------------------
|
|
121
|
+
|
|
122
|
+
{
|
|
123
|
+
:ema :trackingErrorEnvelopeAt (?k ?lo ?hi).
|
|
124
|
+
:ema :tolerance ?tol.
|
|
125
|
+
?hi math:lessThan ?tol.
|
|
126
|
+
}
|
|
127
|
+
=>
|
|
128
|
+
{
|
|
129
|
+
:ema :settledCandidate ?k.
|
|
130
|
+
}.
|
|
131
|
+
|
|
132
|
+
{
|
|
133
|
+
:ema :settledCandidate ?k.
|
|
134
|
+
?k math:greaterThan 0.
|
|
135
|
+
( ?k 1 ) math:difference ?km1.
|
|
136
|
+
:ema :trackingErrorEnvelopeAt (?km1 ?prevLo ?prevHi).
|
|
137
|
+
:ema :tolerance ?tol.
|
|
138
|
+
?prevHi math:notLessThan ?tol.
|
|
139
|
+
}
|
|
140
|
+
=>
|
|
141
|
+
{
|
|
142
|
+
:ema :firstSettledStep ?k.
|
|
143
|
+
}.
|
|
144
|
+
|
|
145
|
+
{
|
|
146
|
+
:ema :firstSettledStep ?k.
|
|
147
|
+
:ema :samplePeriod ?ts.
|
|
148
|
+
( ?k ?ts ) math:product ?t.
|
|
149
|
+
}
|
|
150
|
+
=>
|
|
151
|
+
{
|
|
152
|
+
:ema :firstSettledTime ?t.
|
|
153
|
+
}.
|
|
154
|
+
|
|
155
|
+
# -----------------------------------------------------------
|
|
156
|
+
# Readable engineering summary for the floating-point witness
|
|
157
|
+
# -----------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
{
|
|
160
|
+
:ema :firstSettledStep ?k;
|
|
161
|
+
:firstSettledTime ?t;
|
|
162
|
+
:tolerance ?tol;
|
|
163
|
+
:exactDecaySymbol ?sym;
|
|
164
|
+
:decayLower ?aLo;
|
|
165
|
+
:decayUpper ?aHi.
|
|
166
|
+
}
|
|
167
|
+
log:query
|
|
168
|
+
{
|
|
169
|
+
:result :summary (
|
|
170
|
+
"exact-decay" ?sym
|
|
171
|
+
"double-lower" ?aLo
|
|
172
|
+
"double-upper" ?aHi
|
|
173
|
+
"tolerance" ?tol
|
|
174
|
+
"first-settled-step" ?k
|
|
175
|
+
"first-settled-time-s" ?t
|
|
176
|
+
).
|
|
177
|
+
}.
|
|
178
|
+
|
|
179
|
+
{
|
|
180
|
+
:ema :trackingErrorEnvelopeAt (?k ?lo ?hi).
|
|
181
|
+
}
|
|
182
|
+
log:query
|
|
183
|
+
{
|
|
184
|
+
:result :envelope (?k ?lo ?hi).
|
|
185
|
+
}.
|
|
186
|
+
|
|
187
|
+
{
|
|
188
|
+
:ema :strictContractionAt ?k.
|
|
189
|
+
}
|
|
190
|
+
log:query
|
|
191
|
+
{
|
|
192
|
+
:result :contractionAt ?k.
|
|
193
|
+
}.
|