Skip to content

Commit

Permalink
major refactor for stability and OOP practice
Browse files Browse the repository at this point in the history
  • Loading branch information
Dan committed Feb 1, 2021
1 parent 7db74ee commit f075f17
Show file tree
Hide file tree
Showing 17 changed files with 1,118 additions and 1,168 deletions.
94 changes: 34 additions & 60 deletions OR/BigM.py
Original file line number Diff line number Diff line change
@@ -1,73 +1,47 @@
from pandas import DataFrame
import numpy as np
from .Format import *

from .TableauBase import TableauBase
from .LP import LP
from .ObjectiveFunction import ObjectiveFunction
from .NonNeg import NonNeg

def getRep(num,M_val):
# solve for integer M values, the rest... soz
round5 = lambda x: round(x,5)
if abs(num%M_val) < abs(num%-M_val):
#kM+x
mNo = int((num - num%M_val)/M_val)
defNo = num%M_val
else:
#KM-x
mNo = int((num - num%-M_val)/M_val)
defNo = num%-M_val

mNo = round5(mNo)
defNo = round5(defNo)
from typing import TypeVar
BigM = TypeVar("BigM")

if mNo == 1:
mString = " M "
elif mNo == -1:
mString = "-M "
elif mNo == 0:
mString = ""
else:
mString = f"{mNo}M "

if defNo == 0:
defString = ""
elif defNo > 0:
defString = f"+{defNo}"
else:
defString = f"{defNo}"
if mString + defString == "":
return 0
else:
return mString + defString

class BigM(TableauBase):
@classmethod
def fromLP(cls,inLP,M_Val = 1e6):
constraintSigns = inLP.posRHS().body.loc[:,"signs"]
baseT = super().fromLP(inLP)
body = baseT.dataFrame
basicVar = list(baseT.basicVar)
aVals = []
for i in range(1,len(constraintSigns)):
if constraintSigns[i] in (">","="):
newCol = [0] * body.shape[0]
newCol[0] = M_Val
newCol[i] = 1
body.insert(body.shape[1]-1,f"a{subscript(i)}",newCol)
basicVar[i] = f"a{subscript(i)}"

return BigM(body,tuple(basicVar),aVals,M_Val=M_Val)

def fromTableauBase(self,base):
return BigM(base.dataFrame,base.basicVar,self.aVals)
def fromBase(cls,inLP:LP) -> BigM:
return cls(inLP.dataFrame,inLP.basicVar)

@classmethod
def fromLP(cls,inLP:LP,M_Val: float = 1e6) -> BigM:
inLP = inLP.standardForm()
for i,c in enumerate(inLP.constraints):
if not isinstance(c,NonNeg) and c.sign in ["=",">"]:
inLP.objFunc[f"A{i+1}"] = M_Val
c[f"A{i+1}"] = 1
baseLP = super().fromLP(inLP)
return cls.fromBase(baseLP)

def __init__(self,dataFrame,basicVar,aVals,M_Val=1e6):
super().__init__(dataFrame,basicVar,dispFunc = lambda x: getRep(x,M_Val))
self.aVals = aVals # list of a factors
# self.dispFunc =
def __init__(self,dataFrame:DataFrame,basicVar:DataFrame,M_Val: float=1e6):
self.dataFrame = dataFrame
self.basicVar = basicVar
self.M_Val = M_Val

def canonical(self,echo=False):
def canonical(self,echo=False) -> BigM:
t = super().canonical(echo=echo)
return self.fromTableauBase(t)
return self.fromBase(t)

def isInfeasible(self):
if not self.isOptimal():
return False
elif "A" in "".join(self.getBFS().keys()):
return False
else:
return True

def solve(self,echo=False):
t = super().solve(echo=echo)
return self.fromTableauBase(t)
t = super().canonical(echo=echo).solve(echo=echo)
return self.fromBase(t)
53 changes: 9 additions & 44 deletions OR/CommonLP.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,11 @@
from .ObjectiveFunction import ObjectiveFunction
from .Constraint import Constraint
from .NonNeg import NonNeg
from .LP import LP

class CommonLP():
def __init__(self):
pass

@classmethod
def bevco(cls):
return LP.new(
["min", 2, 3],
[[0.5, 0.25, "<", 4],
[1, 3, ">", 20],
[1, 1, "=", 10], ],
[">", ">"],
factorNames=False)

@classmethod
def bevcoInf(cls):
return LP.new(
["min", 2, 3],
[[0.5, 0.25, "<", 4],
[1, 3, ">", 36],
[1, 1, "=", 10], ],
[">", ">"],
factorNames=False)

@classmethod
def dakota(cls):
return LP.new(
["max", 60, 30, 20],
[[8, 6, 1, "<", 48],
[4, 2, 1.5, "<", 20],
[2, 1.5, 0.5, "<", 8],
[0, 1, 0, "<", 5]],
[">", ">", ">"],
factorNames=False)

@classmethod
def dakotaDual(cls):
return LP.new(
["min", 48, 20, 8, 5],
[[8, 4, 2, 0, ">", 60],
[6, 2, 1.5, 1, ">", 30],
[1, 1.5, 0.5, 0, ">", 20], ],
[">", ">", ">", ">"],
factorNames=["Y₁", "Y₂", "Y₃", "Y₄"])
bevCo = LP(
ObjectiveFunction.new("min", 2, 3),
Constraint.new(0.5, 0.25, "<", 4),
Constraint.new(1, 3, ">", 20),
Constraint.new(1, 1, "=", 10),
*NonNeg.fromArray(">",">",))
119 changes: 119 additions & 0 deletions OR/Constraint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
from pandas import DataFrame

from typing import List,Tuple,TypeVar
Constraint = TypeVar("Constraint")


class Constraint():
invertSign = {">": "<", "=": "=", "<": ">"}

@classmethod
def new(cls, *args, label:str="X", labels=None):
'''
construct a new Constraint from array of values
'''
#label tests
if labels == None:
# labels overrides label
if label.upper() in ["A", "S"]:
raise ValueError(f"A and S are reserved variables")
labels = [f"{label.upper()}{i+1}" for i in range(len(args)-2)]
elif len(labels) != len(args)-2:
raise ValueError(f"provided {len(labels)} labels for {len(args)-2} variables")
else:
label = labels[0][0]
#sign tests
if args[-2] not in ["<", ">", "="]:
raise ValueError(f"{args[-2]} is not a valid constraint sign")

LHS = {k: v for k, v in zip(labels, args[:-2])}
sign = args[-2]
RHS = args[-1]
return cls(label, LHS, sign, RHS)

def __init__(self, label, LHS, sign, RHS):
self.label = label.upper() # keep track of what variable we are using
self.LHS = LHS
self.sign = sign
self.RHS = RHS
# self.iterCount for iterable

def sign(self) -> str:
return self.sign

def RHS(self) -> float:
'''
all numbera are subtypes of float i think><
'''
return self.RHS

def __getitem__(self, key:str) -> float:
if self.LHS.get(key):
return self.LHS.get(key)
else:
return 0

def __setitem__(self, key:str, value: float):
self.LHS[key] = value

def clear(self) -> Constraint:
return Constraint(self.label,dict(),self.sign,0)

def add_S(self, sIndex: int) -> Constraint:
newLHS = self.LHS.copy()
if self.sign == ">":
newLHS[f"S{sIndex}"] = -1
elif self.sign == "<":
newLHS[f"S{sIndex}"] = 1
return Constraint(self.label,newLHS,'=',self.RHS)

def invert(self) -> Constraint:
return Constraint(
self.label,
{k:-v for k,v in self.LHS.items()},
self.invertSign[self.sign],
-self.RHS)

def invertVar(self, variableName:str) -> Constraint:
newLHS = self.LHS.copy()
if newLHS.get(variableName):
newLHS[variableName] = -newLHS[variableName]
return Constraint(
self.label,
newLHS,
self.sign,
self.RHS)

def addVar(self,newVars: List[str]) -> Constraint:
newLHS = self.LHS.copy()
for i in newVars:
newLHS[i] = 0
return Constraint(
self.label,
newLHS,
self.sign,
self.RHS)

def toDF(self,index=0)->DataFrame:
return DataFrame(self.LHS,index=[index]).join(DataFrame({"sign":self.sign,"RHS":self.RHS},index=[index]))

def get_variables(self) -> List[str]:
return list(self.LHS.keys())

def __len__(self) -> int:
return len(self.LHS)

def __repr__(self):
outString = ""
factors = sorted([k for k in self.LHS.keys() if self.label in k])
S_factors = sorted([k for k in self.LHS.keys() if "S" in k])
A_factors = sorted([k for k in self.LHS.keys() if "A" in k])

for k in factors+S_factors+A_factors:
v = self.LHS[k]
if v > 0:
outString += f" +{v}{k}"
elif v < 0:
outString += f" {v}{k}"
outString += f" {self.sign} {self.RHS}"
return outString[1:]
45 changes: 29 additions & 16 deletions OR/Format.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
try:
from ipython import display
except:
pass
def subscript(i):
# ima let you toggle between subscript and full height numbers
# return i
out = ""
for j in str(i):
out +=chr(ord(u'\u2080')+ int(j))
return out
# LP Object
# duality finding method
def displayHelper(item):
#display works on jupyter but might not always be included?
def display2(item):
try:
display(item)
except:
print(item)
print()

class InfeasibleException(Exception):
pass

class OptimalException(Exception):
pass


# try:
# from ipython import display
# except:
# pass
# def subscript(i):
# # ima let you toggle between subscript and full height numbers
# return i
# out = ""
# for j in str(i):
# out +=chr(ord(u'\u2080')+ int(j))
# return out
# # LP Object
# # duality finding method
# def displayHelper(item):
# #display works on jupyter but might not always be included?
# try:
# display(item)
# except:
# print(item)
# print()
Loading

0 comments on commit f075f17

Please sign in to comment.