Skip to content

Commit

Permalink
Added the simple mip interface (untested)
Browse files Browse the repository at this point in the history
  • Loading branch information
SanPen committed Jan 9, 2024
1 parent 4c080d8 commit cd22eb1
Show file tree
Hide file tree
Showing 13 changed files with 1,260 additions and 106 deletions.
211 changes: 110 additions & 101 deletions .idea/workspace.xml

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ networkx>=2.1
pandas>=1.1
xlwt>=1.3.0
xlrd>=1.1.0
ortools>=9.8.0
# ortools>=9.8.0
matplotlib>=2.1.1
qtconsole>=4.3.1
pyDOE>=0.3.8
Expand All @@ -28,4 +28,5 @@ windpowerlib
pvlib
hyperopt
requests
pytest >= 7.2.0
pytest >= 7.2.0
highspy >= 1.5.3
1 change: 1 addition & 0 deletions src/GridCalEngine/Core/Compilers/circuit_to_bentayga.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
BENTAYGA_AVAILABLE = True

except ImportError:
btg = None
BENTAYGA_AVAILABLE = False

# numpy integer type for bentayga's uword
Expand Down
1 change: 1 addition & 0 deletions src/GridCalEngine/Core/Compilers/circuit_to_newton_pa.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
f"instead of {NEWTON_PA_VERSION}")

except ImportError as e:
npa = None
NEWTON_PA_AVAILABLE = False
NEWTON_PA_VERSION = ''

Expand Down
3 changes: 2 additions & 1 deletion src/GridCalEngine/Core/Compilers/circuit_to_optimods.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
import json
import pandas as pd
from GridCalEngine.Core.Devices.multi_circuit import MultiCircuit
from GridCalEngine.basic_structures import Logger, SolverType
from GridCalEngine.enumerations import SolverType
from GridCalEngine.basic_structures import Logger
from GridCalEngine.Simulations.PowerFlow.power_flow_options import PowerFlowOptions
from GridCalEngine.Simulations.PowerFlow.power_flow_results import PowerFlowResults
from GridCalEngine.Simulations.PowerFlow.power_flow_ts_results import PowerFlowTimeSeriesResults
Expand Down
1 change: 1 addition & 0 deletions src/GridCalEngine/Core/Compilers/circuit_to_pgm.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@

PGM_AVAILABLE = True
except ImportError:
pgm = None
PGM_AVAILABLE = False

'''
Expand Down
20 changes: 20 additions & 0 deletions src/GridCalEngine/Utils/MIP/SimpleMip/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# GridCal
# Copyright (C) 2015 - 2023 Santiago Peñate Vera
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

from GridCalEngine.Utils.MIP.SimpleMip.lpexp import LpExp
from GridCalEngine.Utils.MIP.SimpleMip.lpvar import LpVar
from GridCalEngine.Utils.MIP.SimpleMip.problem import LpModel, get_available_mip_solvers, set_var_bounds
88 changes: 88 additions & 0 deletions src/GridCalEngine/Utils/MIP/SimpleMip/highs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# GridCal
# Copyright (C) 2015 - 2023 Santiago Peñate Vera
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
from __future__ import annotations
from typing import TYPE_CHECKING

if TYPE_CHECKING: # Only imports the below statements during type checking
from GridCalEngine.Utils.MIP.SimpleMip.problem import Problem

try:
import highspy
HIGHS_AVAILABLE = True
except ImportError:
highspy = None
HIGHS_AVAILABLE = False


def solve_with_highs(mip: Problem, verbose: int = 0):
"""
Solve MIP using Highs via its python interface
:param mip: Problem to be solved (results are inserted in-place)
:param verbose: print info? for values > 0
"""
if highspy is None:
raise Exception("No highspy available, try installing with: pip install highspy")

# declare the solver
h = highspy.highs.Highs()

# declare the LP problem
lp = highspy.highs.HighsLp()

# set the sense
lp.sense_ = highspy.highs.ObjSense.kMinimize if mip.is_minimize() else highspy.highs.ObjSense.kMaximize

# set the var information
lp.col_lower_, lp.col_cost_, lp.col_upper_, is_int_list = mip.get_var_data()
lp.offset_ = mip.objective.offset

for i in is_int_list:
lp.integrality_.append(i)

# set the constraints information
lp.row_lower_, A, lp.row_upper_ = mip.get_coefficients_data()
lp.num_col_ = A.shape[1]
lp.num_row_ = A.shape[0]

lp.a_matrix_.start_ = A.indptr
lp.a_matrix_.index_ = A.indices
lp.a_matrix_.value_ = A.data

# send the model to the solver
h.passModel(lp)

# solve
h.run()

# gather results
solution = h.getSolution()
basis = h.getBasis()
info = h.getInfo()
model_status = h.getModelStatus()

if verbose > 0:
print("Model status = ", h.modelStatusToString(model_status))
print("Optimal objective = ", info.objective_function_value)
print("Iteration count = ", info.simplex_iteration_count)
print("Primal solution status = ", h.solutionStatusToString(info.primal_solution_status) )

mip.set_solution(col_values=solution.col_value,
col_duals=solution.col_dual,
row_values=solution.row_value,
row_duals=solution.row_dual,
fixed_values=info.objective_function_value,
optimal_values=model_status == highspy.highs.HighsModelStatus.kOptimal)
101 changes: 101 additions & 0 deletions src/GridCalEngine/Utils/MIP/SimpleMip/lpcst.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# GridCal
# Copyright (C) 2015 - 2023 Santiago Peñate Vera
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
from __future__ import annotations
from typing import Tuple, TYPE_CHECKING

if TYPE_CHECKING: # Only imports the below statements during type checking
from GridCalEngine.Utils.MIP.SimpleMip.lpexp import LpExp
from GridCalEngine.Utils.MIP.SimpleMip.lpvar import LpVar


class LpCst:
"""
Constraint
"""
def __init__(self, linear_expression: LpExp, sense: str, coefficient: float,
name="", internal_index: int = 0):
"""
constraint (<=, ==, >=) rhs
:param linear_expression:
:param sense: <=, ==, >=
:param coefficient:
:param name:
:param internal_index:
"""
assert sense in ["<=", "==", ">="]

self.name = name
self.linear_expression = linear_expression
self.sense = sense
self.coefficient = coefficient # Right-hand side value
self._index: int = internal_index # internal index to the solver

def copy(self) -> "LpCst":
"""
Make a deep copy of this constraint
:return: Constraint
"""
return LpCst(linear_expression=self.linear_expression.copy(),
sense=self.sense,
coefficient=self.coefficient,
name=self.name,
internal_index=self._index)

def get_bounds(self) -> Tuple[float, float]:
"""
Get the constraint bounds
:return: lhs <= constraint <= rhs
"""
MIP_INF = 1e20
if self.sense == '==':
return self.coefficient, self.coefficient
elif self.sense == '<=':
return -MIP_INF, self.coefficient
elif self.sense == '>=':
return self.coefficient, MIP_INF
else:
raise Exception(f"Invalid sense: {self.sense}")

def set_index(self, index: int) -> None:
"""
Set the internal indexing
:param index: constraint index in the solver
"""
self._index = index

def get_index(self) -> int:
"""
Get internal index
:return: int
"""
return self._index

def add_term(self, var: LpVar, coeff: float):
"""
Add a term to the constraint
:param var: Variable
:param coeff: coefficient
"""
self.linear_expression += coeff * var

def add_var(self, var: LpVar):
"""
Add a term to the constraint
:param var: Variable
"""
self.linear_expression += var
Loading

0 comments on commit cd22eb1

Please sign in to comment.