flextool 4.0.0__py3-none-any.whl
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.
- flextool/__init__.py +41 -0
- flextool/_mem_sampler.py +193 -0
- flextool/_resources.py +43 -0
- flextool/calibrate/__init__.py +51 -0
- flextool/calibrate/__main__.py +11 -0
- flextool/calibrate/_cli.py +316 -0
- flextool/calibrate/_db_alt.py +166 -0
- flextool/calibrate/_final_outputs.py +110 -0
- flextool/calibrate/_guard.py +151 -0
- flextool/calibrate/_loop.py +558 -0
- flextool/calibrate/_readers.py +223 -0
- flextool/calibrate/_report.py +263 -0
- flextool/calibrate/_sizing.py +699 -0
- flextool/calibrate/_solve.py +134 -0
- flextool/calibrate/_solve_status.py +495 -0
- flextool/cli/__init__.py +9 -0
- flextool/cli/_console.py +51 -0
- flextool/cli/_timing.py +147 -0
- flextool/cli/cmd_execute_flextool_workflow.py +187 -0
- flextool/cli/cmd_export_to_tabular.py +56 -0
- flextool/cli/cmd_import_sensitivities.py +75 -0
- flextool/cli/cmd_migrate_database.py +13 -0
- flextool/cli/cmd_open_results_db.py +269 -0
- flextool/cli/cmd_read_matpower.py +66 -0
- flextool/cli/cmd_read_old_flextool.py +63 -0
- flextool/cli/cmd_read_self_describing_tabular_input.py +50 -0
- flextool/cli/cmd_read_tabular_input.py +81 -0
- flextool/cli/cmd_run_flextool.py +1095 -0
- flextool/cli/cmd_scenario_results.py +284 -0
- flextool/cli/cmd_solve_mps.py +169 -0
- flextool/cli/cmd_update_flextool.py +17 -0
- flextool/cli/cmd_write_outputs.py +125 -0
- flextool/common_utils/__init__.py +1 -0
- flextool/common_utils/plot_mem_shape.py +77 -0
- flextool/common_utils/precision.py +451 -0
- flextool/decomposition/__init__.py +0 -0
- flextool/decomposition/region_decomposition.py +128 -0
- flextool/decomposition/region_filter.py +1261 -0
- flextool/engine_polars/__init__.py +110 -0
- flextool/engine_polars/_axis_enums.py +742 -0
- flextool/engine_polars/_benders.py +3462 -0
- flextool/engine_polars/_block_layout.py +1479 -0
- flextool/engine_polars/_blocks.py +1515 -0
- flextool/engine_polars/_commodity_ladder.py +660 -0
- flextool/engine_polars/_cumulative_invest.py +1165 -0
- flextool/engine_polars/_db_loader.py +153 -0
- flextool/engine_polars/_db_reader.py +127 -0
- flextool/engine_polars/_dc_power_flow.py +445 -0
- flextool/engine_polars/_delay.py +442 -0
- flextool/engine_polars/_derived_arithmetic.py +432 -0
- flextool/engine_polars/_derived_block.py +990 -0
- flextool/engine_polars/_derived_branch.py +769 -0
- flextool/engine_polars/_derived_existing.py +1353 -0
- flextool/engine_polars/_derived_npv.py +1297 -0
- flextool/engine_polars/_derived_params.py +9850 -0
- flextool/engine_polars/_derived_profile.py +881 -0
- flextool/engine_polars/_derived_walks.py +276 -0
- flextool/engine_polars/_determinism.py +70 -0
- flextool/engine_polars/_direct_params.py +2186 -0
- flextool/engine_polars/_dump_csvs.py +1009 -0
- flextool/engine_polars/_emit_arc_unions.py +1631 -0
- flextool/engine_polars/_emit_calc_params.py +729 -0
- flextool/engine_polars/_emit_chain_params.py +709 -0
- flextool/engine_polars/_emit_co2_accumulators.py +400 -0
- flextool/engine_polars/_emit_dispatchers.py +690 -0
- flextool/engine_polars/_emit_energy_margin.py +125 -0
- flextool/engine_polars/_emit_energy_margin_adder.py +290 -0
- flextool/engine_polars/_emit_entity_annual.py +428 -0
- flextool/engine_polars/_emit_inflow_scaling.py +1420 -0
- flextool/engine_polars/_emit_leaf_sets.py +550 -0
- flextool/engine_polars/_emit_lp_scaling.py +665 -0
- flextool/engine_polars/_emit_mid_sets.py +859 -0
- flextool/engine_polars/_emit_pdt_params.py +759 -0
- flextool/engine_polars/_emit_per_solve.py +774 -0
- flextool/engine_polars/_emit_period_calc.py +504 -0
- flextool/engine_polars/_emit_period_params.py +2398 -0
- flextool/engine_polars/_emit_provider_io.py +141 -0
- flextool/engine_polars/_emit_reserve.py +574 -0
- flextool/engine_polars/_emit_solve_time.py +311 -0
- flextool/engine_polars/_emit_solve_writers.py +1249 -0
- flextool/engine_polars/_flex_data_accumulator.py +388 -0
- flextool/engine_polars/_flex_data_provider.py +478 -0
- flextool/engine_polars/_group_slack.py +1253 -0
- flextool/engine_polars/_inmemory_reader.py +140 -0
- flextool/engine_polars/_input_source.py +336 -0
- flextool/engine_polars/_invest_seeds.py +191 -0
- flextool/engine_polars/_native_input_writer.py +100 -0
- flextool/engine_polars/_native_run_model.py +1348 -0
- flextool/engine_polars/_orchestration.py +4314 -0
- flextool/engine_polars/_output_writer.py +439 -0
- flextool/engine_polars/_param_shapes.py +1595 -0
- flextool/engine_polars/_parquet_bundle.py +723 -0
- flextool/engine_polars/_pdt_join.py +167 -0
- flextool/engine_polars/_pdt_lookup.py +547 -0
- flextool/engine_polars/_per_solve_sets.py +335 -0
- flextool/engine_polars/_projection_params.py +2056 -0
- flextool/engine_polars/_provider_keys.py +173 -0
- flextool/engine_polars/_provider_translators.py +225 -0
- flextool/engine_polars/_recursive_solve.py +703 -0
- flextool/engine_polars/_region_filter.py +2508 -0
- flextool/engine_polars/_reserve.py +649 -0
- flextool/engine_polars/_solve_acceptance.py +331 -0
- flextool/engine_polars/_solve_config.py +1001 -0
- flextool/engine_polars/_solve_context.py +885 -0
- flextool/engine_polars/_solve_handoff.py +164 -0
- flextool/engine_polars/_solve_state.py +232 -0
- flextool/engine_polars/_solver_base.py +36 -0
- flextool/engine_polars/_solver_dispatch.py +511 -0
- flextool/engine_polars/_spinedb_reader.py +1165 -0
- flextool/engine_polars/_stochastic.py +593 -0
- flextool/engine_polars/_subprocess_solve.py +1838 -0
- flextool/engine_polars/_timeline.py +1416 -0
- flextool/engine_polars/_vectorize.py +438 -0
- flextool/engine_polars/_warm.py +858 -0
- flextool/engine_polars/autoscale/__init__.py +107 -0
- flextool/engine_polars/autoscale/_config.py +218 -0
- flextool/engine_polars/autoscale/_layer2.py +1253 -0
- flextool/engine_polars/autoscale/_layer2_types.py +584 -0
- flextool/engine_polars/autoscale/_quantity_types.py +621 -0
- flextool/engine_polars/autoscale/_report.py +336 -0
- flextool/engine_polars/chain.py +259 -0
- flextool/engine_polars/input.py +6638 -0
- flextool/engine_polars/model.py +4754 -0
- flextool/env_check.py +388 -0
- flextool/export_to_tabular/__init__.py +5 -0
- flextool/export_to_tabular/db_reader.py +224 -0
- flextool/export_to_tabular/excel_writer.py +3559 -0
- flextool/export_to_tabular/export_settings.yaml +377 -0
- flextool/export_to_tabular/export_to_excel.py +227 -0
- flextool/export_to_tabular/formatting.py +543 -0
- flextool/export_to_tabular/sheet_config.py +876 -0
- flextool/gui/__init__.py +0 -0
- flextool/gui/__main__.py +118 -0
- flextool/gui/calibrate_commands.py +184 -0
- flextool/gui/calibrate_jobs.py +424 -0
- flextool/gui/check_tree.py +142 -0
- flextool/gui/cli_format.py +83 -0
- flextool/gui/config_parser.py +68 -0
- flextool/gui/data_models.py +362 -0
- flextool/gui/db_editor_integration.py +202 -0
- flextool/gui/db_version_check.py +269 -0
- flextool/gui/dialogs/__init__.py +0 -0
- flextool/gui/dialogs/add_dialog.py +1098 -0
- flextool/gui/dialogs/calibrate_dialog.py +1259 -0
- flextool/gui/dialogs/file_picker.py +473 -0
- flextool/gui/dialogs/group_picker.py +299 -0
- flextool/gui/dialogs/migration_consent_dialog.py +106 -0
- flextool/gui/dialogs/migration_progress_dialog.py +237 -0
- flextool/gui/dialogs/plot_dialog.py +459 -0
- flextool/gui/dialogs/plot_settings_picker.py +2184 -0
- flextool/gui/dialogs/project_dialog.py +426 -0
- flextool/gui/dialogs/update_dialog.py +212 -0
- flextool/gui/downsampling.py +88 -0
- flextool/gui/error_handling.py +50 -0
- flextool/gui/execution_manager.py +1715 -0
- flextool/gui/execution_window.py +1377 -0
- flextool/gui/hover_tooltip.py +111 -0
- flextool/gui/input_sources.py +730 -0
- flextool/gui/main_window.py +6181 -0
- flextool/gui/network_graph.py +215 -0
- flextool/gui/output_actions.py +393 -0
- flextool/gui/output_log_window.py +159 -0
- flextool/gui/platform_utils.py +421 -0
- flextool/gui/plot_cache.py +88 -0
- flextool/gui/plot_canvas.py +543 -0
- flextool/gui/plot_config_reader.py +272 -0
- flextool/gui/project_utils.py +100 -0
- flextool/gui/result_viewer.py +4394 -0
- flextool/gui/scenario_key.py +162 -0
- flextool/gui/scenario_lists.py +516 -0
- flextool/gui/settings_io.py +360 -0
- flextool/gui/solve_reader.py +103 -0
- flextool/gui/tree_reorder.py +88 -0
- flextool/gui/ui_metrics.py +420 -0
- flextool/input_derivation/__init__.py +281 -0
- flextool/input_derivation/_commodity_ladder.py +375 -0
- flextool/input_derivation/_commodity_ladder_sets.py +70 -0
- flextool/input_derivation/_dc_power_flow.py +377 -0
- flextool/input_derivation/_method_constants.py +77 -0
- flextool/input_derivation/_process_method.py +258 -0
- flextool/input_derivation/_specs.py +1026 -0
- flextool/input_derivation/_validators.py +321 -0
- flextool/lean_parquet.py +159 -0
- flextool/model_builder/__init__.py +5 -0
- flextool/model_builder/build_model.py +589 -0
- flextool/model_builder/encoding.py +67 -0
- flextool/model_builder/names.py +34 -0
- flextool/model_builder/profiles.py +129 -0
- flextool/plot_outputs/__init__.py +14 -0
- flextool/plot_outputs/axis_helpers.py +355 -0
- flextool/plot_outputs/color_template.py +888 -0
- flextool/plot_outputs/config.py +171 -0
- flextool/plot_outputs/format_helpers.py +345 -0
- flextool/plot_outputs/legend_helpers.py +143 -0
- flextool/plot_outputs/orchestrator.py +1141 -0
- flextool/plot_outputs/perf.py +37 -0
- flextool/plot_outputs/plan.py +1787 -0
- flextool/plot_outputs/plot_bars.py +1510 -0
- flextool/plot_outputs/plot_bars_detail.py +753 -0
- flextool/plot_outputs/plot_lines.py +951 -0
- flextool/plot_outputs/shared_manifest.py +564 -0
- flextool/plot_outputs/subplot_helpers.py +137 -0
- flextool/process_inputs/__init__.py +188 -0
- flextool/process_inputs/import_old_excel_input.json +4159 -0
- flextool/process_inputs/read_matpower.py +451 -0
- flextool/process_inputs/read_old_flextool.py +1288 -0
- flextool/process_inputs/read_self_describing_excel.py +1423 -0
- flextool/process_inputs/read_tabular_with_specification.py +1114 -0
- flextool/process_inputs/write_old_flextool_to_db.py +3077 -0
- flextool/process_inputs/write_self_describing_to_db.py +977 -0
- flextool/process_inputs/write_to_input_db.py +269 -0
- flextool/process_outputs/__init__.py +7 -0
- flextool/process_outputs/_annualize.py +55 -0
- flextool/process_outputs/_inmemory_helpers.py +292 -0
- flextool/process_outputs/_output_meta.py +672 -0
- flextool/process_outputs/calc_capacity_flows.py +107 -0
- flextool/process_outputs/calc_connections.py +136 -0
- flextool/process_outputs/calc_costs.py +260 -0
- flextool/process_outputs/calc_group_flows.py +192 -0
- flextool/process_outputs/calc_slacks.py +103 -0
- flextool/process_outputs/calc_storage_vre.py +160 -0
- flextool/process_outputs/drop_levels.py +208 -0
- flextool/process_outputs/handoff_writers.py +1315 -0
- flextool/process_outputs/out_ancillary.py +544 -0
- flextool/process_outputs/out_capacity.py +179 -0
- flextool/process_outputs/out_costs.py +334 -0
- flextool/process_outputs/out_flowgroup.py +189 -0
- flextool/process_outputs/out_flows.py +301 -0
- flextool/process_outputs/out_group.py +475 -0
- flextool/process_outputs/out_node.py +190 -0
- flextool/process_outputs/persist_realized_slice.py +601 -0
- flextool/process_outputs/process_results.py +24 -0
- flextool/process_outputs/read_highs_solution.py +2256 -0
- flextool/process_outputs/read_parameters.py +1799 -0
- flextool/process_outputs/read_sets.py +1095 -0
- flextool/process_outputs/read_variables.py +553 -0
- flextool/process_outputs/solve_order.py +81 -0
- flextool/process_outputs/spinedb_replay.py +412 -0
- flextool/process_outputs/union_realized_slice.py +224 -0
- flextool/process_outputs/write_outputs.py +1286 -0
- flextool/process_outputs/write_spinedb.py +1267 -0
- flextool/representative_periods/__init__.py +5 -0
- flextool/representative_periods/clustering.py +165 -0
- flextool/representative_periods/force_include.py +563 -0
- flextool/representative_periods/netload.py +365 -0
- flextool/representative_periods/netload_inputs.py +345 -0
- flextool/representative_periods/netload_iterate.py +722 -0
- flextool/representative_periods/preprocess.py +948 -0
- flextool/representative_periods/scenario_stack.py +195 -0
- flextool/representative_periods/weights.py +124 -0
- flextool/scenario_comparison/__init__.py +13 -0
- flextool/scenario_comparison/config_builder.py +158 -0
- flextool/scenario_comparison/constants.py +20 -0
- flextool/scenario_comparison/data_models.py +222 -0
- flextool/scenario_comparison/db_reader.py +399 -0
- flextool/scenario_comparison/dispatch_data.py +1002 -0
- flextool/scenario_comparison/dispatch_mappings.py +205 -0
- flextool/scenario_comparison/dispatch_plots.py +691 -0
- flextool/scenario_comparison/input_entity_colors.py +319 -0
- flextool/scenario_comparison/orchestrator.py +453 -0
- flextool/scenario_comparison/plan_union.py +244 -0
- flextool/scenario_comparison/plot_settings_seed.py +205 -0
- flextool/schemas/AXIS_CONTRACT.md +71 -0
- flextool/schemas/canonical_databases/howto_aggregate_output.json +6225 -0
- flextool/schemas/canonical_databases/howto_connections.json +5606 -0
- flextool/schemas/canonical_databases/howto_demand.json +5518 -0
- flextool/schemas/canonical_databases/howto_hydro_reservoir.json +6239 -0
- flextool/schemas/canonical_databases/howto_hydro_reservoir_with_pump.json +5933 -0
- flextool/schemas/canonical_databases/howto_non_sync_and_curtailment.json +5794 -0
- flextool/schemas/canonical_databases/howto_ramp_and_start_up.json +5707 -0
- flextool/schemas/canonical_databases/howto_stochastics.json +6032 -0
- flextool/schemas/canonical_databases/templates_examples.json +13532 -0
- flextool/schemas/canonical_databases/templates_time_settings_only.json +5340 -0
- flextool/schemas/comparison_settings_template.json +197 -0
- flextool/schemas/default_plot_settings.yaml +260 -0
- flextool/schemas/default_plots.yaml +2293 -0
- flextool/schemas/flextool_axis_contract.json +303 -0
- flextool/schemas/flextool_axis_contract.schema.json +247 -0
- flextool/schemas/old_flextool_import_template.json +4443 -0
- flextool/schemas/output_info_template.json +48 -0
- flextool/schemas/output_settings_template.json +256 -0
- flextool/schemas/pre_v26/flextool_template_constant_default.json +2105 -0
- flextool/schemas/pre_v26/flextool_template_default_optional_output.json +2152 -0
- flextool/schemas/pre_v26/flextool_template_default_value.json +2094 -0
- flextool/schemas/pre_v26/flextool_template_drop_down.json +2080 -0
- flextool/schemas/pre_v26/flextool_template_lifetime_method.json +1990 -0
- flextool/schemas/pre_v26/flextool_template_optional_outputs.json +2094 -0
- flextool/schemas/pre_v26/flextool_template_output_node_flows.json +2105 -0
- flextool/schemas/pre_v26/flextool_template_results_master.json +493 -0
- flextool/schemas/pre_v26/flextool_template_rolling_start_remove.json +2087 -0
- flextool/schemas/pre_v26/flextool_template_rolling_window.json +2059 -0
- flextool/schemas/pre_v26/flextool_template_storage_binding_defaults.json +46 -0
- flextool/schemas/pre_v26/flextool_template_v2.json +1990 -0
- flextool/schemas/pre_v26/flextool_template_v25.json +3864 -0
- flextool/schemas/spinedb_results_schema.json +581 -0
- flextool/schemas/spinedb_schema.json +4636 -0
- flextool/solver_config/copt.opt.template +18 -0
- flextool/solver_config/cplex.opt.template +25 -0
- flextool/solver_config/gurobi.opt.template +18 -0
- flextool/solver_config/highs.opt.template +18 -0
- flextool/solver_config/xpress.opt.template +26 -0
- flextool/spinedb_backend/__init__.py +26 -0
- flextool/spinedb_backend/_axis_enums.py +1119 -0
- flextool/spinedb_backend/_backend.py +1139 -0
- flextool/update_flextool/__init__.py +12 -0
- flextool/update_flextool/canonical_databases.py +251 -0
- flextool/update_flextool/db_migration.py +7108 -0
- flextool/update_flextool/ensure_settings_db.py +138 -0
- flextool/update_flextool/export_database.py +103 -0
- flextool/update_flextool/extend_tests_fixture.py +772 -0
- flextool/update_flextool/generate_canonical.py +274 -0
- flextool/update_flextool/initialize_database.py +42 -0
- flextool/update_flextool/install_info.py +225 -0
- flextool/update_flextool/self_update.py +464 -0
- flextool/update_flextool/sync_master_json_template.py +125 -0
- flextool/update_flextool/test_fixtures.py +187 -0
- flextool-4.0.0.dist-info/METADATA +217 -0
- flextool-4.0.0.dist-info/RECORD +322 -0
- flextool-4.0.0.dist-info/WHEEL +5 -0
- flextool-4.0.0.dist-info/entry_points.txt +17 -0
- flextool-4.0.0.dist-info/licenses/LICENSE.txt +19 -0
- flextool-4.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""FlexTool LP autoscaler — public API surface.
|
|
2
|
+
|
|
3
|
+
After Phase R2 the four-range Layer 1 detector and the HiGHS-native
|
|
4
|
+
Layer 3 recommendation live in :mod:`polar_high.autoscale`. FlexTool
|
|
5
|
+
re-exports them here so the rest of the engine_polars code (and any
|
|
6
|
+
external caller) sees a single stable surface. Layer 2 (semantic
|
|
7
|
+
per-quantity scaling, gated on ``ScalingMode.FULL``) stays FlexTool-side
|
|
8
|
+
because it depends on FlexTool's parameter taxonomy.
|
|
9
|
+
|
|
10
|
+
Three layers, run around the polar-high ``Problem``:
|
|
11
|
+
|
|
12
|
+
* **Layer 1 (detect, polar-high):** read the four standard LP
|
|
13
|
+
coefficient ranges (Matrix / Cost / Bound / RHS) and decide whether
|
|
14
|
+
the model needs scaling at all. Detection only — no LP modification.
|
|
15
|
+
* **Layer 2 (semantic per-type scaling, FlexTool):** apply column
|
|
16
|
+
scalers chosen by ``QuantityType`` so each physical quantity (power,
|
|
17
|
+
energy, price, …) lives on its own well-conditioned magnitude. Fires
|
|
18
|
+
only when ``mode == ScalingMode.FULL``.
|
|
19
|
+
* **Layer 3 (HiGHS-native + bound-scale escape, polar-high):** set
|
|
20
|
+
``user_bound_scale`` / ``user_objective_scale`` / ``simplex_scale_strategy``
|
|
21
|
+
to neutralise residual range pressure inside HiGHS. Honours
|
|
22
|
+
precedence so a caller-set option survives.
|
|
23
|
+
"""
|
|
24
|
+
from polar_high.autoscale import (
|
|
25
|
+
USER_SCALE_CLAMP_HI,
|
|
26
|
+
USER_SCALE_CLAMP_LO,
|
|
27
|
+
Layer3Plan,
|
|
28
|
+
RangeReport,
|
|
29
|
+
ScalingConfig,
|
|
30
|
+
ScalingMode,
|
|
31
|
+
apply_scaling,
|
|
32
|
+
detect_ranges,
|
|
33
|
+
get_explicit_option,
|
|
34
|
+
has_explicit_option,
|
|
35
|
+
mode_enables_layer1,
|
|
36
|
+
mode_enables_layer3,
|
|
37
|
+
ranges_from_arrays,
|
|
38
|
+
ranges_from_streamed,
|
|
39
|
+
recommend_scaling,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
from ._config import (
|
|
43
|
+
USER_BOUND_SCALE_MAX,
|
|
44
|
+
USER_BOUND_SCALE_MIN,
|
|
45
|
+
resolve_scaling_config,
|
|
46
|
+
resolve_user_bound_scale_override,
|
|
47
|
+
)
|
|
48
|
+
from ._layer2 import (
|
|
49
|
+
Layer2Plan,
|
|
50
|
+
apply_layer2,
|
|
51
|
+
apply_layer2_with_exponents,
|
|
52
|
+
bucket_coefficients,
|
|
53
|
+
choose_scale_powers,
|
|
54
|
+
unscale_solution,
|
|
55
|
+
)
|
|
56
|
+
from ._layer2_types import (
|
|
57
|
+
CONSTRAINT_FAMILIES,
|
|
58
|
+
CstrFamily,
|
|
59
|
+
VARIABLE_FAMILIES,
|
|
60
|
+
VarFamily,
|
|
61
|
+
lookup_cstr,
|
|
62
|
+
lookup_var,
|
|
63
|
+
resolve_cstr_rhs_type,
|
|
64
|
+
)
|
|
65
|
+
from ._quantity_types import QuantityType, lookup, resolve_group_capacity_type
|
|
66
|
+
from ._report import format_console_summary, format_nonoptimal_hint, write_report
|
|
67
|
+
|
|
68
|
+
__all__ = [
|
|
69
|
+
"CONSTRAINT_FAMILIES",
|
|
70
|
+
"CstrFamily",
|
|
71
|
+
"Layer2Plan",
|
|
72
|
+
"Layer3Plan",
|
|
73
|
+
"QuantityType",
|
|
74
|
+
"RangeReport",
|
|
75
|
+
"ScalingConfig",
|
|
76
|
+
"ScalingMode",
|
|
77
|
+
"USER_BOUND_SCALE_MAX",
|
|
78
|
+
"USER_BOUND_SCALE_MIN",
|
|
79
|
+
"USER_SCALE_CLAMP_HI",
|
|
80
|
+
"USER_SCALE_CLAMP_LO",
|
|
81
|
+
"VARIABLE_FAMILIES",
|
|
82
|
+
"VarFamily",
|
|
83
|
+
"apply_layer2",
|
|
84
|
+
"apply_layer2_with_exponents",
|
|
85
|
+
"apply_scaling",
|
|
86
|
+
"bucket_coefficients",
|
|
87
|
+
"choose_scale_powers",
|
|
88
|
+
"detect_ranges",
|
|
89
|
+
"format_console_summary",
|
|
90
|
+
"format_nonoptimal_hint",
|
|
91
|
+
"get_explicit_option",
|
|
92
|
+
"has_explicit_option",
|
|
93
|
+
"lookup",
|
|
94
|
+
"lookup_cstr",
|
|
95
|
+
"lookup_var",
|
|
96
|
+
"mode_enables_layer1",
|
|
97
|
+
"mode_enables_layer3",
|
|
98
|
+
"ranges_from_arrays",
|
|
99
|
+
"ranges_from_streamed",
|
|
100
|
+
"recommend_scaling",
|
|
101
|
+
"resolve_cstr_rhs_type",
|
|
102
|
+
"resolve_group_capacity_type",
|
|
103
|
+
"resolve_scaling_config",
|
|
104
|
+
"resolve_user_bound_scale_override",
|
|
105
|
+
"unscale_solution",
|
|
106
|
+
"write_report",
|
|
107
|
+
]
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"""FlexTool-side configuration for the autoscaler.
|
|
2
|
+
|
|
3
|
+
After Phase R2 the autoscaler's policy primitives (``ScalingMode`` enum,
|
|
4
|
+
``ScalingConfig`` dataclass, ``mode_enables_layer1`` / ``mode_enables_layer3``
|
|
5
|
+
predicates) live in :mod:`polar_high.autoscale._config`. This module
|
|
6
|
+
keeps the FlexTool-specific resolver — ``resolve_scaling_config`` — which
|
|
7
|
+
honours the ``--scaling`` CLI flag and the ``FLEXTOOL_SCALING`` env var.
|
|
8
|
+
|
|
9
|
+
Layer 2 (semantic per-quantity scaling) stays in FlexTool because it
|
|
10
|
+
needs FlexTool's parameter taxonomy. Layer 2 fires only when
|
|
11
|
+
``mode == ScalingMode.FULL``.
|
|
12
|
+
|
|
13
|
+
The ``--user-bound-scale N`` CLI override still lives here too: when
|
|
14
|
+
set, it propagates onto ``ScalingConfig.user_bound_scale`` so Layer 3's
|
|
15
|
+
manual-override branch (in polar-high) fires. The legacy DB-side
|
|
16
|
+
``solve.user_bound_scale`` field is normalised by
|
|
17
|
+
:func:`resolve_user_bound_scale_override`, kept for the cascade's
|
|
18
|
+
``_baseline_highs_options`` wire-in.
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import math
|
|
23
|
+
import os
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from typing import Any, Optional
|
|
26
|
+
|
|
27
|
+
from polar_high.autoscale import (
|
|
28
|
+
ScalingConfig,
|
|
29
|
+
ScalingMode,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Clamp range for the HiGHS ``user_bound_scale`` exponent (power of 2). HiGHS
|
|
34
|
+
# itself accepts ``[-30, 30]`` (see ``polar_high.autoscale.USER_SCALE_CLAMP_*``);
|
|
35
|
+
# we cap at ``[-10, 0]`` for the DB-override coercion path because (a) positive
|
|
36
|
+
# scales are nearly always harmful — they expand already-large coefficients,
|
|
37
|
+
# and (b) ``2**-10`` ≈ ``1e-3`` is plenty to neutralise the LP shapes FlexTool
|
|
38
|
+
# produces. Centralised here so both the legacy DB-override coercion and
|
|
39
|
+
# the cascade-level ``user_bound_scale_override`` wire-in use the same bounds.
|
|
40
|
+
USER_BOUND_SCALE_MIN: int = -10
|
|
41
|
+
USER_BOUND_SCALE_MAX: int = 0
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _coerce_env_mode(raw: Optional[str], *, default: ScalingMode) -> ScalingMode:
|
|
45
|
+
"""Parse ``FLEXTOOL_SCALING`` env flag.
|
|
46
|
+
|
|
47
|
+
Accepts the four enum values (``off``, ``solver_only``, ``basic``,
|
|
48
|
+
``full``) case-insensitively. Empty / unrecognised values fall
|
|
49
|
+
through to ``default`` so a misconfigured env doesn't silently flip
|
|
50
|
+
behaviour.
|
|
51
|
+
"""
|
|
52
|
+
if raw is None:
|
|
53
|
+
return default
|
|
54
|
+
v = raw.strip().lower()
|
|
55
|
+
for m in ScalingMode:
|
|
56
|
+
if v == m.value:
|
|
57
|
+
return m
|
|
58
|
+
return default
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _coerce_user_bound_scale(raw: Any) -> Optional[int]:
|
|
62
|
+
"""Coerce a ``--user-bound-scale`` value to ``int | None``.
|
|
63
|
+
|
|
64
|
+
Accepts already-typed ``int`` / ``None``, as well as decimal strings
|
|
65
|
+
(the form ``cli_args`` typically carries them). Non-numeric strings
|
|
66
|
+
raise ``ValueError`` — the CLI surface should reject them; surfacing
|
|
67
|
+
the error here is louder than silently falling back to ``None``.
|
|
68
|
+
"""
|
|
69
|
+
if raw is None:
|
|
70
|
+
return None
|
|
71
|
+
if isinstance(raw, int) and not isinstance(raw, bool):
|
|
72
|
+
return raw
|
|
73
|
+
if isinstance(raw, str):
|
|
74
|
+
s = raw.strip()
|
|
75
|
+
if not s:
|
|
76
|
+
return None
|
|
77
|
+
return int(s)
|
|
78
|
+
raise ValueError(
|
|
79
|
+
f"user_bound_scale must be int, decimal str, or None; got {raw!r}"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _coerce_cli_scaling(raw: Any) -> Optional[ScalingMode]:
|
|
84
|
+
"""Coerce an ``args.scaling`` value to ``ScalingMode | None``.
|
|
85
|
+
|
|
86
|
+
Accepts the four enum-value strings (``"off"`` / ``"solver_only"`` /
|
|
87
|
+
``"basic"`` / ``"full"``), an already-typed ``ScalingMode``, and
|
|
88
|
+
``None`` (CLI unset → fall through to env / default). Unrecognised
|
|
89
|
+
values raise so a typo surfaces loudly rather than silently picking
|
|
90
|
+
a different policy.
|
|
91
|
+
"""
|
|
92
|
+
if raw is None:
|
|
93
|
+
return None
|
|
94
|
+
if isinstance(raw, ScalingMode):
|
|
95
|
+
return raw
|
|
96
|
+
if isinstance(raw, str):
|
|
97
|
+
v = raw.strip().lower()
|
|
98
|
+
if not v:
|
|
99
|
+
return None
|
|
100
|
+
for m in ScalingMode:
|
|
101
|
+
if v == m.value:
|
|
102
|
+
return m
|
|
103
|
+
raise ValueError(
|
|
104
|
+
f"scaling must be one of {[m.value for m in ScalingMode]!r}, "
|
|
105
|
+
f"ScalingMode, or None; got {raw!r}"
|
|
106
|
+
)
|
|
107
|
+
raise ValueError(
|
|
108
|
+
f"scaling must be a string, ScalingMode, or None; got {raw!r}"
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def resolve_scaling_config(cli_args: Any = None) -> ScalingConfig:
|
|
113
|
+
"""Build a :class:`ScalingConfig` from CLI args + environment.
|
|
114
|
+
|
|
115
|
+
Precedence for the mode is **CLI > env > default-full**:
|
|
116
|
+
|
|
117
|
+
* ``cli_args.scaling`` (``"off"`` / ``"solver_only"`` / ``"basic"``
|
|
118
|
+
/ ``"full"`` / ``ScalingMode`` / ``None``) wins when set. The
|
|
119
|
+
``cmd_run_flextool`` CLI defines this via ``--scaling`` and, when
|
|
120
|
+
set, also exports ``FLEXTOOL_SCALING`` so subprocess /
|
|
121
|
+
cascade-internal call sites that resolve config with
|
|
122
|
+
``cli_args=None`` still observe the operator's choice.
|
|
123
|
+
* ``FLEXTOOL_SCALING`` env var (``off`` / ``solver_only`` /
|
|
124
|
+
``basic`` / ``full``) provides the fallback for callers that
|
|
125
|
+
don't have access to the parsed ``args`` (e.g. cascade-internal
|
|
126
|
+
helpers, tests that pre-set the env).
|
|
127
|
+
* Default ``ScalingMode.FULL`` — the full autoscaler is on.
|
|
128
|
+
|
|
129
|
+
``user_bound_scale`` follows the same CLI-wins-over-env layering:
|
|
130
|
+
|
|
131
|
+
* ``cli_args.user_bound_scale`` (int / decimal str / ``None``) wins.
|
|
132
|
+
* ``FLEXTOOL_USER_BOUND_SCALE`` env var (set by ``--user-bound-scale``
|
|
133
|
+
CLI) is the fallback.
|
|
134
|
+
* ``cli_args.autoscale_report_yaml`` overrides the default YAML
|
|
135
|
+
report path; otherwise ``_orchestration`` picks
|
|
136
|
+
``work_folder/solve_data/autoscale_<solve>.yaml``.
|
|
137
|
+
|
|
138
|
+
Returns a frozen :class:`ScalingConfig` (from
|
|
139
|
+
:mod:`polar_high.autoscale`).
|
|
140
|
+
"""
|
|
141
|
+
mode = _coerce_env_mode(
|
|
142
|
+
os.environ.get("FLEXTOOL_SCALING"), default=ScalingMode.FULL,
|
|
143
|
+
)
|
|
144
|
+
user_bound_scale_env = _coerce_user_bound_scale(
|
|
145
|
+
os.environ.get("FLEXTOOL_USER_BOUND_SCALE"),
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
user_bound_scale = user_bound_scale_env
|
|
149
|
+
report_yaml_path: Optional[Path] = None
|
|
150
|
+
|
|
151
|
+
if cli_args is not None:
|
|
152
|
+
cli_mode = _coerce_cli_scaling(getattr(cli_args, "scaling", None))
|
|
153
|
+
if cli_mode is not None:
|
|
154
|
+
mode = cli_mode
|
|
155
|
+
cli_ubs = getattr(cli_args, "user_bound_scale", None)
|
|
156
|
+
if cli_ubs is not None:
|
|
157
|
+
user_bound_scale = _coerce_user_bound_scale(cli_ubs)
|
|
158
|
+
cli_yaml = getattr(cli_args, "autoscale_report_yaml", None)
|
|
159
|
+
if cli_yaml is not None:
|
|
160
|
+
report_yaml_path = Path(cli_yaml)
|
|
161
|
+
|
|
162
|
+
return ScalingConfig(
|
|
163
|
+
mode=mode,
|
|
164
|
+
user_bound_scale=user_bound_scale,
|
|
165
|
+
report_yaml_path=report_yaml_path,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def resolve_user_bound_scale_override(
|
|
170
|
+
user_value: Any,
|
|
171
|
+
) -> Optional[int]:
|
|
172
|
+
"""Coerce a raw DB ``user_bound_scale`` value to a clamped int (or None).
|
|
173
|
+
|
|
174
|
+
Accepts ``None``, integers, floats, or numeric strings and returns:
|
|
175
|
+
|
|
176
|
+
* an integer in ``[USER_BOUND_SCALE_MIN, USER_BOUND_SCALE_MAX]`` when
|
|
177
|
+
the user provided a non-zero, finite, parseable value;
|
|
178
|
+
* ``None`` when the user provided no value, ``0``, or anything
|
|
179
|
+
unparseable — caller should then fall back to the heuristic (Layer 3's
|
|
180
|
+
automatic recommendation).
|
|
181
|
+
|
|
182
|
+
Truncates non-integer floats toward zero (e.g. ``-3.7`` → ``-3``) rather
|
|
183
|
+
than rounding, mirroring the conservative direction (smaller |N| is
|
|
184
|
+
gentler scaling).
|
|
185
|
+
|
|
186
|
+
This helper exists alongside :func:`_coerce_user_bound_scale` because the
|
|
187
|
+
two have different contracts: ``_coerce_user_bound_scale`` is for
|
|
188
|
+
CLI / env values where a non-numeric string is a user error worth
|
|
189
|
+
surfacing as ``ValueError``; this helper is for legacy DB values where a
|
|
190
|
+
malformed entry should fall back to the heuristic rather than crash a
|
|
191
|
+
cascade.
|
|
192
|
+
"""
|
|
193
|
+
if user_value is None:
|
|
194
|
+
return None
|
|
195
|
+
try:
|
|
196
|
+
as_float = float(user_value)
|
|
197
|
+
except (TypeError, ValueError):
|
|
198
|
+
return None
|
|
199
|
+
if not math.isfinite(as_float):
|
|
200
|
+
return None
|
|
201
|
+
n = int(as_float) # truncates toward zero
|
|
202
|
+
if n == 0:
|
|
203
|
+
return None
|
|
204
|
+
if n > USER_BOUND_SCALE_MAX:
|
|
205
|
+
n = USER_BOUND_SCALE_MAX
|
|
206
|
+
if n < USER_BOUND_SCALE_MIN:
|
|
207
|
+
n = USER_BOUND_SCALE_MIN
|
|
208
|
+
return n
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
__all__ = [
|
|
212
|
+
"ScalingConfig",
|
|
213
|
+
"ScalingMode",
|
|
214
|
+
"USER_BOUND_SCALE_MAX",
|
|
215
|
+
"USER_BOUND_SCALE_MIN",
|
|
216
|
+
"resolve_scaling_config",
|
|
217
|
+
"resolve_user_bound_scale_override",
|
|
218
|
+
]
|