v0.8 public API inventory
- Status: Source-finalized on 2026-08-17 with R1–R9 and the post-R9
COPYINGdistribution correction complete and accepted - Historical baseline: v0.6.3
- Previous contract: v0.7.0
- Target: v0.8.0
- v0.8 audit: issue #32
- Policy: API lifecycle and compatibility
- Plan: completed v0.8 development plan
- Decisions: ADR 0004, ADR 0005, ADR 0006, ADR 0007, ADR 0008, ADR 0009, ADR 0010, ADR 0011, ADR 0012, ADR 0013, ADR 0014, ADR 0015, ADR 0016, and ADR 0017
This inventory is the authoritative v0.8 lifecycle contract for public imports, return routes, record schemas, defaults, and scientific semantics. It has been checked against the current source, tests, documentation, executed notebooks, distribution configuration, GitHub Actions workflows, and downstream-shaped regression assets. The exact final source commit is frozen after release finalization and independent review; issue #33 must verify that artifacts built from that exact commit preserve this contract before the public tag is created.
The historical v0.6.3 baseline is retained below because it explains the v0.7 migration. It is not a list of current imports. Current v0.8 exports, signatures, lifecycle classifications, internal boundaries, and removals begin at Current v0.8 contract.
How to maintain this inventory
For every issue that changes public behavior:
- update the relevant row or section in the same change;
- distinguish historical behavior from the current implemented state;
- record aliases, deprecations, and their removal releases;
- include defaults, result fields, record keys, units, and periodic conventions when they carry scientific meaning;
- leave uncertain new surfaces provisional rather than omitting them;
- do not mark a surface stable until its tests and documentation define the contract clearly.
Issue #32 finalizes the v0.8 classifications below. Release review must verify that
__all__, docstrings, guides, reference pages, migration notes, and this
inventory remain synchronized.
Factual v0.6.3 baseline
This section records behavior observed in the v0.6.3 source tree before any
v0.7 public implementation. It was checked against package __all__ values,
call signatures, generated API reference pages, user guides, source notebooks,
and the existing tests. The surviving canonical contracts derived from those
characterizations live in
tests/forward/common/test_forward_api_contract.py and
tests/inverse/separator/test_api_contract.py. Assertions that existed only
for the compatibility routes removed by issue #28 are not retained.
“Baseline” does not make every current convenience stable forever. It identifies what the v0.7 compatibility routes must preserve deliberately and prevents a module move or result-default change from silently changing established behavior.
v0.6.3 public namespaces, documented module routes, and __all__
The exact membership and absence of duplicates in the three package-root lists
and the documented pyvoro2.viz3d list are pinned by the baseline tests; export
ordering is not treated as a compatibility promise. The grouped contents below
are exhaustive for those lists. Documented direct module routes are recorded
separately so a later module move does not preserve only package-root imports.
pyvoro2 — 62 exports
| Group | Exact current exports |
|---|---|
| Domains and operations | Box, OrthorhombicCell, PeriodicCell, compute, locate, ghost_cells |
| Tessellation diagnostics | TessellationDiagnostics, TessellationIssue, TessellationError, analyze_tessellation, validate_tessellation |
| Normalization diagnostics | NormalizationDiagnostics, NormalizationIssue, NormalizationError, validate_normalized_topology |
| Duplicate handling | DuplicatePair, DuplicateError, duplicate_check |
| Geometry annotation | annotate_face_properties |
| Normalization | NormalizedVertices, NormalizedTopology, normalize_vertices, normalize_edges_faces, normalize_topology |
| Weight/radius transforms | radii_to_weights, weights_to_radii |
| Package metadata/namespaces | __version__, planar |
| Historical inverse surface | the exact 34-name list under Historical v0.7 top-level inverse compatibility set |
The weight/radius transforms are implemented in pyvoro2.powerfit.transforms
in v0.6.3 but are listed separately because ADR 0004 assigns them neutral
ownership in v0.7.
pyvoro2.planar — 25 exports
Box
RectangularCell
PlanarComputeResult
compute
locate
ghost_cells
DuplicatePair
DuplicateError
duplicate_check
annotate_edge_properties
plot_tessellation
TessellationIssue
TessellationDiagnostics
TessellationError
analyze_tessellation
validate_tessellation
NormalizedVertices
NormalizedTopology
normalize_vertices
normalize_edges
normalize_topology
NormalizationIssue
NormalizationDiagnostics
NormalizationError
validate_normalized_topology
pyvoro2.powerfit — 42 exports
PairBisectorConstraints
resolve_pair_bisector_constraints
SquaredLoss
HuberLoss
Interval
FixedValue
SoftIntervalPenalty
ExponentialBoundaryPenalty
ReciprocalBoundaryPenalty
L2Regularization
FitModel
AlgebraicEdgeDiagnostics
ConstraintGraphDiagnostics
ConnectivityDiagnostics
ConnectivityDiagnosticsError
HardConstraintConflictTerm
HardConstraintConflict
PowerFitBounds
PowerFitPredictions
PowerFitObjectiveBreakdown
PowerFitProblem
PowerWeightFitResult
build_power_fit_problem
build_power_fit_result
RealizedPairDiagnostics
UnaccountedRealizedPair
UnaccountedRealizedPairError
build_fit_report
build_realized_report
build_active_set_report
dumps_report_json
write_report_json
ActiveSetOptions
ActiveSetIteration
ActiveSetPathSummary
PairConstraintDiagnostics
SelfConsistentPowerFitResult
fit_power_weights
match_realized_pairs
solve_self_consistent_power_weights
radii_to_weights
weights_to_radii
PowerFitBounds, PowerFitPredictions, PowerFitObjectiveBreakdown,
PowerFitProblem, build_power_fit_problem, and build_power_fit_result are
public from pyvoro2.powerfit but are not top-level pyvoro2 exports. v0.7
did not broaden the historical top-level surface while preserving the old
package.
pyvoro2.viz3d — 9 exports
VizStyle
make_view
add_axes
add_sites
add_vertices
add_domain_wireframe
add_cell_wireframe
add_tessellation_wireframe
view_tessellation
pyvoro2.viz2d has no explicit __all__; its documented public function is
plot_tessellation, which is the same object re-exported by
pyvoro2.planar. Importing either visualization module does not require its
optional rendering dependency until a rendering function is called.
Documented direct module routes
The generated v0.6.3 reference documents these direct module routes in addition to the package-root exports:
pyvoro2.api,domains,diagnostics,duplicates,edge_properties,face_properties,normalize,validation,viz2d, andviz3d;pyvoro2.planar.api,domains,diagnostics,normalize,result, andvalidation; andpyvoro2.powerfit.active,constraints,model,realize,report, andsolver.
Where a direct-module object is also exported from its package root, v0.6.3
uses the same object rather than a wrapper. The powerfit.model reference also
documents ScalarMismatch, HardConstraint, and ScalarPenalty directly;
these three base classes are not pyvoro2.powerfit package exports. The
documented submodule __all__ values are:
| Module | Exact __all__ |
|---|---|
pyvoro2.powerfit.report |
build_fit_report, build_realized_report, build_active_set_report, dumps_report_json, write_report_json |
pyvoro2.powerfit.solver |
fit_power_weights, ConnectivityDiagnosticsError |
Private underscore-prefixed helpers rendered nowhere in the public reference are not part of this baseline.
v0.6.3 forward signatures and defaults
The domain constructors are:
pyvoro2.Box(bounds)
pyvoro2.OrthorhombicCell(bounds, periodic=(True, True, True))
pyvoro2.PeriodicCell(vectors, origin=(0.0, 0.0, 0.0))
pyvoro2.planar.Box(bounds)
pyvoro2.planar.RectangularCell(bounds, periodic=(True, True))
Documented domain conveniences, with annotations omitted, are:
pyvoro2.Box.from_points(points, padding=2.0)
pyvoro2.OrthorhombicCell.lattice_vectors
pyvoro2.OrthorhombicCell.remap_cart(
points, *, return_shifts=False, eps=None,
)
pyvoro2.PeriodicCell.from_params(
bx, bxy, by, bxz, byz, bz, *, origin=(0.0, 0.0, 0.0),
)
pyvoro2.PeriodicCell.to_internal_params()
pyvoro2.PeriodicCell.cart_to_internal(points)
pyvoro2.PeriodicCell.internal_to_cart(points_internal)
pyvoro2.PeriodicCell.remap_internal(
points_internal, *, return_shifts=False, eps=None,
)
pyvoro2.PeriodicCell.wrap_internal(points_internal)
pyvoro2.PeriodicCell.remap_cart(
points, *, return_shifts=False, eps=None,
)
pyvoro2.planar.Box.from_points(points, padding=2.0)
pyvoro2.planar.RectangularCell.lattice_vectors
pyvoro2.planar.RectangularCell.remap_cart(
points, *, return_shifts=False, eps=None,
)
The exact spatial operation signatures, with annotations omitted here for readability, are:
compute(
points, *, domain, ids=None,
duplicate_check='off', duplicate_threshold=1e-5,
duplicate_wrap=True, duplicate_max_pairs=10,
block_size=None, blocks=None, init_mem=8,
mode='standard', radii=None,
return_vertices=True, return_adjacency=True, return_faces=True,
return_face_shifts=False, face_shift_search=2, include_empty=False,
validate_face_shifts=True, repair_face_shifts=False, face_shift_tol=None,
return_diagnostics=False, tessellation_check='none',
tessellation_require_reciprocity=None,
tessellation_volume_tol_rel=1e-8,
tessellation_volume_tol_abs=1e-12,
tessellation_plane_offset_tol=None,
tessellation_plane_angle_tol=None,
)
locate(
points, queries, *, domain, ids=None,
duplicate_check='off', duplicate_threshold=1e-5,
duplicate_wrap=True, duplicate_max_pairs=10,
block_size=None, blocks=None, init_mem=8,
mode='standard', radii=None, return_owner_position=False,
)
ghost_cells(
points, queries, *, domain, ids=None,
duplicate_check='off', duplicate_threshold=1e-5,
duplicate_wrap=True, duplicate_max_pairs=10,
block_size=None, blocks=None, init_mem=8,
mode='standard', radii=None, ghost_radius=None,
return_vertices=True, return_adjacency=True, return_faces=True,
include_empty=True,
)
The exact planar operation signatures are:
compute(
points, *, domain, ids=None,
duplicate_check='off', duplicate_threshold=1e-5,
duplicate_wrap=True, duplicate_max_pairs=10,
block_size=None, blocks=None, init_mem=8,
mode='standard', radii=None,
return_vertices=True, return_adjacency=True, return_edges=True,
return_edge_shifts=False, edge_shift_search=2, include_empty=False,
validate_edge_shifts=True, repair_edge_shifts=False, edge_shift_tol=None,
return_diagnostics=False, return_result=False, normalize='none',
normalization_tol=None, tessellation_check='none',
tessellation_require_reciprocity=None,
tessellation_area_tol_rel=1e-8,
tessellation_area_tol_abs=1e-12,
tessellation_line_offset_tol=None,
tessellation_line_angle_tol=None,
)
locate(
points, queries, *, domain, ids=None,
duplicate_check='off', duplicate_threshold=1e-5,
duplicate_wrap=True, duplicate_max_pairs=10,
block_size=None, blocks=None, init_mem=8,
mode='standard', radii=None, return_owner_position=False,
)
ghost_cells(
points, queries, *, domain, ids=None,
duplicate_check='off', duplicate_threshold=1e-5,
duplicate_wrap=True, duplicate_max_pairs=10,
block_size=None, blocks=None, init_mem=8,
mode='standard', radii=None, ghost_radius=None,
return_vertices=True, return_adjacency=True, return_edges=True,
return_edge_shifts=False, edge_shift_search=2, include_empty=True,
validate_edge_shifts=True, repair_edge_shifts=False, edge_shift_tol=None,
)
radii are required when mode='power'; planar and spatial power ghost calls
also require ghost_radius. There is no v0.6.3 forward weights= argument.
All six current forward operations prepare inserted generators centrally.
Non-periodic coordinates use the half-open interval [lo, hi) and periodic
coordinates are remapped to their primary representation before native
dispatch. This includes each temporary ghost_cells query; locate queries
themselves are not inserted and retain their existing query semantics.
The frozen backend-safety floor is squared distance 1e-10 (distance 1e-5),
inclusive. It is always active, uses certified minimum-image geometry for
periodic pairs, and is not controlled by the public duplicate mode, threshold,
wrap flag, or pair-report limit. The existing off/warn/raise policy
applies only to safe pairs strictly below a user threshold above 1e-5;
duplicate_wrap=False changes only that optional metric.
DuplicatePair(i, j, distance) is unchanged. DuplicateError remains a
ValueError with compatible positional args, .pairs, .threshold, and
string behavior. It additionally exposes kind, safety_distance_squared,
safety_distance, user_threshold, minimum_image_used,
optional_wrap_used, truncated, operation, and external_ids. Mandatory
errors use .threshold == 1e-5; optional errors retain the configured user
threshold.
Supporting forward call defaults are also part of the observed surface:
| Call | Current optional parameters and defaults |
|---|---|
spatial analyze_tessellation(cells, domain, ...) |
expected_ids=None, mode=None, volume_tol_rel=1e-8, volume_tol_abs=1e-12, check_reciprocity=True, check_plane_mismatch=True, plane_offset_tol=None, plane_angle_tol=None, mark_faces=True |
spatial validate_tessellation(cells, domain, ...) |
expected_ids=None, mode=None, level='basic', require_reciprocity=None, volume tolerances 1e-8/1e-12, plane tolerances None, mark_faces=None |
planar analyze_tessellation(cells, domain, ...) |
expected_ids=None, mode=None, area_tol_rel=1e-8, area_tol_abs=1e-12, check_reciprocity=True, check_line_mismatch=True, line_offset_tol=None, line_angle_tol=None, mark_edges=True |
planar validate_tessellation(cells, domain, ...) |
expected_ids=None, mode=None, level='basic', require_reciprocity=None, area tolerances 1e-8/1e-12, line tolerances None, mark_edges=None |
spatial/planar duplicate_check(points, ...) |
threshold=1e-5, domain=None, wrap=True, mode='raise', max_pairs=10 |
spatial normalize_vertices(cells, ...) |
required domain; tol=None, require_face_shifts=True, copy_cells=True |
spatial normalize_edges_faces(normalized_vertices, ...) |
required domain; tol=None, copy_cells=True |
spatial normalize_topology(cells, ...) |
required domain; tol=None, require_face_shifts=True, copy_cells=True |
planar normalize_vertices(cells, ...) |
required domain; tol=None, require_edge_shifts=True, copy_cells=True |
planar normalize_edges(normalized_vertices, ...) |
required domain; tol=None, copy_cells=True |
planar normalize_topology(cells, ...) |
required domain; tol=None, require_edge_shifts=True, copy_cells=True |
spatial validate_normalized_topology(normalized, domain, ...) |
level='basic', all four checks enabled, max_examples=10 |
planar validate_normalized_topology(normalized, domain, ...) |
level='basic', all four checks enabled, max_examples=10 |
weights_to_radii(weights, ...) |
r_min=0.0, weight_shift=None; returns (radii, applied_shift) |
radii_to_weights(radii) |
no optional parameters; returns squared radii |
v0.6.3 visualization signatures and defaults
The documented planar visualization entry point is:
pyvoro2.viz2d.plot_tessellation(
cells, *, ax=None, domain=None, show_sites=False, annotate_ids=False,
)
The optional spatial visualization surface is:
VizStyle(
background='0xffffff', site_color='0x777777', site_radius=0.093,
site_label_color='0x000000', site_label_background='0xffffff',
site_label_font_size=8, edge_color='0x1f77b4', edge_line_width=2.5,
domain_color='0x000000', domain_line_width=2.5,
vertex_color='0xff7f0e', vertex_radius=0.04,
vertex_label_color='0x000000', vertex_label_background='0xffffff',
vertex_label_font_size=7, axes_line_width=2.0,
axes_label_font_size=12, axes_color_x='0xff0000',
axes_color_y='0x00aa00', axes_color_z='0x0000ff',
)
make_view(*, width=640, height=480, background='0xffffff')
add_axes(
view, *, origin=(0.0, 0.0, 0.0), length=1.0, line_width=2.0,
label_font_size=12, color_x='0xff0000', color_y='0x00aa00',
color_z='0x0000ff',
)
add_sites(
view, points, *, labels=None, color='0x777777', radius=0.093,
label_color='0x000000', label_background='0xffffff',
label_font_size=8,
)
add_vertices(
view, vertices, *, labels=None, color='0xff7f0e', radius=0.04,
label_color='0x000000', label_background='0xffffff',
label_font_size=7,
)
add_domain_wireframe(view, domain, *, color='0x000000', line_width=2.5)
add_cell_wireframe(view, cell, *, color='0x1f77b4', line_width=2.5)
add_tessellation_wireframe(
view, cells, *, color='0x1f77b4', line_width=2.5, cell_ids=None,
)
view_tessellation(
cells, *, domain=None, show_sites=True, show_site_labels=True,
max_site_labels=200, show_domain=True, show_axes=True, axes_length=None,
wrap_cells=False, cell_ids=None, show_vertices=False,
show_vertex_labels='auto', max_vertex_labels=200, style=None,
width=640, height=480, zoom=True,
)
Characterized v0.6.3 forward return matrix
| Namespace/request | v0.6.3 return |
|---|---|
| spatial default | raw list[dict] |
spatial return_diagnostics=True |
(cells, TessellationDiagnostics) tuple |
spatial tessellation_check='diagnose'|'warn'|'raise' without return_diagnostics |
raw list after the requested check; diagnostics are not returned |
| planar default | raw list[dict] |
planar return_diagnostics=True, with no result/normalization request |
(cells, TessellationDiagnostics) tuple |
planar return_result=True |
PlanarComputeResult |
planar normalize='vertices'|'topology' |
PlanarComputeResult, even when return_result=False |
planar result/normalization plus return_diagnostics=True |
one PlanarComputeResult carrying diagnostics, never a tuple |
planar tessellation_check='diagnose'|'warn'|'raise' without a result or return_diagnostics request |
raw list after the requested check; diagnostics are not returned |
PlanarComputeResult is a frozen, slotted outer dataclass with fields
cells, tessellation_diagnostics=None, normalized_vertices=None, and
normalized_topology=None. Its exact documented conveniences are the
has_tessellation_diagnostics, has_normalized_vertices,
has_normalized_topology, global_vertices, and global_edges properties and
the require_tessellation_diagnostics(), require_normalized_vertices(), and
require_normalized_topology() methods. Its nested raw cell records remain
mutable. There is no spatial counterpart in v0.6.3.
Raw record schemas and ordering retained in v0.8
| Operation | Required/base keys | Optional keys |
|---|---|---|
spatial compute cell |
id, volume, site |
vertices, adjacency, faces; inserted hidden cells also have empty=True |
| spatial face | adjacent_cell, vertices |
adjacent_shift; diagnostic flags orphan, reciprocal_missing, reciprocal_mismatch; annotation fields centroid, normal, area, other_site, intersection, intersection_inside, intersection_centroid_dist, intersection_edge_min_dist |
planar compute cell |
id, area, site |
vertices, adjacency, edges; inserted hidden cells also have empty=True |
| planar edge | adjacent_cell, vertices |
adjacent_shift; diagnostic flags orphan, reciprocal_missing, reciprocal_mismatch; annotation fields midpoint, tangent, normal, length, other_site |
spatial locate |
found, owner_id arrays |
owner_pos when requested |
planar locate |
found, owner_id arrays |
owner_pos when requested |
| spatial ghost cell | normal spatial cell keys plus id=-1, empty, query_index, query |
requested geometry keys |
| planar ghost cell | normal planar cell keys plus id=-1, empty, query_index |
requested geometry keys; unlike spatial ghost records, no query key |
Geometry keys are omitted, not set to None, when the corresponding
return_* switch is false. Wall neighbors retain negative backend IDs.
Nonnegative cell and neighbor IDs are remapped to user ids after computation;
locate.owner_id is remapped in the same way. site remains the generator
coordinate used by the computation and is not replaced by the external ID.
Raw compute output follows backend cell iteration. The characterized ordinary
case returns cells in input/internal-ID order, but consumers should not treat a
raw list position as the public site lookup mechanism. If hidden cells are
actually inserted by include_empty=True, the wrapper sorts by internal site
index before remapping external IDs, yielding a full input-aligned list. With
include_empty=False, hidden power cells are absent and the list is shorter.
For a hidden power cell, include_empty=True inserts id, empty=True, zero
volume/area, site, and empty lists only for requested geometry. Nonempty
records do not gain empty=False. Spatial compute applies its reinsertion
helper whenever include_empty=True; planar compute invokes it only in power
mode. Standard diagrams ordinarily have no hidden cells, so this asymmetry has
no normal observable effect but is part of the implementation baseline.
Spatial normalized result fields are NormalizedVertices(global_vertices,
cells) and NormalizedTopology(global_vertices, global_edges, global_faces,
cells). Planar fields are NormalizedVertices(global_vertices, cells) and
NormalizedTopology(global_vertices, global_edges, cells).
Forward diagnostic fields retained in v0.8
| Type | Exact dataclass fields |
|---|---|
spatial TessellationIssue |
code, severity, message, examples |
spatial TessellationDiagnostics |
domain_volume, sum_cell_volume, volume_ratio, volume_gap, volume_overlap, n_sites_expected, n_cells_returned, missing_ids, empty_ids, face_shift_available, reciprocity_checked, n_faces_total, n_faces_orphan, n_faces_mismatched, issues, ok_volume, ok_reciprocity, ok |
planar TessellationIssue |
code, severity, message, examples |
planar TessellationDiagnostics |
domain_area, sum_cell_area, area_ratio, area_gap, area_overlap, n_sites_expected, n_cells_returned, missing_ids, empty_ids, edge_shift_available, reciprocity_checked, n_edges_total, n_edges_orphan, n_edges_mismatched, issues, ok_area, ok_reciprocity, ok |
spatial NormalizationDiagnostics |
n_cells, n_global_vertices, n_global_edges, n_global_faces, is_periodic_domain, fully_periodic_domain, has_wall_faces, n_vertex_face_shift_mismatch, n_face_vertex_set_mismatch, n_vertices_low_incidence, n_edges_low_incidence, n_cells_bad_euler, issues, ok_vertex_face_shift, ok_face_vertex_sets, ok_incidence, ok_euler, ok |
planar NormalizationDiagnostics |
n_cells, n_global_vertices, n_global_edges, is_periodic_domain, fully_periodic_domain, has_wall_edges, n_vertex_edge_shift_mismatch, n_edge_vertex_set_mismatch, n_vertices_low_incidence, n_cells_bad_polygon, issues, ok_vertex_edge_shift, ok_edge_vertex_sets, ok_incidence, ok_polygon, ok |
The v0.8 diagnostic meaning is fixed by
ADR 0016.
For both dimensions, overall tessellation and normalized-topology ok is true
exactly when no error-severity issue exists and every call-/mode-required
invariant passes. Warning/info-only findings do not fail overall ok, although
a descriptive subcheck such as ok_reciprocity, ok_polygon, or ok_euler
may remain false.
With expected_ids, standard-mode absence is MISSING_IDS/error; power-mode
absence is HIDDEN_IDS/info and the ID appears in both missing_ids and
empty_ids; absence with mode=None is MISSING_IDS/warning. Invalid raw cell
measures use MISSING_CELL_MEASURE, INVALID_CELL_MEASURE,
NONFINITE_CELL_MEASURE, NEGATIVE_CELL_MEASURE, or
EMPTY_CELL_NONZERO_MEASURE, all at error severity. Validated measures use a
stable finite sum, and valid-measure closure failures retain GAP/OVERLAP as
errors.
Public analyze_tessellation(..., check_reciprocity=True) requires the
requested periodic reciprocity check. The existing validation/compute
reciprocity switches choose required errors versus optional info/warnings
privately; no public severity-policy object is added. Strict validation,
compute warning, and compute raising consume the final diag.ok without
reconstructing it from subchecks. Marked analyses reset all analyzer-owned
boundary flags before each pass; disabled marking leaves caller records
untouched.
v0.6.3 inverse signatures and constructor defaults
The high-level and advanced call defaults are:
| Call | Required inputs; current keyword defaults |
|---|---|
resolve_pair_bisector_constraints(points, constraints, ...) |
measurement='fraction', domain=None, ids=None, index_mode='index', image='nearest', image_search=1, confidence=None, allow_empty=False |
fit_power_weights(points, constraints, ...) |
resolver defaults above except allow_empty is internal; model=None, r_min=0.0, weight_shift=None, solver='auto', max_iter=2000, rho=1.0, tol_abs=1e-6, tol_rel=1e-5, connectivity_check='warn' |
build_power_fit_problem(constraints, ...) |
model=None |
build_power_fit_result(problem, weights, ...) |
solver='external', status='optimal', status_detail=None, converged=True, n_iter=0, warnings=(), canonicalize_gauge=True, r_min=0.0, weight_shift=None |
match_realized_pairs(points, ...) |
required keyword-only domain, radii, constraints; return_boundary_measure=False, return_cells=False, return_tessellation_diagnostics=False, tessellation_check='diagnose', unaccounted_pair_check='diagnose' |
solve_self_consistent_power_weights(points, constraints, ...) |
required keyword-only domain; resolver defaults; model=None, active0=None, options=None, r_min=0.0, weight_shift=None, fit_solver='auto', fit_max_iter=2000, fit_rho=1.0, fit_tol_abs=1e-6, fit_tol_rel=1e-5, all four return switches False, tessellation_check='diagnose', connectivity_check='warn', unaccounted_pair_check='warn' |
build_fit_report(result, constraints, ...) |
use_ids=False |
build_realized_report(diagnostics, constraints, ...) |
use_ids=False |
build_active_set_report(result, ...) |
use_ids=False |
dumps_report_json(report, ...) |
indent=2, sort_keys=False |
write_report_json(report, path, ...) |
indent=2, sort_keys=False |
The objective/model constructors are:
SquaredLoss()
HuberLoss(delta=1.0)
Interval(lower, upper)
FixedValue(value)
SoftIntervalPenalty(lower, upper, strength)
ExponentialBoundaryPenalty(
lower=0.0, upper=1.0, margin=0.02,
strength=1.0, tau=0.01,
)
ReciprocalBoundaryPenalty(
lower=0.0, upper=1.0, margin=0.05,
strength=1.0, epsilon=1e-6,
)
L2Regularization(strength=0.0, reference=None)
FitModel(
mismatch=SquaredLoss(), feasible=None, penalties=(),
regularization=L2Regularization(),
)
ActiveSetOptions(
add_after=1, drop_after=2, relax=1.0, max_iter=25,
cycle_window=8, weight_step_tol=1e-8,
)
Inverse result fields retained under canonical v0.8 names
The primary containers have the following exact dataclass fields. The parenthetical names are historical v0.6.3/v0.7 identities and are absent from the v0.8 namespace:
| Type | Fields |
|---|---|
SeparatorObservations (historical PairBisectorConstraints) |
n_points, i, j, shifts, target, confidence, measurement, distance, distance2, delta, target_fraction, target_position, input_index, explicit_shift, ids, warnings |
SeparatorFitProblem (historical PowerFitProblem) |
constraints, model, alpha, beta, z_obs, edge_weight, regularization_strength, regularization_reference, offset_identifying_constraint_mask, bounds, connectivity, hard_feasible, hard_conflict |
SeparatorFitResult (historical PowerWeightFitResult) |
status, hard_feasible, weights, radii, weight_shift, measurement, target, predicted, predicted_fraction, predicted_position, residuals, rms_residual, max_residual, used_shifts, solver, n_iter, converged, conflict, warnings, linear_backend, status_detail, connectivity, edge_diagnostics, objective_breakdown |
RealizedPairDiagnostics |
realized, unrealized, realized_same_shift, realized_other_shift, realized_shifts, endpoint_i_empty, endpoint_j_empty, boundary_measure, cells, tessellation_diagnostics, unaccounted_pairs, warnings |
PairConstraintDiagnostics |
site_i, site_j, shift, target, confidence, predicted, predicted_fraction, predicted_position, residuals, active, realized, realized_same_shift, realized_other_shift, realized_shifts, endpoint_i_empty, endpoint_j_empty, boundary_measure, toggle_count, realized_toggle_count, first_realized_iter, last_realized_iter, marginal, status |
SelfConsistentPowerFitResult |
constraints, fit, realized, diagnostics, active_mask, n_outer_iter, converged, termination, cycle_length, marginal_constraints, rms_residual_all, max_residual_all, tessellation_diagnostics, history, path_summary, warnings, connectivity |
For the experimental active-set result, realized, diagnostics,
rms_residual_all, and max_residual_all are optional. They are all present
for one coherent final weighted state and all None when the final accepted
fit has no usable weights. tessellation_diagnostics remains optional even for
an available state because its analysis is requested separately. The additive
computed properties final_state_available,
final_state_unavailable_reason, and final_refit_converged expose final-layer
availability and final inner-fit convergence without adding stored dataclass
fields. Outer converged remains true exactly for
termination == 'self_consistent'.
SeparatorFitProblem.offset_identifying_constraint_mask retains its historical
field name. It is the model-coupling mask used to decompose solver subproblems:
positive-confidence rows are
included, and hard restrictions or positive-strength penalties make their
affected rows part of the same numerical subproblem. Zero-strength penalties
are mathematically absent and do not affect this mask. It is not the
informative observation mask and does not claim data identification or unique
objective selection.
Supporting fields are exact as follows:
| Type | Fields |
|---|---|
PowerFitBounds |
measurement_lower, measurement_upper, difference_lower, difference_upper |
PowerFitPredictions |
difference, fraction, position, measurement |
PowerFitObjectiveBreakdown |
total, mismatch, penalties_total, penalty_terms, regularization, hard_constraints_satisfied, hard_max_violation, hard_max_tolerance |
SeparatorSolverTerminationView |
status, status_detail, solver, linear_backend, n_iter, converged, hard_feasible, conflict, warnings |
AlgebraicEdgeDiagnostics |
alpha, beta, z_obs, z_fit, residual, edge_weight, weighted_l2, weighted_rmse, rmse, mae |
ConstraintGraphDiagnostics |
n_points, n_constraints, n_edges, isolated_points, connected_components, fully_connected; property n_components |
ConnectivityDiagnostics |
unconstrained_points, candidate_graph, effective_graph, active_graph=None, active_effective_graph=None, candidate_offsets_identified_by_data=False, active_offsets_identified_by_data=None, offsets_identified_in_objective=False, gauge_policy='', messages=() |
HardConstraintConflictTerm |
constraint_index, site_i, site_j, relation, bound_value |
HardConstraintConflict |
component_nodes, cycle_nodes, terms, message; property constraint_indices |
UnaccountedRealizedPair |
site_i, site_j, realized_shifts, boundary_measure=None |
ActiveSetIteration |
iteration, n_active, n_realized, n_added, n_removed, rms_residual_all, max_residual_all, weight_step_norm, n_active_fit, fit_active_graph_n_components, fit_active_effective_graph_n_components, fit_active_offsets_identified_by_data, n_unaccounted_pairs |
ActiveSetPathSummary |
n_iterations, ever_fit_active_graph_disconnected, ever_fit_active_effective_graph_disconnected, ever_fit_active_offsets_unidentified_by_data, ever_unaccounted_pairs, max_fit_active_graph_components, max_fit_active_effective_graph_components, max_n_unaccounted_pairs, first_fit_active_graph_disconnected_iter, first_fit_active_effective_graph_disconnected_iter, first_unaccounted_pairs_iter |
The public inverse dataclasses are generally frozen and slotted.
SeparatorObservations, SeparatorFitProblem, PowerFitBounds,
PowerFitPredictions, and AlgebraicEdgeDiagnostics copy their owned arrays
into read-only arrays. SeparatorFitResult, realization diagnostics, and
active-set result containers do not deep-freeze every contained array; callers
must not infer deep immutability from the frozen outer dataclass.
SeparatorObservations retains the exact public field list above and its
existing public constructor signature. Direct construction validates an exact
dimension of two or three, point count, integer endpoint/shift/input-index
categories and ranges, aligned row shapes, distinct endpoints, unique
non-negative input indices, finite non-negative confidence, finite nonzero
connector geometry, IDs, and warnings. It recomputes distance values from
delta and both measurement forms from the canonical target and distance.
Finite redundant values are accepted only under
np.allclose(..., rtol=8*np.finfo(np.float64).eps, atol=0.0) and are replaced
by the recomputed binary64 values. This tolerance checks internal constructor
consistency; it is not source equivalence.
Issue #13 preserves those exact dataclass fields and adds the following provisional, non-copying access paths:
| Layer | Access path | Existing data exposed |
|---|---|---|
| Fitted state | SeparatorFitResult.state |
weights as mathematical_weights, backend radii, and compatibility weight_shift as global_representation_shift |
| Identification | SeparatorFitResult.identification |
informative positive-confidence components, global-gauge identification (False for separator data), observational component-offset identification, conservative objective selection, component-alignment policy, sites isolated in the informative graph as unconstrained_sites, and connectivity (whose compatibility unconstrained_points remains candidate-based) |
| Observations | SeparatorFitResult.observation_view(observations) |
measurement targets, confidence from the supplied resolved observations, predictions in all existing forms, residuals/summaries, and requested shifts; the supplied set must satisfy the exact observation/source association policy below |
| Objective | SeparatorFitResult.objective |
existing objective_breakdown object |
| Algebraic diagnostics | SeparatorFitResult.algebraic |
existing edge_diagnostics and connectivity objects; no graph-operator representation |
| Fixed solver termination | SeparatorFitResult.solver_termination |
status/detail, solver method, linear backend, iterations, convergence, hard feasibility, conflict, and warnings |
| Requested-image matching | RealizedPairDiagnostics.requested_image_matching |
any/same-shift/other-shift realization, realized shifts, and unrealized indices |
| Realized geometry | RealizedPairDiagnostics.geometry |
empty endpoints, optional boundary measure/cells/tessellation diagnostics, unaccounted pairs, and warnings |
| Active-set organization | SelfConsistentPowerFitResult.inner_fit, .final_realization, .candidate_diagnostics, .outer_termination, .path |
final objects when available, outer termination, active mask, marginals, history, and path summary |
Issue #14 preserves the exact SeparatorFitProblem dataclass fields and adds
two provisional computed properties:
| Mathematical layer | Access path | Contract |
|---|---|---|
| Observation multigraph | SeparatorFitProblem.observation_graph |
SeparatorObservationGraphView with site count, distinct observation rows, oriented endpoints, input indices, requested shifts, shared alpha, beta, z_obs, and rho arrays, a positive-confidence informative mask, existing connectivity, and dense/optional-SciPy incidence conversion |
| Quadratic normal operator | SeparatorFitProblem.quadratic_operator |
SeparatorQuadraticOperatorView with matrix-free and dense/optional-SciPy observation Laplacian and L2-regularized normal operators, scale-safe direct observation_rhs = B @ q for q_r = confidence_r * alpha_r * (target_r - beta_r), regularized_normal_rhs, regularization data, hard-bound metadata, and component/nullity interpretation |
The incidence matrix has shape (n_sites, n_observations) and column r
equal to +1 at site_i[r] and -1 at site_j[r]. Every resolved row is a
column, including repeats, periodic parallel observations, and zero-confidence
rows. The latter have informative_mask[r] == False and rho[r] == 0, so they
do not connect informative components or contribute to the observation
Laplacian and right-hand side. z_obs remains diagnostic and is not required
to reconstruct a finite normal RHS.
The quadratic view is available only for SquaredLoss with no
positive-strength scalar penalties. Zero-strength penalties are absent and do
not hide the view. Optional L2 regularization is included exactly. Hard
interval or equality restrictions may coexist but remain visible through
problem.bounds; the view reports that unconstrained normal equations do not
characterize a constrained fit in general. Huber mismatch and models with
positive-strength scalar penalties retain the graph view but reject
quadratic_operator rather than presenting a partial system as the full
objective. Sparse conversion imports SciPy lazily; SciPy is neither a runtime
dependency nor a solver backend in issue #14.
The canonical component_alignment_policy view value is the same stored string
as compatibility-facing ConnectivityDiagnostics.gauge_policy; only the access
name is clarified. global_representation_shift is the compatibility
weight_shift value used for backend-radius conversion: it selects a backend
representation within the global geometric gauge, is distinct from independent
component offsets, and is not information recovered from observations. All
array-valued views share the arrays already owned by their result or
resolved-observation source.
The connectivity effective_graph and active_effective_graph contain only
positive-confidence rows. The corresponding *_identified_by_data fields are
true exactly when their informative graph is connected. The compatibility
field offsets_identified_in_objective is conservative: it is true when the
relevant informative graph is connected or positive L2 regularization
guarantees selection of otherwise free offsets. Hard restrictions and scalar
penalties do not make these fields true. An exact hard equality can fix an
offset in a particular problem, but that separate constraint-identifiability
case is outside the current view rather than generalized prematurely.
Every valid observation set has source-independent row IDs and an ordered observation-set fingerprint. Its namespace identity contains dimension, point count, measurement, and IDs; each row identity contains endpoints, shift, measurement, target, confidence, resolved connector and distance values, both measurement forms, and the explicit-shift flag. Warnings do not affect identity. Subsets preserve retained row IDs and input indices, while row reordering changes the set fingerprint. These identities do not change if exact source provenance is bound later.
Source binding is separate, optional, exact, and monotonic. Resolver-created
observations are bound; valid directly constructed observations remain
unbound until a source-aware operation independently recomputes and verifies
their connector geometry. The bound source contains caller-order points before
periodic remapping, exact domain representation, dimension/count, and exact ID
provenance. It survives subsets, shallow/deep copy, same-version pickle,
dataclasses.replace(...), and copy.replace(...) where available; an
inconsistent replacement raises. Binding and origin associations are private,
not public dataclass fields or source arguments.
One association policy applies to views, records, reports, realization, and active-set operations: two unbound objects are accepted only for the exact same observation model; two bound objects are accepted only for the exact same source; a bound/unbound pair or two different bound sources are rejected. Fingerprint agreement never replaces exact comparison of canonical values. The private originating-observation association on fit and diagnostic results continues to survive copy, replacement, and pickle reconstruction. Reports use that authoritative origin rather than borrowing provenance from an arbitrary supplied object.
The generated reference also documents these result/problem conveniences:
SeparatorObservations.pair_labels(*, use_ids=False)
SeparatorObservations.to_records(*, use_ids=False)
SeparatorObservations.subset(mask)
SeparatorFitProblem.observation_graph
SeparatorFitProblem.quadratic_operator
SeparatorFitProblem.canonicalize_gauge(weights)
RealizedPairDiagnostics.to_records(constraints, *, use_ids=False)
RealizedPairDiagnostics.unaccounted_records(*, ids=None)
RealizedPairDiagnostics.to_report(constraints, *, use_ids=False)
PairConstraintDiagnostics.to_records(*, ids=None)
SelfConsistentPowerFitResult.to_records(*, use_ids=False)
SelfConsistentPowerFitResult.to_report(*, use_ids=False)
UnaccountedRealizedPair.to_record(*, ids=None)
The active result's to_records(...), final_realization, and
candidate_diagnostics return None when its final fit has no usable weights.
Inverse record schemas retained under canonical v0.8 names
Record order follows constraint order. use_ids=True substitutes the stable
external site IDs where the relevant container has ids. Separator IDs are
input-order-aligned, unique, non-negative integers; Python integers and NumPy
integer scalars are accepted without lossy float or string conversion. Raw
observation endpoints are strict integers in both index_mode='index' and
index_mode='id'.
| Producer | Exact keys |
|---|---|
SeparatorObservations.to_records() |
constraint_index, row_id, site_i, site_j, shift, target, confidence, measurement, distance, target_fraction, target_position, input_index, explicit_shift |
SeparatorFitResult.to_records(...) |
constraint_index, row_id, site_i, site_j, shift, measurement, target, predicted, predicted_fraction, predicted_position, residual, alpha, beta, z_obs, z_fit, algebraic_residual, edge_weight |
RealizedPairDiagnostics.to_records(...) |
constraint_index, row_id, site_i, site_j, shift, realized, realized_same_shift, realized_other_shift, realized_shifts, endpoint_i_empty, endpoint_j_empty, boundary_measure |
PairConstraintDiagnostics.to_records(...) / active result |
constraint_index, row_id, site_i, site_j, shift, target, confidence, predicted, predicted_fraction, predicted_position, residual, active, realized, realized_same_shift, realized_other_shift, realized_shifts, endpoint_i_empty, endpoint_j_empty, boundary_measure, toggle_count, realized_toggle_count, first_realized_iter, last_realized_iter, marginal, status |
HardConstraintConflictTerm.to_record() |
constraint_index, site_i, site_j, relation, bound_value |
UnaccountedRealizedPair.to_record() |
site_i, site_j, realized_shifts, boundary_measure |
Measurement-space residual and algebraic z_obs - z_fit are distinct.
Periodic shifts are integer tuples of the resolved dimension. A confidence-zero
row remains in candidate records but never identifies an informative graph
edge. Configured hard restrictions or penalties may still constrain the row's
predicted separator value; that model coupling is separate from observational
identification.
Inverse report schemas retained in v0.8
All three report families add the exact common top-level keys schema,
producer, source, and observation_set while retaining their existing kind
and family-specific keys. schema is exactly
{"name": "pyvoro2.inverse.separator.report", "version": 1} and producer
is exactly {"name": "pyvoro2", "version": pyvoro2.__version__}.
observation_set has exactly fingerprint, measurement, n_rows, and
row_ids.
| Report | Exact top-level keys | Exact summary keys |
|---|---|---|
| fit | schema, producer, source, observation_set, kind, summary, constraints, fit_records, edge_diagnostics, objective_breakdown, weights, radii, weight_shift, used_shifts, warnings, conflict, connectivity |
status, is_optimal, is_infeasible, hard_feasible, solver, linear_backend, measurement, n_constraints, n_points, converged, status_detail, n_iter, rms_residual, max_residual, conflicting_constraint_indices |
| realized | schema, producer, source, observation_set, kind, summary, records, unrealized, unaccounted_pairs, warnings, tessellation_diagnostics |
n_constraints, n_realized, n_same_shift, n_other_shift, n_unrealized, n_unaccounted_pairs |
| active set | schema, producer, source, observation_set, kind, availability, summary, constraints, fit, realized, diagnostics, marginal_records, history, path_summary, tessellation_diagnostics, warnings, connectivity |
termination, converged, n_outer_iter, cycle_length, n_constraints, n_active_final, n_realized_final, rms_residual_all, max_residual_all, marginal_constraint_indices |
source has exactly binding, fingerprint, dimension, n_points,
points, domain, and ids. An unbound source reports
binding="unbound" and null fingerprint, points, domain, and IDs. A bound
source reports binding="bound", its exact fingerprint and caller-order source
data. Bound domain records use only none, planar_box,
planar_rectangular_cell, spatial_box, spatial_orthorhombic_cell, or
spatial_periodic_cell, with the fields fixed by ADR 0014. A bound
{"kind": "none"} domain is distinct from unbound domain=null.
Nested fit edge_diagnostics uses the fields of
AlgebraicEdgeDiagnostics; objective_breakdown uses the fields of
PowerFitObjectiveBreakdown: total, mismatch, penalties_total,
penalty_terms, regularization, hard_constraints_satisfied,
hard_max_violation, and hard_max_tolerance. Connectivity records contain
unconstrained_points, candidate/effective/active graph records, both
data-identification flags, offsets_identified_in_objective, gauge_policy,
and messages. Graph records contain n_points, n_constraints, n_edges,
isolated_points, connected_components, n_components, and
fully_connected.
Active history rows contain iteration, n_active, n_realized, n_added,
n_removed, rms_residual_all, max_residual_all, weight_step_norm,
n_active_fit, the two fit-active graph component counts,
fit_active_offsets_identified_by_data, and n_unaccounted_pairs. Path summary
records contain all ActiveSetPathSummary fields. Tessellation report records
provide dimension-neutral measure/boundary keys plus the corresponding 2D
area/edge or 3D volume/face aliases.
Issue #13 did not add, remove, or rename report keys. Existing fit report
state keys (weights, radii, weight_shift), connectivity, observation
records/summaries, objective_breakdown, edge_diagnostics, and solver
summary/conflict/warnings correspond respectively to the state,
identification, observations, objective, algebraic, and solver layers.
Realized records/unrealized describe requested-image matching, while
unaccounted pairs, optional record geometry, and tessellation diagnostics
describe realized geometry. Active fit, realized, diagnostics, summary,
history, and path_summary describe the final inner fit, final realization,
per-candidate diagnostics, outer termination, and active-set path.
Issue #36 subsequently adds the approved nested
objective_breakdown.hard_max_tolerance key and separates the fit-summary
solver and linear_backend fields. Issue #42 adds the common versioned
envelope and row_id values above without renaming or removing those existing
fields. Report builders return JSON-native values. Finite fit, realized, and
active reports round-trip exactly; JSON serialization rejects NaN and infinity.
ADR 0015 makes active final-state assembly atomic. Its availability block has
exactly weights, realization, records, and reason. All flags are true
and reason is null for an available final state. All flags are false and
reason is the final fit status when weights are unavailable. In that mode,
realized, diagnostics, marginal_records, and
tessellation_diagnostics are null, as are summary.n_realized_final,
summary.rms_residual_all, and summary.max_residual_all. The nested fit
report remains present; the active summary retains outer termination while the
nested fit retains final inner status and convergence. Every active report
round-trips exactly through strict JSON.
Historical calls exercised by repository examples
The v0.6.3 notebooks used raw spatial forward calls and historical top-level inverse imports. The characterized historical set was:
Box, OrthorhombicCell, PeriodicCell, compute, locate, ghost_cells,
normalize_vertices, normalize_topology, annotate_face_properties,
resolve_pair_bisector_constraints, FitModel, SquaredLoss, Interval, FixedValue,
ExponentialBoundaryPenalty, fit_power_weights, match_realized_pairs,
ActiveSetOptions, solve_self_consistent_power_weights, dumps_report_json,
pyvoro2.viz3d.VizStyle, pyvoro2.viz3d.view_tessellation
They also called to_records(...), to_report(...), and conflict record
helpers. This list is retained only as migration history. Current source
notebooks use TessellationResult, pyvoro2.inverse, and
pyvoro2.inverse.separator; the removed names are not required to execute or
export them.
No manuscript program or paper environment is stored in this repository. The repository-owned paper-style regression subset and downstream-shaped examples use the canonical current APIs. The documented v0.6.3 algebraic formulas, periodic-image semantics, and calls above remain historical context for archived research and migration.
Chemistry-neutral downstream requirements captured by the baseline
A chemvoro-shaped caller needs to be able to:
- keep arbitrary downstream metadata outside pyvoro2 while passing stable integer external IDs;
- resolve separator observations by those IDs and preserve explicit periodic image shifts;
- fit weights, distinguish measurement and algebraic residuals, and inspect component-offset identification;
- realize the fitted radii, detect empty endpoints and wrong/unaccounted realized pairs, and request boundary measure;
- export records/reports without importing private modules; and
- avoid relying on raw cell-list position as the mapping back to downstream objects.
These are requirements for the preferred current surfaces, not permission to add chemistry-specific models or metadata containers to pyvoro2.
Baseline reconciliation with accepted ADRs
The observed v0.6.3 baseline does not contradict ADR 0003, ADR 0004, or ADR 0005. It exposes transition conditions that those decisions already account for:
- current power computation is radius-first and the conversion implementation
is owned by
pyvoro2.powerfit, while ADR 0002 and ADR 0004 require mathematical weight semantics and neutral transform ownership in v0.7; - current separator implementation and broad imports live under
pyvoro2.powerfitand top-levelpyvoro2, while ADR 0004 deliberately keeps those paths as v0.7 compatibility shims during the ownership move; and - current spatial and planar compute calls return different raw/structured variants, while ADR 0005 deliberately changes the preferred default and preserves the characterized variants through explicit compatibility routes.
These are planned migrations rather than incompatible scientific meanings. No WP-01 stop condition was triggered, and dependent implementation may preserve the recorded baseline through the compatibility policy without reopening an accepted decision.
Current v0.8 contract
The current tree has one structured forward contract, one canonical high-level separator route, and one advanced separator namespace. Runtime inspection and the import/signature tests establish these exact package export counts:
| Namespace | Exact __all__ size |
Lifecycle boundary |
|---|---|---|
pyvoro2 |
29 | Stable forward/result surface plus package metadata |
pyvoro2.planar |
25 | Stable explicit 2D surface plus provisional plotting |
pyvoro2.inverse |
6 | Stable normal fixed-observation separator workflow |
pyvoro2.inverse.separator |
53 | Stable core names, provisional advanced objects, experimental active-set objects |
pyvoro2.viz3d |
9 | Provisional optional visualization |
There is no current pyvoro2.powerfit, top-level separator export set,
historical separator alias, PlanarComputeResult, planar return_result=, or
pyvoro2.planar.result. Those names appear below only in migration/history
sections.
The sole weight/radius implementation lives in the internal neutral module
pyvoro2._internal.weight_transforms; the stable exports from pyvoro2,
pyvoro2.inverse, and pyvoro2.inverse.separator are identical function
objects. The other pure-Python helpers live under pyvoro2._internal with
explicit shared, spatial, or planar ownership. The native extensions retain
their root-owned internal names _core and _core2d.
Plain import pyvoro2 imports the pure-Python forward/result surface and the
planar namespace, but not pyvoro2.inverse, _core, or _core2d. Importing
either canonical inverse namespace also leaves the native modules unloaded.
The 3D and 2D wrappers load _core and _core2d, respectively, on the first
forward geometry operation that requires them.
Exact current forward signatures
Annotations are omitted here only for readability; parameter kind, order, and defaults are exact:
pyvoro2.compute(
points, *, domain, ids=None,
duplicate_check='off', duplicate_threshold=1e-5,
duplicate_wrap=True, duplicate_max_pairs=10,
block_size=None, blocks=None, init_mem=8,
mode='standard', weights=None, radii=None,
return_vertices=True, return_adjacency=True, return_faces=True,
return_face_shifts=False, face_shift_search=2, include_empty=False,
validate_face_shifts=True, repair_face_shifts=False, face_shift_tol=None,
return_diagnostics=False, output='result',
tessellation_check='none', tessellation_require_reciprocity=None,
tessellation_volume_tol_rel=1e-8,
tessellation_volume_tol_abs=1e-12,
tessellation_plane_offset_tol=None,
tessellation_plane_angle_tol=None,
)
pyvoro2.planar.compute(
points, *, domain, ids=None,
duplicate_check='off', duplicate_threshold=1e-5,
duplicate_wrap=True, duplicate_max_pairs=10,
block_size=None, blocks=None, init_mem=8,
mode='standard', weights=None, radii=None,
return_vertices=True, return_adjacency=True, return_edges=True,
return_edge_shifts=False, edge_shift_search=2, include_empty=False,
validate_edge_shifts=True, repair_edge_shifts=False, edge_shift_tol=None,
return_diagnostics=False, output='result',
normalize='none', normalization_tol=None,
tessellation_check='none', tessellation_require_reciprocity=None,
tessellation_area_tol_rel=1e-8,
tessellation_area_tol_abs=1e-12,
tessellation_line_offset_tol=None,
tessellation_line_angle_tol=None,
)
The current locate(...), ghost_cells(...), domain, diagnostics,
validation, duplicate, normalization, annotation, transform, and visualization
signatures are exactly the retained signatures listed in the corresponding
sections above. In particular, locate(...) and ghost_cells(...) remain
radius-only in power mode; weights= belongs only to compute(...).
Power-mode compute(...) requires exactly one of weights= and radii=.
Standard mode rejects both. Weight input must have shape (n,) and all
conversion arithmetic must remain finite. Valid direct-radius behavior is
unchanged. Finite representability does not promise geometric resolution when
squared backend radii or genuine weight ranges overwhelm squared geometry
scales.
The current native-construction controls have one shared contract in both
dimensions. init_mem and each explicit blocks entry must be a positive
exact non-Boolean index-protocol scalar within the C++ int range; blocks
has length 3 spatially and length 2 planarly. block_size, when supplied, must
be a positive finite real numeric scalar. Points and queries must have the
dimension-appropriate matrix shape and contain only finite values; radii must
have the required vector shape and be finite and non-negative. Bounds and
periodic constructor parameters must be finite, ordered where applicable, and
safe for the arithmetic evaluated by Voro++. Violations raise ValueError
before native construction.
The rest of the stable forward input surface uses the same strict source-type
policy. External IDs are exact non-Boolean integers, non-negative, unique,
length-aligned where site input is available, and signed-int64 representable;
direct diagnostic expected_ids use the same contract. Search counts and
normalization example caps are non-negative exact integers, with
max_examples=0 retaining its no-examples boundary. Duplicate pair limits are
positive exact integers. Public flags accept only Python or NumPy Boolean
scalars. Duplicate thresholds are positive finite reals, and shift,
normalization, and diagnostic tolerances use their documented positive or
non-negative finite ranges. These stricter rejections do not change signatures,
defaults, result schemas, or valid forward values.
Normalization revalidates mutable raw cell metadata at each public normalization boundary: cell and adjacent-cell IDs, local/global vertex indices, and lattice shifts use exact signed-int64-compatible rules with their field-specific non-negative constraints. Coordinate/tolerance quantization is accepted only when the quotient and rounded key are finite and signed-int64 representable; unsupported relationships raise before topology construction or in-place annotation.
All domain constructors canonicalize bounds, periodic flags, triclinic vectors,
and origins into owned nested built-in tuples. Box.from_points rejects empty
or non-finite data before reduction and accepts non-negative padding only when
the resulting bounds remain finite and strictly ordered. PeriodicCell
requires a right-handed basis while retaining its existing conditioning
warning and rejection thresholds. Remap helpers validate exact Boolean flags,
finite non-negative eps, and finite points, and reject a lattice shift that
cannot fit signed int64 before casting. ADR 0011 records this contract.
All 18 internal native construction routes repeat converted-value checks and
checked constructor arithmetic before allocation. An aggregate source-derived
estimate of known eager native construction allocations may be at most exactly
1 << 30 bytes; estimates greater than 1 GiB raise ValueError, with no
unsafe override. This is a defensive change to invalid-input rejection, not a
new public name, signature, default, result schema, or valid numerical
behavior. The exact layering and estimate policy are fixed by ADR 0010.
Periodic nearest-image inference now certifies the exact Euclidean minimum for the dyadic rational values represented by supplied binary64 coordinates and lattice components. Rectangular and orthorhombic domains use an exact per-axis fast path; fully periodic non-orthogonal 3D cells exact-enumerate a proof-derived finite coefficient box. Separator tie orientation uses the resolved internal site indices in the fixed point ordering, so lattice translation and pair reversal retain their expected shift/displacement invariants. External IDs remain metadata and do not participate in geometric tie selection; no point-array permutation invariant or public tie mode is introduced. Explicit observation shifts remain authoritative even when a different image is nearer.
The public image_search parameter remains a non-negative exact integer with
default one in all three separator entry points below. It is now only a capped
incumbent-seeding hint: it may change private candidate counts and runtime but
cannot change a successful inferred shift, displacement, or distance. There is
no approximate mode or boundary-warning result. If exact certification exceeds
the frozen private resource or signed-int64 shift contract, inference raises
without returning an approximate image. When periodic wrapping is enabled
(wrap=True, or duplicate_wrap=True in forward operations), periodic
duplicate checks use the same exact pair-distance primitive for pairs evaluated
by the current scanner. With wrapping disabled, the established unwrapped
Cartesian check is preserved. R5 still owns complete seam scanning and
mandatory native-safety policy independent of duplicate_wrap. ADR 0012
records this contract.
All generators inserted on non-periodic axes must lie in the half-open interval
[lo, hi); periodic axes are remapped. This is the supported native storage
contract, including temporary ghost generators, rather than a theorem about
which external mathematical sites can influence a clipped diagram. The
mandatory duplicate floor is always enforced and cannot be disabled; the
user-configurable duplicate policy remains a separate diagnostic layer. Native
preflight and postconditions prevent invalid or omitted construction from being
packaged as a successful result. ADR 0013 records this contract.
Exact current inverse signatures and defaults
The stable high-level calls are:
resolve_separator_observations(
points, constraints, *, measurement='fraction', domain=None, ids=None,
index_mode='index', image='nearest', image_search=1, confidence=None,
allow_empty=False,
)
fit_weights_from_separators(
points, constraints, *, measurement='fraction', domain=None, ids=None,
index_mode='index', image='nearest', image_search=1, confidence=None,
model=None, r_min=0.0, weight_shift=None, solver='direct',
linear_backend='dense', admm_max_iter=2000, admm_rho=1.0,
admm_abs_tol=1e-6, admm_rel_tol=1e-5,
connectivity_check='warn',
)
weights_to_radii(weights, *, r_min=0.0, weight_shift=None)
radii_to_weights(radii)
The principal advanced calls are:
build_power_fit_problem(constraints, *, model=None)
build_power_fit_result(
problem, weights, *, solver='external', linear_backend=None,
status='optimal',
status_detail=None, converged=True, n_iter=0, warnings=(),
canonicalize_gauge=True, r_min=0.0, weight_shift=None,
)
match_realized_pairs(
points, *, domain, constraints, weights=None, radii=None,
return_boundary_measure=False, return_cells=False,
return_tessellation_diagnostics=False,
tessellation_check='diagnose', unaccounted_pair_check='diagnose',
)
solve_self_consistent_power_weights(
points, constraints, *, measurement='fraction', domain, ids=None,
index_mode='index', image='nearest', image_search=1, confidence=None,
model=None, active0=None, options=None, r_min=0.0, weight_shift=None,
fit_solver='direct', fit_linear_backend='dense',
fit_admm_max_iter=2000, fit_admm_rho=1.0,
fit_admm_abs_tol=1e-6, fit_admm_rel_tol=1e-5,
return_history=False, return_cells=False,
return_boundary_measure=False, return_tessellation_diagnostics=False,
tessellation_check='diagnose', connectivity_check='warn',
unaccounted_pair_check='warn',
)
match_realized_pairs(...) requires exactly one of weights= and radii=.
The active-set wrapper forwards the same method and linear-backend choices
through the fit_* parameters. Report helper defaults and the
objective/model/active-set constructor defaults are exactly those listed in
the retained constructor table above.
The public row-only chain
SeparatorObservations
-> build_power_fit_problem
-> build_power_fit_result
-> build_fit_report
remains valid for directly constructed source-unbound observations; none of
these builders gains a public points, domain, or source argument. If an already
resolved observation object is fitted with points, those points establish or
verify exact source points. Omitted/default domain=None makes no additional
domain assertion against an already bound object and does not erase its bound
domain. For an unbound object it binds the explicit no-domain representation.
An explicit non-None domain must match or establish that exact domain.
Realization and active-set operations verify the complete source they use.
SciPy is optional. linear_backend='dense' uses NumPy and never imports SciPy.
Explicit linear_backend='sparse' and explicit sparse matrix conversion import
SciPy lazily and raise an actionable ImportError when it is absent. There is
no site-count backend selection. Direct solving accepts only purely quadratic
models; ADMM is required for Huber mismatch, hard restrictions, and active
scalar penalties, and explicit ADMM also executes for a quadratic model
whenever a component solve is required.
Degenerate fits that need no component solve, including empty observation sets
and models with only singleton components, report solver='none',
linear_backend=None, and n_iter=0.
Current retained names and report schema
PowerFitBounds, PowerFitPredictions, PowerFitObjectiveBreakdown, and
SelfConsistentPowerFitResult remain current public names in the provisional or
experimental advanced pyvoro2.inverse.separator surface. Their spelling is
not evidence that the removed pyvoro2.powerfit package survives, and R9 does
not cosmetically rename them.
Separator reports use the frozen schema name
pyvoro2.inverse.separator.report, schema version 1, and these retained
report kinds:
power_weight_fit
realized_pair_diagnostics
self_consistent_power_fit
Report payloads are strict JSON-native round trips. Row-aligned records carry
stable row IDs and explicit bound/unbound provenance. Active results expose
final_state_available, final_state_unavailable_reason, and
final_refit_converged properties to distinguish outer termination from the
final inner fit. Their report availability block has exactly weights,
realization, records, and reason. Unavailable final realization and
diagnostic layers serialize as JSON null; they are never stale objects from a
different iterate. ADR 0014 and ADR 0015 record these contracts.
Accepted v0.8 contract decisions
The following boundaries are already accepted:
pyvoro2.inverseis the canonical inverse namespace;- separator implementation is owned by
pyvoro2.inverse.separator; pyvoro2.powerfit, broad top-level separator exports, and the other v0.7-only routes are removed;- both forward
compute(...)functions returnpyvoro2.TessellationResultby default; output='cells'is the explicit supported raw-output route;TessellationResultis the only planar structured-result name;- deep immutability of nested raw records is not part of the contract;
- squared mismatch, quadratic Huber mismatch, and L2 regularization use the accepted half-factor objective convention;
- zero-strength scalar penalties are absent, hard bounds use the shared float64 scale-aware tolerance, and successful solver results have finite reported soft objectives;
- positive-strength scalar-penalty proximal coordinates succeed only with
private proved exact point signs or an adjacent numeric-binary64 sign bracket,
while an uncertified attempt maps to the existing
numerical_failureschema; - inferred periodic nearest images are exact-certified, explicit image shifts
remain authoritative, and
image_searchis a correctness-neutral seed hint; - non-periodic generators use the half-open storage-domain contract, periodic generators are remapped, and mandatory duplicate safety cannot be disabled;
- source-independent row and observation-set identity is always available, while exact geometry-source provenance is optional and monotonic;
- row-aligned records carry stable row IDs and report schema version 1 carries authoritative source and observation-set provenance;
- active results separate outer termination from final inner status and expose optional final geometry only when one coherent final state exists;
TessellationDiagnostics.okis false for every error-severity issue; standard missing IDs are errors, power hidden IDs are informational, undeclared-mode missing IDs are warnings, and strict/warn/raise paths consume that final severity-complete value.
See ADR 0004 and ADR 0005, as refined by ADR 0006, together with ADR 0007, ADR 0008, ADR 0009, ADR 0010, ADR 0011, ADR 0012, ADR 0013, ADR 0014, ADR 0015, and ADR 0016.
Lifecycle summary for the v0.8 API
| Surface | v0.8 status | Notes |
|---|---|---|
| Domain classes and domain geometry semantics | Stable | Mature bounded and periodic behavior; capability differences remain explicit by dimension. |
pyvoro2.compute and pyvoro2.planar.compute |
Stable | Direct weight/radius behavior, the common structured default, and explicit raw output are implemented and tested. |
weights= and radii= mathematical meaning |
Stable | Mode-specific rejection/exclusivity, one global representation shift, finite and representable conversion, and empty-cell behavior are part of the contract. |
pyvoro2.TessellationResult core contract |
Stable | The shared class and both public compute integrations are stable; direct construction is classified separately as provisional. |
| Detailed optional result conveniences and raw geometry views | Provisional | Refine through implementation and chemvoro-shaped validation. |
pyvoro2.inverse preferred high-level separator workflow |
Stable | Validated normal observations/fit entry point for applications and chemvoro-shaped workflows. |
pyvoro2.inverse.separator advanced problem and operator views |
Provisional | Public for research use; may evolve during v0.9 functional stabilization before the 1.0 contract is frozen. |
| Realization-aware active-set API | Experimental | Practical outer algorithm; no universal convergence claim. |
| Optional sparse linear backend | Provisional | Explicit linear_backend='sparse' supports direct quadratic solving and ADMM weight systems; it requires SciPy and is never selected by site count. |
| v0.7-only inverse and planar transition routes | Removed | Ordinary import, attribute, or argument failure; replacements are in the migration guide. |
pyvoro2._internal, native extensions, and solver-internal modules |
Internal | No compatibility guarantee; _internal has no package-level convenience exports. |
Documented module-route status
Objects imported from these documented modules retain the lifecycle status assigned above. The module route itself has the following status:
| Module route | v0.8 status |
|---|---|
pyvoro2.api, domains, diagnostics, duplicates, face_properties, normalize, validation |
Stable |
pyvoro2.edge_properties |
Stable for its documented annotation helper |
pyvoro2.result |
Stable module route for TessellationResult; direct construction remains provisional |
pyvoro2.viz2d, pyvoro2.viz3d |
Provisional optional conveniences |
pyvoro2.planar.api, domains, diagnostics, duplicates, normalize, validation |
Stable |
pyvoro2.inverse |
Stable high-level route |
pyvoro2.inverse.separator and its non-active submodules |
Mixed route: stable high-level core names and provisional advanced objects |
pyvoro2.inverse.separator.active |
Experimental |
pyvoro2.planar.result, pyvoro2.powerfit, and its direct submodules |
Removed; these are not current module routes |
pyvoro2._internal helpers and native _core/_core2d extensions |
Internal; the native modules remain outside _internal |
Spatial forward namespace: pyvoro2
The exact current 29-name pyvoro2.__all__ is:
Box
OrthorhombicCell
PeriodicCell
TessellationResult
compute
locate
ghost_cells
TessellationDiagnostics
TessellationIssue
TessellationError
analyze_tessellation
validate_tessellation
NormalizationDiagnostics
NormalizationIssue
NormalizationError
validate_normalized_topology
DuplicatePair
DuplicateError
duplicate_check
annotate_face_properties
NormalizedVertices
NormalizedTopology
normalize_vertices
normalize_edges_faces
normalize_topology
radii_to_weights
weights_to_radii
__version__
planar
All are stable names or surfaces. The __version__ value naturally tracks
the installed release, and planar is the explicit 2D namespace.
Visualization remains a direct provisional module route rather than a
top-level export.
Historical v0.7 top-level inverse compatibility set
The following 34 names were top-level pyvoro2 compatibility exports in v0.7.
They are listed only to make the removal inventory exact; none is a current
top-level export:
PairBisectorConstraints
resolve_pair_bisector_constraints
SquaredLoss
HuberLoss
Interval
FixedValue
SoftIntervalPenalty
ExponentialBoundaryPenalty
ReciprocalBoundaryPenalty
L2Regularization
FitModel
AlgebraicEdgeDiagnostics
ConstraintGraphDiagnostics
ConnectivityDiagnostics
ConnectivityDiagnosticsError
HardConstraintConflictTerm
HardConstraintConflict
PowerWeightFitResult
RealizedPairDiagnostics
UnaccountedRealizedPair
UnaccountedRealizedPairError
build_fit_report
build_realized_report
build_active_set_report
dumps_report_json
write_report_json
ActiveSetOptions
ActiveSetIteration
ActiveSetPathSummary
PairConstraintDiagnostics
SelfConsistentPowerFitResult
fit_power_weights
match_realized_pairs
solve_self_consistent_power_weights
Planar namespace: pyvoro2.planar
The exact current 25-name pyvoro2.planar.__all__ is:
Box
RectangularCell
TessellationResult
compute
locate
ghost_cells
DuplicatePair
DuplicateError
duplicate_check
annotate_edge_properties
plot_tessellation
TessellationIssue
TessellationDiagnostics
TessellationError
analyze_tessellation
validate_tessellation
NormalizedVertices
NormalizedTopology
normalize_vertices
normalize_edges
normalize_topology
NormalizationIssue
NormalizationDiagnostics
NormalizationError
validate_normalized_topology
plot_tessellation is provisional and optional. The other 24 names are
stable. The re-exported TessellationResult is the identical class object
as pyvoro2.TessellationResult.
Canonical inverse namespace: pyvoro2.inverse
Preferred high-level separator API
The exact current pyvoro2.inverse.__all__ is:
SeparatorObservations
resolve_separator_observations
SeparatorFitResult
fit_weights_from_separators
weights_to_radii
radii_to_weights
The preferred names have these final lifecycle assignments:
| Name | v0.8 status | Meaning |
|---|---|---|
SeparatorObservations |
Stable | Canonical pairwise separator rows with periodic image labels, confidence, source-independent identity, and optional exact source binding. |
resolve_separator_observations |
Stable | Validate and resolve raw separator observations against sites and domain. |
SeparatorFitResult |
Stable | Existing flat fit contract plus layered state, observation, identification, objective, algebraic, and fixed-solver access. |
fit_weights_from_separators |
Stable | Preferred fixed-observation fit entry point. |
weights_to_radii, radii_to_weights |
Stable re-export where useful | Same neutral transforms as top-level pyvoro2. |
Advanced separator API
After the v0.8 issue-#28 removal,
pyvoro2.inverse.separator.__all__ contains exactly the following 53 names:
SeparatorObservations
resolve_separator_observations
SeparatorFitProblem
SeparatorFitResult
fit_weights_from_separators
SeparatorFitStateView
SeparatorIdentificationView
SeparatorObservationView
SeparatorAlgebraicView
SeparatorSolverTerminationView
SeparatorObservationGraphView
SeparatorQuadraticOperatorView
SquaredLoss
HuberLoss
Interval
FixedValue
SoftIntervalPenalty
ExponentialBoundaryPenalty
ReciprocalBoundaryPenalty
L2Regularization
FitModel
AlgebraicEdgeDiagnostics
ConstraintGraphDiagnostics
ConnectivityDiagnostics
ConnectivityDiagnosticsError
HardConstraintConflictTerm
HardConstraintConflict
PowerFitBounds
PowerFitPredictions
PowerFitObjectiveBreakdown
build_power_fit_problem
build_power_fit_result
RequestedImageMatchView
RealizedGeometryView
RealizedPairDiagnostics
UnaccountedRealizedPair
UnaccountedRealizedPairError
build_fit_report
build_realized_report
build_active_set_report
dumps_report_json
write_report_json
ActiveSetOptions
ActiveSetIteration
ActiveSetPathSummary
ActiveSetTerminationView
ActiveSetPathView
PairConstraintDiagnostics
SelfConsistentPowerFitResult
match_realized_pairs
solve_self_consistent_power_weights
radii_to_weights
weights_to_radii
The canonical core and neutral transforms have the statuses assigned above. The objective model, problem construction/evaluation, fixed-fit and realization view types, realization, reporting, and diagnostic objects are initially provisional. The active-set outer workflow and its options, termination/path views, iteration, path, diagnostic, and result objects are experimental and separator-specific.
The historical v0.7 identity map, removed in v0.8, was:
| Removed historical name | Current canonical name | Historical relationship |
|---|---|---|
PairBisectorConstraints |
SeparatorObservations |
Identity alias; historical name compatibility-only through v0.7 |
resolve_pair_bisector_constraints |
resolve_separator_observations |
Identity alias; historical name compatibility-only through v0.7 |
PowerFitProblem |
SeparatorFitProblem |
Identity alias; historical name compatibility-only through v0.7 |
PowerWeightFitResult |
SeparatorFitResult |
Identity alias; historical name compatibility-only through v0.7 |
fit_power_weights |
fit_weights_from_separators |
Identity alias; historical name compatibility-only through v0.7 |
The accepted provisional advanced surfaces include:
SeparatorFitProblemand problem-building/evaluation helpers;- problem-owned
SeparatorObservationGraphViewandSeparatorQuadraticOperatorView, including dense NumPy and optional lazy SciPy conversions; the explicit sparse solver consumes this operator through the separate fixed-fit entry point; - objective model pieces such as squared/Huber losses, hard intervals, penalties, and regularization;
- graph, connectivity, incidence, Laplacian, and objective-breakdown views;
- result packaging for externally computed weights;
- layered fixed-fit and realization views that reference existing result data;
- realization matching and record/report builders.
Issue #36 freezes the separator objective semantics. For
e = beta + alpha * (w_i - w_j) - target, squared loss is
0.5 * e**2; Huber loss is 0.5 * e**2 for abs(e) <= delta and
delta * (abs(e) - 0.5 * delta) otherwise. Confidence multiplies only
mismatch. L2 is
0.5 * strength * ||weights - reference||**2, so the normal system remains
A = L_obs + strength * I and
b = b_obs + strength * reference.
Soft-interval and exponential strengths retain their existing meanings.
For reciprocal inward distance d, the contribution is zero for
d >= margin, strength * (1 / d - 1 / margin) for
epsilon < d < margin, and
strength * ((1 / epsilon - 1 / margin)
- (d - epsilon) / epsilon**2) for d <= epsilon; lower and upper
contributions are summed. Zero-strength penalties are exact no-ops. Hard rows
use
1e-12 + 64 * finfo(float64).eps * max(abs(lower), abs(prediction),
abs(upper)), and successful solver results require finite reported
soft-objective components and totals. ADR 0007 records the derivative,
continuation, and compatibility rationale.
Issue #37 replaces the scalar penalty proximal loop without changing that
objective or any public name/default/schema. The private solver compiles one
positive-strength term kernel once, evaluates complete source expressions,
represents exact structural breakpoints and one-sided reciprocal-margin
derivatives, and brackets rigorous derivative signs. It accepts only proved
exact point signs or adjacent numeric-binary64 localization. Scaled binary64
accumulation preserves determinable exponential signs through raw overflow;
bounded 80/160-digit work resolves only rare ambiguities. Direct termwise
objective differences select adjacent endpoints. Exhaustion or unresolved
evaluation becomes the existing structured numerical_failure; a failed
proximal attempt does not increment completed ADMM iterations. ADR 0009 records
the private numerical contract.
The realization matcher accepts weight-first and radius-representation inputs as mutually exclusive current routes. New workflows use fitted mathematical weights; direct radii remain a supported advanced representation input.
Separator integer, Boolean, finite-value, and ownership policy is shared with
the forward layer. Observation endpoints, shifts, provenance indices, search
counts, iteration counts, and active-set hysteresis counts are exact
non-Boolean integers with field-specific ranges. Flags and masks are exact
Booleans. Model parameters, confidence, solver tolerances, radii floors, and
optional representation shifts reject non-real or non-finite input before
solver work. A nonzero r_min and explicit weight_shift are rejected as a
deterministic input conflict before solving, active-set iteration, prediction,
objective evaluation, or radius construction. Finite observation source
coordinates must also yield finite representable connector differences,
squared distances, distances, and measurement conversions. Model and option
scalars are stored as built-in Python values.
SeparatorObservations, L2Regularization.reference, and directly retained
problem arrays own C-contiguous read-only copies; FitModel.penalties owns a
tuple. The documented wider non-negative separator external-ID range remains
unchanged. These adoption rules change invalid-input rejection only, not the
R1/R2 objective, backend selection, solver defaults, active-set mathematics,
or valid numerical fit. R6 subsequently adds row/set identity, optional exact
source binding, and report-schema metadata without changing public fit or
builder signatures. In particular, finite extreme-scale source inputs retain
R1/R2's stabilized handling of derived scaled-row infinities inside the
canonical problem builder; direct problem construction itself is finite-strict.
The active-set outer workflow and its path/result types remain experimental. The explicit SciPy sparse linear backend is provisional. It supports direct quadratic solving and ADMM weight systems, including active-set forwarding, without changing the solver method.
During v0.7, pyvoro2.powerfit.__all__ remained the exact 42-name historical
list recorded in the v0.6.3 baseline section. It did not export the canonical
names or contain implementation logic. Issue #28 removed that package, the
broad top-level historical separator set, and the five historical identity
aliases in v0.8.
Forward return contract
Implemented common data contract
Issues #9 and #10 implement one frozen, slotted TessellationResult class and export
the identical class object as both pyvoro2.TessellationResult and
pyvoro2.planar.TessellationResult. Its private shared builder aligns cells by
final external ID, represents omitted empty cells explicitly in aligned
arrays, and does not invoke native computation, diagnostics, normalization, or
boundary annotation.
The stable fields are exact:
| Field | Lifecycle | Semantics |
|---|---|---|
dimension |
Stable | Explicit 2 or 3. |
domain |
Stable | Validated domain used by the computation. |
mode |
Stable | "standard" or "power". |
sites |
Stable | Read-only owned (n, dimension) copy of validated input coordinates in original input order. |
ids |
Stable | Read-only owned (n,) external-ID array in original input order; omitted IDs become np.arange(n, dtype=np.int64). |
cells |
Stable | Exact supplied raw-cell list after ID remapping; the list, dictionaries, and nested records are not copied or frozen. |
cell_measures |
Stable | Read-only owned (n,) construction-time snapshot of areas or volumes aligned with input order; hidden cells are zero. |
empty_mask |
Stable | Read-only owned boolean (n,) construction-time snapshot aligned with input order, including raw records omitted by include_empty=False. |
input_weights |
Stable | Read-only owned mathematical input weights for weight-first power input; otherwise None. |
backend_radii |
Stable | Read-only owned exact native power radii; None in standard mode. |
representation_shift |
Stable | Finite common additive shift for weight-first conversion; None for standard or direct-radius input. |
tessellation_diagnostics |
Stable | Existing dimension-specific diagnostics when computed; otherwise None. |
normalized_vertices |
Stable | Existing dimension-specific vertex normalization when computed; otherwise None. |
normalized_topology |
Stable | Existing dimension-specific topology normalization when computed; otherwise None. |
The following convenience surface remains provisional:
| Convenience | Lifecycle | Semantics |
|---|---|---|
measure_kind |
Provisional | "area" in 2D or "volume" in 3D. |
boundary_kind |
Provisional | "edges" in 2D or "faces" in 3D. |
has_tessellation_diagnostics, has_normalized_vertices, has_normalized_topology |
Provisional | Distinguish absent optional objects from present objects. |
has_boundaries, has_periodic_shifts |
Provisional | Report explicit builder capabilities, including available-but-empty geometry. |
require_tessellation_diagnostics(), require_normalized_vertices(), require_normalized_topology() |
Provisional | Return optional objects or raise a clear ValueError. |
require_boundaries() |
Provisional | Return input-order-aligned edge/face collections, using an empty collection for hidden sites, or raise when boundaries were unavailable. |
global_vertices, global_edges |
Provisional conveniences | Forward to available planar normalized objects; otherwise None. |
The outer object prevents field replacement. Its aligned arrays are copies and
are non-writeable, so construction never marks caller-owned arrays read-only.
The raw cells list and its nested dictionaries remain shared and mutable by
design. Later raw-record mutation does not update the cell_measures or
empty_mask snapshots. Boundary access revalidates mutable boundary record
types, current empty flags, required non-empty records, and periodic-shift
fields and raises if mutation made them inconsistent with the recorded
snapshots or capabilities. An empty cell cannot contain realized edge or face
records; both omitted and explicitly empty boundary collections remain valid.
Direct dataclass construction is provisional and validates documented raw
IDs, measures, empty state, representation metadata, and capability metadata
against the aligned fields. It does not normalize arbitrary hand-written
backend-style dictionaries, recompute derived geometry, or geometrically verify
the records. Weight-first metadata must satisfy the shared exact
weight/shift-to-radius transform. Boundary and periodic-shift availability are
private keyword-only construction state supplied by the shared builder;
keeping them as normal dataclass initialization fields preserves them through
dataclasses.replace() without adding stable public result fields. Deep copies
and same-version pickle round trips preserve the exact existing snapshot state
rather than revalidating it against later permitted raw-record mutation;
reconstructed arrays remain owned and read-only, and capability state is
preserved. No cross-version pickle compatibility is promised.
Preferred compute route
result = pyvoro2.compute(..., output='result')
result = pyvoro2.planar.compute(..., output='result')
Omitting output is equivalent to output='result'. Structured output is
always one TessellationResult, never a tuple. Diagnostics computed because of
return_diagnostics=True or tessellation_check='diagnose'|'warn'|'raise'
are stored in result.tessellation_diagnostics.
Raw output route
cells = pyvoro2.compute(..., output='cells')
cells = pyvoro2.planar.compute(..., output='cells')
This route preserves the established list/tuple behavior. Without
return_diagnostics=True it returns only the raw list, including when a
tessellation check computed diagnostics internally. With
return_diagnostics=True it returns (cells, diagnostics). Raw record schemas,
ordering, external IDs, requested geometry, and numerical behavior remain the
characterized baseline.
Historical v0.7 planar compatibility selector matrix
In v0.7, the public compatibility parameter was
return_result: bool | None = None.
None means that the selector was omitted and follows the output= contract.
Passing either boolean emits DeprecationWarning; output= is the replacement.
| Planar selection | Result |
|---|---|
| both selectors omitted | TessellationResult |
return_result=None |
same as omitted; no deprecation warning |
output='result' |
TessellationResult |
output='cells' |
historical raw list/tuple route |
return_result=True, output omitted |
TessellationResult |
explicit return_result=False, output omitted, no normalization |
historical raw list/tuple route |
explicit return_result=False, output omitted, normalization requested |
TessellationResult, preserving the historical normalization override |
equivalent explicit output and return_result |
requested route, plus warning |
conflicting explicit output and return_result |
ValueError, plus warning |
explicit output='cells' with normalization |
ValueError |
In v0.7, PlanarComputeResult from both pyvoro2.planar and
pyvoro2.planar.result was an identity alias to
pyvoro2.TessellationResult; issue #28 removed both routes in v0.8.
Supported Python and distribution contract
The support claim is derived from pyproject.toml, the CI and wheel workflows,
and tools/check_wheel_matrix.py:
| Layer | Exact v0.8 contract |
|---|---|
| Package metadata | Requires-Python: >=3.10; classifiers list Python 3.10, 3.11, 3.12, 3.13, and 3.14; no blanket OS-independent classifier |
| Supported source builds | Standard GIL-enabled CPython 3.10–3.14 |
| Source-install CI | All five supported versions on Linux, macOS, and Windows |
| Wheel interpreters | CPython tags cp310, cp311, cp312, cp313, cp314 |
| Wheel platforms | manylinux x86_64, Windows AMD64, macOS arm64, macOS x86_64 |
| Release artifact target | Exactly 20 wheels and one matching source distribution; qualification remains issue #33 work |
| License payload | Wheel metadata licenses contain byte-identical LICENSE, COPYING, NOTICE.md, and LICENSE.voro++; the sdist also contains those root files and the vendored Voro++ copy |
| Optional SciPy | Not a runtime dependency; installed for wheel tests and imported only by explicit sparse paths |
The open-ended metadata lower bound allows installation tooling to evaluate a future Python version, but Python versions newer than 3.14 are not part of the tested v0.8 support contract. Free-threaded CPython, alternative interpreters, musllinux, non-x86_64 Linux, 32-bit and arm64 Windows, and macOS universal2 are explicitly excluded from the v0.8 wheel matrix. Source installation on an unlisted environment may succeed but is not a prebuilt-wheel or tested-support promise.
Every release wheel must contain both native modules, _core and _core2d,
and is installed and exercised on a compatible runner. The source distribution
is validated separately, rebuilt into one wheel under build isolation, and
installed in a fresh no-SciPy environment. These distribution checks do not
turn the internal native module names into public API.
R9 built representative local artifacts to validate the source contract, and
the accepted post-R9 correction made the mandatory COPYING payload part of
the checked wheel and sdist contract. That does not qualify the complete
20-wheel plus one-sdist release matrix. #33 qualifies the exact frozen v0.8.0
final source commit accepted after release finalization and independent review.
Scientifically meaningful semantics to inventory explicitly
The following are API even when no dedicated Python class represents them:
- coordinate units are caller-defined but consistent within one computation;
- power weights have squared-coordinate units;
- positive, zero, and negative finite power weights are valid when the global shift and converted representation remain finite and representable;
- non-finite weight input or conversion overflow raises
ValueErrorbefore native computation; - finite representability does not guarantee a numerically resolvable native
tessellation; Voro++ uses binary64 squared-radius arithmetic, so very large
absolute backend
radii**2values or genuine weight ranges relative to squared coordinate/domain scales can lose geometric resolution. No universal safe cutoff is promised, and periodic power tessellations are particularly sensitive; - backend radii have coordinate units;
- one global additive weight shift leaves the complete power diagram unchanged;
- power-mode
compute(...)requires exactly one ofweights=orradii=, while standard mode rejects both arguments; - valid radius-based power computation remains numerically unchanged;
- direct
weights=input currently belongs to the twocompute(...)functions, not to every forward operation; - backend radii are a shifted representation and are not unique physical radii;
- disconnected separator-observation components have additional unidentified offsets that may change global realization;
- external IDs remain attached to original sites;
- periodic neighbor shifts identify the realized image and are not silently replaced by a nearest image;
- every valid separator observation row and ordered observation set has a deterministic source-independent identity; warnings are not row identity;
- exact separator source provenance, when known, preserves caller-order points, exact domain representation, dimension/count, and ID provenance, and cannot be erased or rebound;
- unbound and bound observations never associate with one another, and two bound origins associate only under exact canonical source equality;
- zero-confidence separator rows do not identify weight differences or enter the informative observation graph; hard restrictions and penalties may constrain their predicted values but remain separate from data identification;
- row confidence multiplies only separator mismatch; squared mismatch and the
Huber quadratic branch are
0.5 * residual**2, while L2 regularization is0.5 * strength * ||weights - reference||**2; - zero-strength scalar penalties do not affect objective values, coupling, backend selection, or quadratic-operator availability;
hard_max_violationis a raw violation andhard_max_tolerancerecords the maximum shared absolute-plus-relative float64 tolerance used;- optimal or converged solver results have finite reported soft-objective components and totals;
- algebraic fit does not imply realized-boundary support;
- empty/hidden cells are represented deterministically according to the chosen output route;
- error/status behavior for infeasibility and wrong-image realization is part of the public scientific contract.
Deprecation and fixed removal schedule
| Surface | v0.7 | v0.8 action |
|---|---|---|
pyvoro2.powerfit |
Compatibility-only and deprecated; loading it emits one hidden-by-default DeprecationWarning naming the canonical namespaces and v0.8 horizon |
Remove |
| Broad top-level separator exports | Compatibility-only; no invasive attribute wrappers during v0.7; documented migration path | Remove from top-level |
| Five mapped historical core names | Compatibility-only and deprecated identity aliases; no per-use warnings | Remove from canonical separator exports |
PlanarComputeResult |
Compatibility-only and deprecated alias to TessellationResult |
Remove |
| Raw cell return | Available through output='cells' |
Continue as explicit route unless a later decision removes it |
Planar return_result= |
Compatibility-only and deprecated | Remove |
v0.8 compatibility removal status
Issue #28 completed this schedule without changing canonical numerical behavior. The v0.8 tree:
- has no
pyvoro2.powerfitpackage, direct submodules, or lazy top-level package attribute; - exports no separator-specific objects from top-level
pyvoro2; - contains none of the five mapped historical core aliases in either
pyvoro2.inverse.separatoror their former direct canonical submodules; - exports only
TessellationResultfrom the planar namespace and does not providepyvoro2.planar.result; - has no planar
return_result=parameter; and - retains
output='cells'as the explicit supported raw-output route.
The high-level pyvoro2.inverse export set, canonical class and function names,
solver defaults, numerical values, result fields, record keys, and gauge
policies are unchanged.
v0.8 private-helper organization status
Issue #30 moved all private pure-Python implementation helpers into
pyvoro2._internal:
pyvoro2._internal.cell_output
pyvoro2._internal.inputs
pyvoro2._internal.power_input
pyvoro2._internal.validation
pyvoro2._internal.weight_transforms
pyvoro2._internal.spatial.domain_geometry
pyvoro2._internal.spatial.domain_utils
pyvoro2._internal.spatial.face_shifts
pyvoro2._internal.planar.domain_geometry
pyvoro2._internal.planar.edge_shifts
These module routes and every object available only from them are
internal. The _internal, _internal.spatial, and _internal.planar
package initializers provide no convenience re-exports. The former root helper
modules and former pyvoro2.planar helper modules are absent, with no
compatibility shims, because they were never documented or exported as public
API.
The stable public weights_to_radii and radii_to_weights exports remain
identical function objects across pyvoro2, pyvoro2.inverse, and
pyvoro2.inverse.separator; only their internal implementation-module metadata
now names pyvoro2._internal.weight_transforms. Public signatures, defaults,
transform semantics, forward and inverse numerical results, record schemas,
and lazy native-extension loading are unchanged.
pyvoro2.__about__ remains root-owned build metadata rather than a helper
module. Native pyvoro2._core and pyvoro2._core2d remain root-owned internal
extensions with their established names and loading behavior.
Final release review checklist
- [x] Every preferred public import is listed with a lifecycle category.
- [x] Every removed compatibility alias has a canonical replacement and completed removal record.
- [x]
__all__matches the intended namespace policy. - [x] Forward output modes and diagnostic combinations are characterized.
- [x] Stable
TessellationResultfields and mutable contained values are documented. - [x] Exact current raw and inverse record keys are listed.
- [x] Preferred separator names and exact historical aliases are complete.
- [x] Active-set behavior is labelled experimental; the included sparse quadratic backend is labelled provisional and narrowly scoped.
- [x] Default changes and scientific semantics appear in migration notes and release notes.
- [x] The chemvoro-shaped integration workflow uses only stable or deliberately provisional public surfaces.
- [x] The inventory was re-audited against the v0.8 tree on 2026-07-24 for maintainer and release review.