Skip to content

Commit 72fc49e

Browse files
authored
Merge pull request #441 from PyAutoLabs/prototype/jax-sibson
Add autodifferentiable DelaunayNN mesh
2 parents f2f7a4f + ab5cc66 commit 72fc49e

13 files changed

Lines changed: 1237 additions & 18 deletions

File tree

autoarray/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
from .inversion.mesh.mesh.abstract import AbstractMesh
4242
from .inversion.mesh.interpolator.rectangular import InterpolatorRectangular
4343
from .inversion.mesh.interpolator.delaunay import InterpolatorDelaunay
44+
from .inversion.mesh.interpolator.sibson import InterpolatorDelaunayNN
4445
from .inversion.inversion.imaging.mapping import InversionImagingMapping
4546
from .inversion.inversion.imaging.sparse import InversionImagingSparse
4647
from .inversion.inversion.imaging.inversion_imaging_util import ImagingSparseOperator
@@ -79,6 +80,7 @@
7980
from .inversion.mesh.mesh_geometry.rectangular import MeshGeometryRectangular
8081
from .inversion.mesh.mesh_geometry.delaunay import MeshGeometryDelaunay
8182
from .inversion.mesh.interpolator.delaunay import InterpolatorDelaunay
83+
from .inversion.mesh.interpolator.sibson import InterpolatorDelaunayNN
8284
from .operators.convolver import Convolver
8385
from .operators.interp_2d import interp_2d
8486
from .structures.vectors.uniform import VectorYX2D

autoarray/inversion/mesh/interpolator/delaunay.py

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,9 @@ def _jax_delaunay_tables(points):
133133
NOT an approximation: the callback returns only int32 connectivity
134134
tables, which are piecewise-constant in the vertex positions — their
135135
true derivative is exactly zero everywhere except the measure-zero
136-
re-wiring (triangle-flip) events, where the likelihood itself is
137-
discontinuous and no gradient exists for any method. Every quantity
136+
re-wiring (triangle-flip) events. The barycentric interpolant is
137+
discontinuous there; the Sibson interpolant in ``DelaunayNN`` instead has
138+
matching limits across an ordinary flip. Every quantity
138139
with a non-zero derivative (point location via the visibility walk,
139140
barycentric weights, dual areas, split points) is computed in-graph
140141
from the traced ``points``, so the frozen-tables gradient is the exact
@@ -164,6 +165,7 @@ def pix_indexes_delaunay_walk_from(
164165
simplex_neighbors,
165166
vertex_simplex,
166167
xp=np,
168+
return_simplex_indexes=False,
167169
):
168170
"""JAX/NumPy point location replacing ``scipy.spatial.Delaunay.find_simplex``
169171
on the JAX likelihood path, via the same visibility-walk algorithm
@@ -185,6 +187,11 @@ def pix_indexes_delaunay_walk_from(
185187
vmap (JAX path; the NumPy path — used by the unit tests — processes the
186188
whole array with early exit). Returns a (Q, 3) int32 mapping array with
187189
the same semantics as ``pix_indexes_for_sub_slim_index_delaunay_from``.
190+
191+
When ``return_simplex_indexes`` is true, also return the containing
192+
simplex index for every query (or -1 outside the convex hull). Sibson
193+
interpolation uses this as the seed of its circumcircle-cavity walk, so
194+
point location is not repeated.
188195
"""
189196

190197
def cross(u, v):
@@ -251,10 +258,15 @@ def locate_chunk(q_chunk):
251258

252259
verts = simplices_padded[cur]
253260
fallback = xp.stack([seed, -xp.ones_like(seed), -xp.ones_like(seed)], axis=1)
254-
return xp.where(done[:, None], verts, fallback).astype(xp.int32)
261+
mappings = xp.where(done[:, None], verts, fallback).astype(xp.int32)
262+
simplex_indexes = xp.where(done, cur, -1).astype(xp.int32)
263+
return mappings, simplex_indexes
255264

256265
if xp is np:
257-
return locate_chunk(query_points)
266+
mappings, simplex_indexes = locate_chunk(query_points)
267+
if return_simplex_indexes:
268+
return mappings, simplex_indexes
269+
return mappings
258270

259271
import jax
260272

@@ -265,8 +277,14 @@ def locate_chunk(q_chunk):
265277
q_padded = xp.concatenate(
266278
[query_points, xp.full((pad, 2), 1.0e9, dtype=query_points.dtype)]
267279
)
268-
mappings = jax.lax.map(locate_chunk, q_padded.reshape(-1, chunk, 2)).reshape(-1, 3)
269-
return mappings[:Q]
280+
mappings, simplex_indexes = jax.lax.map(
281+
locate_chunk, q_padded.reshape(-1, chunk, 2)
282+
)
283+
mappings = mappings.reshape(-1, 3)[:Q]
284+
simplex_indexes = simplex_indexes.reshape(-1)[:Q]
285+
if return_simplex_indexes:
286+
return mappings, simplex_indexes
287+
return mappings
270288

271289

272290
def jax_delaunay(points, query_points, areas_factor=0.5):

0 commit comments

Comments
 (0)