nrl-tracker 1.10.0__tar.gz → 1.11.0__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.
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/CONTRIBUTING.md +18 -23
- {nrl_tracker-1.10.0/nrl_tracker.egg-info → nrl_tracker-1.11.0}/PKG-INFO +4 -4
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/README.md +3 -3
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0/nrl_tracker.egg-info}/PKG-INFO +4 -4
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/nrl_tracker.egg-info/SOURCES.txt +15 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pyproject.toml +1 -1
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/__init__.py +3 -3
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/assignment_algorithms/nd_assignment.py +359 -1
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/coordinate_systems/jacobians/jacobians.py +63 -33
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/kalman/matrix_utils.py +133 -35
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/gpu/__init__.py +1 -1
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/gpu/ekf.py +20 -12
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/gpu/matrix_utils.py +14 -9
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/gpu/particle_filter.py +18 -8
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/gpu/ukf.py +7 -7
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/gpu/utils.py +2 -2
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/gravity/clenshaw.py +8 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/gravity/spherical_harmonics.py +17 -10
- nrl_tracker-1.11.0/tests/test_cfar_detection.py +654 -0
- nrl_tracker-1.11.0/tests/test_combinatorics.py +488 -0
- nrl_tracker-1.11.0/tests/test_debye.py +344 -0
- nrl_tracker-1.11.0/tests/test_elliptic.py +323 -0
- nrl_tracker-1.11.0/tests/test_error_functions.py +419 -0
- nrl_tracker-1.11.0/tests/test_gamma_functions.py +391 -0
- nrl_tracker-1.11.0/tests/test_geometry.py +566 -0
- nrl_tracker-1.11.0/tests/test_hypergeometric.py +398 -0
- nrl_tracker-1.11.0/tests/test_ionosphere.py +610 -0
- nrl_tracker-1.11.0/tests/test_marcum_q.py +340 -0
- nrl_tracker-1.11.0/tests/test_matched_filter.py +501 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_nd_assignment.py +227 -0
- nrl_tracker-1.11.0/tests/test_quadrature.py +462 -0
- nrl_tracker-1.11.0/tests/test_rotations.py +569 -0
- nrl_tracker-1.11.0/tests/test_square_root_filters.py +1162 -0
- nrl_tracker-1.11.0/tests/test_wavelets.py +705 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/LICENSE +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/MANIFEST.in +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/nrl_tracker.egg-info/dependency_links.txt +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/nrl_tracker.egg-info/requires.txt +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/nrl_tracker.egg-info/top_level.txt +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/assignment_algorithms/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/assignment_algorithms/data_association.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/assignment_algorithms/dijkstra_min_cost.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/assignment_algorithms/gating.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/assignment_algorithms/jpda.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/assignment_algorithms/network_flow.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/assignment_algorithms/network_simplex.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/assignment_algorithms/three_dimensional/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/assignment_algorithms/three_dimensional/assignment.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/assignment_algorithms/two_dimensional/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/assignment_algorithms/two_dimensional/assignment.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/assignment_algorithms/two_dimensional/kbest.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/astronomical/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/astronomical/ephemerides.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/astronomical/lambert.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/astronomical/orbital_mechanics.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/astronomical/reference_frames.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/astronomical/relativity.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/astronomical/sgp4.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/astronomical/special_orbits.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/astronomical/time_systems.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/astronomical/tle.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/atmosphere/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/atmosphere/ionosphere.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/atmosphere/models.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/atmosphere/nrlmsise00.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/clustering/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/clustering/dbscan.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/clustering/gaussian_mixture.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/clustering/hierarchical.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/clustering/kmeans.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/containers/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/containers/base.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/containers/cluster_set.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/containers/covertree.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/containers/kd_tree.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/containers/measurement_set.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/containers/rtree.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/containers/track_list.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/containers/vptree.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/coordinate_systems/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/coordinate_systems/conversions/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/coordinate_systems/conversions/geodetic.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/coordinate_systems/conversions/spherical.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/coordinate_systems/jacobians/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/coordinate_systems/projections/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/coordinate_systems/projections/projections.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/coordinate_systems/rotations/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/coordinate_systems/rotations/rotations.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/core/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/core/array_utils.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/core/constants.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/core/exceptions.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/core/maturity.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/core/optional_deps.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/core/validation.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/batch_estimation/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/gaussian_sum_filter.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/imm.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/information_filter.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/kalman/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/kalman/constrained.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/kalman/extended.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/kalman/h_infinity.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/kalman/linear.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/kalman/square_root.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/kalman/sr_ukf.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/kalman/types.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/kalman/ud_filter.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/kalman/unscented.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/measurement_update/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/particle_filters/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/particle_filters/bootstrap.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/rbpf.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_estimation/smoothers.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_models/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_models/continuous_time/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_models/continuous_time/dynamics.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_models/discrete_time/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_models/discrete_time/coordinated_turn.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_models/discrete_time/polynomial.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_models/discrete_time/singer.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_models/process_noise/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_models/process_noise/coordinated_turn.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_models/process_noise/polynomial.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/dynamic_models/process_noise/singer.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/gpu/kalman.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/gravity/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/gravity/egm.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/gravity/models.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/gravity/tides.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/logging_config.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/magnetism/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/magnetism/emm.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/magnetism/igrf.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/magnetism/wmm.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/basic_matrix/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/basic_matrix/decompositions.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/basic_matrix/special_matrices.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/combinatorics/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/combinatorics/combinatorics.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/continuous_optimization/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/geometry/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/geometry/geometry.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/interpolation/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/interpolation/interpolation.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/numerical_integration/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/numerical_integration/quadrature.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/polynomials/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/signal_processing/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/signal_processing/detection.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/signal_processing/filters.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/signal_processing/matched_filter.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/special_functions/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/special_functions/bessel.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/special_functions/debye.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/special_functions/elliptic.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/special_functions/error_functions.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/special_functions/gamma_functions.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/special_functions/hypergeometric.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/special_functions/lambert_w.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/special_functions/marcum_q.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/statistics/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/statistics/distributions.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/statistics/estimators.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/transforms/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/transforms/fourier.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/transforms/stft.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/mathematical_functions/transforms/wavelets.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/misc/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/navigation/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/navigation/geodesy.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/navigation/great_circle.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/navigation/ins.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/navigation/ins_gnss.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/navigation/rhumb.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/performance_evaluation/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/performance_evaluation/estimation_metrics.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/performance_evaluation/track_metrics.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/physical_values/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/plotting/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/plotting/coordinates.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/plotting/ellipses.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/plotting/metrics.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/plotting/tracks.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/scheduling/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/static_estimation/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/static_estimation/least_squares.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/static_estimation/maximum_likelihood.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/static_estimation/robust.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/terrain/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/terrain/dem.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/terrain/loaders.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/terrain/visibility.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/trackers/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/trackers/hypothesis.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/trackers/mht.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/trackers/multi_target.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/trackers/single_target.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/pytcl/transponders/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/setup.cfg +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/__init__.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/conftest.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_additional_trees.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_assignment_algorithms.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_astronomical.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_clustering.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_constrained_ekf.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_coordinate_systems.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_coverage_boost.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_coverage_boost_2.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_dynamic_models.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_egm.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_emm.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_ephemerides.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_exceptions.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_gaussian_mixtures.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_gaussian_sum_filter.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_geophysical.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_gpu.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_gpu_utils.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_great_circle.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_h_infinity.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_ins.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_ins_gnss.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_jpda.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_kalman_filters.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_mathematical_functions.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_maximum_likelihood.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_mht.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_network_flow.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_nrlmsise00.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_optional_deps.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_performance_evaluation.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_phase6_specialized.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_plotting.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_projections.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_rbpf.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_relativity.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_rhumb.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_sgp4.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_signal_processing.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_smoothers.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_spatial_containers_parametrized.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_spatial_structures.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_special_functions.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_special_orbits.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_static_estimation.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_terrain.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_terrain_loaders.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_tides.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_tod_mod.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_trackers.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_tracking_containers.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_transforms.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/test_validation.py +0 -0
- {nrl_tracker-1.10.0 → nrl_tracker-1.11.0}/tests/unit/test_core.py +0 -0
|
@@ -222,35 +222,29 @@ When porting a function from the original MATLAB library:
|
|
|
222
222
|
|
|
223
223
|
## Current Development Status
|
|
224
224
|
|
|
225
|
-
**Version:** v1.
|
|
225
|
+
**Version:** v1.11.0
|
|
226
226
|
**MATLAB Parity:** 100% ✅
|
|
227
|
-
**Test Suite:** 2,
|
|
227
|
+
**Test Suite:** 2,894 tests passing
|
|
228
228
|
**Code Coverage:** 76% (target 80%+ in v2.0.0)
|
|
229
229
|
**Quality:** 100% compliance (black, isort, flake8, mypy --strict)
|
|
230
230
|
**GPU Acceleration:** CuPy (NVIDIA) + MLX (Apple Silicon)
|
|
231
|
+
**Performance Optimization:** Numba JIT, lru_cache, sparse matrix support
|
|
231
232
|
|
|
232
|
-
## v2.0.0 Roadmap
|
|
233
|
+
## v2.0.0 Roadmap Progress
|
|
233
234
|
|
|
234
|
-
###
|
|
235
|
-
**
|
|
236
|
-
- **
|
|
237
|
-
- **
|
|
238
|
-
- **
|
|
239
|
-
- **
|
|
235
|
+
### Completed Phases
|
|
236
|
+
- **Phase 1** ✅: Network flow performance (50-100x faster)
|
|
237
|
+
- **Phase 2** ✅: API standardization (exceptions, spatial indexes, optional deps)
|
|
238
|
+
- **Phase 5** ✅: GPU acceleration (CuPy + MLX, 5-15x speedup)
|
|
239
|
+
- **Phase 6** ✅: Test expansion (+761 tests, 2,894 total)
|
|
240
|
+
- **Phase 7** ✅: Performance optimization (Numba JIT, lru_cache, sparse matrices)
|
|
240
241
|
|
|
241
|
-
###
|
|
242
|
-
- Phase
|
|
243
|
-
- Phase
|
|
244
|
-
- Phase 4: Signal processing optimization
|
|
245
|
-
- Phase 5: Advanced estimation methods
|
|
242
|
+
### In Progress
|
|
243
|
+
- **Phase 3**: Documentation expansion
|
|
244
|
+
- **Phase 4**: Jupyter notebooks
|
|
246
245
|
|
|
247
|
-
###
|
|
248
|
-
- **
|
|
249
|
-
- **Focus:** Edge cases, numerical stability, batch operations
|
|
250
|
-
|
|
251
|
-
### Phases 7-8: Documentation & Final Polish
|
|
252
|
-
- Phase 7: Documentation updates and examples
|
|
253
|
-
- Phase 8: Performance tuning and optimization
|
|
246
|
+
### Remaining
|
|
247
|
+
- **Phase 8**: Release preparation (alpha → beta → RC → v2.0.0)
|
|
254
248
|
|
|
255
249
|
## Priority Areas for Contributors
|
|
256
250
|
|
|
@@ -302,13 +296,14 @@ pytest --collect-only -q | tail -1
|
|
|
302
296
|
pytest --cov=pytcl --cov-report=term
|
|
303
297
|
```
|
|
304
298
|
|
|
305
|
-
Current metrics (v1.
|
|
299
|
+
Current metrics (v1.11.0):
|
|
306
300
|
- **Functions:** 1,070+
|
|
307
301
|
- **Modules:** 150+
|
|
308
|
-
- **Tests:** 2,
|
|
302
|
+
- **Tests:** 2,894 (all passing)
|
|
309
303
|
- **Coverage:** 76%
|
|
310
304
|
- **MATLAB Parity:** 100%
|
|
311
305
|
- **GPU Backends:** 2 (CuPy + MLX)
|
|
306
|
+
- **Performance:** Numba JIT + lru_cache optimizations
|
|
312
307
|
|
|
313
308
|
### 3. Sync Examples
|
|
314
309
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: nrl-tracker
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.11.0
|
|
4
4
|
Summary: Python port of the U.S. Naval Research Laboratory's Tracker Component Library for target tracking algorithms
|
|
5
5
|
Author: Original: David F. Crouse, Naval Research Laboratory
|
|
6
6
|
Maintainer: Python Port Contributors
|
|
@@ -71,17 +71,17 @@ Requires-Dist: nrl-tracker[astronomy,dev,geodesy,optimization,signal,visualizati
|
|
|
71
71
|
|
|
72
72
|
# Tracker Component Library (Python)
|
|
73
73
|
|
|
74
|
-
[](https://pypi.org/project/nrl-tracker/)
|
|
75
75
|
[](https://www.python.org/downloads/)
|
|
76
76
|
[](https://en.wikipedia.org/wiki/Public_domain)
|
|
77
77
|
[](https://github.com/psf/black)
|
|
78
|
-
[](https://github.com/nedonatelli/TCL)
|
|
79
79
|
[](docs/gap_analysis.rst)
|
|
80
80
|
[](mypy.ini)
|
|
81
81
|
|
|
82
82
|
A Python port of the [U.S. Naval Research Laboratory's Tracker Component Library](https://github.com/USNavalResearchLaboratory/TrackerComponentLibrary), a comprehensive collection of algorithms for target tracking, estimation, coordinate systems, and related mathematical functions.
|
|
83
83
|
|
|
84
|
-
**1,070+ functions** | **153 modules** | **2,
|
|
84
|
+
**1,070+ functions** | **153 modules** | **2,894 tests** | **100% MATLAB parity**
|
|
85
85
|
|
|
86
86
|
## Overview
|
|
87
87
|
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# Tracker Component Library (Python)
|
|
2
2
|
|
|
3
|
-
[](https://pypi.org/project/nrl-tracker/)
|
|
4
4
|
[](https://www.python.org/downloads/)
|
|
5
5
|
[](https://en.wikipedia.org/wiki/Public_domain)
|
|
6
6
|
[](https://github.com/psf/black)
|
|
7
|
-
[](https://github.com/nedonatelli/TCL)
|
|
8
8
|
[](docs/gap_analysis.rst)
|
|
9
9
|
[](mypy.ini)
|
|
10
10
|
|
|
11
11
|
A Python port of the [U.S. Naval Research Laboratory's Tracker Component Library](https://github.com/USNavalResearchLaboratory/TrackerComponentLibrary), a comprehensive collection of algorithms for target tracking, estimation, coordinate systems, and related mathematical functions.
|
|
12
12
|
|
|
13
|
-
**1,070+ functions** | **153 modules** | **2,
|
|
13
|
+
**1,070+ functions** | **153 modules** | **2,894 tests** | **100% MATLAB parity**
|
|
14
14
|
|
|
15
15
|
## Overview
|
|
16
16
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: nrl-tracker
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.11.0
|
|
4
4
|
Summary: Python port of the U.S. Naval Research Laboratory's Tracker Component Library for target tracking algorithms
|
|
5
5
|
Author: Original: David F. Crouse, Naval Research Laboratory
|
|
6
6
|
Maintainer: Python Port Contributors
|
|
@@ -71,17 +71,17 @@ Requires-Dist: nrl-tracker[astronomy,dev,geodesy,optimization,signal,visualizati
|
|
|
71
71
|
|
|
72
72
|
# Tracker Component Library (Python)
|
|
73
73
|
|
|
74
|
-
[](https://pypi.org/project/nrl-tracker/)
|
|
75
75
|
[](https://www.python.org/downloads/)
|
|
76
76
|
[](https://en.wikipedia.org/wiki/Public_domain)
|
|
77
77
|
[](https://github.com/psf/black)
|
|
78
|
-
[](https://github.com/nedonatelli/TCL)
|
|
79
79
|
[](docs/gap_analysis.rst)
|
|
80
80
|
[](mypy.ini)
|
|
81
81
|
|
|
82
82
|
A Python port of the [U.S. Naval Research Laboratory's Tracker Component Library](https://github.com/USNavalResearchLaboratory/TrackerComponentLibrary), a comprehensive collection of algorithms for target tracking, estimation, coordinate systems, and related mathematical functions.
|
|
83
83
|
|
|
84
|
-
**1,070+ functions** | **153 modules** | **2,
|
|
84
|
+
**1,070+ functions** | **153 modules** | **2,894 tests** | **100% MATLAB parity**
|
|
85
85
|
|
|
86
86
|
## Overview
|
|
87
87
|
|
|
@@ -187,27 +187,38 @@ tests/conftest.py
|
|
|
187
187
|
tests/test_additional_trees.py
|
|
188
188
|
tests/test_assignment_algorithms.py
|
|
189
189
|
tests/test_astronomical.py
|
|
190
|
+
tests/test_cfar_detection.py
|
|
190
191
|
tests/test_clustering.py
|
|
192
|
+
tests/test_combinatorics.py
|
|
191
193
|
tests/test_constrained_ekf.py
|
|
192
194
|
tests/test_coordinate_systems.py
|
|
193
195
|
tests/test_coverage_boost.py
|
|
194
196
|
tests/test_coverage_boost_2.py
|
|
197
|
+
tests/test_debye.py
|
|
195
198
|
tests/test_dynamic_models.py
|
|
196
199
|
tests/test_egm.py
|
|
200
|
+
tests/test_elliptic.py
|
|
197
201
|
tests/test_emm.py
|
|
198
202
|
tests/test_ephemerides.py
|
|
203
|
+
tests/test_error_functions.py
|
|
199
204
|
tests/test_exceptions.py
|
|
205
|
+
tests/test_gamma_functions.py
|
|
200
206
|
tests/test_gaussian_mixtures.py
|
|
201
207
|
tests/test_gaussian_sum_filter.py
|
|
208
|
+
tests/test_geometry.py
|
|
202
209
|
tests/test_geophysical.py
|
|
203
210
|
tests/test_gpu.py
|
|
204
211
|
tests/test_gpu_utils.py
|
|
205
212
|
tests/test_great_circle.py
|
|
206
213
|
tests/test_h_infinity.py
|
|
214
|
+
tests/test_hypergeometric.py
|
|
207
215
|
tests/test_ins.py
|
|
208
216
|
tests/test_ins_gnss.py
|
|
217
|
+
tests/test_ionosphere.py
|
|
209
218
|
tests/test_jpda.py
|
|
210
219
|
tests/test_kalman_filters.py
|
|
220
|
+
tests/test_marcum_q.py
|
|
221
|
+
tests/test_matched_filter.py
|
|
211
222
|
tests/test_mathematical_functions.py
|
|
212
223
|
tests/test_maximum_likelihood.py
|
|
213
224
|
tests/test_mht.py
|
|
@@ -219,9 +230,11 @@ tests/test_performance_evaluation.py
|
|
|
219
230
|
tests/test_phase6_specialized.py
|
|
220
231
|
tests/test_plotting.py
|
|
221
232
|
tests/test_projections.py
|
|
233
|
+
tests/test_quadrature.py
|
|
222
234
|
tests/test_rbpf.py
|
|
223
235
|
tests/test_relativity.py
|
|
224
236
|
tests/test_rhumb.py
|
|
237
|
+
tests/test_rotations.py
|
|
225
238
|
tests/test_sgp4.py
|
|
226
239
|
tests/test_signal_processing.py
|
|
227
240
|
tests/test_smoothers.py
|
|
@@ -229,6 +242,7 @@ tests/test_spatial_containers_parametrized.py
|
|
|
229
242
|
tests/test_spatial_structures.py
|
|
230
243
|
tests/test_special_functions.py
|
|
231
244
|
tests/test_special_orbits.py
|
|
245
|
+
tests/test_square_root_filters.py
|
|
232
246
|
tests/test_static_estimation.py
|
|
233
247
|
tests/test_terrain.py
|
|
234
248
|
tests/test_terrain_loaders.py
|
|
@@ -238,4 +252,5 @@ tests/test_trackers.py
|
|
|
238
252
|
tests/test_tracking_containers.py
|
|
239
253
|
tests/test_transforms.py
|
|
240
254
|
tests/test_validation.py
|
|
255
|
+
tests/test_wavelets.py
|
|
241
256
|
tests/unit/test_core.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nrl-tracker"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.11.0"
|
|
8
8
|
description = "Python port of the U.S. Naval Research Laboratory's Tracker Component Library for target tracking algorithms"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -6,8 +6,8 @@ systems, dynamic models, estimation algorithms, and mathematical functions.
|
|
|
6
6
|
|
|
7
7
|
This is a Python port of the U.S. Naval Research Laboratory's Tracker Component
|
|
8
8
|
Library originally written in MATLAB.
|
|
9
|
-
**Current Version:** 1.
|
|
10
|
-
**Status:** Production-ready, 2,
|
|
9
|
+
**Current Version:** 1.11.0 (January 5, 2026)
|
|
10
|
+
**Status:** Production-ready, 2,894 tests passing, 76% line coverage
|
|
11
11
|
Examples
|
|
12
12
|
--------
|
|
13
13
|
>>> import pytcl as pytcl
|
|
@@ -21,7 +21,7 @@ References
|
|
|
21
21
|
no. 5, pp. 18-27, May 2017.
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
-
__version__ = "1.
|
|
24
|
+
__version__ = "1.11.0"
|
|
25
25
|
__author__ = "Python Port Contributors"
|
|
26
26
|
__original_author__ = "David F. Crouse, Naval Research Laboratory"
|
|
27
27
|
|
|
@@ -9,6 +9,11 @@ enabling more complex assignment scenarios such as:
|
|
|
9
9
|
The module provides a unified interface for solving high-dimensional
|
|
10
10
|
assignment problems using generalized relaxation methods.
|
|
11
11
|
|
|
12
|
+
Performance Notes
|
|
13
|
+
-----------------
|
|
14
|
+
For sparse cost tensors (mostly invalid assignments), use SparseCostTensor
|
|
15
|
+
to reduce memory usage by up to 50% and improve performance on large problems.
|
|
16
|
+
|
|
12
17
|
References
|
|
13
18
|
----------
|
|
14
19
|
.. [1] Poore, A. B., "Multidimensional Assignment Problem and Data
|
|
@@ -18,7 +23,7 @@ References
|
|
|
18
23
|
Drug Discovery," Perspectives in Drug Discovery and Design, 2003.
|
|
19
24
|
"""
|
|
20
25
|
|
|
21
|
-
from typing import NamedTuple, Optional, Tuple
|
|
26
|
+
from typing import List, NamedTuple, Optional, Tuple, Union
|
|
22
27
|
|
|
23
28
|
import numpy as np
|
|
24
29
|
from numpy.typing import NDArray
|
|
@@ -442,3 +447,356 @@ def detect_dimension_conflicts(
|
|
|
442
447
|
return True
|
|
443
448
|
|
|
444
449
|
return False
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
class SparseCostTensor:
|
|
453
|
+
"""
|
|
454
|
+
Sparse representation of N-dimensional cost tensor.
|
|
455
|
+
|
|
456
|
+
For assignment problems where most entries represent invalid
|
|
457
|
+
assignments (infinite cost), storing only valid entries reduces
|
|
458
|
+
memory by 50% or more and speeds up greedy algorithms.
|
|
459
|
+
|
|
460
|
+
Attributes
|
|
461
|
+
----------
|
|
462
|
+
dims : tuple
|
|
463
|
+
Shape of the full tensor (n1, n2, ..., nk).
|
|
464
|
+
indices : ndarray
|
|
465
|
+
Array of shape (n_valid, n_dims) with valid entry indices.
|
|
466
|
+
costs : ndarray
|
|
467
|
+
Array of shape (n_valid,) with costs for valid entries.
|
|
468
|
+
default_cost : float
|
|
469
|
+
Cost for entries not explicitly stored (default: inf).
|
|
470
|
+
|
|
471
|
+
Examples
|
|
472
|
+
--------
|
|
473
|
+
>>> import numpy as np
|
|
474
|
+
>>> # Create sparse tensor for 10x10x10 problem with 50 valid entries
|
|
475
|
+
>>> dims = (10, 10, 10)
|
|
476
|
+
>>> valid_indices = np.random.randint(0, 10, size=(50, 3))
|
|
477
|
+
>>> valid_costs = np.random.rand(50)
|
|
478
|
+
>>> sparse = SparseCostTensor(dims, valid_indices, valid_costs)
|
|
479
|
+
>>> sparse.n_valid
|
|
480
|
+
50
|
|
481
|
+
>>> sparse.sparsity # Fraction of valid entries
|
|
482
|
+
0.05
|
|
483
|
+
|
|
484
|
+
>>> # Convert from dense tensor with inf for invalid
|
|
485
|
+
>>> dense = np.full((5, 5, 5), np.inf)
|
|
486
|
+
>>> dense[0, 0, 0] = 1.0
|
|
487
|
+
>>> dense[1, 1, 1] = 2.0
|
|
488
|
+
>>> sparse = SparseCostTensor.from_dense(dense)
|
|
489
|
+
>>> sparse.n_valid
|
|
490
|
+
2
|
|
491
|
+
"""
|
|
492
|
+
|
|
493
|
+
def __init__(
|
|
494
|
+
self,
|
|
495
|
+
dims: Tuple[int, ...],
|
|
496
|
+
indices: NDArray[np.intp],
|
|
497
|
+
costs: NDArray[np.float64],
|
|
498
|
+
default_cost: float = np.inf,
|
|
499
|
+
):
|
|
500
|
+
"""
|
|
501
|
+
Initialize sparse cost tensor.
|
|
502
|
+
|
|
503
|
+
Parameters
|
|
504
|
+
----------
|
|
505
|
+
dims : tuple
|
|
506
|
+
Shape of the full tensor.
|
|
507
|
+
indices : ndarray
|
|
508
|
+
Valid entry indices, shape (n_valid, n_dims).
|
|
509
|
+
costs : ndarray
|
|
510
|
+
Costs for valid entries, shape (n_valid,).
|
|
511
|
+
default_cost : float
|
|
512
|
+
Cost for invalid (unstored) entries.
|
|
513
|
+
"""
|
|
514
|
+
self.dims = dims
|
|
515
|
+
self.indices = np.asarray(indices, dtype=np.intp)
|
|
516
|
+
self.costs = np.asarray(costs, dtype=np.float64)
|
|
517
|
+
self.default_cost = default_cost
|
|
518
|
+
|
|
519
|
+
# Build lookup for O(1) cost retrieval
|
|
520
|
+
self._cost_map: dict[Tuple[int, ...], float] = {}
|
|
521
|
+
for i in range(len(self.costs)):
|
|
522
|
+
key = tuple(self.indices[i])
|
|
523
|
+
self._cost_map[key] = self.costs[i]
|
|
524
|
+
|
|
525
|
+
@property
|
|
526
|
+
def n_dims(self) -> int:
|
|
527
|
+
"""Number of dimensions."""
|
|
528
|
+
return len(self.dims)
|
|
529
|
+
|
|
530
|
+
@property
|
|
531
|
+
def n_valid(self) -> int:
|
|
532
|
+
"""Number of valid (finite cost) entries."""
|
|
533
|
+
return len(self.costs)
|
|
534
|
+
|
|
535
|
+
@property
|
|
536
|
+
def sparsity(self) -> float:
|
|
537
|
+
"""Fraction of tensor that is valid (0 to 1)."""
|
|
538
|
+
total_size = int(np.prod(self.dims))
|
|
539
|
+
return self.n_valid / total_size if total_size > 0 else 0.0
|
|
540
|
+
|
|
541
|
+
@property
|
|
542
|
+
def memory_savings(self) -> float:
|
|
543
|
+
"""Estimated memory savings vs dense representation (0 to 1)."""
|
|
544
|
+
dense_size = np.prod(self.dims) * 8 # 8 bytes per float64
|
|
545
|
+
sparse_size = self.n_valid * (8 + self.n_dims * 8) # cost + indices
|
|
546
|
+
return max(0, 1 - sparse_size / dense_size) if dense_size > 0 else 0.0
|
|
547
|
+
|
|
548
|
+
def get_cost(self, index: Tuple[int, ...]) -> float:
|
|
549
|
+
"""Get cost for a specific index tuple."""
|
|
550
|
+
return self._cost_map.get(index, self.default_cost)
|
|
551
|
+
|
|
552
|
+
def to_dense(self) -> NDArray[np.float64]:
|
|
553
|
+
"""
|
|
554
|
+
Convert to dense tensor representation.
|
|
555
|
+
|
|
556
|
+
Returns
|
|
557
|
+
-------
|
|
558
|
+
dense : ndarray
|
|
559
|
+
Full tensor with default_cost for unstored entries.
|
|
560
|
+
|
|
561
|
+
Notes
|
|
562
|
+
-----
|
|
563
|
+
May use significant memory for large tensors.
|
|
564
|
+
"""
|
|
565
|
+
dense = np.full(self.dims, self.default_cost, dtype=np.float64)
|
|
566
|
+
for i in range(len(self.costs)):
|
|
567
|
+
dense[tuple(self.indices[i])] = self.costs[i]
|
|
568
|
+
return dense
|
|
569
|
+
|
|
570
|
+
@classmethod
|
|
571
|
+
def from_dense(
|
|
572
|
+
cls,
|
|
573
|
+
dense: NDArray[np.float64],
|
|
574
|
+
threshold: float = 1e10,
|
|
575
|
+
) -> "SparseCostTensor":
|
|
576
|
+
"""
|
|
577
|
+
Create sparse tensor from dense array.
|
|
578
|
+
|
|
579
|
+
Parameters
|
|
580
|
+
----------
|
|
581
|
+
dense : ndarray
|
|
582
|
+
Dense cost tensor.
|
|
583
|
+
threshold : float
|
|
584
|
+
Entries above this value are considered invalid.
|
|
585
|
+
Default 1e10 (catches np.inf and large values).
|
|
586
|
+
|
|
587
|
+
Returns
|
|
588
|
+
-------
|
|
589
|
+
SparseCostTensor
|
|
590
|
+
Sparse representation.
|
|
591
|
+
|
|
592
|
+
Examples
|
|
593
|
+
--------
|
|
594
|
+
>>> import numpy as np
|
|
595
|
+
>>> dense = np.array([[[1, np.inf], [np.inf, 2]],
|
|
596
|
+
... [[np.inf, 3], [4, np.inf]]])
|
|
597
|
+
>>> sparse = SparseCostTensor.from_dense(dense)
|
|
598
|
+
>>> sparse.n_valid
|
|
599
|
+
4
|
|
600
|
+
"""
|
|
601
|
+
valid_mask = dense < threshold
|
|
602
|
+
indices = np.array(np.where(valid_mask)).T
|
|
603
|
+
costs = dense[valid_mask]
|
|
604
|
+
return cls(dense.shape, indices, costs, default_cost=np.inf)
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
def greedy_assignment_nd_sparse(
|
|
608
|
+
sparse_cost: SparseCostTensor,
|
|
609
|
+
max_assignments: Optional[int] = None,
|
|
610
|
+
) -> AssignmentNDResult:
|
|
611
|
+
"""
|
|
612
|
+
Greedy solver for sparse N-dimensional assignment.
|
|
613
|
+
|
|
614
|
+
Selects minimum-cost tuples from valid entries only, which is much
|
|
615
|
+
faster than dense greedy when sparsity < 0.5.
|
|
616
|
+
|
|
617
|
+
Parameters
|
|
618
|
+
----------
|
|
619
|
+
sparse_cost : SparseCostTensor
|
|
620
|
+
Sparse cost tensor with valid entries only.
|
|
621
|
+
max_assignments : int, optional
|
|
622
|
+
Maximum number of assignments (default: min(dimensions)).
|
|
623
|
+
|
|
624
|
+
Returns
|
|
625
|
+
-------
|
|
626
|
+
AssignmentNDResult
|
|
627
|
+
Assignments, total cost, and algorithm info.
|
|
628
|
+
|
|
629
|
+
Examples
|
|
630
|
+
--------
|
|
631
|
+
>>> import numpy as np
|
|
632
|
+
>>> # Create sparse problem
|
|
633
|
+
>>> dims = (10, 10, 10)
|
|
634
|
+
>>> # Only 20 valid assignments out of 1000
|
|
635
|
+
>>> indices = np.array([[i, i, i] for i in range(10)] +
|
|
636
|
+
... [[i, (i+1)%10, (i+2)%10] for i in range(10)])
|
|
637
|
+
>>> costs = np.random.rand(20)
|
|
638
|
+
>>> sparse = SparseCostTensor(dims, indices, costs)
|
|
639
|
+
>>> result = greedy_assignment_nd_sparse(sparse)
|
|
640
|
+
>>> result.converged
|
|
641
|
+
True
|
|
642
|
+
|
|
643
|
+
Notes
|
|
644
|
+
-----
|
|
645
|
+
Time complexity is O(n_valid * log(n_valid)) vs O(total_size * log(total_size))
|
|
646
|
+
for dense greedy. For a 10x10x10 tensor with 50 valid entries, this is
|
|
647
|
+
50*log(50) vs 1000*log(1000), about 20x faster.
|
|
648
|
+
"""
|
|
649
|
+
dims = sparse_cost.dims
|
|
650
|
+
n_dims = sparse_cost.n_dims
|
|
651
|
+
|
|
652
|
+
if max_assignments is None:
|
|
653
|
+
max_assignments = min(dims)
|
|
654
|
+
|
|
655
|
+
# Sort valid entries by cost
|
|
656
|
+
sorted_indices = np.argsort(sparse_cost.costs)
|
|
657
|
+
|
|
658
|
+
assignments: List[Tuple[int, ...]] = []
|
|
659
|
+
used_indices: List[set[int]] = [set() for _ in range(n_dims)]
|
|
660
|
+
total_cost = 0.0
|
|
661
|
+
|
|
662
|
+
for sorted_idx in sorted_indices:
|
|
663
|
+
if len(assignments) >= max_assignments:
|
|
664
|
+
break
|
|
665
|
+
|
|
666
|
+
multi_idx = tuple(sparse_cost.indices[sorted_idx])
|
|
667
|
+
|
|
668
|
+
# Check if any dimension index is already used
|
|
669
|
+
conflict = False
|
|
670
|
+
for d, idx in enumerate(multi_idx):
|
|
671
|
+
if idx in used_indices[d]:
|
|
672
|
+
conflict = True
|
|
673
|
+
break
|
|
674
|
+
|
|
675
|
+
if not conflict:
|
|
676
|
+
assignments.append(multi_idx)
|
|
677
|
+
total_cost += sparse_cost.costs[sorted_idx]
|
|
678
|
+
for d, idx in enumerate(multi_idx):
|
|
679
|
+
used_indices[d].add(idx)
|
|
680
|
+
|
|
681
|
+
assignments_array = np.array(assignments, dtype=np.intp)
|
|
682
|
+
if assignments_array.size == 0:
|
|
683
|
+
assignments_array = np.empty((0, n_dims), dtype=np.intp)
|
|
684
|
+
|
|
685
|
+
return AssignmentNDResult(
|
|
686
|
+
assignments=assignments_array,
|
|
687
|
+
cost=total_cost,
|
|
688
|
+
converged=True,
|
|
689
|
+
n_iterations=1,
|
|
690
|
+
gap=0.0,
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
def assignment_nd(
|
|
695
|
+
cost: Union[NDArray[np.float64], SparseCostTensor],
|
|
696
|
+
method: str = "auto",
|
|
697
|
+
max_assignments: Optional[int] = None,
|
|
698
|
+
max_iterations: int = 100,
|
|
699
|
+
tolerance: float = 1e-6,
|
|
700
|
+
epsilon: float = 0.01,
|
|
701
|
+
verbose: bool = False,
|
|
702
|
+
) -> AssignmentNDResult:
|
|
703
|
+
"""
|
|
704
|
+
Unified interface for N-dimensional assignment.
|
|
705
|
+
|
|
706
|
+
Automatically selects between dense and sparse algorithms based on
|
|
707
|
+
input type and sparsity.
|
|
708
|
+
|
|
709
|
+
Parameters
|
|
710
|
+
----------
|
|
711
|
+
cost : ndarray or SparseCostTensor
|
|
712
|
+
Cost tensor (dense) or sparse cost representation.
|
|
713
|
+
method : str
|
|
714
|
+
Algorithm to use: 'auto', 'greedy', 'relaxation', 'auction'.
|
|
715
|
+
'auto' selects greedy for sparse, relaxation for dense.
|
|
716
|
+
max_assignments : int, optional
|
|
717
|
+
Maximum number of assignments for greedy methods.
|
|
718
|
+
max_iterations : int
|
|
719
|
+
Maximum iterations for iterative methods.
|
|
720
|
+
tolerance : float
|
|
721
|
+
Convergence tolerance for relaxation.
|
|
722
|
+
epsilon : float
|
|
723
|
+
Price increment for auction algorithm.
|
|
724
|
+
verbose : bool
|
|
725
|
+
Print progress information.
|
|
726
|
+
|
|
727
|
+
Returns
|
|
728
|
+
-------
|
|
729
|
+
AssignmentNDResult
|
|
730
|
+
Assignment solution.
|
|
731
|
+
|
|
732
|
+
Examples
|
|
733
|
+
--------
|
|
734
|
+
>>> import numpy as np
|
|
735
|
+
>>> # Dense usage
|
|
736
|
+
>>> cost = np.random.rand(4, 4, 4)
|
|
737
|
+
>>> result = assignment_nd(cost, method='greedy')
|
|
738
|
+
>>> result.converged
|
|
739
|
+
True
|
|
740
|
+
|
|
741
|
+
>>> # Sparse usage (more efficient for large sparse problems)
|
|
742
|
+
>>> dense = np.full((20, 20, 20), np.inf)
|
|
743
|
+
>>> for i in range(20):
|
|
744
|
+
... dense[i, i, i] = np.random.rand()
|
|
745
|
+
>>> sparse = SparseCostTensor.from_dense(dense)
|
|
746
|
+
>>> result = assignment_nd(sparse, method='auto')
|
|
747
|
+
>>> result.converged
|
|
748
|
+
True
|
|
749
|
+
|
|
750
|
+
See Also
|
|
751
|
+
--------
|
|
752
|
+
greedy_assignment_nd : Dense greedy algorithm.
|
|
753
|
+
greedy_assignment_nd_sparse : Sparse greedy algorithm.
|
|
754
|
+
relaxation_assignment_nd : Lagrangian relaxation.
|
|
755
|
+
auction_assignment_nd : Auction algorithm.
|
|
756
|
+
"""
|
|
757
|
+
if isinstance(cost, SparseCostTensor):
|
|
758
|
+
# Sparse input - use sparse algorithm
|
|
759
|
+
if method in ("auto", "greedy"):
|
|
760
|
+
return greedy_assignment_nd_sparse(cost, max_assignments)
|
|
761
|
+
else:
|
|
762
|
+
# Convert to dense for other methods
|
|
763
|
+
dense = cost.to_dense()
|
|
764
|
+
if method == "relaxation":
|
|
765
|
+
return relaxation_assignment_nd(
|
|
766
|
+
dense, max_iterations, tolerance, verbose
|
|
767
|
+
)
|
|
768
|
+
elif method == "auction":
|
|
769
|
+
return auction_assignment_nd(
|
|
770
|
+
dense, max_iterations, epsilon=epsilon, verbose=verbose
|
|
771
|
+
)
|
|
772
|
+
else:
|
|
773
|
+
raise ValueError(f"Unknown method: {method}")
|
|
774
|
+
else:
|
|
775
|
+
# Dense input
|
|
776
|
+
cost = np.asarray(cost, dtype=np.float64)
|
|
777
|
+
if method == "auto":
|
|
778
|
+
# Use relaxation for better solutions on dense
|
|
779
|
+
return relaxation_assignment_nd(cost, max_iterations, tolerance, verbose)
|
|
780
|
+
elif method == "greedy":
|
|
781
|
+
return greedy_assignment_nd(cost, max_assignments)
|
|
782
|
+
elif method == "relaxation":
|
|
783
|
+
return relaxation_assignment_nd(cost, max_iterations, tolerance, verbose)
|
|
784
|
+
elif method == "auction":
|
|
785
|
+
return auction_assignment_nd(
|
|
786
|
+
cost, max_iterations, epsilon=epsilon, verbose=verbose
|
|
787
|
+
)
|
|
788
|
+
else:
|
|
789
|
+
raise ValueError(f"Unknown method: {method}")
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
__all__ = [
|
|
793
|
+
"AssignmentNDResult",
|
|
794
|
+
"SparseCostTensor",
|
|
795
|
+
"validate_cost_tensor",
|
|
796
|
+
"greedy_assignment_nd",
|
|
797
|
+
"greedy_assignment_nd_sparse",
|
|
798
|
+
"relaxation_assignment_nd",
|
|
799
|
+
"auction_assignment_nd",
|
|
800
|
+
"detect_dimension_conflicts",
|
|
801
|
+
"assignment_nd",
|
|
802
|
+
]
|