Skip to content

Support pickle and deepcopy on Triangulation#11

Merged
basnijholt merged 3 commits into
mainfrom
fix/pickle-support
Jun 10, 2026
Merged

Support pickle and deepcopy on Triangulation#11
basnijholt merged 3 commits into
mainfrom
fix/pickle-support

Conversation

@basnijholt

Copy link
Copy Markdown
Member

Summary

Found via LearnerND integration: LearnerND._get_data() does deepcopy(self.__dict__) — which backs learner.save(), copy.deepcopy(learner), and pickling learners for distributed runners — and all of them crashed with TypeError: cannot pickle 'builtins.Triangulation' under the Rust backend. The Python reference is a plain class, so it pickles for free; this was a real drop-in gap (in v0.1.0 and v0.2.0).

Three pieces:

  • the constructor gains an optional simplices argument that restores an exact prior state via from_simplices instead of triangulating — no scipy, no re-insertion, so hand-modified simplex sets and unconnected vertices survive the round-trip
  • __reduce__ serializes (vertices, simplices) and reconstructs through that argument; one implementation covers pickle, copy.copy, and copy.deepcopy
  • the pyclass now declares module = "adaptive_triangulation._rust" so pickle can resolve the class by reference (it previously advertised itself as builtins.Triangulation)

Tests

  • pickle and deepcopy round-trips asserting vertices, simplices, vertex_to_simplices, and reference_invariant() — including a hand-deleted simplex (exact-state restore, not re-triangulation) and an unconnected duplicate vertex
  • clone independence (mutating the copy doesn't touch the original)
  • end-to-end LearnerND: 30 points, then copy.deepcopy(learner) and a _get_data()/_set_data() cycle, with both restored learners continuing to learn afterwards

115 pytest + 18 cargo tests pass; pre-commit clean. Suggest tagging v0.2.1 once merged.

adaptive's LearnerND does deepcopy(self.__dict__) in _get_data, which
backs learner.save(), copy.deepcopy(learner), and pickling learners
for distributed runners - all of which crashed with
'cannot pickle builtins.Triangulation' under the Rust backend (the
reference is a plain Python class, so it pickles for free).

- the constructor gains an optional simplices argument that restores
  an exact prior state via from_simplices instead of triangulating
  (no scipy, no re-insertion; hand-modified simplex sets and
  unconnected vertices survive)
- __reduce__ serializes (vertices, simplices) and reconstructs through
  that argument; this covers pickle, copy.copy, and copy.deepcopy
- the pyclass declares module = 'adaptive_triangulation._rust' so
  pickle can resolve the class by reference (it previously advertised
  itself as builtins.Triangulation)

Tested: pickle/deepcopy round-trips (including a hand-deleted simplex
and an unconnected duplicate vertex), clone independence, and an
end-to-end LearnerND _get_data/_set_data + deepcopy cycle that keeps
learning afterwards.
- return Bound values directly instead of unbind/into_any noise, with
  a PickleReduction type alias for the pickle protocol shape
- sort the simplices before serializing so equal triangulations pickle
  to identical bytes (the hash-map iteration order is arbitrary)
@basnijholt basnijholt merged commit e38be20 into main Jun 10, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant