Skip to content

Commit ef01d14

Browse files
committed
refactor: tidy __reduce__ and make pickle bytes deterministic
- 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)
1 parent 5bb2c42 commit ef01d14

1 file changed

Lines changed: 21 additions & 17 deletions

File tree

src/py.rs

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,10 @@ fn scipy_delaunay_simplices(
294294
}
295295
Ok(Some(initial))
296296
}
297+
/// What `__reduce__` hands to pickle: the class and its constructor
298+
/// arguments `(vertices, simplices)`.
299+
type PickleReduction<'py> = (Bound<'py, PyAny>, (Bound<'py, PyAny>, Bound<'py, PyAny>));
300+
297301
/// Python-facing `Triangulation`, a thin argument-parsing wrapper around the
298302
/// pure-Rust [`Triangulation`] core. Drop-in compatible with
299303
/// `adaptive.learner.triangulation.Triangulation`.
@@ -600,28 +604,28 @@ impl PyTriangulation {
600604
/// adaptive expects triangulations to survive `deepcopy` and `pickle`,
601605
/// e.g. in `LearnerND._get_data`). Reconstructs through the constructor's
602606
/// `simplices` argument, restoring the exact simplex set without
603-
/// re-triangulating.
604-
fn __reduce__(&self, py: Python<'_>) -> PyResult<(Py<PyAny>, Py<PyAny>)> {
605-
let class = py.get_type::<PyTriangulation>();
606-
let vertices: Vec<Py<PyAny>> = self
607+
/// re-triangulating. The simplices are sorted so equal triangulations
608+
/// pickle to identical bytes.
609+
fn __reduce__<'py>(&self, py: Python<'py>) -> PyResult<PickleReduction<'py>> {
610+
let vertices: Vec<Py<PyTuple>> = self
607611
.core
608612
.vertices
609613
.iter()
610-
.map(|point| point_tuple(py, point).into())
614+
.map(|point| point_tuple(py, point))
611615
.collect();
612-
let simplices: Vec<Py<PyAny>> = self
613-
.core
614-
.simplices()
615-
.map(|simplex| simplex_tuple(py, simplex).into())
616+
let mut simplices: Vec<&Simplex> = self.core.simplices().collect();
617+
simplices.sort_unstable();
618+
let simplices: Vec<Py<PyTuple>> = simplices
619+
.into_iter()
620+
.map(|simplex| simplex_tuple(py, simplex))
616621
.collect();
617-
let args = PyTuple::new(
618-
py,
619-
[
620-
PyList::new(py, vertices)?.into_any(),
621-
PyList::new(py, simplices)?.into_any(),
622-
],
623-
)?;
624-
Ok((class.unbind().into_any(), args.unbind().into_any()))
622+
623+
let class = py.get_type::<PyTriangulation>().into_any();
624+
let args = (
625+
PyList::new(py, vertices)?.into_any(),
626+
PyList::new(py, simplices)?.into_any(),
627+
);
628+
Ok((class, args))
625629
}
626630

627631
fn add_simplex(&mut self, simplex: &Bound<'_, PyAny>) -> PyResult<()> {

0 commit comments

Comments
 (0)