-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
major refactor for stability and OOP practice
- Loading branch information
Dan
committed
Feb 1, 2021
1 parent
7db74ee
commit f075f17
Showing
17 changed files
with
1,118 additions
and
1,168 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(">",">",)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
Oops, something went wrong.