tiergraph 0.2.1__tar.gz → 0.2.2__tar.gz
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.
- {tiergraph-0.2.1 → tiergraph-0.2.2}/CHANGELOG.md +68 -1
- {tiergraph-0.2.1 → tiergraph-0.2.2}/CONTRIBUTING.md +4 -1
- {tiergraph-0.2.1 → tiergraph-0.2.2}/Makefile +20 -4
- {tiergraph-0.2.1 → tiergraph-0.2.2}/PKG-INFO +1 -1
- {tiergraph-0.2.1 → tiergraph-0.2.2}/RELEASING.md +3 -2
- {tiergraph-0.2.1 → tiergraph-0.2.2}/docs/guide/folding.md +198 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/docs/manifest.json +16 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/docs/reference/api.md +305 -11
- {tiergraph-0.2.1 → tiergraph-0.2.2}/docs/reference/cli.md +5 -5
- {tiergraph-0.2.1 → tiergraph-0.2.2}/scripts/check_reservations.py +0 -25
- {tiergraph-0.2.1 → tiergraph-0.2.2}/scripts/check_tracked_clean.py +1 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/__init__.py +14 -1
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/cli/__init__.py +1 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/fold.py +6 -4
- tiergraph-0.2.2/src/tiergraph/pathplan.py +863 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/semiring.py +120 -1
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/conformance/schema_codec.py +42 -11
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_cli.py +21 -0
- tiergraph-0.2.2/tests/test_pathplan.py +953 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_publishability_guards.py +11 -5
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_reservation_register.py +48 -16
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_schema_codec_conformance.py +40 -9
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_semiring.py +48 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/.github/workflows/ci.yml +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/.github/workflows/publish.yml +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/.gitignore +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/.pre-commit-config.yaml +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/LICENSE +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/README.md +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/SECURITY.md +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/corpus/accepted-documents.jsonl +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/denied-name-digests.txt +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/docs/README.md +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/docs/concepts.md +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/docs/contributing-docs.md +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/docs/format.md +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/docs/getting-started.md +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/docs/guide/construction.md +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/docs/guide/profiles.md +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/docs/guide/recognize-and-act.md +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/docs/guide/selection-and-traversal.md +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/docs/guide/serialization.md +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/docs/guide/span-views.md +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/docs/guide/timing.md +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/examples/__init__.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/examples/caption_alignment.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/examples/critical_path.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/examples/json_document.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/examples/mix_paths.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/examples/mixing.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/examples/text_segmentation.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/pyproject.toml +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/schema/tiergraph.schema.json +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/schema/tiergraph.schema.sha256 +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/scripts/__init__.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/scripts/capture_corpus.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/scripts/check_changelog_claims.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/scripts/check_documented.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/scripts/check_format_growth.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/scripts/check_format_semantics.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/scripts/generate_docs.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/scripts/generate_schema.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/__main__.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/action.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/build.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/clock.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/core.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/grammar.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/inspect.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/machine.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/machine_codec.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/path.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/profile.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/py.typed +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/rewrite.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/root.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/schema.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/selection.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/spanview.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/textgrid.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/traversal.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/value.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph/wire.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph_dot/__init__.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/src/tiergraph_dot/py.typed +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/__init__.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/conformance/__init__.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/conformance/action.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/conformance/declared_schema_codec_divergences.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/conformance/fold.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/conformance/kernel.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/conformance/machine.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/conformance/recognition.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/conformance/schema.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/conformance/selection.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/conformance/traversal.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/conformance/wire.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/fixtures/textgrid/integer-long.TextGrid +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/fixtures/textgrid/reference-long.TextGrid +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/fixtures/textgrid/reference-short.TextGrid +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/semiring_laws.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_action.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_alignment_witness.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_alternation_witness.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_boundary_witness.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_build.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_canonical_properties.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_capture_corpus.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_changelog_claims.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_cli_envelope.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_clock.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_core.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_displacement.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_docs.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_dot.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_edit.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_examples.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_external_reference_witness.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_fold.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_fold_exactness.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_fold_witness.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_format_growth.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_format_semantics.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_gate_environment.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_grammar.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_inspect.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_machine.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_machine_codec.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_machine_decoders.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_mix_paths.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_mix_paths_render.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_ordered_containment.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_ordered_polyadic_traversal.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_package.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_path.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_path_alternation.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_polyadic_relations.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_polyadic_view_reachability.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_profile.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_refusal_order.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_rewrite.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_root.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_schema.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_selection.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_selection_semiring.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_semiring_law_discrimination.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_shipped_surface.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_spanview.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_text_domain.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_textgrid.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_traversal.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_value.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_value_embedding.py +0 -0
- {tiergraph-0.2.1 → tiergraph-0.2.2}/tests/test_wire.py +0 -0
|
@@ -7,6 +7,72 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.2] - 2026-09-14
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Added `LOG_PROBABILITY`, the log-sum-exp semiring over finite IEEE-double log
|
|
15
|
+
weights with `-inf` as its zero: stable addition, log-product multiplication,
|
|
16
|
+
positive weights admitted, overflow refused, every required law except
|
|
17
|
+
addition commutativity declared approximate, and no star. `tiergraph semirings` lists it as
|
|
18
|
+
`log-probability`, and `tiergraph fold --semiring log-probability` runs it.
|
|
19
|
+
Its `normalize` method is the declared readout that turns log weights into
|
|
20
|
+
probabilities of a total; `ExpectationSemiring` still refuses this inexact
|
|
21
|
+
base (#186).
|
|
22
|
+
- Added `PathPlan`, which compiles a `FoldDeclaration` over one `OR` relation on
|
|
23
|
+
an acyclic graph once and evaluates it again under any vector of carrier
|
|
24
|
+
values in the plan's item order. `evaluate` reproduces what `run` returns for
|
|
25
|
+
the same values, with the same provenance and cost account: exactly under the
|
|
26
|
+
`ARCTIC` and `TROPICAL` carriers, and within the algebra's declared
|
|
27
|
+
approximation under `LOG_PROBABILITY`, where a gathered log-sum-exp sums its
|
|
28
|
+
exponentials in a different order than pairwise addition does. `marginals`
|
|
29
|
+
adds the outside pass and returns `PathMarginals`, whose
|
|
30
|
+
`posteriors(readout=...)` reads probabilities through the readout the caller
|
|
31
|
+
declares and the algebra publishes, and reports zero mass rather than
|
|
32
|
+
fabricating a distribution, as `PathPosteriors` with the readout named. Under
|
|
33
|
+
those three carriers the plan runs the algebra's operations in a fused
|
|
34
|
+
schedule when the declaration states no witness order, and under the extremum
|
|
35
|
+
carriers also under an `AlgebraOrder` — a witness order by an algebra's own
|
|
36
|
+
selective addition — with `CHOOSE_FIRST`, whose selection it fuses too; every
|
|
37
|
+
other declaration runs the general schedule. Ranked output, index axes,
|
|
38
|
+
several relations, `AND` transitions, and cycles are refused by name (#186).
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
|
|
42
|
+
- The package root now exports every semiring constant the shell can name:
|
|
43
|
+
`ARCTIC`, `DECIMAL_ARCTIC`, `LOG_PROBABILITY` and `TROPICAL` join `BOOLEAN`,
|
|
44
|
+
`COUNTING`, `DECIMAL_TROPICAL` and `PATH` there. `PATH_WITNESSES`, which the
|
|
45
|
+
shell has no spelling for, stays on `tiergraph.semiring`, as every constant
|
|
46
|
+
still does (#186).
|
|
47
|
+
- `FoldDeclaration` now states where a readout above the algebra is declared,
|
|
48
|
+
and the `declared-readout` reservation is discharged: the reservation
|
|
49
|
+
register carries four entries, and `PathMarginals.posteriors` records the
|
|
50
|
+
readout it applies (#186).
|
|
51
|
+
- The gate's three hash-seed test passes run concurrently under `make
|
|
52
|
+
determinism`; `DETERMINISM_JOBS=1` runs them one at a time, and each seed
|
|
53
|
+
writes its own log under the virtualenv and prints only its summary line when
|
|
54
|
+
it passes; a failing seed prints its whole log.
|
|
55
|
+
The publishability test that plants a sentinel under the checkout's ignored
|
|
56
|
+
local agent directory names it per process, so concurrent passes over one
|
|
57
|
+
checkout no longer race on its cleanup (#187).
|
|
58
|
+
- The schema-codec conformance suite audits its generated probes once per
|
|
59
|
+
module and subtracts each test's policy from that audit, through the
|
|
60
|
+
harness's new `audit_drifts` and `subtract_declared`, whose composition
|
|
61
|
+
`undeclared_drifts` still is; a test holds the two answers equal. The three
|
|
62
|
+
tests that each ran the full audit took most of the suite's time (#187).
|
|
63
|
+
- `FORMAT_VERSION` stays `"0.2.0"`: nothing in this line touches the wire
|
|
64
|
+
format, and `make format-growth` reports no break.
|
|
65
|
+
|
|
66
|
+
### Documentation
|
|
67
|
+
|
|
68
|
+
- The release runbook's step 3 no longer says a stale local `dist/` build is
|
|
69
|
+
what the publish workflow would upload; the workflow builds from a fresh
|
|
70
|
+
checkout of the tag. It now separates the two local checks: the wheel package
|
|
71
|
+
listing reads `dist/*.whl`, so a stale archive is what it would read, while
|
|
72
|
+
the printed version imports from `src` and never consults `dist/` at all. The
|
|
73
|
+
`dist/` filename listing is the only step that would expose a stale build, as
|
|
74
|
+
an extra pair of archives (#185).
|
|
75
|
+
|
|
10
76
|
## [0.2.1] - 2026-09-13
|
|
11
77
|
|
|
12
78
|
### Added
|
|
@@ -896,7 +962,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
896
962
|
- TG-PATH canonical addressing for structural and durable items and boundaries, profile-owned alternatives, kind checks, and typed refusals with offender details.
|
|
897
963
|
- Canonical selection, bounded bipartite walks, ordered polyadic traversal, and ordered containment queries that preserve declared incidence and child order where applicable.
|
|
898
964
|
|
|
899
|
-
[Unreleased]: https://github.com/lenzo-ka/tiergraph/compare/v0.2.
|
|
965
|
+
[Unreleased]: https://github.com/lenzo-ka/tiergraph/compare/v0.2.2...HEAD
|
|
966
|
+
[0.2.2]: https://github.com/lenzo-ka/tiergraph/compare/v0.2.1...v0.2.2
|
|
900
967
|
[0.2.1]: https://github.com/lenzo-ka/tiergraph/compare/v0.2.0...v0.2.1
|
|
901
968
|
[0.2.0]: https://github.com/lenzo-ka/tiergraph/compare/v0.1.0...v0.2.0
|
|
902
969
|
[0.1.0]: https://github.com/lenzo-ka/tiergraph/releases/tag/v0.1.0
|
|
@@ -52,7 +52,10 @@ version this section used to carry fell a step behind twice: a copied list is a
|
|
|
52
52
|
claim about the gate that nothing checks.
|
|
53
53
|
|
|
54
54
|
`make check` builds the virtualenv and then runs `make gate`, which is the same
|
|
55
|
-
steps against an environment that already exists.
|
|
55
|
+
steps against an environment that already exists. The `determinism` step runs
|
|
56
|
+
its three seeds concurrently, each writing a log under the virtualenv and
|
|
57
|
+
printing its summary line; on a machine that cannot hold three interpreters at
|
|
58
|
+
once, `make gate DETERMINISM_JOBS=1` runs them one at a time. Run `gate` where an index
|
|
56
59
|
cannot be reached, rather than copying its steps out by hand. Coverage is
|
|
57
60
|
measured for the `tiergraph` and `tiergraph_dot` packages and for the `scripts`
|
|
58
61
|
gates, and must remain at 100% branch coverage.
|
|
@@ -26,7 +26,7 @@ export PYTHONPATH := $(MAKEFILE_DIR)/src$(if $(PYTHONPATH),:$(PYTHONPATH))
|
|
|
26
26
|
# and runs nothing. A target left off this line is silent until something in the
|
|
27
27
|
# tree happens to share its name, which is why `format-semantics` and
|
|
28
28
|
# `corpus-capture` sat missing here without ever being noticed.
|
|
29
|
-
.PHONY: venv lint format-check types test determinism-seed determinism schema schema-check format-growth format-semantics corpus-capture docs docs-check tracked-clean documented reservations changelog-claims gate-fast gate check
|
|
29
|
+
.PHONY: venv lint format-check types test determinism-seed determinism determinism-seed-0 determinism-seed-12345 determinism-seed-999 schema schema-check format-growth format-semantics corpus-capture docs docs-check tracked-clean documented reservations changelog-claims gate-fast gate check
|
|
30
30
|
|
|
31
31
|
# Development happens in an isolated environment: a shared interpreter drags in
|
|
32
32
|
# packages this project does not depend on, and they surface as type errors in
|
|
@@ -56,10 +56,26 @@ determinism-seed:
|
|
|
56
56
|
@test -n "$(HASH_SEED)" || (echo "HASH_SEED is required" >&2; exit 2)
|
|
57
57
|
@PYTHONHASHSEED=$(HASH_SEED) $(VENV_PYTHON) -m pytest
|
|
58
58
|
|
|
59
|
+
# The three seeds are independent, so they run concurrently: measured serial,
|
|
60
|
+
# they were the larger half of the gate's wall time. Each seed writes its own
|
|
61
|
+
# log under the virtualenv (ignored, so it cannot ship) and prints only its
|
|
62
|
+
# summary line, so three interleaved dot streams do not have to be read. A
|
|
63
|
+
# failing seed prints its whole log. DETERMINISM_JOBS=1 runs them one at a
|
|
64
|
+
# time on a machine that cannot hold three interpreters at once.
|
|
65
|
+
DETERMINISM_JOBS ?= 3
|
|
66
|
+
DETERMINISM_SEEDS := 0 12345 999
|
|
67
|
+
|
|
59
68
|
determinism:
|
|
60
|
-
|
|
61
|
-
$(
|
|
62
|
-
|
|
69
|
+
@$(MAKE) --no-print-directory -j$(DETERMINISM_JOBS) \
|
|
70
|
+
$(addprefix determinism-seed-,$(DETERMINISM_SEEDS))
|
|
71
|
+
|
|
72
|
+
determinism-seed-0 determinism-seed-12345 determinism-seed-999:
|
|
73
|
+
@seed=$(@:determinism-seed-%=%); log=$(VENV)/determinism-$$seed.log; \
|
|
74
|
+
if $(MAKE) --no-print-directory determinism-seed HASH_SEED=$$seed >$$log 2>&1; then \
|
|
75
|
+
printf 'seed %s: %s\n' "$$seed" "$$(tail -n 1 $$log)"; \
|
|
76
|
+
else \
|
|
77
|
+
status=$$?; cat $$log; echo "seed $$seed failed" >&2; exit $$status; \
|
|
78
|
+
fi
|
|
63
79
|
|
|
64
80
|
tracked-clean:
|
|
65
81
|
@$(VENV_PYTHON) scripts/check_tracked_clean.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: tiergraph
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Ordered tiers, declared relations, and an algebra over them
|
|
5
5
|
Project-URL: Homepage, https://github.com/lenzo-ka/tiergraph
|
|
6
6
|
Project-URL: Repository, https://github.com/lenzo-ka/tiergraph
|
|
@@ -117,8 +117,9 @@ job references it; the OIDC identity is scoped to it).
|
|
|
117
117
|
displaying it — so without this line the doc asks for a comparison it
|
|
118
118
|
gives no way to make. Expect exactly `tiergraph-X.Y.Z.tar.gz` and
|
|
119
119
|
`tiergraph-X.Y.Z-py3-none-any.whl`; a stale build left in `dist/` from an
|
|
120
|
-
earlier version shows up here as an extra pair
|
|
121
|
-
|
|
120
|
+
earlier version shows up here as an extra pair. The publish workflow never
|
|
121
|
+
sees it — it builds from a fresh checkout of the tag — but the local
|
|
122
|
+
checks below would read the wrong archive.
|
|
122
123
|
- **The wheel listing must show both packages.** The grep exits 0 when
|
|
123
124
|
either package is present, so the exit status decides nothing: read the
|
|
124
125
|
output and confirm entries under `tiergraph/` *and* under `tiergraph_dot/`.
|
|
@@ -335,6 +335,204 @@ Use `TROPICAL` or `ARCTIC`, whose associativity check is approximate, for
|
|
|
335
335
|
`xsd:double` values. The refusal is a declaration-time guard, so a fold that
|
|
336
336
|
runs has already been checked for this mismatch.
|
|
337
337
|
|
|
338
|
+
## Preparing a path plan
|
|
339
|
+
|
|
340
|
+
A fold over one `OR` relation on an acyclic graph is a path graph: every
|
|
341
|
+
derivation is a root-to-sink path, and its value is the product of the local
|
|
342
|
+
values along it. When the graph stays fixed and only the values change — an
|
|
343
|
+
expectation step re-weighting the same lattice, a sweep over parameters —
|
|
344
|
+
`PathPlan` compiles the declaration's topology once and evaluates it under any
|
|
345
|
+
vector of carrier values given in the plan's item order. `PathPlan.evaluate`
|
|
346
|
+
returns what `FoldDeclaration.run` returns, provenance and cost account
|
|
347
|
+
included, and `PathPlan.marginals` adds the outside pass: for every item, the
|
|
348
|
+
sum over the derivations that pass through it.
|
|
349
|
+
|
|
350
|
+
The example is a small lattice whose states and arcs are items of one tier,
|
|
351
|
+
joined by a `next` relation, with the arcs after the states. Under
|
|
352
|
+
`LOG_PROBABILITY` the values are log weights and `-INF` is the zero. A sink
|
|
353
|
+
accepts with its own value, so the final state carries `0.0` and a dead end
|
|
354
|
+
would carry `-INF`.
|
|
355
|
+
|
|
356
|
+
```python
|
|
357
|
+
import math
|
|
358
|
+
|
|
359
|
+
from tiergraph import (
|
|
360
|
+
AttributeDeclaration,
|
|
361
|
+
AttributeDomain,
|
|
362
|
+
AttributeValuation,
|
|
363
|
+
AttributeValue,
|
|
364
|
+
BipartiteRelationDeclaration,
|
|
365
|
+
ChildCombination,
|
|
366
|
+
FoldDeclaration,
|
|
367
|
+
FoldTransition,
|
|
368
|
+
Graph,
|
|
369
|
+
Item,
|
|
370
|
+
ItemRef,
|
|
371
|
+
NamespaceDeclaration,
|
|
372
|
+
QualifiedName,
|
|
373
|
+
RelationInstance,
|
|
374
|
+
SimpleRelationDeclaration,
|
|
375
|
+
Tier,
|
|
376
|
+
TierDeclaration,
|
|
377
|
+
TiePolicy,
|
|
378
|
+
XsdType,
|
|
379
|
+
)
|
|
380
|
+
from tiergraph.pathplan import AlgebraOrder, PathPlan
|
|
381
|
+
from tiergraph.semiring import ARCTIC, LOG_PROBABILITY
|
|
382
|
+
|
|
383
|
+
ns = "https://example.com/plan"
|
|
384
|
+
nodes = QualifiedName(ns, "nodes")
|
|
385
|
+
node = QualifiedName(ns, "node")
|
|
386
|
+
follows = QualifiedName(ns, "next")
|
|
387
|
+
weight = QualifiedName(ns, "weight")
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def item(label: str, log_weight: float) -> Item:
|
|
391
|
+
lexical = "-INF" if log_weight == -math.inf else repr(log_weight)
|
|
392
|
+
return Item(label, (AttributeValue(weight, XsdType.DOUBLE, lexical),))
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
labels = ("s", "m", "f", "merged", "first", "silent")
|
|
396
|
+
lattice_refs = {label: ItemRef(nodes, index) for index, label in enumerate(labels)}
|
|
397
|
+
graph = Graph(
|
|
398
|
+
(NamespaceDeclaration("lattice", ns),),
|
|
399
|
+
(
|
|
400
|
+
Tier(
|
|
401
|
+
TierDeclaration(nodes, "States and arcs"),
|
|
402
|
+
(
|
|
403
|
+
item("s", 0.0),
|
|
404
|
+
item("m", 0.0),
|
|
405
|
+
item("f", 0.0),
|
|
406
|
+
item("merged", math.log(0.4)),
|
|
407
|
+
item("first", math.log(0.3)),
|
|
408
|
+
item("silent", math.log(0.5)),
|
|
409
|
+
),
|
|
410
|
+
),
|
|
411
|
+
),
|
|
412
|
+
(
|
|
413
|
+
SimpleRelationDeclaration(QualifiedName(ns, "membership"), nodes, node),
|
|
414
|
+
BipartiteRelationDeclaration(follows, node, node, acyclic=True),
|
|
415
|
+
),
|
|
416
|
+
tuple(
|
|
417
|
+
RelationInstance(follows, lattice_refs[left], lattice_refs[right])
|
|
418
|
+
for left, right in (
|
|
419
|
+
("s", "merged"),
|
|
420
|
+
("merged", "f"),
|
|
421
|
+
("s", "first"),
|
|
422
|
+
("first", "m"),
|
|
423
|
+
("m", "silent"),
|
|
424
|
+
("silent", "f"),
|
|
425
|
+
)
|
|
426
|
+
),
|
|
427
|
+
(AttributeDeclaration(weight, AttributeDomain.ITEM, XsdType.DOUBLE),),
|
|
428
|
+
)
|
|
429
|
+
valuation = AttributeValuation("weight", weight, (nodes,))
|
|
430
|
+
transitions = (FoldTransition(follows, ChildCombination.OR),)
|
|
431
|
+
|
|
432
|
+
plan = PathPlan.prepare(
|
|
433
|
+
FoldDeclaration(
|
|
434
|
+
"lattice",
|
|
435
|
+
graph,
|
|
436
|
+
valuation,
|
|
437
|
+
LOG_PROBABILITY,
|
|
438
|
+
lambda value, _label: value,
|
|
439
|
+
transitions,
|
|
440
|
+
roots=(lattice_refs["s"],),
|
|
441
|
+
)
|
|
442
|
+
)
|
|
443
|
+
marginals = plan.marginals()
|
|
444
|
+
print("log mass:", round(marginals.total, 6))
|
|
445
|
+
posteriors = marginals.posteriors(readout="normalize")
|
|
446
|
+
print("readout:", posteriors.readout)
|
|
447
|
+
probabilities = posteriors.values
|
|
448
|
+
assert probabilities is not None # zero mass is reported, never fabricated
|
|
449
|
+
for label, probability in zip(plan.labels, probabilities):
|
|
450
|
+
print(f"{label}: {probability:.4f}")
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
```text
|
|
454
|
+
log mass: -0.597837
|
|
455
|
+
readout: normalize
|
|
456
|
+
s: 1.0000
|
|
457
|
+
m: 0.2727
|
|
458
|
+
f: 1.0000
|
|
459
|
+
merged: 0.7273
|
|
460
|
+
first: 0.2727
|
|
461
|
+
silent: 0.2727
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
The mass is `0.4 + 0.3 × 0.5 = 0.55`, and each posterior is the share of that
|
|
465
|
+
mass passing through the item. Normalizing is a division above the algebra,
|
|
466
|
+
so the caller declares the readout by name and the carrier must publish it —
|
|
467
|
+
`normalize` is the one `LOG_PROBABILITY` publishes — and `PathPosteriors`
|
|
468
|
+
records the readout it applied. A zero total is reported as `zero_mass` with
|
|
469
|
+
no values rather than as a fabricated distribution. Aggregating
|
|
470
|
+
posteriors by anything other than item — by the output an arc emits, say — is
|
|
471
|
+
the caller's readout in turn; two arcs producing the same output each carry
|
|
472
|
+
their own share here.
|
|
473
|
+
|
|
474
|
+
New values reuse the compiled topology. `plan.values` holds what the
|
|
475
|
+
declaration lifted, `plan.index` locates an item's position, and a vector of
|
|
476
|
+
another length is refused:
|
|
477
|
+
|
|
478
|
+
```python
|
|
479
|
+
reweighted = list(plan.values)
|
|
480
|
+
reweighted[plan.index(lattice_refs["merged"])] = math.log(0.1)
|
|
481
|
+
print("re-weighted log mass:", round(plan.marginals(reweighted).total, 6))
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
```text
|
|
485
|
+
re-weighted log mass: -1.386294
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
A best path runs the same plan under a selective carrier. `AlgebraOrder` is a
|
|
489
|
+
witness order by the algebra's own addition, so the fold and the plan agree on
|
|
490
|
+
what wins; with `CHOOSE_FIRST` a tie goes to the alternative earliest in the
|
|
491
|
+
graph's canonical order, deterministically, and `ALL` keeps every tied path up
|
|
492
|
+
to `output_cap`.
|
|
493
|
+
|
|
494
|
+
```python
|
|
495
|
+
best = PathPlan.prepare(
|
|
496
|
+
FoldDeclaration(
|
|
497
|
+
"best",
|
|
498
|
+
graph,
|
|
499
|
+
valuation,
|
|
500
|
+
ARCTIC,
|
|
501
|
+
lambda value, _label: value,
|
|
502
|
+
transitions,
|
|
503
|
+
roots=(lattice_refs["s"],),
|
|
504
|
+
witness_order=AlgebraOrder(ARCTIC),
|
|
505
|
+
tie_policy=TiePolicy.CHOOSE_FIRST,
|
|
506
|
+
)
|
|
507
|
+
)
|
|
508
|
+
result = best.evaluate()
|
|
509
|
+
print("best:", round(result.value, 6), result.provenance)
|
|
510
|
+
print("carrier ops:", result.cost.carrier_work)
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
```text
|
|
514
|
+
best: -0.916291 (('s', 'merged', 'f'),)
|
|
515
|
+
carrier ops: 8
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
Under `LOG_PROBABILITY`, `ARCTIC`, and `TROPICAL` the plan runs a fused
|
|
519
|
+
schedule that gathers each item's alternatives at once with the algebra's
|
|
520
|
+
operations inlined, and an `AlgebraOrder` with `CHOOSE_FIRST` fuses the
|
|
521
|
+
selection too. The cost account is the general schedule's. A gathered
|
|
522
|
+
log-sum-exp sums its exponentials in a different order than pairwise
|
|
523
|
+
addition, so under the log carrier the plan agrees with `run` within the
|
|
524
|
+
algebra's declared approximation — at the rounding scale of the operands,
|
|
525
|
+
which a total near cancellation can show in its result — and under the
|
|
526
|
+
extremum carriers exactly. Every other declaration runs the general schedule
|
|
527
|
+
through the algebra's methods and agrees with `run` exactly. A result
|
|
528
|
+
that would leave the finite double carrier is refused as overflow rather than
|
|
529
|
+
read as mass created or destroyed, and a value vector outside the carrier —
|
|
530
|
+
`NaN`, a Boolean, the excluded infinity — is refused before anything runs.
|
|
531
|
+
|
|
532
|
+
A plan refuses what a path cannot carry, each by name: ranked output, an index
|
|
533
|
+
product, more than one dependency relation, an `AND` transition, and a cycle,
|
|
534
|
+
which is reported by its closing edge.
|
|
535
|
+
|
|
338
536
|
## From the command line
|
|
339
537
|
|
|
340
538
|
`tiergraph semirings` lists the algebras the `tiergraph fold` shell can name,
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"GraphEditor"
|
|
39
39
|
],
|
|
40
40
|
"fold": [
|
|
41
|
+
"AlgebraOrder",
|
|
41
42
|
"AttributeValuation",
|
|
42
43
|
"ChildCombination",
|
|
43
44
|
"ExactnessRefusal",
|
|
@@ -48,6 +49,9 @@
|
|
|
48
49
|
"FoldHomomorphism",
|
|
49
50
|
"FoldResult",
|
|
50
51
|
"FoldTransition",
|
|
52
|
+
"PathMarginals",
|
|
53
|
+
"PathPlan",
|
|
54
|
+
"PathPosteriors",
|
|
51
55
|
"TiePolicy"
|
|
52
56
|
],
|
|
53
57
|
"grammar": [
|
|
@@ -196,10 +200,14 @@
|
|
|
196
200
|
"selection_loads"
|
|
197
201
|
],
|
|
198
202
|
"semirings": [
|
|
203
|
+
"ARCTIC",
|
|
199
204
|
"BOOLEAN",
|
|
200
205
|
"COUNTING",
|
|
206
|
+
"DECIMAL_ARCTIC",
|
|
201
207
|
"DECIMAL_TROPICAL",
|
|
208
|
+
"LOG_PROBABILITY",
|
|
202
209
|
"PATH",
|
|
210
|
+
"TROPICAL",
|
|
203
211
|
"StarRefusal",
|
|
204
212
|
"StarSelector",
|
|
205
213
|
"ZeroClosedStar"
|
|
@@ -298,6 +306,12 @@
|
|
|
298
306
|
"text",
|
|
299
307
|
"python",
|
|
300
308
|
"text",
|
|
309
|
+
"python",
|
|
310
|
+
"text",
|
|
311
|
+
"python",
|
|
312
|
+
"text",
|
|
313
|
+
"python",
|
|
314
|
+
"text",
|
|
301
315
|
"console"
|
|
302
316
|
],
|
|
303
317
|
"docs/guide/profiles.md": [
|
|
@@ -379,6 +393,7 @@
|
|
|
379
393
|
"COUNTING",
|
|
380
394
|
"DECIMAL_ARCTIC",
|
|
381
395
|
"DECIMAL_TROPICAL",
|
|
396
|
+
"LOG_PROBABILITY",
|
|
382
397
|
"PATH",
|
|
383
398
|
"PATH_WITNESSES",
|
|
384
399
|
"TROPICAL",
|
|
@@ -390,6 +405,7 @@
|
|
|
390
405
|
"ExpectationSemiring",
|
|
391
406
|
"LawCheck",
|
|
392
407
|
"LexicographicSemiring",
|
|
408
|
+
"LogProbabilitySemiring",
|
|
393
409
|
"Path",
|
|
394
410
|
"PathSemiring",
|
|
395
411
|
"PathValue",
|