1'''
2This module interface COIN-OR's ``CoinMpsIO``. When you call
3:func:`cylp.cy.CyClpSimplex.readMps` then ``CoinMpsIO``'s ``readMps`` is
4called.  The main reason why cylp interfaces this class is to be able to read
5an ``mps`` file without creating a Simplex object. This way it is possible to
6read a QP using CoinMpsIO and work on the elements of the problem, e.g. the
7Hessian,...
8'''
9# cython: embedsignature=True
10
11import numpy as np
12from scipy import sparse
13from scipy.sparse import identity, dia_matrix
14from cylp.py.utils.sparseUtil import csc_matrixPlus, csr_matrixPlus
15
16cdef class CyCoinMpsIO:
17    def __cinit__(self):
18        self.CppSelf = new CppICoinMpsIO()
19        self.Hessian = 0
20
21    def readMps(self, filename):
22        '''
23        Read an mps file. Check if the file is a QP symmetrisize its Hessian
24        and store it.
25
26        >>> import numpy as np
27        >>> from cylp.cy import CyCoinMpsIO
28        >>> from cylp.cy.CyCoinMpsIO import getQpsExample
29        >>> problem = CyCoinMpsIO()
30        >>> problem.readMps(getQpsExample())
31        0
32        >>> problem.nVariables
33        5
34        >>> problem.nConstraints
35        5
36        >>> signs = problem.constraintSigns
37        >>> [chr(i) for i in signs] == problem.nConstraints * ['G']
38        True
39        >>> c = problem.matrixByRow
40        >>> (abs(c.elements -
41        ...     np.array([-1., -1., -1., -1., -1.,  10.,  10.,  -3.,
42        ...                5., 4.,  -8., 1., -2., -5., 3., 8., -1., 2.,
43        ...                5., -3.,  -4.,  -2., 3., -5., 1.])) <
44        ...                            10 ** -8).all()
45        True
46        >>> (c.indices ==
47        ...       np.array([0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1,
48        ...                 2, 3, 4, 0, 1, 2, 3, 4], dtype=np.int32)).all()
49        True
50        >>> (c.vectorStarts ==
51        ...        np.array([0, 5, 10, 15, 20, 25], dtype=np.int32)).all()
52        True
53        >>> (problem.rightHandSide ==
54        ...        np.array([-5., 20., -40., 11., -30.])).all()
55        True
56        >>> H = problem.Hessian.todense()
57        >>> (abs(H -
58        ... np.matrix([[20394., -24908., -2026., 3896., 658.],
59        ...            [-24908., 41818., -3466., -9828., -372.],
60        ...            [-2026., -3466., 3510., 2178., -348.],
61        ...            [3896., -9828., 2178., 3030., -44.],
62        ...            [658., -372., -348., -44., 54.]])) <
63        ...                            10 ** -8).all()
64        True
65
66        '''
67        if isinstance(filename, str):
68            # Cast strings/unicode to bytes
69            filename = filename.encode('utf-8')
70
71        ret = self.CppSelf.readMps(filename)
72        if ret == 0 and self.CppSelf.IreadQuadraticMps(NULL, 0) == 0:
73            start = self.QPColumnStarts
74            col = self.QPColumns
75            el = self.QPElements
76
77            n = self.nVariables
78            m = self.nConstraints
79
80            Hessian = csr_matrixPlus((el, col, start), shape=(n, n))
81            # Fill the other half of the symmetric Hessian
82            Hessian = Hessian + Hessian.T - dia_matrix((Hessian.diagonal(),[0]), shape=(n, n))
83            self.Hessian = Hessian
84
85        return ret
86
87    def readQuadraticMps(self, filename, checkSymmetry):
88        return self.CppSelf.IreadQuadraticMps(NULL, checkSymmetry)
89
90    property Hessian:
91        def __get__(self):
92            return self.Hessian
93
94        def __set__(self, h):
95            self.Hessian = h
96
97    property variableLower:
98        def __get__(self):
99            return <object>self.CppSelf.np_getColLower()
100
101    property variableUpper:
102        def __get__(self):
103            return <object>self.CppSelf.np_getColUpper()
104
105    property constraintSigns:
106        def __get__(self):
107            return <object>self.CppSelf.np_getRowSense()
108
109    property rightHandSide:
110        def __get__(self):
111            return <object>self.CppSelf.np_getRightHandSide()
112
113    property constraintRange:
114        def __get__(self):
115            return <object>self.CppSelf.np_getRowRange()
116
117    property constraintLower:
118        def __get__(self):
119            return <object>self.CppSelf.np_getRowLower()
120
121    property constraintUpper:
122        def __get__(self):
123            return <object>self.CppSelf.np_getRowUpper()
124
125    property objCoefficients:
126        def __get__(self):
127            return <object>self.CppSelf.np_getObjCoefficients()
128
129    property integerColumns:
130        def __get__(self):
131            return <object>self.CppSelf.np_integerColumns()
132
133    property QPColumnStarts:
134        def __get__(self):
135            return <object>self.CppSelf.getQPColumnStarts()
136
137    property QPColumns:
138        def __get__(self):
139            return <object>self.CppSelf.getQPColumns()
140
141    property QPElements:
142        def __get__(self):
143            return <object>self.CppSelf.getQPElements()
144
145    property matrixByRow:
146        def __get__(self):
147            cdef CppCoinPackedMatrix* m = self.CppSelf.IgetMatrixByRow()
148            cym = CyCoinPackedMatrix()
149            cym.CppSelf = m
150            return cym
151
152    property matrixByCol:
153        def __get__(self):
154            cdef CppCoinPackedMatrix* m = self.CppSelf.IgetMatrixByCol()
155            cym = CyCoinPackedMatrix()
156            cym.CppSelf = m
157            return cym
158
159    property objectiveOffset:
160        def __get__(self):
161            return self.CppSelf.getObjectiveOffset()
162
163    property nVariables:
164        def __get__(self):
165            return self.CppSelf.getNumCols()
166
167    property nConstraints:
168        def __get__(self):
169            return self.CppSelf.getNumRows()
170
171
172def getQpsExample():
173    '''
174    Return full path to a QPS example file for doctests
175    '''
176    import os
177    import inspect
178    curpath = os.path.dirname(inspect.getfile(inspect.currentframe()))
179    return os.path.join(curpath, '../input/hs268.qps')
180
181
182#   cdef int CLP_readQuadraticMps(self, char* filename,
183#               int * columnStart, int * column2, double * elements,
184#               int checkSymmetry):
185#       return self.CppSelf.readQuadraticMps(filename, columnStart,
186#                                        column2, elements, checkSymmetry)
187
188#   def readQuadraticMps(self,
189#                           filename,
190#                           np.ndarray[np.int32_t,ndim=1] columnStart,
191#                           np.ndarray[np.int32_t,ndim=1] column2,
192#                           np.ndarray[np.double_t,ndim=1] elements,
193#                           checkSymmetry):
194#       return self.CLP_readQuadraticMps(filename,
195#                                           <int*>columnStart.data,
196#                                           <int*>column2.data,
197#                                           <double*>elements.data,
198#                                           checkSymmetry)
199