Release preparation for v4.0.0 - #45
Merged
Merged
Conversation
Two changes that turn out to be one. `.symbolic()` could be called with no
arguments on nine of the library's structures, because each declares a
`default_signature` naming its operators and the codec that turns Python values
into elements. It could not on Büchi arithmetic or MSO0: those were built by
plain functions returning a bare presentation, with nowhere to hang a
signature. `autstr.arithmetic` existed to fill that gap -- for ONE of the three,
which is what made it look like a second, rival interface.
So the signature moves onto the presentation, where the rest of the library
keeps it, and the module that worked around its absence goes away. All three
gain their codec, verified against the automata rather than assumed:
- N: binary, least significant bit first (checked against A(x,y,z)),
- Z: sign symbol then magnitude (as before),
- MSO0: a set as a bitmask, position i set iff i is a member, in CANONICAL
form -- the universe rejects trailing zeros, so {0} is `1` and the empty
set is the empty word (checked against Subset over all subsets of {0,1,2}).
MSO0 therefore gains an interface it never had: union, intersection and
difference as `+`, `*`, `-`, and solutions that come back as Python sets.
`buildin` was the other half of the same confusion: every structure the library
ships is built in, so the name carved nothing. Its contents move to where their
subject already lives -- `arithmetic` (Buechi N and Z), `powerset` (MSO0),
`tree_arithmetic` (Skolem, matching algebra/tree_algebra), and the generic
automaton constructors to `utils.automata_tools`, which was already importing
`one` from across the package boundary. `CompiledPresentation` holds the part
the presentations share.
The precompiled `.autstr` artifacts go too. They saved 0.03s, 0.23s and 0.44s
against building from scratch, and the built presentations are identical to the
loaded ones relation by relation. Serialization stays fully supported; it just
stops shipping artifacts nobody needed.
BREAKING: `autstr.buildin.*` is gone, as are `autstr.arithmetic.integers` and
`signature`; encode/decode are now classmethods on the presentations.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The abelian-groups cell was labelled "has an element of order 4" and asked for
an element of order 2: `A(x,x,x)` spells x = 0, so `A(x,x,z) and A(z,z,z)` says
2x = 0. Z/2 alone satisfied it. The corrected formula asks for y = 2x, z = 2y = 0
with x and y both nonzero, which separates the two groups of order 4 -- Z/4 has
such an element, Z/2 + Z/2 does not.
That cell is also where the symbolic interface earns its place: with `+` and
`.eq` the query reads as the mathematics, and a class has no constant symbols,
so the identity is named by what defines it (t + t = t).
MSO0 gains its codec's payoff: union, intersection and difference as `+`, `*`,
`-`, membership and subset as methods, and solutions that come back as Python
sets -- the eight ways to split {0,1,2} in two, enumerated from the automaton.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The composition notebook becomes "building new structures from old": the
integers constructed from the naturals as a 2-dimensional quotient
interpretation, which is small enough to read and real enough to be worth
saving. It carries the codec story -- the construction is correct but
illegible until a codec says that the pair (a, b) denotes a - b -- and closes
on the shipped example of the same idea, `autstr.ordinals`.
A new notebook, `infinite_structures`, covers what had none: the integer grid,
the regular tree, Turing configuration graphs and level 2 collapsible pushdown
graphs. The last two are the point. Both present computation as a graph and
both have a decidable first-order theory, but reachability is the halting
problem for one and a relation of the other -- so `Reach` is there to be
queried, and transitivity over all runs of all lengths is decided in under a
second.
Writing those two turned up three real defects, none of which any test saw:
- ENUMERATION WAS BROKEN for every interpreted structure of dimension > 1,
including the shipped `Ordinal(2)`. `iterate_language` built each tape by
string-concatenating letters, which holds only while a letter is one
character; over a product alphabet a letter is a tuple, and str() flattened
('0','1') into text no codec could read. Tapes are tuples of letters now.
Membership worked throughout, which is why this went unseen.
- SERIALIZING SUCH A PRESENTATION could not be reloaded: JSON has no tuples,
so the alphabet came back as a set of lists (unhashable) and the padding
symbol as a list. Both are restored on load.
- The v2 back-compat loader lost its only test when the `.autstr` artifacts
went away -- `test_the_legacy_flat_payload_still_loads` was left building a
presentation and querying it, touching no legacy payload at all. It now
constructs a v2 payload by hand and reads it.
Also: the abelian-groups notebook cell and `is_deterministic`'s docstring, which
still said reachability was not built.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The countable atomless Boolean algebra had no notebook coverage. It sits next to the finite Boolean algebras, where the contrast is the point: finite ones are powersets and full of atoms, and dropping finiteness leaves exactly one countable algebra in which every nonzero element splits forever. Its elements are trees, so it is the tree engine's turn. Bipartiteness in the graphs notebook now appears twice: as the formula string, and built from `symbolic()` with `.implies`, `.all` and `.drop`. A set variable is just a variable, which is what makes the query monadic second-order, and the symbolic form makes that legible instead of nesting six parentheses. Checked to agree with the string form on all three of that cell's graphs. `infinite_structures` joins the documentation's notebook toctree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The README carried the whole release history, which made it long and made the history shallow -- there was never room to say what a release actually changed. CHANGELOG.md takes it over, with v4.0.0 written out properly: what was added, what moved, what was fixed, and an upgrade path from 3.x. The docs build copies it in, so it has a page on the site too. What stays in the README is what only the README can say: the AI-assisted algorithm engineering narrative, the gource animation, the highlights of the newest version, and a link to the rest. The quick start now opens on the symbolic interface -- which is the honest entry point, and was also the one broken example in the file, since it still imported a class that the arithmetic rewrite deleted a release ago. Version 4.0.0 rather than 3.2: `autstr.buildin` is gone, `autstr.arithmetic` holds something else, and the old term-algebra front end has no replacement of the same shape. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Gource over the full commit history, now reaching the symbolic layer, the interpretations, the infinite graphs and the collapsible pushdown work. The sieve automaton is unchanged, byte for byte -- the example it renders did not move. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The string side had it and the tree side did not, which put the expensive constructions on the wrong side of the line: a tree relation is where the cost lives, and `Reach` for a collapsible pushdown graph is exponential in the system's control states. It turned out to be a short walk. A tree automaton's compiled form is a sorted table of child pairs with one decision-diagram root each, so the payload is the pair keys plus the shared sub-DAG below those roots -- and `STORE.export` / `import_nodes` are generic over roots, so the diagram half needed no new code. A relation over a convolution alphabet too wide to enumerate still writes out in the size of its diagrams, exactly as on the string side. Two deliberate differences from the string presentation serializer: each automaton's payload is stored as raw bytes with a length prefix rather than as a JSON list of integers (which costs about four bytes of file per byte of data), and the JSON-has-no-tuples fix moved to `utils.misc` instead of being copied, since both engines need it now. Measured on the case it exists for: the collapsible `Reach` presentation is 22709 bytes, reloads in 0.01s against 0.5s to rebuild, and the reloaded relation is still reflexive, still contains the edge relation, and is still transitive. Skolem arithmetic round-trips and answers identical queries; a flipped payload byte is caught by the checksum. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The README carried eleven references and the documentation none, which is backwards -- a reader of the docs meets the constructions and has nowhere to follow them. `references.md` now holds the bibliography, grouped and annotated with what each work IS in this library, and the README keeps the four it leans on hardest plus a link. Citing where it was due exposed a gap: the overview had no section on any of the v4 material. It gains two -- interpretations (with the tree quotient's least description, and the ordinals as the payoff) and infinite graphs (the grid, the tree, Turing configuration graphs, and the collapsible pushdown graphs where reachability comes back). Each names its source at the point of use: Kuske & Weidner and Colcombet & Loeding on tree quotients, Delhomme on why `Ordinal` takes an exponent, Kartzow on the encoding and the run decomposition, Courcelle on the linear-time claim, Abu Zaid/Graedel/Reinhardt on advice. Three entries had no bibliographic details anywhere in the repository and could not be verified offline (Delhomme 2004, Oum & Seymour 2006, Colcombet & Loeding 2007); they carry author, title and venue but no DOI, pending a check. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
All four now carry a DOI, and one annotation was wrong.
Delhomme 2004 -> 10.1016/j.crma.2004.03.035 (CR Math. 339(1), 5-10)
Oum & Seymour -> 10.1016/j.jctb.2005.10.006 (JCTB 96(4), 514-528)
Colcombet & -> 10.2168/LMCS-3(2:4)2007 (LMCS 3(2:4), 1-36)
Loeding
Kuske & -> 10.1007/978-3-642-22993-0_39 (LNCS 6907, 424-435)
Weidner
Kartzow -> 10.2168/LMCS-9(1:12)2013 (issue was 9(1:12), not 9(1))
Oum & Seymour is the one that needed care. A publisher's abstract page claims
rank-width is not defined there, which would have made it the wrong citation
for `RankWidthClass`; Oum's own later survey settles it -- "Rank-width was
introduced by Oum and Seymour [71]", where [71] is exactly this paper. The
entry now says so, as the branch-width of the cut-rank function.
Colcombet and Loeding's role is stated more carefully too: the existence of
injective presentations for tree-automatic structures, which is what makes a
quotient interpretation well posed over trees, with Kuske and Weidner making it
effective.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release preparation for v4.0.0. The theme is that queries stop being
strings and structures start declaring their own vocabulary — and that the
package stops pretending some of its structures are more "built in" than
others.
The two proposals, checked first
The precompiled
.autstrartifacts are gone. Load vs build from scratch was0.017s/0.049s (Büchi ℕ), 0.023s/0.252s (Büchi ℤ), 0.022s/0.458s (MSO0), and the
built presentations are
equivalentto the loaded ones relation by relationwith identical state counts. They bought 0.03–0.44s while being a second source
of truth that could silently drift.
The "arithmetic interface" was already gone — deleted a release earlier by
the symbolic rewrite. What looked like a live second interface was the README's
quick start, which still imported
VariableETermand had been broken code eversince. The 85 lines that remained were the symbolic interface applied to ℤ.
Asking why it existed at all found the real asymmetry: nine structures
declared a
default_signature; the three inbuildincould not, because theywere plain functions returning a bare presentation with nowhere to hang one. So
the signature moved onto the presentations and the module became redundant.
autstr.buildinis dissolvedEvery structure the library ships is built in, so the name carved nothing:
buildin.presentationsautstr.arithmetic(Büchi ℕ/ℤ) +autstr.powerset(MSO0)buildin.tree_presentationsautstr.tree_arithmeticbuildin.automataautstr.utils.automata_tools(which already importedonefrom it)All three presentations gained a codec, each verified against the automata
rather than assumed: ℕ is LSB-first binary (checked against
A(x,y,z)), andMSO0 is a bitmask in canonical form — the universe rejects trailing zeros, so
{0}is1and ∅ is the empty word (checked againstSubsetover all subsetsof {0,1,2}). MSO0 thereby gains an interface it never had: union, intersection
and difference as
+,*,-, with solutions returned as Python sets.Three latent bugs, all found by writing examples
None of these had a failing test; each was exposed by a notebook cell.
including the shipped
Ordinal(2).iterate_languagebuilt each tape bystring-concatenating letters, which holds only while a letter is one
character; over a product alphabet a letter is a tuple, and
str()flattened('0','1')into text no codec could read. Membership worked throughout,which is why it went unseen.
no tuples, so the alphabet came back as a set of lists (unhashable) and the
padding symbol as a list.
away:
test_the_legacy_flat_payload_still_loadswas left building apresentation and querying it, touching no legacy payload at all. It now
constructs a v2 payload by hand.
Plus a
groups.ipynbcell labelled "has an element of order 4" that asked fororder 2 —
Z/2alone satisfied it.Serialization for the tree engine
The expensive constructions are the tree ones, so this was on the wrong side of
the line. A tree automaton's compiled form is a sorted table of child pairs with
one diagram root each, and
STORE.export/import_nodesare generic over roots,so the diagram half needed no new code. The collapsible
Reachpresentation is22709 bytes, reloads in 0.01s against 0.5s to rebuild, and the reloaded relation
is still reflexive, contains
E, and is transitive.Notebooks and documentation
Six notebooks, not five.
infinite_structuresis new — the integer grid, theregular tree, Turing configuration graphs, and level 2 collapsible pushdown
graphs, where the contrast is the point: both present computation as a graph and
both have a decidable first-order theory, but reachability is halting for one
and a relation of the other.
compositiongained interpretations, built aroundthe construction of ℤ from ℕ as a 2-dimensional quotient interpretation, with
the codec as the step that makes it legible.
The changelog moved to
CHANGELOG.md(and a docs page), leaving the README withwhat only it can say. References went the other way: four in the README, the
full annotated bibliography in the docs, cited at the point of use — which
exposed that the overview had no section on any v4 material. It now has two.
Checks
-W --keep-going, every notebook executed, allcross-references resolving.
Every bibliography entry carries a DOI, checked against the publisher or the
journal rather than supplied from memory. One annotation was wrong and is
fixed: a publisher's abstract page claims rank-width is not defined in Oum &
Seymour 2006, which would have made it the wrong citation for
RankWidthClass— Oum's own survey settles it ("Rank-width was introduced by Oum and Seymour
[71]", where [71] is that paper). Kartzow's issue was also off, 9(1:12) rather
than 9(1).
Generated with Claude Code