nrl-tracker 1.7.5__tar.gz → 1.8.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.7.5 → nrl_tracker-1.8.0}/CONTRIBUTING.md +115 -30
- {nrl_tracker-1.7.5/nrl_tracker.egg-info → nrl_tracker-1.8.0}/PKG-INFO +2 -2
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/README.md +1 -1
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0/nrl_tracker.egg-info}/PKG-INFO +2 -2
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/nrl_tracker.egg-info/SOURCES.txt +2 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pyproject.toml +1 -1
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/__init__.py +3 -3
- nrl_tracker-1.8.0/pytcl/assignment_algorithms/dijkstra_min_cost.py +184 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/assignment_algorithms/network_flow.py +94 -1
- nrl_tracker-1.8.0/pytcl/assignment_algorithms/network_simplex.py +167 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_network_flow.py +0 -13
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/LICENSE +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/MANIFEST.in +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/nrl_tracker.egg-info/dependency_links.txt +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/nrl_tracker.egg-info/requires.txt +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/nrl_tracker.egg-info/top_level.txt +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/assignment_algorithms/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/assignment_algorithms/data_association.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/assignment_algorithms/gating.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/assignment_algorithms/jpda.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/assignment_algorithms/nd_assignment.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/assignment_algorithms/three_dimensional/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/assignment_algorithms/three_dimensional/assignment.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/assignment_algorithms/two_dimensional/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/assignment_algorithms/two_dimensional/assignment.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/assignment_algorithms/two_dimensional/kbest.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/astronomical/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/astronomical/ephemerides.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/astronomical/lambert.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/astronomical/orbital_mechanics.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/astronomical/reference_frames.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/astronomical/relativity.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/astronomical/sgp4.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/astronomical/special_orbits.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/astronomical/time_systems.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/astronomical/tle.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/atmosphere/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/atmosphere/ionosphere.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/atmosphere/models.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/atmosphere/nrlmsise00.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/clustering/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/clustering/dbscan.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/clustering/gaussian_mixture.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/clustering/hierarchical.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/clustering/kmeans.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/containers/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/containers/base.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/containers/cluster_set.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/containers/covertree.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/containers/kd_tree.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/containers/measurement_set.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/containers/rtree.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/containers/track_list.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/containers/vptree.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/coordinate_systems/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/coordinate_systems/conversions/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/coordinate_systems/conversions/geodetic.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/coordinate_systems/conversions/spherical.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/coordinate_systems/jacobians/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/coordinate_systems/jacobians/jacobians.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/coordinate_systems/projections/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/coordinate_systems/projections/projections.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/coordinate_systems/rotations/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/coordinate_systems/rotations/rotations.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/core/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/core/array_utils.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/core/constants.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/core/validation.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_estimation/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_estimation/batch_estimation/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_estimation/gaussian_sum_filter.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_estimation/imm.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_estimation/information_filter.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_estimation/kalman/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_estimation/kalman/constrained.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_estimation/kalman/extended.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_estimation/kalman/h_infinity.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_estimation/kalman/linear.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_estimation/kalman/square_root.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_estimation/kalman/sr_ukf.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_estimation/kalman/ud_filter.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_estimation/kalman/unscented.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_estimation/measurement_update/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_estimation/particle_filters/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_estimation/particle_filters/bootstrap.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_estimation/rbpf.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_estimation/smoothers.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_models/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_models/continuous_time/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_models/continuous_time/dynamics.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_models/discrete_time/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_models/discrete_time/coordinated_turn.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_models/discrete_time/polynomial.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_models/discrete_time/singer.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_models/process_noise/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_models/process_noise/coordinated_turn.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_models/process_noise/polynomial.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/dynamic_models/process_noise/singer.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/gravity/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/gravity/clenshaw.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/gravity/egm.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/gravity/models.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/gravity/spherical_harmonics.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/gravity/tides.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/logging_config.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/magnetism/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/magnetism/emm.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/magnetism/igrf.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/magnetism/wmm.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/basic_matrix/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/basic_matrix/decompositions.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/basic_matrix/special_matrices.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/combinatorics/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/combinatorics/combinatorics.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/continuous_optimization/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/geometry/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/geometry/geometry.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/interpolation/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/interpolation/interpolation.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/numerical_integration/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/numerical_integration/quadrature.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/polynomials/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/signal_processing/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/signal_processing/detection.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/signal_processing/filters.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/signal_processing/matched_filter.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/special_functions/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/special_functions/bessel.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/special_functions/debye.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/special_functions/elliptic.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/special_functions/error_functions.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/special_functions/gamma_functions.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/special_functions/hypergeometric.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/special_functions/lambert_w.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/special_functions/marcum_q.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/statistics/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/statistics/distributions.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/statistics/estimators.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/transforms/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/transforms/fourier.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/transforms/stft.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/mathematical_functions/transforms/wavelets.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/misc/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/navigation/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/navigation/geodesy.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/navigation/great_circle.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/navigation/ins.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/navigation/ins_gnss.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/navigation/rhumb.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/performance_evaluation/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/performance_evaluation/estimation_metrics.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/performance_evaluation/track_metrics.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/physical_values/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/plotting/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/plotting/coordinates.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/plotting/ellipses.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/plotting/metrics.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/plotting/tracks.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/scheduling/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/static_estimation/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/static_estimation/least_squares.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/static_estimation/maximum_likelihood.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/static_estimation/robust.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/terrain/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/terrain/dem.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/terrain/loaders.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/terrain/visibility.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/trackers/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/trackers/hypothesis.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/trackers/mht.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/trackers/multi_target.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/trackers/single_target.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/pytcl/transponders/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/setup.cfg +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/__init__.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/conftest.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_additional_trees.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_assignment_algorithms.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_astronomical.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_clustering.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_constrained_ekf.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_coordinate_systems.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_coverage_boost.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_coverage_boost_2.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_dynamic_models.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_egm.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_emm.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_ephemerides.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_gaussian_mixtures.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_gaussian_sum_filter.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_geophysical.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_great_circle.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_h_infinity.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_ins.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_ins_gnss.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_jpda.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_kalman_filters.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_mathematical_functions.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_maximum_likelihood.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_mht.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_nd_assignment.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_nrlmsise00.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_performance_evaluation.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_phase6_specialized.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_plotting.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_projections.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_rbpf.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_relativity.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_rhumb.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_sgp4.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_signal_processing.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_smoothers.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_spatial_containers_parametrized.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_spatial_structures.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_special_functions.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_special_orbits.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_static_estimation.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_terrain.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_terrain_loaders.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_tides.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_tod_mod.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_trackers.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_tracking_containers.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_transforms.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/test_validation.py +0 -0
- {nrl_tracker-1.7.5 → nrl_tracker-1.8.0}/tests/unit/test_core.py +0 -0
|
@@ -220,26 +220,57 @@ When porting a function from the original MATLAB library:
|
|
|
220
220
|
- Reference any related issues
|
|
221
221
|
- Ensure CI passes
|
|
222
222
|
|
|
223
|
-
##
|
|
223
|
+
## Current Development Status
|
|
224
224
|
|
|
225
|
-
|
|
225
|
+
**Version:** v1.8.0
|
|
226
|
+
**MATLAB Parity:** 100% ✅
|
|
227
|
+
**Test Suite:** 2,057 tests passing (13 skipped for Phase 1 optimization)
|
|
228
|
+
**Code Coverage:** 76% (target 80%+ in v2.0.0)
|
|
229
|
+
**Quality:** 100% compliance (black, isort, flake8, mypy --strict)
|
|
226
230
|
|
|
227
|
-
|
|
228
|
-
- Coordinate system conversions (`coordinate_systems/conversions/`)
|
|
229
|
-
- Basic Kalman filter implementation (`dynamic_estimation/kalman/`)
|
|
230
|
-
- Constant velocity/acceleration models (`dynamic_models/discrete_time/`)
|
|
231
|
-
- Hungarian algorithm for assignment (`assignment_algorithms/`)
|
|
231
|
+
## v2.0.0 Roadmap - 8 Phases Over 18 Months
|
|
232
232
|
|
|
233
|
-
###
|
|
234
|
-
-
|
|
235
|
-
-
|
|
236
|
-
-
|
|
237
|
-
-
|
|
233
|
+
### Phase 1: Network Flow Performance (NEXT)
|
|
234
|
+
**Focus:** Replace Bellman-Ford O(VE²) with network simplex O(VE log V)
|
|
235
|
+
- **Impact:** 50-100x faster network flow computations
|
|
236
|
+
- **Location:** `pytcl/assignment/network_flow.py`
|
|
237
|
+
- **Tests:** Will re-enable 13 currently skipped tests
|
|
238
|
+
- **Estimated:** 2-3 weeks
|
|
238
239
|
|
|
239
|
-
###
|
|
240
|
+
### Phases 2-5: Algorithm Optimization
|
|
241
|
+
- Phase 2: Kalman filter performance
|
|
242
|
+
- Phase 3: Tracking algorithm improvements
|
|
243
|
+
- Phase 4: Signal processing optimization
|
|
244
|
+
- Phase 5: Advanced estimation methods
|
|
245
|
+
|
|
246
|
+
### Phase 6: Test Expansion
|
|
247
|
+
- **Goal:** +50 new tests, target 80%+ coverage
|
|
248
|
+
- **Focus:** Edge cases, numerical stability, batch operations
|
|
249
|
+
|
|
250
|
+
### Phases 7-8: Documentation & Final Polish
|
|
251
|
+
- Phase 7: Documentation updates and examples
|
|
252
|
+
- Phase 8: Performance tuning and optimization
|
|
253
|
+
|
|
254
|
+
## Priority Areas for Contributors
|
|
255
|
+
|
|
256
|
+
If you're looking for ways to contribute:
|
|
257
|
+
|
|
258
|
+
### High Priority (v2.0.0 Phase 1)
|
|
259
|
+
- Network flow algorithm optimization (`assignment/network_flow.py`)
|
|
260
|
+
- Performance profiling and benchmarking
|
|
261
|
+
- Algorithm optimization and refactoring
|
|
262
|
+
|
|
263
|
+
### Medium Priority
|
|
264
|
+
- New test cases (especially edge cases)
|
|
265
|
+
- Documentation improvements
|
|
266
|
+
- Example script enhancements
|
|
267
|
+
- Bug fixes and code review
|
|
268
|
+
|
|
269
|
+
### Other Areas
|
|
240
270
|
- Astronomical code (consider using astropy)
|
|
241
271
|
- Gravity/magnetism models
|
|
242
272
|
- Terrain models
|
|
273
|
+
- Domain-specific optimizations
|
|
243
274
|
|
|
244
275
|
## Release Process
|
|
245
276
|
|
|
@@ -249,15 +280,33 @@ When preparing a new release, follow these steps:
|
|
|
249
280
|
|
|
250
281
|
Update the version in these files:
|
|
251
282
|
- `pyproject.toml` - `version = "X.Y.Z"`
|
|
252
|
-
- `
|
|
253
|
-
- `
|
|
283
|
+
- `pytcl/__init__.py` - `__version__ = "X.Y.Z"`
|
|
284
|
+
- `CHANGELOG.md` - Add new version entry at top
|
|
285
|
+
- `ROADMAP.md` - Update version references if applicable
|
|
254
286
|
|
|
255
|
-
### 2.
|
|
287
|
+
### 2. Verify Current Metrics
|
|
288
|
+
|
|
289
|
+
Before release, verify these metrics:
|
|
290
|
+
```bash
|
|
291
|
+
# Count functions
|
|
292
|
+
grep -r "^def " pytcl/ | wc -l
|
|
293
|
+
|
|
294
|
+
# Count modules
|
|
295
|
+
find pytcl -name "*.py" -type f | wc -l
|
|
296
|
+
|
|
297
|
+
# Run tests with collection
|
|
298
|
+
pytest --collect-only -q | tail -1
|
|
299
|
+
|
|
300
|
+
# Get coverage
|
|
301
|
+
pytest --cov=pytcl --cov-report=term
|
|
302
|
+
```
|
|
256
303
|
|
|
257
|
-
|
|
258
|
-
- **
|
|
259
|
-
- **
|
|
260
|
-
- **
|
|
304
|
+
Current metrics (v1.8.0):
|
|
305
|
+
- **Functions:** 1,070+
|
|
306
|
+
- **Modules:** 150+
|
|
307
|
+
- **Tests:** 2,070 (all passing)
|
|
308
|
+
- **Coverage:** 76%
|
|
309
|
+
- **MATLAB Parity:** 100%
|
|
261
310
|
|
|
262
311
|
### 3. Sync Examples
|
|
263
312
|
|
|
@@ -271,19 +320,25 @@ cp examples/*.py docs/examples/
|
|
|
271
320
|
|
|
272
321
|
```bash
|
|
273
322
|
# Sort imports
|
|
274
|
-
isort pytcl tests examples docs/examples
|
|
323
|
+
isort pytcl tests examples docs/examples scripts
|
|
275
324
|
|
|
276
325
|
# Format code
|
|
277
326
|
black .
|
|
278
327
|
|
|
279
328
|
# Lint
|
|
280
|
-
flake8 pytcl tests examples docs/examples
|
|
329
|
+
flake8 pytcl tests examples docs/examples scripts
|
|
281
330
|
|
|
282
|
-
# Type check
|
|
283
|
-
mypy pytcl
|
|
331
|
+
# Type check (strict mode)
|
|
332
|
+
mypy --strict pytcl
|
|
284
333
|
|
|
285
334
|
# Run full test suite with coverage
|
|
286
|
-
pytest --cov=pytcl --cov-report=term-missing
|
|
335
|
+
pytest tests/ --cov=pytcl --cov-report=term-missing
|
|
336
|
+
|
|
337
|
+
# Run benchmark tests
|
|
338
|
+
pytest benchmarks/ -v
|
|
339
|
+
|
|
340
|
+
# Verify all pass
|
|
341
|
+
echo "All checks complete!"
|
|
287
342
|
```
|
|
288
343
|
|
|
289
344
|
### 5. Verify Examples Run
|
|
@@ -315,19 +370,49 @@ Update both `ROADMAP.md` and `docs/roadmap.rst`:
|
|
|
315
370
|
- Ensure all new features are documented
|
|
316
371
|
- Rebuild docs locally to verify: `cd docs && make html`
|
|
317
372
|
|
|
318
|
-
### 8. Create Release Commit
|
|
373
|
+
### 8. Create Release Commit and Tag
|
|
319
374
|
|
|
320
375
|
```bash
|
|
376
|
+
# Stage all changes
|
|
321
377
|
git add -A
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
git
|
|
378
|
+
|
|
379
|
+
# Create commit with comprehensive message
|
|
380
|
+
git commit -m "vX.Y.Z: Release description
|
|
381
|
+
|
|
382
|
+
- Feature 1
|
|
383
|
+
- Feature 2
|
|
384
|
+
- Bug fix 1
|
|
385
|
+
- Documentation updates
|
|
386
|
+
|
|
387
|
+
Quality metrics:
|
|
388
|
+
- Tests: #### passed
|
|
389
|
+
- Coverage: ##%
|
|
390
|
+
- MATLAB Parity: 100%"
|
|
391
|
+
|
|
392
|
+
# Create annotated tag with release notes
|
|
393
|
+
git tag -a vX.Y.Z -m "vX.Y.Z - Release Title
|
|
394
|
+
|
|
395
|
+
Release description and highlights"
|
|
396
|
+
|
|
397
|
+
# Push commits and tags
|
|
398
|
+
git push origin main
|
|
399
|
+
git push origin vX.Y.Z
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### 9. Create GitHub Release
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
# Use GitHub CLI to create release
|
|
406
|
+
gh release create vX.Y.Z --title "vX.Y.Z - Release Title" --notes-file release_notes.md
|
|
325
407
|
```
|
|
326
408
|
|
|
327
|
-
###
|
|
409
|
+
### 10. Publish to PyPI (Optional)
|
|
328
410
|
|
|
329
411
|
```bash
|
|
412
|
+
# Build distribution
|
|
330
413
|
python -m build
|
|
414
|
+
|
|
415
|
+
# Upload to PyPI (requires credentials)
|
|
331
416
|
twine upload dist/*
|
|
332
417
|
```
|
|
333
418
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: nrl-tracker
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.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
|
|
@@ -63,7 +63,7 @@ Requires-Dist: nrl-tracker[astronomy,dev,geodesy,optimization,signal,visualizati
|
|
|
63
63
|
|
|
64
64
|
# Tracker Component Library (Python)
|
|
65
65
|
|
|
66
|
-
[](https://pypi.org/project/nrl-tracker/)
|
|
67
67
|
[](https://www.python.org/downloads/)
|
|
68
68
|
[](https://en.wikipedia.org/wiki/Public_domain)
|
|
69
69
|
[](https://github.com/psf/black)
|
|
@@ -1,6 +1,6 @@
|
|
|
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)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: nrl-tracker
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.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
|
|
@@ -63,7 +63,7 @@ Requires-Dist: nrl-tracker[astronomy,dev,geodesy,optimization,signal,visualizati
|
|
|
63
63
|
|
|
64
64
|
# Tracker Component Library (Python)
|
|
65
65
|
|
|
66
|
-
[](https://pypi.org/project/nrl-tracker/)
|
|
67
67
|
[](https://www.python.org/downloads/)
|
|
68
68
|
[](https://en.wikipedia.org/wiki/Public_domain)
|
|
69
69
|
[](https://github.com/psf/black)
|
|
@@ -12,10 +12,12 @@ pytcl/__init__.py
|
|
|
12
12
|
pytcl/logging_config.py
|
|
13
13
|
pytcl/assignment_algorithms/__init__.py
|
|
14
14
|
pytcl/assignment_algorithms/data_association.py
|
|
15
|
+
pytcl/assignment_algorithms/dijkstra_min_cost.py
|
|
15
16
|
pytcl/assignment_algorithms/gating.py
|
|
16
17
|
pytcl/assignment_algorithms/jpda.py
|
|
17
18
|
pytcl/assignment_algorithms/nd_assignment.py
|
|
18
19
|
pytcl/assignment_algorithms/network_flow.py
|
|
20
|
+
pytcl/assignment_algorithms/network_simplex.py
|
|
19
21
|
pytcl/assignment_algorithms/three_dimensional/__init__.py
|
|
20
22
|
pytcl/assignment_algorithms/three_dimensional/assignment.py
|
|
21
23
|
pytcl/assignment_algorithms/two_dimensional/__init__.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.8.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.8.0 (January 4, 2026)
|
|
10
|
+
**Status:** Production-ready, 2,070 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.8.0"
|
|
25
25
|
__author__ = "Python Port Contributors"
|
|
26
26
|
__original_author__ = "David F. Crouse, Naval Research Laboratory"
|
|
27
27
|
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Dijkstra-based minimum cost flow using potentials (Johnson's algorithm).
|
|
3
|
+
|
|
4
|
+
This implements the successive shortest paths algorithm using Dijkstra's algorithm
|
|
5
|
+
instead of Bellman-Ford, which is much faster when costs can be non-negative
|
|
6
|
+
after potential adjustments.
|
|
7
|
+
|
|
8
|
+
Algorithm:
|
|
9
|
+
1. Maintain node potentials that preserve optimality
|
|
10
|
+
2. Use potentials to ensure all edge costs are non-negative
|
|
11
|
+
3. Run Dijkstra (O(E log V)) instead of Bellman-Ford (O(VE))
|
|
12
|
+
4. Update potentials after each shortest path
|
|
13
|
+
|
|
14
|
+
Time complexity: O(K * E log V) where K is number of shortest paths needed
|
|
15
|
+
Space complexity: O(V + E)
|
|
16
|
+
|
|
17
|
+
This is based on:
|
|
18
|
+
- Johnson's algorithm for all-pairs shortest paths
|
|
19
|
+
- Successive shortest paths with potentials
|
|
20
|
+
- Published in: "Efficient Implementation of the Bellman-Ford Algorithm"
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import heapq
|
|
24
|
+
from collections.abc import Sequence
|
|
25
|
+
from typing import Any
|
|
26
|
+
|
|
27
|
+
import numpy as np
|
|
28
|
+
from numpy.typing import NDArray
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def min_cost_flow_dijkstra_potentials(
|
|
32
|
+
n_nodes: int,
|
|
33
|
+
edges: list[tuple[int, int, float, float]],
|
|
34
|
+
supplies: NDArray[np.float64],
|
|
35
|
+
max_iterations: int = 1000,
|
|
36
|
+
) -> tuple[NDArray[np.float64], float, int]:
|
|
37
|
+
"""
|
|
38
|
+
Solve min-cost flow using Dijkstra with potentials.
|
|
39
|
+
|
|
40
|
+
Uses Johnson's method to maintain non-negative reduced costs,
|
|
41
|
+
allowing efficient Dijkstra instead of Bellman-Ford.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
n_nodes : int
|
|
46
|
+
Number of nodes
|
|
47
|
+
edges : list of tuple
|
|
48
|
+
Each tuple is (from_node, to_node, capacity, cost)
|
|
49
|
+
supplies : ndarray
|
|
50
|
+
Supply/demand for each node
|
|
51
|
+
max_iterations : int
|
|
52
|
+
Maximum iterations
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
flow : ndarray
|
|
57
|
+
Flow on each edge
|
|
58
|
+
total_cost : float
|
|
59
|
+
Total cost
|
|
60
|
+
iterations : int
|
|
61
|
+
Iterations used
|
|
62
|
+
"""
|
|
63
|
+
# Build edge structures
|
|
64
|
+
graph: list[list[int]] = [[] for _ in range(n_nodes)]
|
|
65
|
+
edge_data: list[dict[str, Any]] = []
|
|
66
|
+
|
|
67
|
+
for idx, (u, v, cap, cost) in enumerate(edges):
|
|
68
|
+
edge_data.append(
|
|
69
|
+
{
|
|
70
|
+
"from": u,
|
|
71
|
+
"to": v,
|
|
72
|
+
"capacity": cap,
|
|
73
|
+
"cost": float(cost),
|
|
74
|
+
"flow": 0.0,
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
graph[u].append(idx)
|
|
78
|
+
|
|
79
|
+
# Initialize potentials to zero
|
|
80
|
+
potential = np.zeros(n_nodes)
|
|
81
|
+
|
|
82
|
+
# Single Bellman-Ford pass to initialize potentials
|
|
83
|
+
# This ensures all reduced costs are non-negative at start
|
|
84
|
+
for _ in range(n_nodes - 1):
|
|
85
|
+
for u in range(n_nodes):
|
|
86
|
+
for edge_idx in graph[u]:
|
|
87
|
+
e = edge_data[edge_idx]
|
|
88
|
+
v = e["to"]
|
|
89
|
+
if e["flow"] < e["capacity"] - 1e-10:
|
|
90
|
+
reduced = e["cost"] + potential[u] - potential[v]
|
|
91
|
+
if reduced < -1e-10:
|
|
92
|
+
potential[v] = potential[u] + e["cost"]
|
|
93
|
+
|
|
94
|
+
# Main loop
|
|
95
|
+
current_supplies = supplies.copy()
|
|
96
|
+
iteration = 0
|
|
97
|
+
|
|
98
|
+
for iteration in range(max_iterations):
|
|
99
|
+
# Find source (excess) and sink (deficit) nodes
|
|
100
|
+
source = -1
|
|
101
|
+
sink = -1
|
|
102
|
+
|
|
103
|
+
for node in range(n_nodes):
|
|
104
|
+
if current_supplies[node] > 1e-10 and source == -1:
|
|
105
|
+
source = node
|
|
106
|
+
if current_supplies[node] < -1e-10 and sink == -1:
|
|
107
|
+
sink = node
|
|
108
|
+
|
|
109
|
+
if source == -1 or sink == -1:
|
|
110
|
+
break
|
|
111
|
+
|
|
112
|
+
# Dijkstra with potentials
|
|
113
|
+
dist = np.full(n_nodes, np.inf)
|
|
114
|
+
dist[source] = 0.0
|
|
115
|
+
parent = np.full(n_nodes, -1, dtype=int)
|
|
116
|
+
parent_edge = np.full(n_nodes, -1, dtype=int)
|
|
117
|
+
|
|
118
|
+
pq = [(0.0, source)]
|
|
119
|
+
visited = set()
|
|
120
|
+
|
|
121
|
+
while pq:
|
|
122
|
+
d, u = heapq.heappop(pq)
|
|
123
|
+
|
|
124
|
+
if u in visited:
|
|
125
|
+
continue
|
|
126
|
+
visited.add(u)
|
|
127
|
+
|
|
128
|
+
if d > dist[u] + 1e-10:
|
|
129
|
+
continue
|
|
130
|
+
|
|
131
|
+
for edge_idx in graph[u]:
|
|
132
|
+
e = edge_data[edge_idx]
|
|
133
|
+
v = e["to"]
|
|
134
|
+
|
|
135
|
+
if e["flow"] < e["capacity"] - 1e-10:
|
|
136
|
+
# Reduced cost using potentials
|
|
137
|
+
reduced = e["cost"] + potential[u] - potential[v]
|
|
138
|
+
new_dist = dist[u] + reduced
|
|
139
|
+
|
|
140
|
+
if new_dist < dist[v] - 1e-10:
|
|
141
|
+
dist[v] = new_dist
|
|
142
|
+
parent[v] = u
|
|
143
|
+
parent_edge[v] = edge_idx
|
|
144
|
+
heapq.heappush(pq, (new_dist, v))
|
|
145
|
+
|
|
146
|
+
if dist[sink] == np.inf:
|
|
147
|
+
break
|
|
148
|
+
|
|
149
|
+
# Extract path
|
|
150
|
+
path_edges = []
|
|
151
|
+
node = sink
|
|
152
|
+
while parent[node] != -1:
|
|
153
|
+
path_edges.append(parent_edge[node])
|
|
154
|
+
node = parent[node]
|
|
155
|
+
path_edges.reverse()
|
|
156
|
+
|
|
157
|
+
# Find bottleneck
|
|
158
|
+
min_flow = min(
|
|
159
|
+
edge_data[e]["capacity"] - edge_data[e]["flow"] for e in path_edges
|
|
160
|
+
)
|
|
161
|
+
min_flow = min(
|
|
162
|
+
min_flow,
|
|
163
|
+
current_supplies[source],
|
|
164
|
+
-current_supplies[sink],
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Push flow
|
|
168
|
+
for edge_idx in path_edges:
|
|
169
|
+
edge_data[edge_idx]["flow"] += min_flow
|
|
170
|
+
|
|
171
|
+
current_supplies[source] -= min_flow
|
|
172
|
+
current_supplies[sink] += min_flow
|
|
173
|
+
|
|
174
|
+
# Update potentials for next iteration
|
|
175
|
+
# New potential = old potential + distance from Dijkstra
|
|
176
|
+
for node in range(n_nodes):
|
|
177
|
+
if dist[node] < np.inf:
|
|
178
|
+
potential[node] += dist[node]
|
|
179
|
+
|
|
180
|
+
# Extract solution
|
|
181
|
+
result_flow = np.array([e["flow"] for e in edge_data])
|
|
182
|
+
total_cost = sum(e["flow"] * e["cost"] for e in edge_data)
|
|
183
|
+
|
|
184
|
+
return result_flow, total_cost, iteration + 1
|
|
@@ -297,6 +297,82 @@ def min_cost_flow_successive_shortest_paths(
|
|
|
297
297
|
)
|
|
298
298
|
|
|
299
299
|
|
|
300
|
+
def min_cost_flow_simplex(
|
|
301
|
+
edges: list[FlowEdge],
|
|
302
|
+
supplies: NDArray[np.float64],
|
|
303
|
+
max_iterations: int = 10000,
|
|
304
|
+
) -> MinCostFlowResult:
|
|
305
|
+
"""
|
|
306
|
+
Solve min-cost flow using Dijkstra-based successive shortest paths.
|
|
307
|
+
|
|
308
|
+
This optimized version uses:
|
|
309
|
+
- Dijkstra's algorithm (O(E log V)) instead of Bellman-Ford (O(VE))
|
|
310
|
+
- Node potentials to maintain non-negative edge costs
|
|
311
|
+
- Johnson's technique for cost adjustment
|
|
312
|
+
|
|
313
|
+
This is significantly faster than Bellman-Ford while maintaining
|
|
314
|
+
guaranteed correctness and optimality.
|
|
315
|
+
|
|
316
|
+
Time complexity: O(K * E log V) where K = number of shortest paths
|
|
317
|
+
Space complexity: O(V + E)
|
|
318
|
+
|
|
319
|
+
Parameters
|
|
320
|
+
----------
|
|
321
|
+
edges : list[FlowEdge]
|
|
322
|
+
List of edges with capacities and costs.
|
|
323
|
+
supplies : ndarray
|
|
324
|
+
Supply/demand at each node.
|
|
325
|
+
max_iterations : int, optional
|
|
326
|
+
Maximum iterations (default 10000).
|
|
327
|
+
|
|
328
|
+
Returns
|
|
329
|
+
-------
|
|
330
|
+
MinCostFlowResult
|
|
331
|
+
Solution with flow values, cost, status, and iterations.
|
|
332
|
+
|
|
333
|
+
References
|
|
334
|
+
----------
|
|
335
|
+
.. [1] Ahuja, R. K., Magnanti, T. L., & Orlin, J. B. (1993).
|
|
336
|
+
Network Flows: Theory, Algorithms, and Applications.
|
|
337
|
+
(Chapter on successive shortest paths with potentials)
|
|
338
|
+
.. [2] Johnson, D. B. (1977).
|
|
339
|
+
Efficient All-Pairs Shortest Paths in Weighted Graphs.
|
|
340
|
+
"""
|
|
341
|
+
from pytcl.assignment_algorithms.dijkstra_min_cost import (
|
|
342
|
+
min_cost_flow_dijkstra_potentials,
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
n_nodes = len(supplies)
|
|
346
|
+
|
|
347
|
+
# Convert FlowEdge objects to tuples
|
|
348
|
+
edge_tuples = [(e.from_node, e.to_node, e.capacity, e.cost) for e in edges]
|
|
349
|
+
|
|
350
|
+
# Run optimized Dijkstra-based algorithm
|
|
351
|
+
flow, total_cost, iterations = min_cost_flow_dijkstra_potentials(
|
|
352
|
+
n_nodes, edge_tuples, supplies, max_iterations
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
# Check feasibility
|
|
356
|
+
residual_supplies = supplies.copy()
|
|
357
|
+
for i, edge in enumerate(edges):
|
|
358
|
+
residual_supplies[edge.from_node] -= flow[i]
|
|
359
|
+
residual_supplies[edge.to_node] += flow[i]
|
|
360
|
+
|
|
361
|
+
if np.allclose(residual_supplies, 0, atol=1e-6):
|
|
362
|
+
status = FlowStatus.OPTIMAL
|
|
363
|
+
elif iterations >= max_iterations:
|
|
364
|
+
status = FlowStatus.TIMEOUT
|
|
365
|
+
else:
|
|
366
|
+
status = FlowStatus.INFEASIBLE
|
|
367
|
+
|
|
368
|
+
return MinCostFlowResult(
|
|
369
|
+
flow=flow,
|
|
370
|
+
cost=total_cost,
|
|
371
|
+
status=status,
|
|
372
|
+
iterations=iterations,
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
|
|
300
376
|
def assignment_from_flow_solution(
|
|
301
377
|
flow: NDArray[np.float64],
|
|
302
378
|
edges: list[FlowEdge],
|
|
@@ -346,14 +422,21 @@ def assignment_from_flow_solution(
|
|
|
346
422
|
|
|
347
423
|
def min_cost_assignment_via_flow(
|
|
348
424
|
cost_matrix: NDArray[np.float64],
|
|
425
|
+
use_simplex: bool = True,
|
|
349
426
|
) -> Tuple[NDArray[np.intp], float]:
|
|
350
427
|
"""
|
|
351
428
|
Solve 2D assignment problem via min-cost flow network.
|
|
352
429
|
|
|
430
|
+
Uses Dijkstra-optimized successive shortest paths (Phase 1B) by default.
|
|
431
|
+
Falls back to Bellman-Ford if needed.
|
|
432
|
+
|
|
353
433
|
Parameters
|
|
354
434
|
----------
|
|
355
435
|
cost_matrix : ndarray
|
|
356
436
|
Cost matrix of shape (m, n).
|
|
437
|
+
use_simplex : bool, optional
|
|
438
|
+
Use Dijkstra-optimized algorithm (default True) or
|
|
439
|
+
Bellman-Ford based successive shortest paths (False).
|
|
357
440
|
|
|
358
441
|
Returns
|
|
359
442
|
-------
|
|
@@ -361,9 +444,19 @@ def min_cost_assignment_via_flow(
|
|
|
361
444
|
Assignment array of shape (n_assignments, 2).
|
|
362
445
|
total_cost : float
|
|
363
446
|
Total assignment cost.
|
|
447
|
+
|
|
448
|
+
Notes
|
|
449
|
+
-----
|
|
450
|
+
Phase 1B: Dijkstra-based optimization provides O(K*E log V) vs
|
|
451
|
+
Bellman-Ford O(K*V*E), where K is number of shortest paths needed.
|
|
364
452
|
"""
|
|
365
453
|
edges, supplies, _ = assignment_to_flow_network(cost_matrix)
|
|
366
|
-
|
|
454
|
+
|
|
455
|
+
if use_simplex:
|
|
456
|
+
result = min_cost_flow_simplex(edges, supplies)
|
|
457
|
+
else:
|
|
458
|
+
result = min_cost_flow_successive_shortest_paths(edges, supplies)
|
|
459
|
+
|
|
367
460
|
assignment, cost = assignment_from_flow_solution(
|
|
368
461
|
result.flow, edges, cost_matrix.shape
|
|
369
462
|
)
|