-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/ant 1267 #22
Feature/ant 1267 #22
Changes from all commits
c50f174
c077dfc
7ef19d0
9f2c568
148ef92
2927fb7
5d07993
823d851
e500cdb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,7 @@ | |
""" | ||
import math | ||
from dataclasses import dataclass, field | ||
from typing import Dict, List, Mapping, Optional, Tuple, TypeVar, Union, cast | ||
from typing import Any, Dict, List, Mapping, Optional, Tuple, TypeVar, Union, cast | ||
|
||
from andromede.simulation.optimization import OptimizationProblem | ||
from andromede.study.data import TimeScenarioIndex | ||
|
@@ -247,3 +247,107 @@ def _are_mappings_close( | |
) | ||
else: | ||
return True | ||
|
||
|
||
@dataclass(frozen=True) | ||
class BendersSolution: | ||
data: Dict[str, Any] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that we could use Can be a future improvement, it's an implementation detail. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't know that, I will have a look later :) |
||
|
||
def __eq__(self, other: object) -> bool: | ||
if not isinstance(other, BendersSolution): | ||
return NotImplemented | ||
return ( | ||
self.overall_cost == other.overall_cost | ||
and self.candidates == other.candidates | ||
) | ||
|
||
def is_close( | ||
self, | ||
other: "BendersSolution", | ||
*, | ||
rel_tol: float = 1.0e-9, | ||
abs_tol: float = 0.0, | ||
) -> bool: | ||
return ( | ||
math.isclose( | ||
self.overall_cost, other.overall_cost, abs_tol=abs_tol, rel_tol=rel_tol | ||
) | ||
and self.candidates.keys() == other.candidates.keys() | ||
and all( | ||
math.isclose( | ||
self.candidates[key], | ||
other.candidates[key], | ||
rel_tol=rel_tol, | ||
abs_tol=abs_tol, | ||
) | ||
for key in self.candidates | ||
) | ||
) | ||
|
||
def __str__(self) -> str: | ||
lpad = 30 | ||
rpad = 12 | ||
|
||
string = "Benders' solution:\n" | ||
string += f"{'Overall cost':<{lpad}} : {self.overall_cost:>{rpad}}\n" | ||
string += f"{'Investment cost':<{lpad}} : {self.investment_cost:>{rpad}}\n" | ||
string += f"{'Operational cost':<{lpad}} : {self.operational_cost:>{rpad}}\n" | ||
string += "-" * (lpad + rpad + 3) + "\n" | ||
for candidate, investment in self.candidates.items(): | ||
string += f"{candidate:<{lpad}} : {investment:>{rpad}}\n" | ||
|
||
return string | ||
|
||
@property | ||
def investment_cost(self) -> float: | ||
return self.data["solution"]["investment_cost"] | ||
|
||
@property | ||
def operational_cost(self) -> float: | ||
return self.data["solution"]["operational_cost"] | ||
|
||
@property | ||
def overall_cost(self) -> float: | ||
return self.data["solution"]["overall_cost"] | ||
|
||
@property | ||
def candidates(self) -> Dict[str, float]: | ||
return self.data["solution"]["values"] | ||
|
||
@property | ||
def status(self) -> str: | ||
return self.data["solution"]["problem_status"] | ||
|
||
@property | ||
def absolute_gap(self) -> float: | ||
return self.data["solution"]["optimality_gap"] | ||
|
||
@property | ||
def relative_gap(self) -> float: | ||
return self.data["solution"]["relative_gap"] | ||
|
||
@property | ||
def stopping_criterion(self) -> str: | ||
return self.data["solution"]["stopping_criterion"] | ||
|
||
|
||
@dataclass(frozen=True, eq=False) | ||
class BendersMergedSolution(BendersSolution): | ||
@property | ||
def lower_bound(self) -> float: | ||
return self.data["solution"]["lb"] | ||
|
||
@property | ||
def upper_bound(self) -> float: | ||
return self.data["solution"]["ub"] | ||
|
||
|
||
@dataclass(frozen=True, eq=False) | ||
class BendersDecomposedSolution(BendersSolution): | ||
@property | ||
def nb_iterations(self) -> int: | ||
return self.data["solution"]["iteration"] | ||
|
||
@property | ||
def duration(self) -> float: | ||
return self.data["run_duration"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Raise an error message ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. For now I would leave like this and would raise an error in the PR with the CI integration