Skip to content

Commit

Permalink
See #ANT-958 Move ModelSelectionStrategy to simulation directory
Browse files Browse the repository at this point in the history
  • Loading branch information
ianmnz committed Feb 26, 2024
1 parent 2a26daa commit 07fca4f
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 109 deletions.
10 changes: 1 addition & 9 deletions src/andromede/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,7 @@

from .common import ProblemContext, ValueType
from .constraint import Constraint
from .model import (
InvestmentProblemStrategy,
MergedProblemStrategy,
Model,
ModelPort,
ModelSelectionStrategy,
OperationalProblemStrategy,
model,
)
from .model import Model, ModelPort, model
from .parameter import Parameter, float_parameter, int_parameter
from .port import PortField, PortType
from .variable import Variable, float_variable, int_variable
79 changes: 1 addition & 78 deletions src/andromede/model/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@
defining parameters, variables, and equations.
"""
import itertools
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Dict, Generator, Iterable, Optional
from typing import Dict, Iterable, Optional

from andromede.expression import (
AdditionNode,
Expand Down Expand Up @@ -47,7 +46,6 @@
from andromede.expression.indexing import IndexingStructureProvider, compute_indexation
from andromede.expression.indexing_structure import IndexingStructure
from andromede.expression.visitor import T, visit
from andromede.model.common import ProblemContext
from andromede.model.constraint import Constraint
from andromede.model.parameter import Parameter
from andromede.model.port import PortType
Expand Down Expand Up @@ -186,81 +184,6 @@ def get_all_constraints(self) -> Iterable[Constraint]:
)


class ModelSelectionStrategy(ABC):
"""
Abstract class to specify the strategy of the created problem.
Its derived classes select variables and constraints for the optimization problems:
- InvestmentProblemStrategy: Keep investment and coupling variables and constraints only for a BendersDecomposed master
- OperationalProblemStrategy: Keep operational and coupling variables and constraints only for a BendersDecomposed sub-problems
- MergedProblemStrategy: Keep all variables and constraints
"""

@classmethod
def get_variables(cls, model: Model) -> Generator[Variable, None, None]:
for variable in model.variables.values():
if cls._keep_from_context(variable.context):
yield variable

@classmethod
def get_constraints(cls, model: Model) -> Generator[Constraint, None, None]:
for constraint in model.get_all_constraints():
if cls._keep_from_context(constraint.context):
yield constraint

@classmethod
@abstractmethod
def _keep_from_context(cls, context: ProblemContext) -> bool:
...

@classmethod
@abstractmethod
def get_objectives(
cls, model: Model
) -> Generator[Optional[ExpressionNode], None, None]:
...


class MergedProblemStrategy(ModelSelectionStrategy):
@classmethod
def _keep_from_context(cls, context: ProblemContext) -> bool:
return True

@classmethod
def get_objectives(
cls, model: Model
) -> Generator[Optional[ExpressionNode], None, None]:
yield model.objective_operational_contribution
yield model.objective_investment_contribution


class InvestmentProblemStrategy(ModelSelectionStrategy):
@classmethod
def _keep_from_context(cls, context: ProblemContext) -> bool:
return (
context == ProblemContext.INVESTMENT or context == ProblemContext.COUPLING
)

@classmethod
def get_objectives(
cls, model: Model
) -> Generator[Optional[ExpressionNode], None, None]:
yield model.objective_investment_contribution


class OperationalProblemStrategy(ModelSelectionStrategy):
@classmethod
def _keep_from_context(cls, context: ProblemContext) -> bool:
return (
context == ProblemContext.OPERATIONAL or context == ProblemContext.COUPLING
)

@classmethod
def get_objectives(
cls, model: Model
) -> Generator[Optional[ExpressionNode], None, None]:
yield model.objective_operational_contribution


def model(
id: str,
constraints: Optional[Iterable[Constraint]] = None,
Expand Down
1 change: 1 addition & 0 deletions src/andromede/simulation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@
)
from .optimization import BlockBorderManagement, OptimizationProblem, build_problem
from .output_values import OutputValues
from .strategy import MergedProblemStrategy, ModelSelectionStrategy
from .time_block import TimeBlock
17 changes: 10 additions & 7 deletions src/andromede/simulation/benders_decomposed.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,15 @@
import sys
from typing import Any, Dict, List

from andromede.model.model import InvestmentProblemStrategy, OperationalProblemStrategy
from andromede.simulation.optimization import (
BlockBorderManagement,
OptimizationProblem,
build_problem,
)
from andromede.simulation.strategy import (
InvestmentProblemStrategy,
OperationalProblemStrategy,
)
from andromede.simulation.time_block import TimeBlock
from andromede.study.data import DataBase
from andromede.study.network import Network
Expand All @@ -36,7 +39,7 @@

class BendersDecomposedProblem:
"""
A simpler interface for the Xpansion problem
A simpler interface for the Benders Decomposed problem
"""

master: OptimizationProblem
Expand Down Expand Up @@ -186,10 +189,10 @@ def build_benders_decomposed_problem(
"""
Entry point to build the xpansion problem for a time period
Returns a Xpansion problem
Returns a Benders Decomposed problem
"""

# Xpansion Master Problem
# Benders Decomposed Master Problem
master = build_problem(
network,
database,
Expand All @@ -198,10 +201,10 @@ def build_benders_decomposed_problem(
problem_name="master",
border_management=border_management,
solver_id=solver_id,
problem_strategy=InvestmentProblemStrategy,
problem_strategy=InvestmentProblemStrategy(),
)

# Xpansion Sub-problems
# Benders Decomposed Sub-problems
subproblem = build_problem(
network,
database,
Expand All @@ -210,7 +213,7 @@ def build_benders_decomposed_problem(
problem_name="subproblem",
border_management=border_management,
solver_id=solver_id,
problem_strategy=OperationalProblemStrategy,
problem_strategy=OperationalProblemStrategy(),
)

return BendersDecomposedProblem(master, [subproblem])
14 changes: 5 additions & 9 deletions src/andromede/simulation/optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,11 @@
from andromede.expression.port_resolver import PortFieldKey, resolve_port
from andromede.expression.scenario_operator import Expectation
from andromede.expression.time_operator import TimeEvaluation, TimeShift, TimeSum
from andromede.model.common import ProblemContext
from andromede.model.constraint import Constraint
from andromede.model.model import (
MergedProblemStrategy,
ModelSelectionStrategy,
PortFieldId,
)
from andromede.model.model import PortFieldId
from andromede.simulation.linear_expression import LinearExpression, Term
from andromede.simulation.linearize import linearize_expression
from andromede.simulation.strategy import MergedProblemStrategy, ModelSelectionStrategy
from andromede.simulation.time_block import TimeBlock
from andromede.study.data import DataBase
from andromede.study.network import Component, Network
Expand Down Expand Up @@ -671,14 +667,14 @@ class OptimizationProblem:
name: str
solver: lp.Solver
context: OptimizationContext
strategy: Type[ModelSelectionStrategy]
strategy: ModelSelectionStrategy

def __init__(
self,
name: str,
solver: lp.Solver,
opt_context: OptimizationContext,
build_strategy: Type[ModelSelectionStrategy] = MergedProblemStrategy,
build_strategy: ModelSelectionStrategy = MergedProblemStrategy(),
) -> None:
self.name = name
self.solver = solver
Expand Down Expand Up @@ -819,7 +815,7 @@ def build_problem(
problem_name: str = "optimization_problem",
border_management: BlockBorderManagement = BlockBorderManagement.CYCLE,
solver_id: str = "GLOP",
problem_strategy: Type[ModelSelectionStrategy] = MergedProblemStrategy,
problem_strategy: ModelSelectionStrategy = MergedProblemStrategy(),
) -> OptimizationProblem:
"""
Entry point to build the optimization problem for a time period.
Expand Down
82 changes: 82 additions & 0 deletions src/andromede/simulation/strategy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Copyright (c) 2024, RTE (https://www.rte-france.com)
#
# See AUTHORS.txt
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# SPDX-License-Identifier: MPL-2.0
#
# This file is part of the Antares project.

from abc import ABC, abstractmethod
from typing import Generator, Optional

from andromede.expression import ExpressionNode
from andromede.model import Constraint, Model, ProblemContext, Variable


class ModelSelectionStrategy(ABC):
"""
Abstract class to specify the strategy of the created problem.
Its derived classes select variables and constraints for the optimization problems:
- InvestmentProblemStrategy: Keep investment and coupling variables and constraints only for a BendersDecomposed master
- OperationalProblemStrategy: Keep operational and coupling variables and constraints only for a BendersDecomposed sub-problems
- MergedProblemStrategy: Keep all variables and constraints
"""

def get_variables(self, model: Model) -> Generator[Variable, None, None]:
for variable in model.variables.values():
if self._keep_from_context(variable.context):
yield variable

def get_constraints(self, model: Model) -> Generator[Constraint, None, None]:
for constraint in model.get_all_constraints():
if self._keep_from_context(constraint.context):
yield constraint

@abstractmethod
def _keep_from_context(self, context: ProblemContext) -> bool:
...

@abstractmethod
def get_objectives(
self, model: Model
) -> Generator[Optional[ExpressionNode], None, None]:
...


class MergedProblemStrategy(ModelSelectionStrategy):
def _keep_from_context(self, context: ProblemContext) -> bool:
return True

def get_objectives(
self, model: Model
) -> Generator[Optional[ExpressionNode], None, None]:
yield model.objective_operational_contribution
yield model.objective_investment_contribution


class InvestmentProblemStrategy(ModelSelectionStrategy):
def _keep_from_context(self, context: ProblemContext) -> bool:
return (
context == ProblemContext.INVESTMENT or context == ProblemContext.COUPLING
)

def get_objectives(
self, model: Model
) -> Generator[Optional[ExpressionNode], None, None]:
yield model.objective_investment_contribution


class OperationalProblemStrategy(ModelSelectionStrategy):
def _keep_from_context(self, context: ProblemContext) -> bool:
return (
context == ProblemContext.OPERATIONAL or context == ProblemContext.COUPLING
)

def get_objectives(
self, model: Model
) -> Generator[Optional[ExpressionNode], None, None]:
yield model.objective_operational_contribution
9 changes: 3 additions & 6 deletions tests/andromede/test_xpansion.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,9 @@
int_variable,
model,
)
from andromede.model.model import (
MergedProblemStrategy,
PortFieldDefinition,
PortFieldId,
)
from andromede.model.model import PortFieldDefinition, PortFieldId
from andromede.simulation import (
MergedProblemStrategy,
OutputValues,
TimeBlock,
build_benders_decomposed_problem,
Expand Down Expand Up @@ -224,7 +221,7 @@ def test_generation_xpansion_single_time_step_single_scenario(
database,
TimeBlock(1, [0]),
scenarios,
problem_strategy=MergedProblemStrategy,
problem_strategy=MergedProblemStrategy(),
)
status = problem.solver.Solve()

Expand Down

0 comments on commit 07fca4f

Please sign in to comment.