Skip to content

Commit bc5b857

Browse files
Jammy2211Jammy2211
authored andcommitted
cleaning up to retain Voronoi functionality
1 parent ec5ed6f commit bc5b857

File tree

1 file changed

+70
-38
lines changed

1 file changed

+70
-38
lines changed

autoarray/structures/mesh/delaunay_2d.py

Lines changed: 70 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import numpy as np
22
import scipy.spatial
3-
from scipy.spatial import cKDTree
3+
from scipy.spatial import cKDTree, Delaunay
44
from typing import List, Union, Optional, Tuple
55

66
from autoconf import cached_property
@@ -16,8 +16,6 @@
1616

1717
def scipy_delaunay(points_np, query_points_np, max_simplices):
1818
"""Compute Delaunay simplices (simplices_padded) and Voronoi areas in one call."""
19-
from scipy.spatial import Delaunay
20-
import numpy as np
2119

2220
# --- Delaunay mesh using source plane data grid ---
2321
tri = Delaunay(points_np)
@@ -331,6 +329,16 @@ def geometry(self):
331329
scaled_minima=scaled_minima,
332330
)
333331

332+
@cached_property
333+
def mesh_grid_xy(self):
334+
"""
335+
The default convention in `scipy.spatial` is to represent 2D coordinates as (x,y) pairs, whereas PyAutoArray
336+
represents 2D coordinates as (y,x) pairs.
337+
338+
Therefore, this property simply converts the (y,x) grid of irregular coordinates into an (x,y) grid.
339+
"""
340+
return self._xp.stack([self.array[:, 1], self.array[:, 0]]).T
341+
334342
@cached_property
335343
def delaunay(self) -> "scipy.spatial.Delaunay":
336344
"""
@@ -345,22 +353,19 @@ def delaunay(self) -> "scipy.spatial.Delaunay":
345353
to compute the Voronoi mesh are ill posed. These exceptions are caught and combined into a single
346354
`MeshException`, which helps exception handling in the `inversion` package.
347355
"""
348-
349-
mesh_grid = self._xp.stack([self.array[:, 0], self.array[:, 1]]).T
350-
351356
if self._xp.__name__.startswith("jax"):
352357

353358
import jax.numpy as jnp
354359

355360
points, simplices, mappings, split_points, splitted_mappings = jax_delaunay(
356-
points=mesh_grid,
361+
points=self.mesh_grid_xy,
357362
query_points=self._source_plane_data_grid_over_sampled
358363
)
359364

360365
else:
361366

362367
points, simplices, mappings, split_points, splitted_mappings = scipy_delaunay(
363-
points_np=mesh_grid,
368+
points=self.mesh_grid_xy,
364369
query_points_np=self.source_plane_data_grid_over_sampled
365370
)
366371

@@ -415,35 +420,11 @@ def neighbors(self) -> Neighbors:
415420

416421
return Neighbors(arr=neighbors.astype("int"), sizes=sizes.astype("int"))
417422

418-
@property
419-
def voronoi(self) -> "scipy.spatial.Voronoi":
420-
"""
421-
Returns a `scipy.spatial.Voronoi` object from the 2D (y,x) grid of irregular coordinates, which correspond to
422-
the centre of every Voronoi pixel.
423-
424-
This object contains numerous attributes describing a Voronoi mesh. PyAutoArray uses
425-
the `vertex_neighbor_vertices` attribute to determine the neighbors of every Delaunay triangle.
426-
427-
There are numerous exceptions that `scipy.spatial.Delaunay` may raise when the input grid of coordinates used
428-
to compute the Delaunay triangulation are ill posed. These exceptions are caught and combined into a single
429-
`MeshException`, which helps exception handling in the `inversion` package.
430-
"""
431-
import scipy.spatial
432-
from scipy.spatial import QhullError
433-
434-
try:
435-
return scipy.spatial.Voronoi(
436-
np.asarray([self.array[:, 1], self.array[:, 0]]).T,
437-
qhull_options="Qbb Qc Qx Qm",
438-
)
439-
except (ValueError, OverflowError, QhullError) as e:
440-
raise exc.MeshException() from e
441-
442423
def interpolated_array_from(
443-
self,
444-
values: np.ndarray,
445-
shape_native: Tuple[int, int] = (401, 401),
446-
extent: Optional[Tuple[float, float, float, float]] = None,
424+
self,
425+
values: np.ndarray,
426+
shape_native: Tuple[int, int] = (401, 401),
427+
extent: Optional[Tuple[float, float, float, float]] = None,
447428
) -> Array2D:
448429
"""
449430
The reconstruction of data on a `Delaunay` triangulation (e.g. the `reconstruction` output from an `Inversion`)
@@ -470,21 +451,72 @@ def interpolated_array_from(
470451
The (x0, x1, y0, y1) extent of the grid in scaled coordinates over which the grid is created if it
471452
is input.
472453
"""
454+
# Uses find simplex so recomputes delaunay internally
455+
delaunay = Delaunay(self.mesh_grid_xy)
456+
473457
interpolation_grid = self.interpolation_grid_from(
474458
shape_native=shape_native, extent=extent
475459
)
476460

477461
interpolated_array = mesh_numba_util.delaunay_interpolated_array_from(
478462
shape_native=shape_native,
479463
interpolation_grid_slim=np.array(interpolation_grid.slim.array),
480-
delaunay=self.delaunay,
464+
delaunay=delaunay,
481465
pixel_values=values,
482466
)
483467

484468
return Array2D.no_mask(
485469
values=interpolated_array, pixel_scales=interpolation_grid.pixel_scales
486470
)
487471

472+
@cached_property
473+
def voronoi(self) -> "scipy.spatial.Voronoi":
474+
"""
475+
Returns a `scipy.spatial.Voronoi` object from the 2D (y,x) grid of irregular coordinates, which correspond to
476+
the centre of every Voronoi pixel.
477+
478+
This object contains numerous attributes describing a Voronoi mesh. PyAutoArray uses
479+
the `vertex_neighbor_vertices` attribute to determine the neighbors of every Delaunay triangle.
480+
481+
There are numerous exceptions that `scipy.spatial.Delaunay` may raise when the input grid of coordinates used
482+
to compute the Delaunay triangulation are ill posed. These exceptions are caught and combined into a single
483+
`MeshException`, which helps exception handling in the `inversion` package.
484+
"""
485+
import scipy.spatial
486+
from scipy.spatial import QhullError
487+
488+
try:
489+
return scipy.spatial.Voronoi(
490+
self.mesh_grid_xy,
491+
qhull_options="Qbb Qc Qx Qm",
492+
)
493+
except (ValueError, OverflowError, QhullError) as e:
494+
raise exc.MeshException() from e
495+
496+
@property
497+
def voronoi_areas(self):
498+
499+
N = self.mesh_grid_xy.shape[0]
500+
501+
voronoi_areas = np.zeros(N)
502+
503+
for i in range(N):
504+
region_vertices_indexes = self.voronoi.regions[self.voronoi.point_region[i]]
505+
if -1 in region_vertices_indexes:
506+
voronoi_areas[i] = -1
507+
else:
508+
509+
points_of_region = self.voronoivertices[region_vertices_indexes]
510+
511+
x = points_of_region[:, 1]
512+
y = points_of_region[:, 0]
513+
514+
voronoi_areas[i] = 0.5 * np.abs(
515+
np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1))
516+
)
517+
518+
return voronoi_areas
519+
488520
@property
489521
def areas_for_magnification(self) -> np.ndarray:
490522
"""
@@ -494,7 +526,7 @@ def areas_for_magnification(self) -> np.ndarray:
494526
calculation. This method therefore sets their areas to zero so they do not impact the magnification
495527
calculation.
496528
"""
497-
areas = self.delaunay.voronoi_areas
529+
areas = self.voronoi_areas
498530

499531
areas[areas == -1] = 0.0
500532

0 commit comments

Comments
 (0)