gridstate 2.4.0__tar.gz → 2.4.1__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.
- {gridstate-2.4.0 → gridstate-2.4.1}/CHANGELOG.md +22 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/PKG-INFO +1 -1
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/_version.py +3 -3
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/algorithms/ipm_pd.py +48 -8
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate.egg-info/PKG-INFO +1 -1
- gridstate-2.4.1/gridstate.egg-info/scm_version.json +8 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_ipm_pd.py +66 -1
- gridstate-2.4.0/gridstate.egg-info/scm_version.json +0 -8
- {gridstate-2.4.0 → gridstate-2.4.1}/.github/workflows/ci.yml +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/.github/workflows/release.yml +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/.gitignore +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/.pre-commit-config.yaml +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/LICENSE +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/Makefile +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/README.md +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/build_cython.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/__init__.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/__pyinstaller/__init__.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/__pyinstaller/hook-gridstate.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/_arrays.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/adapters/__init__.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/adapters/from_pandapower.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/algebra/__init__.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/algebra/_selinv_kernels.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/algebra/base.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/algebra/estimators.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/algebra/jacobian_pattern.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/algebra/normalized_residuals.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/algorithms/__init__.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/algorithms/_gram_numba.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/algorithms/ipm.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/algorithms/kkt_solver.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/algorithms/status.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/algorithms/wls.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/api.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/bad_data_repass.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/bounds.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/constants.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/contract/__init__.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/contract/_legacy.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/contract/containers.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/contract/derived.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/contract/derived_tables.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/contract/serialize.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/contract/tables.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/contract/validate.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/contract/version.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/ipm_hints.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/load_characteristics.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/losses.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/measurement_ids.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/pipeline/__init__.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/pipeline/config.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/pipeline/context.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/pipeline/manifest.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/pipeline/orchestration.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/pipeline/passes.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/pipeline/preparation.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/pipeline/runner.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/pipeline/steps.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/post_processing.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/preprocessing/__init__.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/preprocessing/_scan.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/preprocessing/ipm_setup.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/preprocessing/meas_rows.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/preprocessing/mirror_voltage.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/preprocessing/node_props.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/preprocessing/pseudo_measurements.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/py.typed +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/quality_summary.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/resolve_pass.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/result.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/runtime.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/shunt_sanity.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/solution_hints.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/state.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/telemetry/__init__.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/telemetry/_filters.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/telemetry/apply_resolved.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/telemetry/generators.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/telemetry/kinds.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/telemetry/load_only_injection.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/telemetry/loss_filter.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/telemetry/measurements.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/telemetry/noise.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/telemetry/on_line.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/telemetry/quality.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/telemetry/rpn.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/telemetry/shunts.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/telemetry/sigma_floor.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/telemetry/units.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/telemetry/voltage_filter.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/telemetry/voltage_nominal.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/topology.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/units.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/utils.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/v_mirror.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/v_refine.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/validation/__init__.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/validation/_diagnostics.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/validation/bad_data.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/validation/chi2_test.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/validation/observability.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/working.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/ybus.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate/z_vector.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate.egg-info/SOURCES.txt +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate.egg-info/dependency_links.txt +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate.egg-info/entry_points.txt +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate.egg-info/requires.txt +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate.egg-info/scm_file_list.json +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/gridstate.egg-info/top_level.txt +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/pyproject.toml +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/setup.cfg +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/__init__.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/_golden.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/conftest.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_adapters_lazy_import.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_aggregate_generators.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_algebra_base.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_anti_overshoot.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_arrays_helpers.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_bad_data.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_bad_data_repass.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_bounds.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_boxed_balance.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_breaker_reactance.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_chain_mirror.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_contract.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_contract_edge_cases.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_contract_runtime.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_data/golden/case118.json +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_data/golden/case118.npz +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_data/golden/case118_degraded.json +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_data/golden/case118_degraded.npz +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_data/golden/case14.json +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_data/golden/case14.npz +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_data/golden/case14_degraded.json +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_data/golden/case14_degraded.npz +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_data/golden/case30.json +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_data/golden/case30.npz +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_data/golden/case30_degraded.json +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_data/golden/case30_degraded.npz +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_data/golden/manifest.json +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_data/ieee/case118.npz +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_data/ieee/case118_truth.npz +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_data/ieee/case14.npz +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_data/ieee/case14_truth.npz +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_data/ieee/case30.npz +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_data/ieee/case30_truth.npz +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_derived_tables.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_enum_codes.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_flow_sigma_floor.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_from_pandapower.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_generator_shunt_kernels.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_golden.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_gram_numba.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_ieee_cases.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_ipm.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_ipm_balance_modes.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_ipm_hints.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_ipm_injection_roles.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_ipm_integration.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_jacobian_pattern.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_kkt_solver.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_load_characteristic_segments.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_load_only_injection.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_losses_breakdown.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_losses_q_convention.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_losses_trafo_classification.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_materialize_kernel.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_measurement_filter_kernels.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_measurement_ids.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_mirror_voltage_kernel.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_near_critical_ranking.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_neutral_wording.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_noise_model.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_normalized_residuals.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_numpy_only_runtime.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_observability.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_on_line_kernel.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_perf_synthetic_network.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_pipeline.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_pipeline_config_validation.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_pipeline_idempotent.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_pipeline_invariant_errors.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_pipeline_step_order.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_pipeline_warm_start.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_pipeline_working_layer.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_post_processing.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_preparation_steps.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_prepare_network.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_pseudo_unknown_load.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_pseudo_v_unobservable.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_quality_summary_paths.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_reconcile_balance.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_refine_merge.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_resolve_pass.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_result_quality_summary.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_rn_selected_inversion.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_serialize_npz.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_shunt_sanity.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_solution_hints.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_solve_status.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_state.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_state_range.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_telemetry_apply_kernel.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_telemetry_bounds.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_telemetry_physics_kernels.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_tm_code_quality.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_topology_kernels.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_topology_port.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_transformer_from_shunt.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_units.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_units_array_kernels.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_v_mirror.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_v_refine.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_variance_charging.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_version.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_voltage_calibration_gen_nodes.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_voltage_range_filter_fallback.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_wls.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_wls_robustness.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_working_container.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_ybus.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tests/test_z_vector.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tools/gen_ieee_fixtures.py +0 -0
- {gridstate-2.4.0 → gridstate-2.4.1}/tools/golden.py +0 -0
|
@@ -9,6 +9,28 @@ types of the merged pull requests; breaking and behavior-changing items are
|
|
|
9
9
|
listed separately under "Breaking changes" and "Behavior changes". A release is
|
|
10
10
|
cut by tagging the merged main commit; the version is derived from that tag.
|
|
11
11
|
|
|
12
|
+
## v2.4.1 (2026-10-18)
|
|
13
|
+
|
|
14
|
+
### Behavior changes
|
|
15
|
+
|
|
16
|
+
- `ipm_pd`: the proximal term on the box-variable diagonal adapts to the step
|
|
17
|
+
length: ×10 after a step αp < 0.1, up to `box_prox_max` (new, default 1e-2 of
|
|
18
|
+
the median weight), ÷10 after αp ≥ 0.5, down to `box_prox`. It damps the step
|
|
19
|
+
only; the fixed point is unchanged. Estimates on the test models move by
|
|
20
|
+
≤ 1.6e-8 p.u. V with unchanged iteration counts.
|
|
21
|
+
- `ipm_pd`: a second stopping test, `fit_tol` (new, default 1e-2): the step moves
|
|
22
|
+
no data row by more than that fraction of its sigma (the μ, feasibility and
|
|
23
|
+
equality tests still apply).
|
|
24
|
+
|
|
25
|
+
### Fix
|
|
26
|
+
|
|
27
|
+
- `ipm_pd` no longer runs into its iteration limit and the primal fallback when
|
|
28
|
+
box variables are seen only through near-zero-weight rows (loads with
|
|
29
|
+
effectively free injections): such directions have barrier curvature only,
|
|
30
|
+
their steps dwarf the box and the boundary rule cut every step to a tiny αp.
|
|
31
|
+
A state the data do not determine no longer keeps the V/δ step test from
|
|
32
|
+
passing (it could move, or cycle, without changing any estimate).
|
|
33
|
+
|
|
12
34
|
## v2.4.0 (2026-10-17)
|
|
13
35
|
|
|
14
36
|
### Behavior changes
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '2.4.
|
|
22
|
-
__version_tuple__ = version_tuple = (2, 4,
|
|
21
|
+
__version__ = version = '2.4.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 4, 1)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g640125491'
|
|
@@ -50,6 +50,19 @@ corrector's second-order term may spoil descent, and then the centering
|
|
|
50
50
|
direction is taken instead (one more solve on the same factor). The dual step
|
|
51
51
|
keeps its own fraction-to-boundary length.
|
|
52
52
|
|
|
53
|
+
Adaptive box regularisation. A box variable seen only through rows of
|
|
54
|
+
negligible weight (a load whose injection row is a near-zero-weight pseudo
|
|
55
|
+
measurement) or through a combination the data do not resolve (a reactive flow
|
|
56
|
+
circulating between two such loads) has no curvature but the barrier's, and
|
|
57
|
+
the Newton step along it is orders of magnitude longer than the box. The
|
|
58
|
+
fraction-to-boundary rule then cuts every step to ``αp ≪ 1``, the
|
|
59
|
+
complementarity stops decreasing and the iteration crawls to its limit. The
|
|
60
|
+
proximal term on the box diagonal is therefore raised tenfold after every
|
|
61
|
+
step shorter than 0.1 (up to ``box_prox_max``) and lowered tenfold after
|
|
62
|
+
every step of 0.5 or longer (down to ``box_prox``). The term damps the step
|
|
63
|
+
only, so the fixed point is unchanged; a well-scaled problem takes long steps
|
|
64
|
+
and keeps the base term.
|
|
65
|
+
|
|
53
66
|
Start on the central path: ``s`` = half-range of a two-sided constraint (the
|
|
54
67
|
distance for a one-sided one), ``y = μ0/s``; the initial point need not satisfy
|
|
55
68
|
``g − l = s``.
|
|
@@ -96,6 +109,14 @@ def _blocking_constraint(
|
|
|
96
109
|
return ku, False
|
|
97
110
|
|
|
98
111
|
|
|
112
|
+
# Adaptive box regularisation (see the module docstring): a primal step shorter
|
|
113
|
+
# than _PROX_SHORT raises the term by _PROX_GROW, one of _PROX_LONG or longer
|
|
114
|
+
# lowers it by the same factor.
|
|
115
|
+
_PROX_SHORT = 0.1
|
|
116
|
+
_PROX_LONG = 0.5
|
|
117
|
+
_PROX_GROW = 10.0
|
|
118
|
+
|
|
119
|
+
|
|
99
120
|
Direction = tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]
|
|
100
121
|
|
|
101
122
|
|
|
@@ -212,6 +233,7 @@ def solve_ipm_pd(
|
|
|
212
233
|
armijo_max: int = 10,
|
|
213
234
|
max_search_failures: int = 20,
|
|
214
235
|
step_tol: float = 1e-4,
|
|
236
|
+
fit_tol: float = 1e-2,
|
|
215
237
|
feas_tol: float = 1e-6,
|
|
216
238
|
max_iter: int = 100,
|
|
217
239
|
box_eps: float = 1e-3,
|
|
@@ -225,6 +247,7 @@ def solve_ipm_pd(
|
|
|
225
247
|
kkt_solver: KKTSolver | None = None,
|
|
226
248
|
eq_prox: float = 1e-9,
|
|
227
249
|
box_prox: float = 1e-6,
|
|
250
|
+
box_prox_max: float = 1e-2,
|
|
228
251
|
) -> IPMResult:
|
|
229
252
|
"""Mehrotra predictor-corrector IPM (see the module docstring).
|
|
230
253
|
|
|
@@ -251,6 +274,11 @@ def solve_ipm_pd(
|
|
|
251
274
|
outside ``box_idx`` (V/δ) is below it. Box variables may still move
|
|
252
275
|
along directions the data do not see (the common level of a node's
|
|
253
276
|
generation and load), which change neither the state nor the residuals.
|
|
277
|
+
fit_tol: alternatively, stop once that step moves no data row by more
|
|
278
|
+
than ``fit_tol`` of its sigma (``max √w·|H·αp·Δx|``). A state the data
|
|
279
|
+
do not determine (a node behind loads whose injection rows carry
|
|
280
|
+
near-zero weight) can keep moving, or cycle, without changing any
|
|
281
|
+
estimated measurement; the fit has converged all the same.
|
|
254
282
|
feas_tol: admissible primal infeasibility of the inequalities (p.u.).
|
|
255
283
|
max_iter: iteration limit (one factorization each).
|
|
256
284
|
box_eps: margin from the bounds when projecting ``x_init``.
|
|
@@ -271,6 +299,9 @@ def solve_ipm_pd(
|
|
|
271
299
|
has no curvature along that common level but the barrier's, so a
|
|
272
300
|
cold start produces huge box steps cut to tiny ``αp`` by the boundary
|
|
273
301
|
rule; the term bounds those steps without moving the fixed point.
|
|
302
|
+
box_prox_max: ceiling of the adaptive box term (same units as
|
|
303
|
+
``box_prox``, see the module docstring); a value not above
|
|
304
|
+
``box_prox`` keeps the term fixed.
|
|
274
305
|
|
|
275
306
|
Returns:
|
|
276
307
|
:class:`~gridstate.algorithms.ipm.IPMResult`; ``status`` is ``"kkt"`` when
|
|
@@ -325,10 +356,9 @@ def solve_ipm_pd(
|
|
|
325
356
|
is_eq = np.zeros(m, dtype=bool)
|
|
326
357
|
is_eq[eq_rows] = True
|
|
327
358
|
rho = w_base[eq_rows]
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
)
|
|
359
|
+
eq_prox_abs = eq_prox * float(np.max(rho)) if rho.size else 0.0
|
|
360
|
+
w_median = float(np.median(w_base[~is_eq])) if (~is_eq).any() else 0.0
|
|
361
|
+
box_scale = box_prox
|
|
332
362
|
|
|
333
363
|
huber_mask_arr = (
|
|
334
364
|
np.asarray(huber_mask, dtype=bool) if huber_mask is not None else np.zeros(m, dtype=bool)
|
|
@@ -378,6 +408,7 @@ def solve_ipm_pd(
|
|
|
378
408
|
w = w_base
|
|
379
409
|
mu = mu0 if n_pairs else 0.0
|
|
380
410
|
step_inf = float("inf")
|
|
411
|
+
fit_inf = float("inf")
|
|
381
412
|
eq_inf = 0.0
|
|
382
413
|
grad_inf = float("inf")
|
|
383
414
|
status = "stalled"
|
|
@@ -399,7 +430,7 @@ def solve_ipm_pd(
|
|
|
399
430
|
and mu <= mu_min
|
|
400
431
|
and feas_inf <= feas_tol
|
|
401
432
|
and bool(np.all(np.abs(c) <= eq_tol_arr))
|
|
402
|
-
and step_inf <= step_tol
|
|
433
|
+
and (step_inf <= step_tol or fit_inf <= fit_tol)
|
|
403
434
|
):
|
|
404
435
|
status = "kkt"
|
|
405
436
|
break
|
|
@@ -413,7 +444,7 @@ def solve_ipm_pd(
|
|
|
413
444
|
w_mat[eq_rows] = rho
|
|
414
445
|
w_mat[mb_rows] += d_k[nb:]
|
|
415
446
|
diag = np.zeros(n)
|
|
416
|
-
diag[box_idx] = d_k[:nb] +
|
|
447
|
+
diag[box_idx] = d_k[:nb] + max(eq_prox_abs, box_scale * w_median)
|
|
417
448
|
# Stationarity residual of the current point (for reporting).
|
|
418
449
|
y_dual = np.zeros(nb + mb_rows.size)
|
|
419
450
|
y_dual[il] += y_l
|
|
@@ -490,7 +521,8 @@ def solve_ipm_pd(
|
|
|
490
521
|
il=il,
|
|
491
522
|
iu=iu,
|
|
492
523
|
)
|
|
493
|
-
|
|
524
|
+
hdx = H @ dx
|
|
525
|
+
slope = merit.slope(lin, hdx, ds_l, ds_u)
|
|
494
526
|
if slope >= 0.0 and n_pairs:
|
|
495
527
|
# The corrector's second-order term can spoil descent; the pure
|
|
496
528
|
# centering direction is a Newton step on the merit and descends
|
|
@@ -498,9 +530,11 @@ def solve_ipm_pd(
|
|
|
498
530
|
dx_c, ds_l_c, ds_u_c, dy_l_c, dy_u_c, dlam_c = lin.direction(
|
|
499
531
|
target - s_l * y_l, target - s_u * y_u
|
|
500
532
|
)
|
|
501
|
-
|
|
533
|
+
hdx_c = H @ dx_c
|
|
534
|
+
slope_c = merit.slope(lin, hdx_c, ds_l_c, ds_u_c)
|
|
502
535
|
if slope_c < 0.0 and np.all(np.isfinite(dx_c)):
|
|
503
536
|
dx, ds_l, ds_u, dy_l, dy_u, dlam = dx_c, ds_l_c, ds_u_c, dy_l_c, dy_u_c, dlam_c
|
|
537
|
+
hdx = hdx_c
|
|
504
538
|
slope = slope_c
|
|
505
539
|
n_centering += 1
|
|
506
540
|
|
|
@@ -521,6 +555,7 @@ def solve_ipm_pd(
|
|
|
521
555
|
a_p = tr_radius / free_inf
|
|
522
556
|
# The stopping test measures the Newton step, not how far the search cut it.
|
|
523
557
|
step_inf = a_p * free_inf
|
|
558
|
+
fit_inf = a_p * float(np.max(np.sqrt(w_data) * np.abs(hdx))) if m else 0.0
|
|
524
559
|
a_full = a_p
|
|
525
560
|
r_new = None
|
|
526
561
|
# A direction that does not descend (inconsistent slacks of a nonlinear
|
|
@@ -563,6 +598,11 @@ def solve_ipm_pd(
|
|
|
563
598
|
eq_inf,
|
|
564
599
|
feas_inf,
|
|
565
600
|
)
|
|
601
|
+
if box_prox_max > box_prox:
|
|
602
|
+
if a_p < _PROX_SHORT:
|
|
603
|
+
box_scale = min(box_scale * _PROX_GROW, box_prox_max)
|
|
604
|
+
elif a_p >= _PROX_LONG:
|
|
605
|
+
box_scale = max(box_scale / _PROX_GROW, box_prox)
|
|
566
606
|
x = x + a_p * dx
|
|
567
607
|
s_l = s_l + a_p * ds_l
|
|
568
608
|
s_u = s_u + a_p * ds_u
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from types import SimpleNamespace
|
|
6
|
+
from typing import Any
|
|
6
7
|
|
|
7
8
|
import numpy as np
|
|
8
9
|
import pytest
|
|
@@ -148,6 +149,68 @@ def test_equality_rows_with_a_box_free_common_level() -> None:
|
|
|
148
149
|
assert out.x[1] - out.x[2] == pytest.approx(0.7, abs=1e-5)
|
|
149
150
|
|
|
150
151
|
|
|
152
|
+
def test_pd_adaptive_box_prox_keeps_the_fixed_point(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
153
|
+
# Every step counts as short, so the box term climbs to its ceiling and stays
|
|
154
|
+
# there: the iteration is damped, the solution is not. Rows 1-2 see the box
|
|
155
|
+
# variables (g, l) only through near-zero weights, as a free load does.
|
|
156
|
+
import gridstate.algorithms.ipm_pd as pd_mod
|
|
157
|
+
|
|
158
|
+
A = np.array([[1.0, 0.0, 0.0], [1.0, -1.0, 1.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]])
|
|
159
|
+
z = np.array([0.7, 0.0, 3.0, 0.75])
|
|
160
|
+
w = np.array([1.0, 1e-8, 1e-8, 1.0])
|
|
161
|
+
res, jac = _linear(A, z)
|
|
162
|
+
kw: dict[str, Any] = {
|
|
163
|
+
"box_idx": np.array([1, 2]),
|
|
164
|
+
"box_lo": np.array([-50.0, -50.0]),
|
|
165
|
+
"box_hi": np.array([50.0, 50.0]),
|
|
166
|
+
"step_tol": 1e-9,
|
|
167
|
+
"max_iter": 300,
|
|
168
|
+
}
|
|
169
|
+
fixed = solve_ipm_pd(np.zeros(3), res, jac, w, box_prox_max=0.0, **kw)
|
|
170
|
+
monkeypatch.setattr(pd_mod, "_PROX_SHORT", 2.0)
|
|
171
|
+
damped = solve_ipm_pd(np.zeros(3), res, jac, w, box_prox_max=1.0, **kw)
|
|
172
|
+
assert fixed.success and damped.success
|
|
173
|
+
assert damped.x[0] == pytest.approx(0.725, abs=1e-6)
|
|
174
|
+
np.testing.assert_allclose(damped.x[0], fixed.x[0], atol=1e-6)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def test_pd_adaptive_box_prox_is_inactive_on_long_steps() -> None:
|
|
178
|
+
# A well-scaled problem takes long steps: the ceiling never comes into play.
|
|
179
|
+
rng = np.random.default_rng(4)
|
|
180
|
+
A = rng.normal(size=(8, 3))
|
|
181
|
+
z = rng.normal(size=8)
|
|
182
|
+
res, jac = _linear(A, z)
|
|
183
|
+
kw: dict[str, Any] = {
|
|
184
|
+
"box_idx": np.array([2]),
|
|
185
|
+
"box_lo": np.array([-5.0]),
|
|
186
|
+
"box_hi": np.array([5.0]),
|
|
187
|
+
}
|
|
188
|
+
base = solve_ipm_pd(np.zeros(3), res, jac, np.ones(8), box_prox_max=0.0, **kw)
|
|
189
|
+
adaptive = solve_ipm_pd(np.zeros(3), res, jac, np.ones(8), **kw)
|
|
190
|
+
assert base.iterations_outer == adaptive.iterations_outer
|
|
191
|
+
np.testing.assert_array_equal(base.x, adaptive.x)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def test_pd_fit_tol_stops_once_the_estimates_settle() -> None:
|
|
195
|
+
# x1 enters only a row of negligible weight: once the measured x0 has
|
|
196
|
+
# settled, the remaining steps move x1 alone and no estimate noticeably.
|
|
197
|
+
z = np.array([1.0, 1.2, 3.0])
|
|
198
|
+
w = np.array([1.0, 1.0, 1e-12])
|
|
199
|
+
|
|
200
|
+
def residual_fn(x: np.ndarray) -> np.ndarray:
|
|
201
|
+
return z - np.array([x[0], x[0], np.sinh(x[1])])
|
|
202
|
+
|
|
203
|
+
def jacobian_fn(x: np.ndarray) -> csr_matrix:
|
|
204
|
+
return csr_matrix(np.array([[1.0, 0.0], [1.0, 0.0], [0.0, np.cosh(x[1])]]))
|
|
205
|
+
|
|
206
|
+
kw: dict[str, Any] = {"step_tol": 1e-12, "max_iter": 60}
|
|
207
|
+
by_fit = solve_ipm_pd(np.zeros(2), residual_fn, jacobian_fn, w, **kw)
|
|
208
|
+
by_step = solve_ipm_pd(np.zeros(2), residual_fn, jacobian_fn, w, fit_tol=0.0, **kw)
|
|
209
|
+
assert by_fit.success and by_step.success
|
|
210
|
+
assert by_fit.iterations_outer < by_step.iterations_outer
|
|
211
|
+
assert by_fit.x[0] == pytest.approx(1.1, abs=1e-9)
|
|
212
|
+
|
|
213
|
+
|
|
151
214
|
def test_pd_merit_search_stops_gauss_newton_overshoot() -> None:
|
|
152
215
|
# h = atan(x), z = 0 from x0 = 1.5: the Gauss-Newton step −(1 + x²)·atan(x)
|
|
153
216
|
# overshoots and the full-step iteration diverges; the merit search does not.
|
|
@@ -157,7 +220,9 @@ def test_pd_merit_search_stops_gauss_newton_overshoot() -> None:
|
|
|
157
220
|
def jacobian_fn(x: np.ndarray) -> csr_matrix:
|
|
158
221
|
return csr_matrix(np.array([[1.0 / (1.0 + x[0] ** 2)]]))
|
|
159
222
|
|
|
160
|
-
|
|
223
|
+
# fit_tol off: far out on the plateau of atan a diverging step barely moves
|
|
224
|
+
# the estimate, and this test is about the search, not the stopping rule.
|
|
225
|
+
kw = {"tr_radius": 1e6, "step_tol": 1e-10, "fit_tol": 0.0, "max_iter": 50}
|
|
161
226
|
no_search = solve_ipm_pd(
|
|
162
227
|
np.array([1.5]), residual_fn, jacobian_fn, np.ones(1), armijo_max=0, **kw
|
|
163
228
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|