autoarray 2026.9.26.1__tar.gz → 2026.9.27.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/AGENTS.md +14 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/PKG-INFO +1 -1
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/__init__.py +1 -1
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/dataset_interface.py +9 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/factory.py +10 -9
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/interferometer/mapping.py +4 -2
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/interferometer/sparse.py +16 -6
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/interferometer_numba/sparse.py +1 -1
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/operators/transformer.py +12 -7
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/operators/transformer_util.py +11 -6
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/triangles/array.py +131 -3
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/triangles/coordinate_array.py +120 -9
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/.claude/hooks/end-at-deliverable.sh +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/.claude/hooks/session-start.sh +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/.claude/settings.json +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/.gitattributes +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/.github/copilot-instructions.md +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/.github/workflows/main.yml +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/.url_check_allowlist.txt +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/AI_POLICY.md +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/CLAUDE.md +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/CONTRIBUTING.md +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/LICENSE +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/MANIFEST.in +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/README.md +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/abstract_ndarray.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/config/.gitignore +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/config/README.md +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/config/general.yaml +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/config/logging.yaml +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/config/visualize/README.md +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/config/visualize/general.yaml +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/config/visualize/plots.yaml +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/dataset/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/dataset/abstract/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/dataset/abstract/dataset.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/dataset/dataset_model.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/dataset/grids.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/dataset/imaging/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/dataset/imaging/dataset.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/dataset/imaging/simulator.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/dataset/interferometer/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/dataset/interferometer/dataset.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/dataset/interferometer/simulator.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/dataset/mock/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/dataset/mock/mock_dataset.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/dataset/plot/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/dataset/plot/imaging_plots.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/dataset/plot/interferometer_plots.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/dataset/preprocess.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/exc.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/fit/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/fit/fit_dataset.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/fit/fit_imaging.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/fit/fit_interferometer.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/fit/fit_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/fit/mock/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/fit/mock/mock_fit_imaging.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/fit/mock/mock_fit_interferometer.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/fit/plot/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/fit/plot/fit_imaging_plots.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/fit/plot/fit_interferometer_plots.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/fixtures.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/geometry/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/geometry/abstract_2d.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/geometry/geometry_1d.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/geometry/geometry_2d.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/geometry/geometry_2d_irregular.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/geometry/geometry_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/abstract.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/imaging/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/imaging/abstract.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/imaging/inversion_imaging_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/imaging/mapping.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/imaging/sparse.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/imaging_numba/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/imaging_numba/inversion_imaging_numba_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/imaging_numba/sparse.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/interferometer/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/interferometer/abstract.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/interferometer/inversion_interferometer_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/interferometer_numba/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/interferometer_numba/inversion_interferometer_numba_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/inversion_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/nnls_memo.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/linear_obj/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/linear_obj/func_list.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/linear_obj/linear_obj.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/linear_obj/neighbors.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/linear_obj/unique_mappings.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mappers/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mappers/abstract.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mappers/mapper_numba_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mappers/mapper_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mappings/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mappings/mapping.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/border_relocator.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/image_mesh/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/image_mesh/abstract.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/image_mesh/abstract_weighted.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/image_mesh/hilbert.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/image_mesh/kmeans.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/image_mesh/overlay.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/interpolator/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/interpolator/abstract.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/interpolator/delaunay.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/interpolator/knn.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/interpolator/rectangular.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/interpolator/rectangular_uniform.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/interpolator/sibson.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/mesh/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/mesh/abstract.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/mesh/delaunay.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/mesh/delaunay_nn.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/mesh/knn.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/mesh/rectangular_bilinear_adapt_density.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/mesh/rectangular_bilinear_adapt_image.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/mesh/rectangular_rtu_adapt_density.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/mesh/rectangular_rtu_adapt_image.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/mesh/rectangular_uniform.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/mesh_geometry/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/mesh_geometry/abstract.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/mesh_geometry/delaunay.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mesh/mesh_geometry/rectangular.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mock/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mock/mock_image_mesh.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mock/mock_interpolator.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mock/mock_inversion.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mock/mock_inversion_imaging.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mock/mock_inversion_interferometer.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mock/mock_linear_obj.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mock/mock_linear_obj_func_list.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mock/mock_mapper.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mock/mock_mesh.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mock/mock_pixelization.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/mock/mock_regularization.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/pixelization.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/plot/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/plot/inversion_plots.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/plot/mapper_plots.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/abstract.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/adapt.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/adapt_power.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/adapt_split.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/adapt_split_power.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/adapt_split_zeroth.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/adapt_split_zeroth_power.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/brightness_zeroth.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/constant.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/constant_split.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/constant_zeroth.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/curvature_mask.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/exponential_kernel.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/fourth_order_mask.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/gaussian_kernel.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/matern_adapt_kernel.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/matern_adapt_power_kernel.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/matern_kernel.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/regularization_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/regularization/zeroth.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/layout/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/layout/layout.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/layout/layout_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/layout/region.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/mask/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/mask/abstract_mask.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/mask/derive/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/mask/derive/grid_1d.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/mask/derive/grid_2d.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/mask/derive/indexes_2d.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/mask/derive/mask_1d.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/mask/derive/mask_2d.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/mask/derive/zoom_2d.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/mask/mask_1d.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/mask/mask_1d_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/mask/mask_2d.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/mask/mask_2d_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/mask/mock/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/mask/mock/mock_mask.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/mock.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/numba_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/operators/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/operators/coarse_interp_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/operators/contour.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/operators/convolver.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/operators/derivative_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/operators/interp_2d.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/operators/mock/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/operators/mock/mock_psf.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/operators/over_sampling/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/operators/over_sampling/decorator.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/operators/over_sampling/over_sample_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/operators/over_sampling/over_sampler.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/plot/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/plot/array.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/plot/grid.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/plot/inversion.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/plot/output.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/plot/segmentdata.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/plot/utils.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/plot/yx.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/preloads/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/preloads/abstract.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/preloads/imaging.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/preloads/interferometer.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/settings.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/abstract_structure.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/arrays/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/arrays/array_1d_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/arrays/array_2d_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/arrays/irregular.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/arrays/rgb.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/arrays/uniform_1d.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/arrays/uniform_2d.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/decorators/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/decorators/abstract.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/decorators/to_array.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/decorators/to_grid.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/decorators/to_vector_yx.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/decorators/transform.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/grids/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/grids/grid_1d_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/grids/grid_2d_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/grids/irregular_2d.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/grids/sparse_2d_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/grids/uniform_1d.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/grids/uniform_2d.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/header.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/mock/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/mock/mock_decorators.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/mock/mock_grid.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/plot/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/plot/structure_plots.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/triangles/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/triangles/abstract.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/triangles/array_np.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/triangles/coordinate_array_np.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/triangles/shape.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/vectors/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/vectors/abstract.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/vectors/irregular.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/vectors/uniform.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/visibilities.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/type.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/util/__init__.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/util/cholesky_funcs.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/util/dataset_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/util/fnnls.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/util/jax_active_set.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/util/jax_nnls.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/util/misc_util.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/validate.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray.egg-info/SOURCES.txt +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/docs/Makefile +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/docs/agents/jax_and_decorators.md +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/docs/conf.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/docs/index.md +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/docs/make.bat +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/files/cdf_audit.md +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/files/ghost_peak.png +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/files/ghost_peak_experiment.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/files/ghost_peak_findings.md +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/files/pca_rotation.png +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/files/pca_rotation_experiment.py +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/llms.txt +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/pyproject.toml +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/readthedocs.yml +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/setup.cfg +0 -0
- {autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/setup.py +0 -0
|
@@ -138,3 +138,17 @@ outlives the turn to wait for CI, a review or a merge: no `send_later`, no
|
|
|
138
138
|
armed hourly check-ins on 2026-08-31, and a mobile `/prm` re-armed a 60-minute
|
|
139
139
|
`send_later` hourly all night on 2026-09-03 with no task active, draining usage.
|
|
140
140
|
<!-- repos_sync:deliverable:end -->
|
|
141
|
+
|
|
142
|
+
<!-- repos_sync:filing:begin -->
|
|
143
|
+
## Where to file
|
|
144
|
+
|
|
145
|
+
Questions, help with code or an analysis, ideas, bug reports and results from a
|
|
146
|
+
user or collaborator — or an agent acting for one — go to
|
|
147
|
+
<https://github.com/orgs/PyAutoLabs/discussions> in the matching category
|
|
148
|
+
(Help & Questions, Ideas & Proposals, Bugs & Errors, Show and tell;
|
|
149
|
+
Announcements is maintainers-only), never to this repo's Issues. An agent never
|
|
150
|
+
runs `gh issue create` for such a report: it drafts the title, category and
|
|
151
|
+
body and hands them to the human (sessions cannot create Discussions). Only the
|
|
152
|
+
development flow — Mind prompt → `/start_dev` → `/create_issue` → one issue per
|
|
153
|
+
task → PR — opens issues here. Why: `PyAutoMind/policy/community_surface.md`.
|
|
154
|
+
<!-- repos_sync:filing:end -->
|
|
@@ -111,7 +111,7 @@ conf.instance.register(__file__)
|
|
|
111
111
|
# Last manual sync of the source stamp. Release wheels are stamped at build
|
|
112
112
|
# time and the git tag is the release truth — deliberately never bumped per
|
|
113
113
|
# release (PyAutoBuild#118/#120).
|
|
114
|
-
__version__ = "2026.9.
|
|
114
|
+
__version__ = "2026.9.27.2"
|
|
115
115
|
|
|
116
116
|
# ---------------------------------------------------------------------------
|
|
117
117
|
# Public re-export of the autonerves configuration / serialization surface.
|
{autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/inversion/inversion/dataset_interface.py
RENAMED
|
@@ -8,6 +8,7 @@ class DatasetInterface:
|
|
|
8
8
|
transformer=None,
|
|
9
9
|
sparse_operator=None,
|
|
10
10
|
noise_covariance_matrix=None,
|
|
11
|
+
sparse_dirty_image=None,
|
|
11
12
|
):
|
|
12
13
|
"""
|
|
13
14
|
Generic class which acts as an interface between a dataset and an inversion.
|
|
@@ -49,6 +50,13 @@ class DatasetInterface:
|
|
|
49
50
|
noise_covariance_matrix
|
|
50
51
|
A noise-map covariance matrix representing the covariance between noise in every `data` value, which
|
|
51
52
|
can be used via a bespoke fit to account for correlated noise in the data.
|
|
53
|
+
sparse_dirty_image
|
|
54
|
+
The noise-weighted dirty image `Re(Fᴴ W d)` of this interface's `data`, used by the sparse (w-tilde)
|
|
55
|
+
interferometer inversion to form its data vector. The `sparse_operator` caches the dirty image of the
|
|
56
|
+
visibilities it was built from, so this is only needed when `data` differs from them (e.g. when the
|
|
57
|
+
visibilities of ordinary light profiles have been subtracted). If `None`, the operator's cached dirty
|
|
58
|
+
image is used. This is distinct from `Interferometer.dirty_image`, the unweighted dirty image of the
|
|
59
|
+
data used for visualization.
|
|
52
60
|
"""
|
|
53
61
|
self.data = data
|
|
54
62
|
self.noise_map = noise_map
|
|
@@ -57,6 +65,7 @@ class DatasetInterface:
|
|
|
57
65
|
self.transformer = transformer
|
|
58
66
|
self.sparse_operator = sparse_operator
|
|
59
67
|
self.noise_covariance_matrix = noise_covariance_matrix
|
|
68
|
+
self.sparse_dirty_image = sparse_dirty_image
|
|
60
69
|
|
|
61
70
|
@property
|
|
62
71
|
def mask(self):
|
|
@@ -125,6 +125,10 @@ def inversion_imaging_from(
|
|
|
125
125
|
An `Inversion` whose type is determined by the input `dataset` and `settings`.
|
|
126
126
|
"""
|
|
127
127
|
|
|
128
|
+
# Unlike `inversion_interferometer_from`, imaging keeps func-list-only inversions (e.g. an MGE with no
|
|
129
|
+
# pixelization) on the mapping formalism. A func-list-only imaging mapping inversion is cheap (one PSF
|
|
130
|
+
# convolution per basis function), and the imaging sparse data vector's handling of profile-subtracted
|
|
131
|
+
# data is tracked separately, so the interferometer routing change is deliberately not mirrored here.
|
|
128
132
|
use_sparse_operator = True
|
|
129
133
|
|
|
130
134
|
if all(
|
|
@@ -199,15 +203,12 @@ def inversion_interferometer_from(
|
|
|
199
203
|
-------
|
|
200
204
|
An `Inversion` whose type is determined by the input `dataset` and `settings`.
|
|
201
205
|
"""
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
use_sparse_operator = False
|
|
209
|
-
|
|
210
|
-
if dataset.sparse_operator is not None and use_sparse_operator:
|
|
206
|
+
# A sparse operator selects the w-tilde formalism for every linear object list, including func-list-only
|
|
207
|
+
# ones (e.g. an MGE with no pixelization). For interferometer data this avoids the dense
|
|
208
|
+
# `transform_mapping_matrix` of shape (N_vis, S), which dominates run time and memory at large N_vis, and
|
|
209
|
+
# `InversionInterferometerSparse` handles zero mappers (the func-func curvature blocks and the data vector
|
|
210
|
+
# need no mapper). `_use_interferometer_numba` returns False whenever a func-list is present.
|
|
211
|
+
if dataset.sparse_operator is not None:
|
|
211
212
|
|
|
212
213
|
if _use_interferometer_numba(
|
|
213
214
|
linear_obj_list=linear_obj_list,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
from typing import Dict, List, Union
|
|
3
3
|
|
|
4
|
+
from autonerves import cached_property
|
|
5
|
+
|
|
4
6
|
from autoarray.dataset.interferometer.dataset import Interferometer
|
|
5
7
|
from autoarray.inversion.inversion.dataset_interface import DatasetInterface
|
|
6
8
|
from autoarray.inversion.inversion.interferometer.abstract import (
|
|
@@ -50,7 +52,7 @@ class InversionInterferometerMapping(AbstractInversionInterferometer):
|
|
|
50
52
|
dataset=dataset, linear_obj_list=linear_obj_list, settings=settings, xp=xp
|
|
51
53
|
)
|
|
52
54
|
|
|
53
|
-
@
|
|
55
|
+
@cached_property
|
|
54
56
|
def data_vector(self) -> np.ndarray:
|
|
55
57
|
"""
|
|
56
58
|
The `data_vector` is a 1D vector whose values are solved for by the simultaneous linear equations constructed
|
|
@@ -71,7 +73,7 @@ class InversionInterferometerMapping(AbstractInversionInterferometer):
|
|
|
71
73
|
noise_map=self.noise_map,
|
|
72
74
|
)
|
|
73
75
|
|
|
74
|
-
@
|
|
76
|
+
@cached_property
|
|
75
77
|
def curvature_matrix(self) -> np.ndarray:
|
|
76
78
|
"""
|
|
77
79
|
The `curvature_matrix` is a 2D matrix which uses the mappings between the data and the linear objects to
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
from typing import Dict, List, Optional, Union
|
|
3
3
|
|
|
4
|
+
from autonerves import cached_property
|
|
5
|
+
|
|
4
6
|
from autoarray import exc
|
|
5
7
|
from autoarray.dataset.interferometer.dataset import Interferometer
|
|
6
8
|
from autoarray.inversion.inversion.dataset_interface import DatasetInterface
|
|
@@ -67,7 +69,7 @@ class InversionInterferometerSparse(AbstractInversionInterferometer):
|
|
|
67
69
|
preloads=preloads,
|
|
68
70
|
)
|
|
69
71
|
|
|
70
|
-
@
|
|
72
|
+
@cached_property
|
|
71
73
|
def data_vector(self) -> np.ndarray:
|
|
72
74
|
"""
|
|
73
75
|
The `data_vector` is a 1D vector whose values are solved for by the simultaneous linear equations constructed
|
|
@@ -88,10 +90,18 @@ class InversionInterferometerSparse(AbstractInversionInterferometer):
|
|
|
88
90
|
which is exactly the entry the mapping (dense) formalism computes via the linear
|
|
89
91
|
function's transformed mapping matrix. Linear function lists therefore require no
|
|
90
92
|
separate branch here.
|
|
93
|
+
|
|
94
|
+
The cached dirty image is that of the visibilities the `sparse_operator` was built from. When
|
|
95
|
+
the inversion's data differs from them (e.g. a `DatasetInterface` whose data has the visibilities
|
|
96
|
+
of ordinary light profiles subtracted), the dataset's `sparse_dirty_image` of that data is used
|
|
97
|
+
instead, so that `D` is consistent with the data the chi-squared is computed from.
|
|
91
98
|
"""
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
99
|
+
dirty_image = getattr(self.dataset, "sparse_dirty_image", None)
|
|
100
|
+
|
|
101
|
+
if dirty_image is None:
|
|
102
|
+
dirty_image = self.dataset.sparse_operator.dirty_image
|
|
103
|
+
|
|
104
|
+
return self._xp.dot(self.mapping_matrix.T, dirty_image)
|
|
95
105
|
|
|
96
106
|
def _sparse_triplets_curvature_from(self, mapper: Mapper):
|
|
97
107
|
"""
|
|
@@ -121,7 +131,7 @@ class InversionInterferometerSparse(AbstractInversionInterferometer):
|
|
|
121
131
|
xp=self._xp,
|
|
122
132
|
)
|
|
123
133
|
|
|
124
|
-
@
|
|
134
|
+
@cached_property
|
|
125
135
|
def curvature_matrix(self) -> np.ndarray:
|
|
126
136
|
"""
|
|
127
137
|
The `curvature_matrix` is a 2D matrix which uses the mappings between the data and the linear objects to
|
|
@@ -179,7 +189,7 @@ class InversionInterferometerSparse(AbstractInversionInterferometer):
|
|
|
179
189
|
|
|
180
190
|
return curvature_matrix
|
|
181
191
|
|
|
182
|
-
@
|
|
192
|
+
@cached_property
|
|
183
193
|
def curvature_matrix_diag(self) -> np.ndarray:
|
|
184
194
|
"""
|
|
185
195
|
The `curvature_matrix` is a 2D matrix which uses the mappings between the data and the linear objects to
|
|
@@ -181,7 +181,7 @@ class InversionInterferometerSparseNumba(InversionInterferometerSparse):
|
|
|
181
181
|
|
|
182
182
|
return inputs
|
|
183
183
|
|
|
184
|
-
@
|
|
184
|
+
@cached_property
|
|
185
185
|
def curvature_matrix_diag(self) -> np.ndarray:
|
|
186
186
|
"""
|
|
187
187
|
`F = Aᵀ W~ A` for the inversion's single mapper, from the `direct_conv` numba
|
|
@@ -199,9 +199,12 @@ class TransformerDFT:
|
|
|
199
199
|
mask as this transformer's `real_space_mask`.
|
|
200
200
|
"""
|
|
201
201
|
image_slim = transformer_util.image_direct_from(
|
|
202
|
-
visibilities=
|
|
202
|
+
visibilities=xp.stack(
|
|
203
|
+
(xp.real(visibilities.array), xp.imag(visibilities.array)), axis=-1
|
|
204
|
+
),
|
|
203
205
|
grid_radians=self.grid.array,
|
|
204
206
|
uv_wavelengths=self.uv_wavelengths,
|
|
207
|
+
xp=xp,
|
|
205
208
|
)
|
|
206
209
|
|
|
207
210
|
image_native = array_2d_util.array_2d_native_from(
|
|
@@ -522,14 +525,15 @@ class TransformerNUFFT:
|
|
|
522
525
|
if xp.__name__.startswith("jax"):
|
|
523
526
|
import jax.numpy as jnp
|
|
524
527
|
|
|
525
|
-
|
|
526
|
-
|
|
528
|
+
# Real scatter, then cast: GPU complex128 scatter floor (autolens_profiling#308).
|
|
529
|
+
mm_T = jnp.asarray(mapping_matrix).T
|
|
530
|
+
source_images = jnp.zeros((n_src, n_y, n_x), dtype=mm_T.dtype)
|
|
527
531
|
source_images = source_images.at[
|
|
528
532
|
jnp.arange(n_src)[:, None],
|
|
529
533
|
jnp.asarray(rows)[None, :],
|
|
530
534
|
jnp.asarray(cols)[None, :],
|
|
531
535
|
].set(mm_T)
|
|
532
|
-
flipped = source_images[:, ::-1, :]
|
|
536
|
+
flipped = source_images[:, ::-1, :].astype(jnp.complex128)
|
|
533
537
|
x = jnp.asarray(self._x)
|
|
534
538
|
y = jnp.asarray(self._y)
|
|
535
539
|
shift = jnp.asarray(self._shift)
|
|
@@ -539,10 +543,11 @@ class TransformerNUFFT:
|
|
|
539
543
|
)
|
|
540
544
|
return vis_batched.T
|
|
541
545
|
|
|
542
|
-
|
|
543
|
-
|
|
546
|
+
# Real scatter, then cast: GPU complex128 scatter floor (autolens_profiling#308).
|
|
547
|
+
mm_T = np.asarray(mapping_matrix).T
|
|
548
|
+
source_images = np.zeros((n_src, n_y, n_x), dtype=mm_T.dtype)
|
|
544
549
|
source_images[np.arange(n_src)[:, None], rows[None, :], cols[None, :]] = mm_T
|
|
545
|
-
flipped = source_images[:, ::-1, :]
|
|
550
|
+
flipped = source_images[:, ::-1, :].astype(np.complex128)
|
|
546
551
|
vis_batched = (
|
|
547
552
|
_nufftax.nufft2d2(self._x, self._y, flipped, self.eps, -1)
|
|
548
553
|
* self._shift[None, :]
|
|
@@ -50,7 +50,10 @@ def visibilities_from(
|
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
def image_direct_from(
|
|
53
|
-
visibilities: np.ndarray,
|
|
53
|
+
visibilities: np.ndarray,
|
|
54
|
+
grid_radians: np.ndarray,
|
|
55
|
+
uv_wavelengths: np.ndarray,
|
|
56
|
+
xp=np,
|
|
54
57
|
) -> np.ndarray:
|
|
55
58
|
"""
|
|
56
59
|
Reconstruct a real-valued sky image from complex interferometric visibilities
|
|
@@ -69,6 +72,8 @@ def image_direct_from(
|
|
|
69
72
|
|
|
70
73
|
uv_wavelengths
|
|
71
74
|
The (u, v) spatial frequencies in units of wavelengths for each baseline.
|
|
75
|
+
xp
|
|
76
|
+
The array module (`numpy` or `jax.numpy`) the transform is computed with.
|
|
72
77
|
|
|
73
78
|
Returns
|
|
74
79
|
-------
|
|
@@ -78,15 +83,15 @@ def image_direct_from(
|
|
|
78
83
|
# Compute the phase term for each (pixel, visibility) pair
|
|
79
84
|
phase = (
|
|
80
85
|
2.0
|
|
81
|
-
*
|
|
86
|
+
* xp.pi
|
|
82
87
|
* (
|
|
83
|
-
|
|
84
|
-
+
|
|
88
|
+
xp.outer(grid_radians[:, 1], uv_wavelengths[:, 0])
|
|
89
|
+
+ xp.outer(grid_radians[:, 0], uv_wavelengths[:, 1])
|
|
85
90
|
)
|
|
86
91
|
)
|
|
87
92
|
|
|
88
|
-
real_part =
|
|
89
|
-
imag_part =
|
|
93
|
+
real_part = xp.dot(xp.cos(phase), visibilities[:, 0])
|
|
94
|
+
imag_part = xp.dot(xp.sin(phase), visibilities[:, 1])
|
|
90
95
|
|
|
91
96
|
image_1d = real_part - imag_part
|
|
92
97
|
|
|
@@ -1,11 +1,60 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Optional, Tuple
|
|
3
|
+
|
|
1
4
|
import numpy as np
|
|
2
5
|
|
|
3
6
|
from autoarray.structures.triangles.abstract import HEIGHT_FACTOR
|
|
4
7
|
|
|
5
8
|
from autoarray.structures.triangles.abstract import AbstractTriangles
|
|
9
|
+
from autoarray.structures.triangles.shape import Point
|
|
6
10
|
from autoarray.structures.triangles.shape import Shape
|
|
11
|
+
from autoarray.structures.triangles.shape import _barycentric_contains
|
|
12
|
+
|
|
13
|
+
MAX_CONTAINING_SIZE = 20
|
|
14
|
+
|
|
15
|
+
# Private A/B switch for the step-0 containment route (point-source CPU phase 4b,
|
|
16
|
+
# PyAutoArray#579). It only affects `ArrayTriangles` built by
|
|
17
|
+
# `CoordinateArrayTriangles.with_vertices` on the static initial lattice (those carrying a
|
|
18
|
+
# `Step0Layout`) and tested against a `Point`; every other containment keeps the general
|
|
19
|
+
# `shape.mask(self.triangles)` path. All routes return bit-identical kept indices:
|
|
20
|
+
#
|
|
21
|
+
# - "gather": the general path -- pad, ``(N, 3, 2)`` gather, no-op NaN ``where``.
|
|
22
|
+
# - "nopad": the ``(N, 3, 2)`` gather without the pad / NaN ``where`` (no index is -1).
|
|
23
|
+
# - "components": six ``(N,)`` 1-D gathers, one per vertex component; no ``(N, 3, 2)`` array.
|
|
24
|
+
# - "structured": strided slices of the traced vertex table reshaped to its lattice rows; no
|
|
25
|
+
# gather at all, the boolean mask is interleaved back to triangle order.
|
|
26
|
+
#
|
|
27
|
+
# The switch is read at trace time: a jitted function must be re-traced (fresh closure plus
|
|
28
|
+
# ``jax.clear_caches()``) after changing it.
|
|
29
|
+
_STEP0_CONTAINMENT = "structured"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class Step0Layout:
|
|
34
|
+
"""
|
|
35
|
+
The closed-form layout of the static initial lattice's vertex table (see
|
|
36
|
+
`autoarray.structures.triangles.coordinate_array.static_lattice_layout`).
|
|
37
|
+
|
|
38
|
+
Every value is a Python int, so a layout is hashable and can sit in pytree aux data: under
|
|
39
|
+
``jit`` / ``vmap`` it is a trace-time constant.
|
|
40
|
+
|
|
41
|
+
Attributes
|
|
42
|
+
----------
|
|
43
|
+
n_rows, n_cols
|
|
44
|
+
The triangle lattice is ``n_rows x n_cols`` triangles, stored row-major.
|
|
45
|
+
grid
|
|
46
|
+
``None`` when the vertex table does not follow the closed-form pattern (only the
|
|
47
|
+
"gather" / "nopad" / "components" routes then apply), else
|
|
48
|
+
``(n_pairs, pair_width, pad, corners)``: the ``(V, 2)`` table padded by ``pad`` rows is a
|
|
49
|
+
``(n_pairs, pair_width, 2)`` grid, and ``corners[p][k] = (p0, c0, na, nb)`` says that for
|
|
50
|
+
the parity class ``p = 2 * (i % 2) + (j % 2)`` of triangle row ``i`` and column ``j``,
|
|
51
|
+
vertex ``k`` of the class's triangle ``(a, b)`` (``i = 2a + i % 2``, ``j = 2b + j % 2``)
|
|
52
|
+
is grid entry ``(p0 + a, c0 + b)``.
|
|
53
|
+
"""
|
|
7
54
|
|
|
8
|
-
|
|
55
|
+
n_rows: int
|
|
56
|
+
n_cols: int
|
|
57
|
+
grid: Optional[Tuple] = None
|
|
9
58
|
|
|
10
59
|
|
|
11
60
|
class ArrayTriangles(AbstractTriangles):
|
|
@@ -14,6 +63,7 @@ class ArrayTriangles(AbstractTriangles):
|
|
|
14
63
|
indices,
|
|
15
64
|
vertices,
|
|
16
65
|
max_containing_size=MAX_CONTAINING_SIZE,
|
|
66
|
+
step0_layout: Optional[Step0Layout] = None,
|
|
17
67
|
**kwargs,
|
|
18
68
|
):
|
|
19
69
|
"""
|
|
@@ -26,10 +76,16 @@ class ArrayTriangles(AbstractTriangles):
|
|
|
26
76
|
with the three indices of the vertices.
|
|
27
77
|
vertices
|
|
28
78
|
The vertices of the triangles.
|
|
79
|
+
step0_layout
|
|
80
|
+
Set only by `CoordinateArrayTriangles.with_vertices` on the static initial lattice:
|
|
81
|
+
the static layout of ``indices`` into ``vertices``, which lets `containing_indices`
|
|
82
|
+
test a `Point` without materialising the ``(N, 3, 2)`` triangle array (see
|
|
83
|
+
`_STEP0_CONTAINMENT`). A plain Python constant (pytree aux data), never traced.
|
|
29
84
|
"""
|
|
30
85
|
self._indices = indices
|
|
31
86
|
self._vertices = vertices
|
|
32
87
|
self.max_containing_size = max_containing_size
|
|
88
|
+
self.step0_layout = step0_layout
|
|
33
89
|
|
|
34
90
|
def __len__(self):
|
|
35
91
|
return len(self.triangles)
|
|
@@ -159,7 +215,13 @@ class ArrayTriangles(AbstractTriangles):
|
|
|
159
215
|
"""
|
|
160
216
|
import jax.numpy as jnp
|
|
161
217
|
|
|
162
|
-
inside =
|
|
218
|
+
inside = None
|
|
219
|
+
# Exactly `Point`: `Circle`, `Triangle`, `Polygon` and `Square` subclass `Point` but
|
|
220
|
+
# override `mask`, so they must keep the general path.
|
|
221
|
+
if self.step0_layout is not None and type(shape) is Point:
|
|
222
|
+
inside = self._step0_point_mask(shape)
|
|
223
|
+
if inside is None:
|
|
224
|
+
inside = shape.mask(self.triangles)
|
|
163
225
|
|
|
164
226
|
return jnp.where(
|
|
165
227
|
inside,
|
|
@@ -167,6 +229,70 @@ class ArrayTriangles(AbstractTriangles):
|
|
|
167
229
|
fill_value=-1,
|
|
168
230
|
)[0]
|
|
169
231
|
|
|
232
|
+
def _step0_point_mask(self, point: "Point"):
|
|
233
|
+
"""
|
|
234
|
+
`Point.mask` of the static initial lattice without the general ``(N, 3, 2)`` gather, by
|
|
235
|
+
the route `_STEP0_CONTAINMENT` names; ``None`` selects the general path.
|
|
236
|
+
|
|
237
|
+
Every route feeds `_barycentric_contains` the same six component values the general path
|
|
238
|
+
takes from ``self.triangles`` (at step 0 no index is -1, so its NaN ``where`` is a no-op),
|
|
239
|
+
in the same operation order, so the mask -- and the kept indices -- are bit-identical.
|
|
240
|
+
"""
|
|
241
|
+
import jax.numpy as jnp
|
|
242
|
+
|
|
243
|
+
route = _STEP0_CONTAINMENT
|
|
244
|
+
vertices = self.vertices
|
|
245
|
+
indices = self.indices
|
|
246
|
+
|
|
247
|
+
if route == "nopad":
|
|
248
|
+
return point.mask(vertices[indices])
|
|
249
|
+
|
|
250
|
+
if route == "components":
|
|
251
|
+
v0 = vertices[:, 0]
|
|
252
|
+
v1 = vertices[:, 1]
|
|
253
|
+
i0 = indices[:, 0]
|
|
254
|
+
i1 = indices[:, 1]
|
|
255
|
+
i2 = indices[:, 2]
|
|
256
|
+
return _barycentric_contains(
|
|
257
|
+
v0[i0], v1[i0], v0[i1], v1[i1], v0[i2], v1[i2], point.x, point.y
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
if route == "structured" and self.step0_layout.grid is not None:
|
|
261
|
+
n_rows = self.step0_layout.n_rows
|
|
262
|
+
n_cols = self.step0_layout.n_cols
|
|
263
|
+
n_pairs, pair_width, pad, corners = self.step0_layout.grid
|
|
264
|
+
if pad:
|
|
265
|
+
vertices = jnp.pad(vertices, ((0, pad), (0, 0)))
|
|
266
|
+
grid = vertices.reshape(n_pairs, pair_width, 2)
|
|
267
|
+
|
|
268
|
+
half_rows = (n_rows + 1) // 2
|
|
269
|
+
half_cols = (n_cols + 1) // 2
|
|
270
|
+
|
|
271
|
+
classes = []
|
|
272
|
+
for corner in corners:
|
|
273
|
+
components = []
|
|
274
|
+
for p0, c0, na, nb in corner:
|
|
275
|
+
block = grid[p0 : p0 + na, c0 : c0 + nb]
|
|
276
|
+
components += [block[..., 0], block[..., 1]]
|
|
277
|
+
na, nb = corner[0][2], corner[0][3]
|
|
278
|
+
mask = _barycentric_contains(*components, point.x, point.y)
|
|
279
|
+
classes.append(
|
|
280
|
+
jnp.pad(mask, ((0, half_rows - na), (0, half_cols - nb)))
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
# classes[2 * pi + pj][a, b] is triangle (2a + pi, 2b + pj): interleave to row-major.
|
|
284
|
+
interleaved = jnp.stack(
|
|
285
|
+
(
|
|
286
|
+
jnp.stack((classes[0], classes[1]), axis=-1),
|
|
287
|
+
jnp.stack((classes[2], classes[3]), axis=-1),
|
|
288
|
+
),
|
|
289
|
+
axis=1,
|
|
290
|
+
).reshape(2 * half_rows, 2 * half_cols)
|
|
291
|
+
|
|
292
|
+
return interleaved[:n_rows, :n_cols].reshape(-1)
|
|
293
|
+
|
|
294
|
+
return None
|
|
295
|
+
|
|
170
296
|
def for_indexes(self, indexes: np.ndarray) -> "ArrayTriangles":
|
|
171
297
|
"""
|
|
172
298
|
Create a new ArrayTriangles containing indices and vertices corresponding to the given indexes
|
|
@@ -311,6 +437,7 @@ class ArrayTriangles(AbstractTriangles):
|
|
|
311
437
|
indices=self.indices,
|
|
312
438
|
vertices=vertices,
|
|
313
439
|
max_containing_size=self.max_containing_size,
|
|
440
|
+
step0_layout=self.step0_layout,
|
|
314
441
|
)
|
|
315
442
|
|
|
316
443
|
def tree_flatten(self):
|
|
@@ -320,7 +447,7 @@ class ArrayTriangles(AbstractTriangles):
|
|
|
320
447
|
return (
|
|
321
448
|
self.indices,
|
|
322
449
|
self.vertices,
|
|
323
|
-
), (self.max_containing_size,)
|
|
450
|
+
), (self.max_containing_size, self.step0_layout)
|
|
324
451
|
|
|
325
452
|
@classmethod
|
|
326
453
|
def tree_unflatten(cls, aux_data, children):
|
|
@@ -331,6 +458,7 @@ class ArrayTriangles(AbstractTriangles):
|
|
|
331
458
|
indices=children[0],
|
|
332
459
|
vertices=children[1],
|
|
333
460
|
max_containing_size=aux_data[0],
|
|
461
|
+
step0_layout=aux_data[1],
|
|
334
462
|
)
|
|
335
463
|
|
|
336
464
|
|
{autoarray-2026.9.26.1 → autoarray-2026.9.27.2}/autoarray/structures/triangles/coordinate_array.py
RENAMED
|
@@ -7,6 +7,7 @@ import numpy as np
|
|
|
7
7
|
from autoarray.structures.triangles.abstract import HEIGHT_FACTOR
|
|
8
8
|
from autoarray.structures.triangles.abstract import AbstractTriangles
|
|
9
9
|
from autoarray.structures.triangles.array import ArrayTriangles
|
|
10
|
+
from autoarray.structures.triangles.array import Step0Layout
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
def _lattice_coordinates(
|
|
@@ -83,8 +84,27 @@ def static_vertex_table(
|
|
|
83
84
|
axis=1,
|
|
84
85
|
)
|
|
85
86
|
|
|
87
|
+
keys = _vertex_keys(coordinates)
|
|
88
|
+
|
|
89
|
+
_, first, inverse = np.unique(keys, axis=0, return_index=True, return_inverse=True)
|
|
90
|
+
|
|
91
|
+
vertices = np.ascontiguousarray(triangles.reshape(-1, 2)[first], dtype=np.float64)
|
|
92
|
+
indices = np.ascontiguousarray(inverse.reshape(-1, 3))
|
|
93
|
+
|
|
94
|
+
vertices.setflags(write=False)
|
|
95
|
+
indices.setflags(write=False)
|
|
96
|
+
|
|
97
|
+
return vertices, indices
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _vertex_keys(coordinates: np.ndarray) -> np.ndarray:
|
|
101
|
+
"""
|
|
102
|
+
The exact integer lattice key of every ``(3N)`` vertex slot, ordered as
|
|
103
|
+
`static_vertex_table` orders them (see its docstring).
|
|
104
|
+
"""
|
|
105
|
+
flip = np.where((coordinates[:, 0] + coordinates[:, 1]) % 2 != 0, -1, 1)[:, None]
|
|
86
106
|
offsets = np.array([[0, 1], [1, -1], [-1, -1]])
|
|
87
|
-
|
|
107
|
+
return np.stack(
|
|
88
108
|
(
|
|
89
109
|
coordinates[:, None, 0] + flip * offsets[None, :, 0],
|
|
90
110
|
2 * coordinates[:, None, 1] + flip * offsets[None, :, 1],
|
|
@@ -92,15 +112,90 @@ def static_vertex_table(
|
|
|
92
112
|
axis=-1,
|
|
93
113
|
).reshape(-1, 2)
|
|
94
114
|
|
|
95
|
-
_, first, inverse = np.unique(keys, axis=0, return_index=True, return_inverse=True)
|
|
96
115
|
|
|
97
|
-
|
|
98
|
-
|
|
116
|
+
@lru_cache(maxsize=32)
|
|
117
|
+
def static_lattice_layout(
|
|
118
|
+
y_min: float, y_max: float, x_min: float, x_max: float, scale: float
|
|
119
|
+
) -> Step0Layout:
|
|
120
|
+
"""
|
|
121
|
+
The closed-form layout of `static_vertex_table`'s index map, so the step-0 containment can
|
|
122
|
+
read each triangle's vertices by strided slicing instead of a gather (PyAutoArray#579).
|
|
123
|
+
|
|
124
|
+
The lattice's triangles are row-major, ``n_rows x n_cols``. The vertex table is sorted on the
|
|
125
|
+
integer key ``(ky, kx)``; key rows alternate between two widths ``W0, W1`` (equal when the
|
|
126
|
+
lattice has an odd number of columns), so the ``(V, 2)`` table -- padded by one row when the
|
|
127
|
+
number of key rows is odd -- is a ``(n_pairs, W0 + W1, 2)`` grid of row pairs. Splitting the
|
|
128
|
+
triangles into the four parity classes of their (row, column) index fixes each class's flip
|
|
129
|
+
and the parity of every vertex's key row, so vertex ``k`` of the class's triangle ``(a, b)``
|
|
130
|
+
is grid entry ``(p0 + a, c0 + b)``: one contiguous 2-D slice per class and vertex.
|
|
131
|
+
|
|
132
|
+
The layout is derived from, and checked element-wise against, the actual index map in NumPy:
|
|
133
|
+
if any class did not follow the strided pattern, ``grid`` is ``None`` and the structured
|
|
134
|
+
route is not used for this geometry. Built once per geometry (cached) and made only of Python
|
|
135
|
+
ints, so it is hashable pytree aux data.
|
|
136
|
+
"""
|
|
137
|
+
vertices, indices = static_vertex_table(y_min, y_max, x_min, x_max, scale)
|
|
138
|
+
coordinates = _lattice_coordinates(y_min, y_max, x_min, x_max, scale)
|
|
99
139
|
|
|
100
|
-
|
|
101
|
-
|
|
140
|
+
ys = np.unique(coordinates[:, 0])
|
|
141
|
+
xs = np.unique(coordinates[:, 1])
|
|
142
|
+
n_rows, n_cols = ys.shape[0], xs.shape[0]
|
|
102
143
|
|
|
103
|
-
|
|
144
|
+
grid_coordinates = np.stack(np.meshgrid(ys, xs, indexing="ij"), axis=-1)
|
|
145
|
+
if coordinates.shape[0] != n_rows * n_cols or not np.array_equal(
|
|
146
|
+
coordinates, grid_coordinates.reshape(-1, 2)
|
|
147
|
+
):
|
|
148
|
+
return Step0Layout(n_rows=0, n_cols=0, grid=None)
|
|
149
|
+
|
|
150
|
+
unique_keys = np.unique(_vertex_keys(coordinates), axis=0)
|
|
151
|
+
_, row_counts = np.unique(unique_keys[:, 0], return_counts=True)
|
|
152
|
+
R = row_counts.shape[0]
|
|
153
|
+
W0 = int(row_counts[0])
|
|
154
|
+
W1 = int(row_counts[1]) if R > 1 else W0
|
|
155
|
+
if (
|
|
156
|
+
unique_keys.shape[0] != vertices.shape[0]
|
|
157
|
+
or np.any(row_counts[0::2] != W0)
|
|
158
|
+
or np.any(row_counts[1::2] != W1)
|
|
159
|
+
):
|
|
160
|
+
return Step0Layout(n_rows=n_rows, n_cols=n_cols, grid=None)
|
|
161
|
+
|
|
162
|
+
# Key rows come in pairs of widths (W0, W1). Viewing the table as (n_pairs, W0 + W1, 2) --
|
|
163
|
+
# padded by one row when R is odd -- puts vertex (row r, column c) at
|
|
164
|
+
# (r // 2, (r % 2) * W0 + c).
|
|
165
|
+
pair_width = W0 + W1
|
|
166
|
+
n_pairs = (R + 1) // 2
|
|
167
|
+
row_start = np.concatenate(([0], np.cumsum(row_counts)[:-1]))
|
|
168
|
+
vertex_key_row = np.repeat(np.arange(R), row_counts)
|
|
169
|
+
vertex_key_col = np.arange(vertices.shape[0]) - row_start[vertex_key_row]
|
|
170
|
+
vertex_pair = (vertex_key_row // 2)[indices].reshape(n_rows, n_cols, 3)
|
|
171
|
+
vertex_offset = ((vertex_key_row % 2) * W0 + vertex_key_col)[indices].reshape(
|
|
172
|
+
n_rows, n_cols, 3
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
corners = []
|
|
176
|
+
for pi in (0, 1):
|
|
177
|
+
for pj in (0, 1):
|
|
178
|
+
corner = []
|
|
179
|
+
for k in range(3):
|
|
180
|
+
class_pair = vertex_pair[pi::2, pj::2, k]
|
|
181
|
+
class_offset = vertex_offset[pi::2, pj::2, k]
|
|
182
|
+
na, nb = class_pair.shape
|
|
183
|
+
p0, c0 = int(class_pair[0, 0]), int(class_offset[0, 0])
|
|
184
|
+
expected_pair = p0 + np.arange(na)[:, None] + 0 * class_pair
|
|
185
|
+
expected_offset = c0 + np.arange(nb)[None, :] + 0 * class_offset
|
|
186
|
+
if not (
|
|
187
|
+
np.array_equal(class_pair, expected_pair)
|
|
188
|
+
and np.array_equal(class_offset, expected_offset)
|
|
189
|
+
):
|
|
190
|
+
return Step0Layout(n_rows=n_rows, n_cols=n_cols, grid=None)
|
|
191
|
+
corner.append((p0, c0, int(na), int(nb)))
|
|
192
|
+
corners.append(tuple(corner))
|
|
193
|
+
|
|
194
|
+
return Step0Layout(
|
|
195
|
+
n_rows=n_rows,
|
|
196
|
+
n_cols=n_cols,
|
|
197
|
+
grid=(n_pairs, pair_width, int(n_pairs * pair_width - vertices.shape[0]), tuple(corners)),
|
|
198
|
+
)
|
|
104
199
|
|
|
105
200
|
|
|
106
201
|
class CoordinateArrayTriangles(AbstractTriangles, ABC):
|
|
@@ -113,6 +208,7 @@ class CoordinateArrayTriangles(AbstractTriangles, ABC):
|
|
|
113
208
|
y_offset: float = 0.0,
|
|
114
209
|
flipped: bool = False,
|
|
115
210
|
vertex_table: Optional[Tuple[np.ndarray, np.ndarray]] = None,
|
|
211
|
+
step0_layout: Optional[Step0Layout] = None,
|
|
116
212
|
):
|
|
117
213
|
"""
|
|
118
214
|
Represents a set of triangles by integer coordinates.
|
|
@@ -134,11 +230,17 @@ class CoordinateArrayTriangles(AbstractTriangles, ABC):
|
|
|
134
230
|
sets it. Derived lattices (`for_indexes`, `up_sample`, `neighborhood`) do not inherit
|
|
135
231
|
it, and it is not part of the pytree (`tree_flatten`), so an unflattened copy falls
|
|
136
232
|
back to the flat table -- which is still correct, only unshared.
|
|
233
|
+
step0_layout
|
|
234
|
+
The `static_lattice_layout` of ``vertex_table``, set with it by
|
|
235
|
+
`for_limits_and_scale(..., static_vertices=True)` and passed by `with_vertices` to the
|
|
236
|
+
`ArrayTriangles` it returns, whose `containing_indices` then avoids the ``(N, 3, 2)``
|
|
237
|
+
gather. Dropped exactly where ``vertex_table`` is.
|
|
137
238
|
"""
|
|
138
239
|
import jax.numpy as jnp
|
|
139
240
|
|
|
140
241
|
self.coordinates = coordinates
|
|
141
242
|
self.vertex_table = vertex_table
|
|
243
|
+
self.step0_layout = step0_layout if vertex_table is not None else None
|
|
142
244
|
self.side_length = side_length
|
|
143
245
|
self.flipped = flipped
|
|
144
246
|
|
|
@@ -191,10 +293,17 @@ class CoordinateArrayTriangles(AbstractTriangles, ABC):
|
|
|
191
293
|
import jax.numpy as jnp
|
|
192
294
|
|
|
193
295
|
vertex_table = None
|
|
296
|
+
step0_layout = None
|
|
194
297
|
if static_vertices:
|
|
195
|
-
|
|
196
|
-
float(y_min),
|
|
298
|
+
geometry = (
|
|
299
|
+
float(y_min),
|
|
300
|
+
float(y_max),
|
|
301
|
+
float(x_min),
|
|
302
|
+
float(x_max),
|
|
303
|
+
float(scale),
|
|
197
304
|
)
|
|
305
|
+
vertex_table = static_vertex_table(*geometry)
|
|
306
|
+
step0_layout = static_lattice_layout(*geometry)
|
|
198
307
|
|
|
199
308
|
return cls(
|
|
200
309
|
coordinates=jnp.array(
|
|
@@ -202,6 +311,7 @@ class CoordinateArrayTriangles(AbstractTriangles, ABC):
|
|
|
202
311
|
),
|
|
203
312
|
side_length=scale,
|
|
204
313
|
vertex_table=vertex_table,
|
|
314
|
+
step0_layout=step0_layout,
|
|
205
315
|
)
|
|
206
316
|
|
|
207
317
|
def tree_flatten(self):
|
|
@@ -437,6 +547,7 @@ class CoordinateArrayTriangles(AbstractTriangles, ABC):
|
|
|
437
547
|
return ArrayTriangles(
|
|
438
548
|
indices=self.indices,
|
|
439
549
|
vertices=vertices,
|
|
550
|
+
step0_layout=self.step0_layout,
|
|
440
551
|
)
|
|
441
552
|
|
|
442
553
|
def for_indexes(self, indexes: np.ndarray) -> "CoordinateArrayTriangles":
|