diff --git a/src/andromede/thermal_heuristic/problem.py b/src/andromede/thermal_heuristic/problem.py index 5ffacbbf..0867d6aa 100644 --- a/src/andromede/thermal_heuristic/problem.py +++ b/src/andromede/thermal_heuristic/problem.py @@ -116,7 +116,6 @@ def create_main_problem( time_block, scenarios, border_management=BlockBorderManagement.CYCLE, - solver_id="XPRESS", ) return problem @@ -257,7 +256,6 @@ def create_problem_accurate_heuristic( time_block, scenarios, border_management=BlockBorderManagement.CYCLE, - solver_id="XPRESS", ) return problem @@ -376,7 +374,6 @@ def create_problem_fast_heuristic( time_block, 1, border_management=BlockBorderManagement.CYCLE, - solver_id="XPRESS", ) parameters = pywraplp.MPSolverParameters() diff --git a/tests/functional/data_complex_case/accurate/1/details-hourly.txt b/tests/functional/data_complex_case/accurate/1/details-hourly.txt index 0f1b9a76..ab192b26 100644 --- a/tests/functional/data_complex_case/accurate/1/details-hourly.txt +++ b/tests/functional/data_complex_case/accurate/1/details-hourly.txt @@ -114,7 +114,7 @@ BA00 hourly cluster_1 cluster_2 cluster_3 Pondage_turb cluster_1 cluster_2 cl 107 05 JAN 10:00 0 120 1067 0 0 2 4 0 0 2 4 0 108 05 JAN 11:00 0 120 1059 0 0 2 4 0 0 2 4 0 109 05 JAN 12:00 0 120 1049 0 0 24503 4 0 0 2 4 0 - 110 05 JAN 13:00 0 120 1093 0 0 3 4 0 0 2 4 0 + 110 05 JAN 13:00 0 180 1033 0 0 3 4 0 0 3 4 0 111 05 JAN 14:00 0 180 1038 0 0 3 4 0 0 3 4 0 112 05 JAN 15:00 0 180 1038 0 0 3 4 0 0 3 4 0 113 05 JAN 16:00 0 180 1087 0 0 3 4 0 0 3 4 0 @@ -124,8 +124,8 @@ BA00 hourly cluster_1 cluster_2 cluster_3 Pondage_turb cluster_1 cluster_2 cl 117 05 JAN 20:00 0 135 1100 0 0 2 4 0 0 2 4 0 118 05 JAN 21:00 0 120 1023 0 0 2 4 0 0 2 4 0 119 05 JAN 22:00 0 120 910 0 0 2 4 0 0 2 4 0 - 120 05 JAN 23:00 0 60 808 0 0 0 4 0 0 1 4 0 - 121 06 JAN 00:00 0 60 605 0 0 0 4 0 0 1 4 0 + 120 05 JAN 23:00 0 120 748 0 0 0 4 0 0 2 4 0 + 121 06 JAN 00:00 0 0 665 0 0 0 4 0 0 0 4 0 122 06 JAN 01:00 0 0 600 0 0 0 4 0 0 0 4 0 123 06 JAN 02:00 0 0 600 0 0 0 4 0 0 0 4 0 124 06 JAN 03:00 0 0 600 0 0 0 4 0 0 0 4 0 diff --git a/tests/functional/data_complex_case/milp/0/details-hourly.txt b/tests/functional/data_complex_case/milp/0/details-hourly.txt index 12397ff9..ce403a17 100644 --- a/tests/functional/data_complex_case/milp/0/details-hourly.txt +++ b/tests/functional/data_complex_case/milp/0/details-hourly.txt @@ -133,8 +133,8 @@ BA00 hourly cluster_1 cluster_2 cluster_3 Pondage_turb cluster_1 cluster_2 cl 126 06 JAN 05:00 180 0 300 0 1 0 2 0 1 0 2 0 127 06 JAN 06:00 230 0 300 0 1 0 2 0 1 0 2 0 128 06 JAN 07:00 404 0 300 0 1 24501 2 0 1 0 2 0 - 129 06 JAN 08:00 410 60 358 0 1 1 2 0 1 1 2 0 - 130 06 JAN 09:00 410 60 393 0 1 1 2 0 1 1 2 0 + 129 06 JAN 08:00 410 0 418 0 1 1 2 0 1 0 2 0 + 130 06 JAN 09:00 410 0 453 0 1 1 2 0 1 0 2 0 131 06 JAN 10:00 410 60 375 0 1 1 2 0 1 1 2 0 132 06 JAN 11:00 410 60 339 0 1 1 2 0 1 1 2 0 133 06 JAN 12:00 410 60 313 0 1 1 2 0 1 1 2 0 @@ -144,8 +144,8 @@ BA00 hourly cluster_1 cluster_2 cluster_3 Pondage_turb cluster_1 cluster_2 cl 137 06 JAN 16:00 410 60 445 0 1 1 2 0 1 1 2 0 138 06 JAN 17:00 410 60 532 0 100501 1 2 0 1 1 2 0 139 06 JAN 18:00 410 60 515 0 1 1 2 0 1 1 2 0 - 140 06 JAN 19:00 410 0 522 0 1 0 2 0 1 0 2 0 - 141 06 JAN 20:00 410 0 459 0 1 0 2 0 1 0 2 0 + 140 06 JAN 19:00 410 60 462 0 1 0 2 0 1 1 2 0 + 141 06 JAN 20:00 410 60 399 0 1 0 2 0 1 1 2 0 142 06 JAN 21:00 410 0 402 0 1 0 2 0 1 0 2 0 143 06 JAN 22:00 410 0 320 0 1 0 2 0 1 0 2 0 144 06 JAN 23:00 350 0 300 0 1 0 2 0 1 0 2 0 @@ -317,27 +317,27 @@ BA00 hourly cluster_1 cluster_2 cluster_3 Pondage_turb cluster_1 cluster_2 cl 310 13 JAN 21:00 410 270 0 0 1 3 0 0 1 3 0 0 311 13 JAN 22:00 410 270 0 0 1 3 0 0 1 3 0 0 312 13 JAN 23:00 410 270 0 0 1 3 0 0 1 3 0 0 - 313 14 JAN 00:00 410 120 245 0 1 2 69501 0 1 2 1 0 - 314 14 JAN 01:00 407 120 150 0 1 2 1 0 1 2 1 0 - 315 14 JAN 02:00 354 120 150 0 1 2 1 0 1 2 1 0 - 316 14 JAN 03:00 323 120 150 0 1 2 1 0 1 2 1 0 - 317 14 JAN 04:00 325 120 150 0 1 2 1 0 1 2 1 0 - 318 14 JAN 05:00 373 120 150 0 1 2 1 0 1 2 1 0 - 319 14 JAN 06:00 410 120 223 0 1 2 1 0 1 2 1 0 - 320 14 JAN 07:00 410 120 378 0 1 2 69502 0 1 2 2 0 - 321 14 JAN 08:00 410 120 514 0 1 2 2 0 1 2 2 0 - 322 14 JAN 09:00 410 120 575 0 1 2 69503 0 1 2 3 0 - 323 14 JAN 10:00 410 120 573 0 1 2 3 0 1 2 3 0 - 324 14 JAN 11:00 410 120 563 0 1 2 3 0 1 2 3 0 - 325 14 JAN 12:00 410 120 553 0 1 2 3 0 1 2 3 0 - 326 14 JAN 13:00 410 120 590 0 1 2 3 0 1 2 3 0 - 327 14 JAN 14:00 410 120 609 0 1 2 3 0 1 2 3 0 - 328 14 JAN 15:00 410 120 633 0 1 2 3 0 1 2 3 0 - 329 14 JAN 16:00 410 120 731 0 1 2 3 0 1 2 3 0 - 330 14 JAN 17:00 410 143 825 0 1 2 3 0 1 2 3 0 - 331 14 JAN 18:00 410 148 825 0 1 2 3 0 1 2 3 0 - 332 14 JAN 19:00 410 120 810 0 1 2 3 0 1 2 3 0 - 333 14 JAN 20:00 410 60 814 0 1 1 3 0 1 1 3 0 + 313 14 JAN 00:00 410 90 275 0 1 2 69501 0 1 1 1 0 + 314 14 JAN 01:00 410 0 267 0 1 2 1 0 1 0 1 0 + 315 14 JAN 02:00 410 0 214 0 1 2 1 0 1 0 1 0 + 316 14 JAN 03:00 410 0 183 0 1 2 1 0 1 0 1 0 + 317 14 JAN 04:00 410 0 185 0 1 2 1 0 1 0 1 0 + 318 14 JAN 05:00 410 0 233 0 1 2 1 0 1 0 1 0 + 319 14 JAN 06:00 410 0 343 0 1 2 1 0 1 0 2 0 + 320 14 JAN 07:00 410 0 498 0 1 2 69502 0 1 0 2 0 + 321 14 JAN 08:00 410 0 634 0 1 2 2 0 1 0 3 0 + 322 14 JAN 09:00 410 0 695 0 1 2 69503 0 1 0 3 0 + 323 14 JAN 10:00 410 0 693 0 1 2 3 0 1 0 3 0 + 324 14 JAN 11:00 410 0 683 0 1 2 3 0 1 0 3 0 + 325 14 JAN 12:00 410 0 673 0 1 2 3 0 1 0 3 0 + 326 14 JAN 13:00 410 0 710 0 1 2 3 0 1 0 3 0 + 327 14 JAN 14:00 410 0 729 0 1 2 3 0 1 0 3 0 + 328 14 JAN 15:00 410 0 753 0 1 2 3 0 1 0 3 0 + 329 14 JAN 16:00 410 0 851 0 1 2 3 0 1 0 4 0 + 330 14 JAN 17:00 410 0 968 0 1 2 3 0 1 0 4 0 + 331 14 JAN 18:00 410 0 973 0 1 2 3 0 1 0 4 0 + 332 14 JAN 19:00 410 0 930 0 1 2 3 0 1 0 4 0 + 333 14 JAN 20:00 410 0 874 0 1 1 3 0 1 0 4 0 334 14 JAN 21:00 410 0 819 0 1 0 3 0 1 0 3 0 335 14 JAN 22:00 410 0 713 0 1 0 3 0 1 0 3 0 336 14 JAN 23:00 410 0 567 0 1 0 3 0 1 0 3 0 diff --git a/tests/functional/data_complex_case/milp/1/details-hourly.txt b/tests/functional/data_complex_case/milp/1/details-hourly.txt index e8004bdb..abb3ae72 100644 --- a/tests/functional/data_complex_case/milp/1/details-hourly.txt +++ b/tests/functional/data_complex_case/milp/1/details-hourly.txt @@ -184,7 +184,7 @@ BA00 hourly cluster_1 cluster_2 cluster_3 Pondage_turb cluster_1 cluster_2 cl 177 08 JAN 08:00 410 0 656 0 1 0 69503 0 1 0 3 0 178 08 JAN 09:00 410 0 709 0 1 0 3 0 1 0 3 0 179 08 JAN 10:00 410 0 716 0 1 0 3 0 1 0 3 0 - 180 08 JAN 11:00 410 60 662 0 1 24501 3 0 1 1 3 0 + 180 08 JAN 11:00 410 0 722 0 1 24501 3 0 1 0 3 0 181 08 JAN 12:00 410 60 651 0 1 1 3 0 1 1 3 0 182 08 JAN 13:00 410 60 691 0 1 1 3 0 1 1 3 0 183 08 JAN 14:00 410 60 690 0 1 1 3 0 1 1 3 0 @@ -195,7 +195,7 @@ BA00 hourly cluster_1 cluster_2 cluster_3 Pondage_turb cluster_1 cluster_2 cl 188 08 JAN 19:00 410 60 724 0 1 1 3 0 1 1 3 0 189 08 JAN 20:00 410 60 647 0 1 1 3 0 1 1 3 0 190 08 JAN 21:00 410 60 550 0 1 1 2 0 1 1 2 0 - 191 08 JAN 22:00 410 0 505 0 1 0 2 0 1 0 2 0 + 191 08 JAN 22:00 410 60 445 0 1 0 2 0 1 1 2 0 192 08 JAN 23:00 410 0 354 0 1 0 2 0 1 0 2 0 193 09 JAN 00:00 348 0 300 0 1 0 2 0 1 0 2 0 194 09 JAN 01:00 249 0 300 0 1 0 2 0 1 0 2 0 @@ -206,9 +206,9 @@ BA00 hourly cluster_1 cluster_2 cluster_3 Pondage_turb cluster_1 cluster_2 cl 199 09 JAN 06:00 410 0 316 0 1 0 2 0 1 0 2 0 200 09 JAN 07:00 410 0 495 0 1 0 2 0 1 0 2 0 201 09 JAN 08:00 410 0 615 0 1 0 69503 0 1 0 3 0 - 202 09 JAN 09:00 410 60 592 0 1 0 3 0 1 1 3 0 - 203 09 JAN 10:00 410 60 586 0 1 0 3 0 1 1 3 0 - 204 09 JAN 11:00 410 60 587 0 1 0 3 0 1 1 3 0 + 202 09 JAN 09:00 410 0 652 0 1 0 3 0 1 0 3 0 + 203 09 JAN 10:00 410 0 646 0 1 0 3 0 1 0 3 0 + 204 09 JAN 11:00 410 0 647 0 1 0 3 0 1 0 3 0 205 09 JAN 12:00 410 60 581 0 1 24501 3 0 1 1 3 0 206 09 JAN 13:00 410 60 630 0 1 1 3 0 1 1 3 0 207 09 JAN 14:00 410 60 651 0 1 1 3 0 1 1 3 0 @@ -217,9 +217,9 @@ BA00 hourly cluster_1 cluster_2 cluster_3 Pondage_turb cluster_1 cluster_2 cl 210 09 JAN 17:00 410 60 804 0 1 1 3 0 1 1 3 0 211 09 JAN 18:00 410 60 815 0 1 1 3 0 1 1 3 0 212 09 JAN 19:00 410 60 795 0 1 1 3 0 1 1 3 0 - 213 09 JAN 20:00 410 0 801 0 1 1 3 0 1 0 3 0 - 214 09 JAN 21:00 410 0 711 0 1 1 3 0 1 0 3 0 - 215 09 JAN 22:00 410 0 583 0 1 1 3 0 1 0 3 0 + 213 09 JAN 20:00 410 60 741 0 1 1 3 0 1 1 3 0 + 214 09 JAN 21:00 410 60 651 0 1 1 3 0 1 1 3 0 + 215 09 JAN 22:00 410 60 523 0 1 1 3 0 1 1 3 0 216 09 JAN 23:00 387 0 450 0 1 0 3 0 1 0 3 0 217 10 JAN 00:00 280 0 450 0 1 0 3 0 1 0 3 0 218 10 JAN 01:00 196 0 450 0 1 0 3 0 1 0 3 0 diff --git a/tests/functional/test_heuristic_complex_case.py b/tests/functional/test_heuristic_complex_case.py index 8f9ad3ae..edecb078 100644 --- a/tests/functional/test_heuristic_complex_case.py +++ b/tests/functional/test_heuristic_complex_case.py @@ -54,6 +54,7 @@ def test_milp_version() -> None: parameters = pywraplp.MPSolverParameters() parameters.SetIntegerParam(parameters.PRESOLVE, parameters.PRESOLVE_OFF) parameters.SetIntegerParam(parameters.SCALING, 0) + parameters.SetDoubleParam(parameters.RELATIVE_MIP_GAP, 1e-5) status = problem.solver.Solve(parameters) @@ -63,7 +64,7 @@ def test_milp_version() -> None: problem, "milp", week, scenario=scenario, dir_path="data_complex_case" ) - expected_cost = [[78933742, 102109698], [17472101, 17424769]] + expected_cost = [[78933742, 102103587], [17472101, 17424769]] assert problem.solver.Objective().Value() == pytest.approx( expected_cost[scenario][week] ) @@ -166,7 +167,7 @@ def test_accurate_heuristic() -> None: expected_cost = [ [78996726, 102215087 - 69500], - [17587733, 17641808], + [17589534, 17641808], ] assert problem_optimization_2.solver.Objective().Value() == pytest.approx( expected_cost[scenario][week] diff --git a/tests/functional/test_heuristic_second_complex_case.py b/tests/functional/test_heuristic_second_complex_case.py index 699dd2c9..6faf3225 100644 --- a/tests/functional/test_heuristic_second_complex_case.py +++ b/tests/functional/test_heuristic_second_complex_case.py @@ -110,4 +110,6 @@ def test_fast_heuristic() -> None: f"tests/functional/data_second_complex_case/fast/itr2_fast_cluster{j+1}.txt" ) for time_step in range(number_hours): - assert mingen_heuristic.values[time_step, 0] == expected_output[time_step] + assert mingen_heuristic.values[time_step, 0] == pytest.approx( + expected_output[time_step] + ) diff --git a/tests/functional/test_resolution_with_differents_scenarios.py b/tests/functional/test_resolution_with_differents_scenarios.py index 54b056ee..bbed4111 100644 --- a/tests/functional/test_resolution_with_differents_scenarios.py +++ b/tests/functional/test_resolution_with_differents_scenarios.py @@ -60,7 +60,91 @@ def test_one_problem_per_scenario() -> None: number_hours = 168 scenarios = 2 - for scenario in range(scenarios): + solver = pywraplp.Solver.CreateSolver("XPRESS") + if solver: + + for scenario in range(scenarios): + for week in range(2): + problem = create_complex_problem( + { + "G1": ConstantData(0), + "G2": ConstantData(0), + "G3": ConstantData(0), + }, + number_hours, + lp_relaxation=False, + fast=False, + week=week, + scenarios=[scenario], + ) + + parameters = pywraplp.MPSolverParameters() + parameters.SetIntegerParam(parameters.PRESOLVE, parameters.PRESOLVE_OFF) + parameters.SetIntegerParam(parameters.SCALING, 0) + parameters.SetDoubleParam(parameters.PRIMAL_TOLERANCE, 1e-7) + parameters.SetDoubleParam(parameters.DUAL_TOLERANCE, 1e-7) + parameters.SetDoubleParam(parameters.RELATIVE_MIP_GAP, 0.0001) + + status = problem.solver.Solve(parameters) + + assert status == problem.solver.OPTIMAL + + expected_cost = [[78933841, 102109698], [17472101, 17424769]] + assert problem.solver.Objective().Value() == pytest.approx( + expected_cost[scenario][week] + ) + + +def test_one_problem_per_scenario_with_different_parameters() -> None: + """Resolve the same problem as above with more restrictive parameters. If the problem is solved scenario per scenario the result is better than above.""" + number_hours = 168 + scenarios = 2 + + solver = pywraplp.Solver.CreateSolver("XPRESS") + if solver: + + for scenario in range(scenarios): + for week in range(2): + problem = create_complex_problem( + { + "G1": ConstantData(0), + "G2": ConstantData(0), + "G3": ConstantData(0), + }, + number_hours, + lp_relaxation=False, + fast=False, + week=week, + scenarios=[scenario], + ) + + parameters = pywraplp.MPSolverParameters() + parameters.SetIntegerParam(parameters.PRESOLVE, parameters.PRESOLVE_OFF) + parameters.SetIntegerParam(parameters.SCALING, 0) + parameters.SetDoubleParam(parameters.PRIMAL_TOLERANCE, 1e-7) + parameters.SetDoubleParam(parameters.DUAL_TOLERANCE, 1e-7) + parameters.SetDoubleParam(parameters.RELATIVE_MIP_GAP, 0.000001) + + problem.solver.EnableOutput() + + status = problem.solver.Solve(parameters) + + assert status == problem.solver.OPTIMAL + + expected_cost = [[78933742, 102103588], [17472101, 17424769]] + assert problem.solver.Objective().Value() == pytest.approx( + expected_cost[scenario][week] + ) + + +def test_one_problem_for_all_scenarios() -> None: + """Resolve the same problem as above with same parameters as Antares. If the problem is solved for all scenarios at the same time, the result is worse than solving the problem one by one.""" + number_hours = 168 + scenarios = [0, 1] + + solver = pywraplp.Solver.CreateSolver("XPRESS") + if solver: + for week in range(2): problem = create_complex_problem( {"G1": ConstantData(0), "G2": ConstantData(0), "G3": ConstantData(0)}, @@ -68,7 +152,7 @@ def test_one_problem_per_scenario() -> None: lp_relaxation=False, fast=False, week=week, - scenarios=[scenario], + scenarios=scenarios, ) parameters = pywraplp.MPSolverParameters() @@ -83,17 +167,19 @@ def test_one_problem_per_scenario() -> None: assert status == problem.solver.OPTIMAL expected_cost = [[78933841, 102109698], [17472101, 17424769]] - assert problem.solver.Objective().Value() == pytest.approx( - expected_cost[scenario][week] - ) + # assert problem.solver.Objective().Value() == pytest.approx( + # sum([expected_cost[s][week] for s in scenarios]) / len(scenarios) + # ) -def test_one_problem_per_scenario_with_different_parameters() -> None: - """Resolve the same problem as above with more restrictive parameters. If the problem is solved scenario per scenario the result is better than above.""" +def test_one_problem_for_all_scenarios_with_different_parameters() -> None: + """Resolve the same problem as above with more restrictive parameters. If the problem is solved for all scenarios at the same time, the result is the same than solving the problem one by one. All those tests show that solver parameters and solving scenario at one or one by one are important factors to take into account.""" number_hours = 168 - scenarios = 2 + scenarios = [0, 1] + + solver = pywraplp.Solver.CreateSolver("XPRESS") + if solver: - for scenario in range(scenarios): for week in range(2): problem = create_complex_problem( {"G1": ConstantData(0), "G2": ConstantData(0), "G3": ConstantData(0)}, @@ -101,7 +187,7 @@ def test_one_problem_per_scenario_with_different_parameters() -> None: lp_relaxation=False, fast=False, week=week, - scenarios=[scenario], + scenarios=scenarios, ) parameters = pywraplp.MPSolverParameters() @@ -119,76 +205,10 @@ def test_one_problem_per_scenario_with_different_parameters() -> None: expected_cost = [[78933742, 102103588], [17472101, 17424769]] assert problem.solver.Objective().Value() == pytest.approx( - expected_cost[scenario][week] + sum([expected_cost[s][week] for s in scenarios]) / len(scenarios) ) -def test_one_problem_for_all_scenarios() -> None: - """Resolve the same problem as above with same parameters as Antares. If the problem is solved for all scenarios at the same time, the result is worse than solving the problem one by one.""" - number_hours = 168 - scenarios = [0, 1] - - for week in range(2): - problem = create_complex_problem( - {"G1": ConstantData(0), "G2": ConstantData(0), "G3": ConstantData(0)}, - number_hours, - lp_relaxation=False, - fast=False, - week=week, - scenarios=scenarios, - ) - - parameters = pywraplp.MPSolverParameters() - parameters.SetIntegerParam(parameters.PRESOLVE, parameters.PRESOLVE_OFF) - parameters.SetIntegerParam(parameters.SCALING, 0) - parameters.SetDoubleParam(parameters.PRIMAL_TOLERANCE, 1e-7) - parameters.SetDoubleParam(parameters.DUAL_TOLERANCE, 1e-7) - parameters.SetDoubleParam(parameters.RELATIVE_MIP_GAP, 0.0001) - - status = problem.solver.Solve(parameters) - - assert status == problem.solver.OPTIMAL - - expected_cost = [[78933841, 102109698], [17472101, 17424769]] - # assert problem.solver.Objective().Value() == pytest.approx( - # sum([expected_cost[s][week] for s in scenarios]) / len(scenarios) - # ) - - -def test_one_problem_for_all_scenarios_with_different_parameters() -> None: - """Resolve the same problem as above with more restrictive parameters. If the problem is solved for all scenarios at the same time, the result is the same than solving the problem one by one. All those tests show that solver parameters and solving scenario at one or one by one are important factors to take into account.""" - number_hours = 168 - scenarios = [0, 1] - - for week in range(2): - problem = create_complex_problem( - {"G1": ConstantData(0), "G2": ConstantData(0), "G3": ConstantData(0)}, - number_hours, - lp_relaxation=False, - fast=False, - week=week, - scenarios=scenarios, - ) - - parameters = pywraplp.MPSolverParameters() - parameters.SetIntegerParam(parameters.PRESOLVE, parameters.PRESOLVE_OFF) - parameters.SetIntegerParam(parameters.SCALING, 0) - parameters.SetDoubleParam(parameters.PRIMAL_TOLERANCE, 1e-7) - parameters.SetDoubleParam(parameters.DUAL_TOLERANCE, 1e-7) - parameters.SetDoubleParam(parameters.RELATIVE_MIP_GAP, 0.000001) - - problem.solver.EnableOutput() - - status = problem.solver.Solve(parameters) - - assert status == problem.solver.OPTIMAL - - expected_cost = [[78933742, 102103588], [17472101, 17424769]] - assert problem.solver.Objective().Value() == pytest.approx( - sum([expected_cost[s][week] for s in scenarios]) / len(scenarios) - ) - - CONSTANT = IndexingStructure(False, False) TIME_AND_SCENARIO_FREE = IndexingStructure(True, True) ANTICIPATIVE_TIME_VARYING = IndexingStructure(True, True)