Skip to content

v0.1.0

Choose a tag to compare

@jdsika jdsika released this 08 Sep 14:35
· 57 commits to main since this release
c21110b

First release with the determinism guarantee actually enforced end to end.

0.0.2 was extracted from an early prototype and had drifted behind the
hardened copy that upstream LinkML merged and reviewed. This release closes
that gap and adds a public primitive so tools that already canonicalize can
gain diff stability without replacing their own pipeline.

Fixed

  • The pyoxigraph fallback was not deterministic. Graphs pyoxigraph rejects
    (literal predicates, relative IRIs) fell back to a plain graph.serialize(),
    which labels blank nodes from run-local state, so output differed between
    processes — on exactly the inputs that need reproducibility most.
  • json-ld bypassed canonicalization entirely. Any format outside
    pyoxigraph's reach returned raw rdflib output, leaking run-local blank-node
    labels and rdflib's set-iteration node ordering.
  • Hash bases raised ValueError. rdflib relativizes against graph.base
    by naive string prefixing, which is not RFC-3986-correct: http://ex.org/d#a
    under base http://ex.org/d# was emitted as <a> and re-resolved to a
    different IRI, tripping the round-trip guard. Hash namespaces are the most
    common OWL style. The base is no longer carried over.
  • A relative base crashed the serializer. Relative bases are legal in
    rdflib; "Invalid base IRI" is now recovered from, like "Invalid prefix".
  • deterministic_json rejected non-string dict keys, raising TypeError
    on input that json.dumps serializes fine.
  • Unused prefix declarations are filtered to the namespaces actually used.

Added

wl_blank_node_labels() and wl_relabel_quads() expose the Weisfeiler-Lehman
relabelling step on its own. A tool that already runs RDFC-1.0 does not need
deterministic_turtle — that would replace its own prefix, base and fallback
handling. It can insert one step instead:

dataset.canonicalize(CanonicalizationAlgorithm.RDFC_1_0)
quads = wl_relabel_quads(list(dataset))   # <- the only added line

Format-agnostic, and measures the same: on a 20-shape graph, adding one class
changes 62 lines to 4.

wl_relabel_quads also remaps blank nodes used as graph names; leaving them
unmapped splits one node in two and silently breaks the dataset.

Changed

The diff-stability guarantee is now stated accurately: WL bounds relabelling to
roughly iterations hops around a change, not to the directly involved blank
nodes, and a node referenced from many subjects re-labels whenever any of those
references changes.

Canonicalization normalizations are documented: language-tag lowercasing
(case-insensitive per RDF 1.1), xsd:string identity, prefix filtering, and the
dropped base.

Testing

188 to 210 tests, on Python 3.10-3.13. Determinism is asserted across separate
interpreter processes for all seven formats — run-local blank-node ids are
stable within one process, so same-process tests cannot catch this bug class.
Every regression test was verified to fail without its fix.