Source code for cylp.py.pivots.PositiveEdgePivot
'''
As a part of ``cylp.python.pivots`` it implements the positive edge
pivot selection rule.
'''
from __future__ import print_function
import random
import numpy as np
from cylp.cy import CyCoinIndexedVector
from cylp.cy.CyClpSimplex import cydot
from .PivotPythonBase import PivotPythonBase
[docs]class PositiveEdgePivot(PivotPythonBase):
'''
Positive Edge pivot rule implementation.
.. _custom-pivot-usage:
**Usage**
>>> from cylp.cy import CyClpSimplex
>>> from cylp.py.pivots import PositiveEdgePivot
>>> from cylp.py.pivots.PositiveEdgePivot import getMpsExample
>>> # Get the path to a sample mps file
>>> f = getMpsExample()
>>> s = CyClpSimplex()
>>> s.readMps(f) # Returns 0 if OK
0
>>> pivot = PositiveEdgePivot(s)
>>> s.setPivotMethod(pivot)
>>> s.primal()
'optimal'
>>> round(s.objectiveValue, 5)
2520.57174
'''
def __init__(self, clpModel, EPSILON=10 ** (-7)):
self.clpModel = clpModel
self.dim = self.clpModel.nRows + self.clpModel.nCols
self.isDegenerate = False
# Create some numpy arrays here ONCE to prevent memory
# allocation at each iteration
self.aColumn = CyCoinIndexedVector()
self.aColumn.reserve(self.dim)
self.w = CyCoinIndexedVector()
self.w.reserve(self.clpModel.nRows)
self.rhs = np.empty(self.clpModel.nRows, dtype=np.double)
self.EPSILON = EPSILON
self.lastUpdateIteration = 0
def updateP(self):
'''Finds constraints with abs(rhs) <= epsilon and put
their indices in "z"
'''
s = self.clpModel
nRows = s.nRows
rhs = self.rhs
s.getRightHandSide(rhs)
#self.p = np.where(np.abs(rhs) > self.EPSILON)[0]
self.z = np.where(np.abs(rhs) <= self.EPSILON)[0]
#print('degeneracy level : ', (len(self.z)) / float(nRows)))
self.isDegenerate = (len(self.z) > 0)
def updateW(self):
'''Sets "w" to be a vector of random vars with "0"
at indices defined in "p"
Note that vectorTimesB_1 changes "w"
'''
self.updateP()
self.w.clear()
self.w[self.z] = np.random.random(len(self.z))
s = self.clpModel
s.vectorTimesB_1(self.w)
self.lastUpdateIteration = s.iteration
def random(self):
'Defines how random vector "w" components are generated'
return random.random()
def isCompatible(self, varInd):
if not self.isDegenerate:
return False
s = self.clpModel
s.getACol(varInd, self.aColumn)
return abs(cydot(self.aColumn, self.w)) < self.EPSILON
def checkVar(self, i):
return self.isCompatible(i)
def pivotColumn(self, updates, spareRow1, spareRow2, spareCol1, spareCol2):
self.updateReducedCosts(updates, spareRow1, spareRow2, spareCol1, spareCol2)
s = self.clpModel
rc = s.reducedCosts
tol = s.dualTolerance
indicesToConsider = np.where(s.varNotFlagged & s.varNotFixed &
s.varNotBasic &
(((rc > tol) & s.varIsAtUpperBound) |
((rc < -tol) & s.varIsAtLowerBound) |
s.varIsFree))[0]
rc2 = abs(rc[indicesToConsider])
maxRc = maxCompRc = maxInd = maxCompInd = -1
if self.isDegenerate:
w = self.w.elements
compatibility = np.zeros(s.nCols + s.nRows, dtype=np.double)
if len(indicesToConsider) > 0:
s.transposeTimesSubsetAll(indicesToConsider.astype(np.int64),
w, compatibility)
comp_varInds = indicesToConsider[np.where(abs(
compatibility[indicesToConsider]) <
self.EPSILON)[0]]
comp_rc = abs(rc[comp_varInds])
if len(comp_rc) > 0:
maxCompInd = comp_varInds[np.argmax(comp_rc)]
maxCompRc = rc[maxCompInd]
if len(rc2) > 0:
maxInd = indicesToConsider[np.argmax(rc2)]
maxRc = rc[maxInd]
del rc2
if maxCompInd != -1 and abs(maxCompRc) > 0.1 * abs(maxRc):
return maxCompInd
self.updateW()
return maxInd
def saveWeights(self, model, mode):
self.clpModel = model
def isPivotAcceptable(self):
return True
def getMpsExample():
import os
import inspect
cylpDir = os.environ['CYLP_SOURCE_DIR']
return os.path.join(cylpDir, 'cylp', 'input', 'p0033.mps')