Created
December 21, 2020 07:23
-
-
Save 270ajay/e241d574f92dc4f5b32dbe773915f393 to your computer and use it in GitHub Desktop.
Advanced constraint programming modeling
This file contains hidden or 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
from ortools.sat.python import cp_model | |
''' course: https://www.coursera.org/learn/advanced-modeling#about ''' | |
def theCavalryWedgeProblem(): | |
model = cp_model.CpModel() | |
horseList = ["H1", "H2", "H3", "H4", "H5", "H6", "H7", "H8", "H9", "H10"] | |
riderList = ["R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11"] | |
speedList = [10, 9, 8, 7, 6, 5, 7, 4, 3, 2] | |
enduranceList = [8, 4, 3, 2, 6, 4, 2, 6, 7, 5, 3] | |
strengthList = [5, 2, 8, 9, 4, 2, 1, 3, 4, 5, 9] | |
compatibleList = [[0,1,1,0,0,0,0,0,0,0,1], [0,0,0,0,1,1,0,0,0,0,0], [0,0,0,0,0,0,0,1,0,0,0], | |
[1,0,0,0,1,0,0,0,0,0,0], [0,0,0,1,0,0,0,0,0,0,0], [0,1,0,0,0,0,1,0,0,0,0], | |
[1,0,1,0,0,0,0,0,0,0,0], [1,0,0,0,0,0,0,0,1,1,0], [0,0,1,0,0,0,0,0,0,0,1], | |
[0,0,0,0,0,0,1,0,1,1,0]] | |
sizeOfWedge = 5 # should be odd | |
assert sizeOfWedge%2 == 1, "sizeOfWedge (currentValue:" +str(sizeOfWedge)+" must be odd" | |
numOfHorses = len(horseList) | |
numOfRiders = len(riderList) | |
maxSpeed = max(speedList) | |
maxEndurance = max(enduranceList) | |
maxStrength = max(strengthList) | |
compatibleListFlattened = sum(compatibleList, []) | |
positionHorseVarList = [] | |
positionRiderVarList = [] | |
for position in range(sizeOfWedge): | |
positionHorseVarList.append(model.NewIntVar(0, numOfHorses-1, "HorseForPosition"+str(position))) | |
positionRiderVarList.append(model.NewIntVar(0, numOfRiders-1, "RiderForPosition"+str(position))) | |
model.AddAllDifferent(positionHorseVarList) | |
model.AddAllDifferent(positionRiderVarList) | |
speedVarList = [] | |
enduranceVarList = [] | |
strengthVarList = [] | |
for position in range(sizeOfWedge): | |
speedVarList.append(model.NewIntVar(0, maxSpeed, "Speed"+str(position))) | |
enduranceVarList.append(model.NewIntVar(0, maxEndurance, "Endurance"+str(position))) | |
strengthVarList.append(model.NewIntVar(0, maxStrength, "Strength"+str(position))) | |
model.AddElement(positionHorseVarList[position], speedList, speedVarList[position]) | |
model.AddElement(positionRiderVarList[position], enduranceList, enduranceVarList[position]) | |
model.AddElement(positionRiderVarList[position], strengthList, strengthVarList[position]) | |
for position in range(sizeOfWedge//2): | |
print("speed[" + str(position) + "] < speed[" + str(position + 1) + "]") | |
model.Add(speedVarList[position] < speedVarList[position+1]) | |
print("endur[" + str(position) + "] < endur[" + str(position + 1) + "]") | |
model.Add(enduranceVarList[position] < enduranceVarList[position+1]) | |
for position in range(sizeOfWedge//2, sizeOfWedge-1): | |
print("speed[" + str(position) + "] > speed[" + str(position + 1) + "]") | |
model.Add(speedVarList[position] > speedVarList[position+1]) | |
print("endur[" + str(position) + "] > endur[" + str(position + 1) + "]") | |
model.Add(enduranceVarList[position] > enduranceVarList[position+1]) | |
compatibilityVarList = [] | |
for position in range(sizeOfWedge): | |
indexVar = model.NewIntVar(0, numOfRiders*numOfHorses, "indexVar"+str(position)) | |
compatibilityVarList.append(model.NewBoolVar("compatibility" + str(position))) | |
model.Add(indexVar == (numOfRiders * positionHorseVarList[position]) + positionRiderVarList[position]) | |
model.AddElement(indexVar, compatibleListFlattened, compatibilityVarList[position]) | |
model.Add(compatibilityVarList[position] == 1) | |
model.Maximize(sum(strengthVarList)) | |
solver = cp_model.CpSolver() | |
status = solver.Solve(model) | |
if status == cp_model.INFEASIBLE: | |
print("Infeasible model") | |
if status == cp_model.OPTIMAL: | |
print('Maximum strength: %i' % solver.ObjectiveValue(), "\n") | |
for position in range(sizeOfWedge): | |
horseIndex = solver.Value(positionHorseVarList[position]) | |
riderIndex = solver.Value(positionRiderVarList[position]) | |
print("Position: "+str(position)+ " - Horse: "+str(horseList[horseIndex])+" - Rider: "+str(riderList[riderIndex])+" - Compatibility: "+str(solver.Value(compatibilityVarList[position]))) | |
print("----------------------------") | |
#----------------------------------------------------------------------------------------------------------------------# | |
def pickTheEscapePathProblem(): | |
model = cp_model.CpModel() | |
pathTimeList = [2, 4, 4, 4, 14, 10, 8, 14, 12, 10] | |
escapeGuardTimeList = [10, 8, 2, 4, 0, 0, 0, 0, 0, 0] | |
NUM_OF_WEATHERS = 3 # 0 = storm, 1 = rain, 2 = sun | |
numOfPaths = len(pathTimeList) | |
maxTimeForPath = max(pathTimeList) | |
maxTimeForEscapingGuard = max(escapeGuardTimeList) | |
weatherVar = model.NewIntVar(1, NUM_OF_WEATHERS-1, "Weather") # can't take storm because no solution | |
pathVar = model.NewIntVar(0, numOfPaths-1, "Path") | |
timePathOnWeatherVar = model.NewIntVar(0, maxTimeForPath, "TimeOnPathOnWeather") | |
timeEscapingGuardOnWeatherVar = model.NewIntVar(0, maxTimeForEscapingGuard * NUM_OF_WEATHERS-1, "TimeEscapingGuardOnWeather") | |
guardTimeVar = model.NewIntVar(0, maxTimeForEscapingGuard, "GuardTime") | |
model.AddElement(pathVar, escapeGuardTimeList, guardTimeVar) | |
model.AddMultiplicationEquality(timeEscapingGuardOnWeatherVar, [weatherVar, guardTimeVar]) | |
pathTimeVar = model.NewIntVar(0, maxTimeForPath, "PathTime") | |
model.AddElement(pathVar, pathTimeList, pathTimeVar) | |
model.AddDivisionEquality(timePathOnWeatherVar, pathTimeVar, weatherVar) | |
ObjVar = model.NewIntVar(0, maxTimeForPath + (maxTimeForEscapingGuard * NUM_OF_WEATHERS-1), "ObjVar") | |
model.Add(ObjVar == timeEscapingGuardOnWeatherVar + timePathOnWeatherVar) | |
model.Minimize(ObjVar) | |
solver = cp_model.CpSolver() | |
status = solver.Solve(model) | |
if status == cp_model.INFEASIBLE: | |
print("Infeasible model") | |
if status == cp_model.OPTIMAL: | |
print("Path: "+str(solver.Value(pathVar))+ ", MinimumTime: "+str(solver.Value(ObjVar))) | |
print("----------------------------") | |
#----------------------------------------------------------------------------------------------------------------------# | |
def twoGeneralsProblem(): | |
model = cp_model.CpModel() | |
heroList = ["hero1", "hero2", "hero3"] | |
spotList = ["Baihui", "Danzhong", "Quchi", "Tianshu", "Yongquan"] | |
damageList = [[9, 7, 6, 5, 8], [8, 2, 5, 1, 4], [4, 3, 7, 2, 5]] | |
damageListFlattened = sum(damageList, []) | |
maxDamage = max(damageListFlattened) | |
numOfSpots = len(spotList) | |
numOfHeroes = len(heroList) | |
lowerPositionsAfterIndex = 2 | |
position1VarList = [] | |
position2VarList = [] | |
for hero in heroList: | |
position1VarList.append(model.NewIntVar(1, numOfSpots-1, "position1"+str(hero))) # position1 cant be equal to Baihui | |
position2VarList.append(model.NewIntVar(0, numOfSpots - 1, "position2"+str(hero))) | |
model.AddAllDifferent(position1VarList) | |
model.AddAllDifferent(position2VarList) | |
for i in range(numOfHeroes): | |
strikedLowerPosFirstVar = model.NewBoolVar("StrikedLowerPositionFirst") | |
model.Add(position1VarList[i] > lowerPositionsAfterIndex).OnlyEnforceIf(strikedLowerPosFirstVar) | |
model.Add(position1VarList[i] <= lowerPositionsAfterIndex).OnlyEnforceIf(strikedLowerPosFirstVar.Not()) | |
model.Add(position2VarList[i] <= lowerPositionsAfterIndex).OnlyEnforceIf(strikedLowerPosFirstVar) | |
damage1VarList = [] | |
index1VarList = [] | |
damage2VarList = [] | |
index2VarList = [] | |
for i, hero in enumerate(heroList): | |
damage1VarList.append(model.NewIntVar(0, maxDamage, "Damage1"+str(hero))) | |
index1VarList.append(model.NewIntVar(0, numOfSpots*numOfHeroes, "Index1"+str(hero))) | |
model.Add(index1VarList[i] == (numOfSpots * i) + position1VarList[i]) | |
model.AddElement(index1VarList[i], damageListFlattened, damage1VarList[i]) | |
damage2VarList.append(model.NewIntVar(0, maxDamage, "Damage2" + str(hero))) | |
index2VarList.append(model.NewIntVar(0, numOfSpots*numOfHeroes, "Index2"+str(hero))) | |
model.Add(index2VarList[i] == (numOfSpots * i) + position2VarList[i]) | |
model.AddElement(index2VarList[i], damageListFlattened, damage2VarList[i]) | |
model.Maximize(sum(damage1VarList) + sum(damage2VarList)) | |
solver = cp_model.CpSolver() | |
status = solver.Solve(model) | |
if status == cp_model.INFEASIBLE: | |
print("Infeasible model") | |
if status == cp_model.OPTIMAL: | |
print('Maximum damage: %i' % solver.ObjectiveValue(), "\n") | |
for i, hero in enumerate(heroList): | |
print("Hero: "+str(hero)+ " - position1: "+spotList[solver.Value(position1VarList[i])] +", position2: "+spotList[solver.Value(position2VarList[i])]) | |
print("----------------------------") | |
#----------------------------------------------------------------------------------------------------------------------# | |
def planningEscapeProblem(): | |
model = cp_model.CpModel() | |
guardList = [0, 8, 3, 5, 0, 3, 9, 8, 0, 4, 0, 0, 10, 8, 14, 8] | |
edgeList = [[0, 1], [0, 9], [1, 2], [1, 6], [2, 3], | |
[2, 7], [2, 10], [3, 4], [3, 7], [3, 15], | |
[4, 15], [5, 9], [5, 10], [6, 7], [6, 11], | |
[7, 9], [7, 11], [8, 8], [8, 14], [8, 15], | |
[10, 13], [11, 12], [11, 13], [11, 14], [12, 14], | |
[12, 15], [13, 14]] | |
undirectionalEdgeList = [] | |
for edgePair in edgeList: | |
undirectionalEdgeList.append(edgePair) | |
undirectionalEdgeList.append([edgePair[1], edgePair[0]]) | |
REST = 3 | |
START = 0 | |
DESTINATION = 8 | |
numOfNodes = len(guardList) | |
MAX_STEP = 10 | |
stepVar = model.NewIntVar(0, MAX_STEP-1, "stepVar") | |
pathVarList = [] | |
for step in range(MAX_STEP): | |
pathVarList.append(model.NewIntVar(0, numOfNodes-1, "NodeForPath"+str(step))) | |
model.Add(pathVarList[0] == START) | |
for i in range(MAX_STEP-1): | |
model.AddAllowedAssignments([pathVarList[i], pathVarList[i+1]], undirectionalEdgeList) | |
stepVarLEVarList = [] | |
for i in range(MAX_STEP): | |
stepVarLEVarList.append(model.NewBoolVar("stepVarLE"+str(i))) | |
model.Add(i >= stepVar).OnlyEnforceIf(stepVarLEVarList[i]) | |
model.Add(i < stepVar).OnlyEnforceIf(stepVarLEVarList[i].Not()) | |
model.Add(pathVarList[i] == DESTINATION).OnlyEnforceIf(stepVarLEVarList[i]) | |
guardVarList = [] | |
hasNoGuardVarList = [] | |
for i in range(MAX_STEP): | |
guardVarList.append(model.NewIntVar(0, max(guardList), "NumOfGuardsAt" + str(i))) | |
model.AddElement(pathVarList[i], guardList, guardVarList[i]) | |
hasNoGuardVarList.append(model.NewBoolVar("HasNoGuard"+str(i))) | |
model.Add(guardVarList[i] == 0).OnlyEnforceIf(hasNoGuardVarList[i]) | |
model.Add(guardVarList[i] != 0).OnlyEnforceIf(hasNoGuardVarList[i].Not()) | |
for i in range(MAX_STEP - REST): | |
model.AddLinearConstraint(sum([hasNoGuardVarList[i], hasNoGuardVarList[i+1], hasNoGuardVarList[i+2]]), 1, REST) | |
model.Minimize(sum(guardVarList)) | |
solver = cp_model.CpSolver() | |
status = solver.Solve(model) | |
if status == cp_model.INFEASIBLE: | |
print("Infeasible model") | |
if status == cp_model.OPTIMAL: | |
print('Minimum guards encountered : %i' % solver.ObjectiveValue(), "\n") | |
solList = [] | |
for i in range(MAX_STEP): | |
solList.append(solver.Value(pathVarList[i])) | |
print("Path =", solList) | |
print("----------------------------") | |
#----------------------------------------------------------------------------------------------------------------------# | |
class VarArraySolutionPrinter(cp_model.CpSolverSolutionCallback): | |
"""Print intermediate solutions.""" | |
def __init__(self, variables): | |
cp_model.CpSolverSolutionCallback.__init__(self) | |
self.__variables = variables | |
self.__solution_count = 0 | |
def on_solution_callback(self): | |
self.__solution_count += 1 | |
for v in self.__variables: | |
print('%s=%i' % (v, self.Value(v)), end=' ') | |
print() | |
def solution_count(self): | |
return self.__solution_count | |
def paradeProblem(): | |
model = cp_model.CpModel() | |
STRAW_SOLDIER_OR_EMPTY = 0 | |
heightList = [6, 7, 9, 13, 12, 9, 8, 10, 11, 8, 3, 4, 6, 6, 7] | |
numOfSoldiers = len(heightList) | |
MAX_ROWS = 8 | |
MAX_COLUMNS = 8 | |
positionVarList = [] | |
positionDict = {} | |
counter = 0 | |
for row in range(MAX_ROWS): | |
for col in range(MAX_COLUMNS): | |
positionVarList.append(model.NewIntVar(0, numOfSoldiers-1, "positionFor"+str(row)+str(col))) | |
positionDict[(row, col)] = counter | |
counter += 1 | |
rowVar = model.NewIntVar(0, MAX_ROWS-1, "RowVar") | |
colVar = model.NewIntVar(0, MAX_COLUMNS-1, "ColumnVar") | |
heightVarList = [] | |
for row in range(MAX_ROWS): | |
for col in range(MAX_COLUMNS): | |
heightVarList.append(model.NewIntVar(0, max(heightList), "HeightAt"+str(row)+str(col))) | |
model.AddElement(positionVarList[positionDict[(row, col)]], heightList, heightVarList[-1]) | |
for soldier in range(1, numOfSoldiers): # 0 is straw soldier | |
isSoldierVarList = [] | |
for row in range(MAX_ROWS): | |
for col in range(MAX_COLUMNS): | |
isSoldierVarList.append(model.NewBoolVar("isSoldier"+str(soldier)+"at"+str(row)+str(col))) | |
model.Add(positionVarList[positionDict[(row, col)]] == soldier).OnlyEnforceIf(isSoldierVarList[-1]) | |
model.Add(positionVarList[positionDict[(row, col)]] != soldier).OnlyEnforceIf(isSoldierVarList[-1].Not()) | |
model.Add(sum(isSoldierVarList) == 1) | |
for row in range(MAX_ROWS): | |
for col in range(MAX_COLUMNS): | |
isEmptyVar1 = model.NewBoolVar("IsEmpty1"+str(row)+str(col)) | |
model.Add(row > rowVar).OnlyEnforceIf(isEmptyVar1) | |
model.Add(row <= rowVar).OnlyEnforceIf(isEmptyVar1.Not()) | |
model.Add(positionVarList[positionDict[(row, col)]] == STRAW_SOLDIER_OR_EMPTY).OnlyEnforceIf(isEmptyVar1) | |
isEmptyVar2 = model.NewBoolVar("IsEmpty2" + str(row) + str(col)) | |
model.Add(col > colVar).OnlyEnforceIf(isEmptyVar2) | |
model.Add(col <= colVar).OnlyEnforceIf(isEmptyVar2.Not()) | |
model.Add(positionVarList[positionDict[(row, col)]] == STRAW_SOLDIER_OR_EMPTY).OnlyEnforceIf(isEmptyVar2) | |
isStrawSoldierVarList = [] | |
for row in range(MAX_ROWS): | |
for col in range(MAX_COLUMNS): | |
isLERowVar = model.NewBoolVar("isLERowVar"+str(row)+str(col)) | |
isLEColVar = model.NewBoolVar("isLEColVar"+str(row)+str(col)) | |
equalsStrawOrEmptyVar = model.NewBoolVar("equalsStrawOrEmpty"+str(row)+str(col)) | |
isStrawSoldierVarList.append(model.NewBoolVar("isStrawSoldier"+str(row)+str(col))) | |
model.AddBoolAnd([isLERowVar, isLEColVar, equalsStrawOrEmptyVar]).OnlyEnforceIf(isStrawSoldierVarList[-1]) | |
model.AddBoolOr([isLERowVar.Not(), isLEColVar.Not(), equalsStrawOrEmptyVar.Not(), isStrawSoldierVarList[-1]]) | |
model.Add(row <= rowVar).OnlyEnforceIf(isLERowVar) | |
model.Add(row > rowVar).OnlyEnforceIf(isLERowVar.Not()) | |
model.Add(col <= colVar).OnlyEnforceIf(isLEColVar) | |
model.Add(col > colVar).OnlyEnforceIf(isLEColVar.Not()) | |
model.Add(positionVarList[positionDict[(row,col)]] == STRAW_SOLDIER_OR_EMPTY).OnlyEnforceIf(equalsStrawOrEmptyVar) | |
model.Add(positionVarList[positionDict[(row, col)]] != STRAW_SOLDIER_OR_EMPTY).OnlyEnforceIf(equalsStrawOrEmptyVar.Not()) | |
hasRealSoldierToTheRightVar = model.NewBoolVar("hasRealSoldierToTheRight"+str(row)+str(col)) | |
if col < MAX_COLUMNS-1: | |
model.Add(positionVarList[positionDict[(row, col+1)]] != STRAW_SOLDIER_OR_EMPTY).OnlyEnforceIf(hasRealSoldierToTheRightVar) | |
hasRealSoldierToTheLeftVar = model.NewBoolVar("hasRealSoldierToTheLeft" + str(row) + str(col)) | |
if col > 0: | |
model.Add(positionVarList[positionDict[(row, col-1)]] != STRAW_SOLDIER_OR_EMPTY).OnlyEnforceIf(hasRealSoldierToTheLeftVar) | |
hasRealSoldierToTheBackVar = model.NewBoolVar("hasRealSoldierToTheBack"+str(row)+str(col)) | |
if row < MAX_ROWS-1: | |
model.Add(positionVarList[positionDict[(row+1, col)]] != STRAW_SOLDIER_OR_EMPTY).OnlyEnforceIf(hasRealSoldierToTheBackVar) | |
hasRealSoldierToTheFrontVar = model.NewBoolVar("hasRealSoldierToTheFront"+str(row)+str(col)) | |
if row > 0: | |
model.Add(positionVarList[positionDict[(row-1, col)]] != STRAW_SOLDIER_OR_EMPTY).OnlyEnforceIf(hasRealSoldierToTheFrontVar) | |
model.AddBoolOr([hasRealSoldierToTheRightVar, hasRealSoldierToTheLeftVar, | |
hasRealSoldierToTheBackVar, hasRealSoldierToTheFrontVar]).OnlyEnforceIf(isStrawSoldierVarList[-1]) | |
for row in range(1, MAX_ROWS): | |
for col in range(MAX_COLUMNS): | |
varList = [] | |
for row2 in range(row): | |
varList.append(heightVarList[positionDict[(row2, col)]]) | |
maxVar = model.NewIntVar(0, max(heightList), "maxVar"+str(row)+str(col)) | |
model.AddMaxEquality(maxVar, varList) | |
indexOfList = positionDict[(row, col)] | |
model.Add(maxVar > heightVarList[indexOfList]).OnlyEnforceIf(isStrawSoldierVarList[indexOfList]) | |
objVar = model.NewIntVar(0, MAX_ROWS*MAX_COLUMNS, "ObjVar") | |
model.AddMultiplicationEquality(objVar, [rowVar, colVar]) | |
# model.Maximize(objVar) # Slow to solve till optimality. | |
solList = [7, 9, 13, 12, 10, 8, 9, 6, | |
6, 6, 6, 6, 6, 6, 6, 6, | |
6, 11, 6, 6, 6, 8, 6, 6, | |
6, 6, 6, 3, 6, 6, 6, 6, | |
4, 6, 6, 6, 6, 6, 6, 6, | |
6, 6, 6, 6, 7, 6, 6, 6, | |
6, 6, 6, 6, 6, 6, 6, 6, | |
6, 6, 6, 6, 6, 6, 6, 6] | |
for i, heightVar in enumerate(heightVarList): | |
model.AddHint(heightVar, solList[i]) | |
solver = cp_model.CpSolver() | |
# solution_printer = VarArraySolutionPrinter(heightVarList) | |
# status = solver.SearchForAllSolutions(model, solution_printer) | |
status = solver.Solve(model) | |
if status == cp_model.INFEASIBLE: | |
print("Infeasible model") | |
if status == cp_model.OPTIMAL: | |
print('Max row * column : %i' % solver.ObjectiveValue(), "\n") | |
for row in range(MAX_ROWS): | |
for col in range(MAX_COLUMNS): | |
index = positionDict[(row, col)] | |
height = solver.Value(heightVarList[index]) | |
if solver.Value(positionVarList[index]) == STRAW_SOLDIER_OR_EMPTY: | |
print(" . ", end=" ") | |
else: | |
print(" "+str(height)+" ", end=" ") | |
print() | |
print(solver.Value(rowVar), "rowVar") | |
print(solver.Value(colVar), "colVar") | |
print("----------------------------") | |
#----------------------------------------------------------------------------------------------------------------------# | |
if __name__ == "__main__": | |
theCavalryWedgeProblem() | |
pickTheEscapePathProblem() | |
twoGeneralsProblem() | |
planningEscapeProblem() | |
paradeProblem() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment