1#  ___________________________________________________________________________
2#
3#  Pyomo: Python Optimization Modeling Objects
4#  Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC
5#  Under the terms of Contract DE-NA0003525 with National Technology and
6#  Engineering Solutions of Sandia, LLC, the U.S. Government retains certain
7#  rights in this software.
8#  This software is distributed under the 3-clause BSD License.
9#  ___________________________________________________________________________
10#
11# Unit Tests for pyomo.opt.base.convert
12#
13
14from itertools import zip_longest
15import re
16import sys
17import os
18from os.path import join
19from filecmp import cmp
20
21import pyomo.common.unittest as unittest
22
23from pyomo.common.errors import ApplicationError
24from pyomo.common.fileutils import this_file_dir
25from pyomo.common.tempfiles import TempfileManager
26
27from pyomo.opt import ProblemFormat, ConverterError, convert_problem
28from pyomo.common import Executable
29
30def filter(line):
31    return 'Problem' in line or line.startswith('NAME')
32
33currdir = this_file_dir()
34deleteFiles = True
35
36class MockArg(object):
37
38    def __init__(self):
39        pass
40
41    def valid_problem_types(self):
42        return [ProblemFormat.pyomo]
43
44    def write(self,filename="", format=None, solver_capability=None, io_options={}):
45        return (filename,None)
46
47class MockArg2(MockArg):
48
49    def valid_problem_types(self):
50        return [ProblemFormat.nl]
51
52    def write(self,filename="", format=None, solver_capability=None, io_options={}):
53        OUTPUT=open(filename,"w")
54        INPUT=open(join(currdir, "test4.nl"))
55        for line in INPUT:
56            OUTPUT.write(line)
57        OUTPUT.close()
58        INPUT.close()
59        return (filename,None)
60
61class MockArg3(MockArg):
62
63    def valid_problem_types(self):
64        return [ProblemFormat.mod]
65
66    def write(self,filename="", format=None, solver_capability=None, io_options={}):
67        return (filename,None)
68
69class MockArg4(MockArg):
70
71    def write(self,filename="", format=None, solver_capability=None, io_options={}):
72        OUTPUT=open(filename,"w")
73        INPUT=open(join(currdir, "test4.nl"))
74        for line in INPUT:
75            OUTPUT.write(line)
76        OUTPUT.close()
77        INPUT.close()
78        return (filename,None)
79
80
81class Test(unittest.TestCase):
82
83    @classmethod
84    def setUpClass(cls):
85        import pyomo.environ
86
87    def setUp(self):
88        TempfileManager.push()
89
90    def tearDown(self):
91        TempfileManager.pop(remove=deleteFiles or self.currentTestPassed())
92
93    def test_nl_nl1(self):
94        #""" Convert from NL to NL """
95        ans = convert_problem( ("test4.nl",), None, [ProblemFormat.nl])
96        self.assertEqual(ans[0],("test4.nl",))
97
98    def test_nl_nl2(self):
99        #""" Convert from NL to NL """
100        ans = convert_problem( ("test4.nl","tmp.nl"), None, [ProblemFormat.nl])
101        self.assertEqual(ans[0],("test4.nl","tmp.nl"))
102
103    @unittest.skipUnless(
104        Executable("pico_convert").available(), 'pico_convert required')
105    def test_nl_lp1(self):
106        #""" Convert from NL to LP """
107        ans = convert_problem(
108            (join(currdir, "test4.nl"),), None, [ProblemFormat.cpxlp])
109        self.assertEqual(ans[0][0][-15:],"pico_convert.lp")
110        _out, _log = ans[0][0], join(currdir, "test1_convert.lp")
111        self.assertTrue(cmp(_out, _log),
112                        msg="Files %s and %s differ" % (_out, _log))
113
114    @unittest.skipUnless(Executable("glpsol").available(), 'glpsol required')
115    def test_mod_lp1(self):
116        #""" Convert from MOD to LP """
117        ans = convert_problem(
118            (join(currdir, "test3.mod"),), None, [ProblemFormat.cpxlp])
119        self.assertTrue(ans[0][0].endswith("glpsol.lp"))
120        with open(ans[0][0], 'r') as f1, open(join(currdir, "test2_convert.lp"), 'r') as f2:
121            for line1, line2 in zip_longest(f1, f2):
122                if 'Problem' in line1:
123                    continue
124                self.assertEqual(line1, line2)
125
126    @unittest.skipUnless(Executable("glpsol").available(), 'glpsol required')
127    def test_mod_lp2(self):
128        #""" Convert from MOD+DAT to LP """
129        ans = convert_problem(
130            (join(currdir, "test5.mod"), join(currdir, "test5.dat")),
131            None, [ProblemFormat.cpxlp])
132        self.assertTrue(ans[0][0].endswith("glpsol.lp"))
133        with open(ans[0][0], 'r') as f1, open(join(currdir, "test3_convert.lp"), 'r') as f2:
134            for line1, line2 in zip_longest(f1, f2):
135                if 'Problem' in line1:
136                    continue
137                self.assertEqual(line1, line2)
138
139    @unittest.skipUnless(Executable("ampl").available(), 'ampl required')
140    def test_mod_nl1(self):
141        #""" Convert from MOD to NL """
142        ans = convert_problem(
143            (join(currdir, "test3.mod"),), None, [ProblemFormat.nl])
144        self.assertTrue(ans[0][0].endswith('.nl'))
145        #self.assertFileEqualsBinaryFile(ans[0][0], join(currdir, "test_mod_nl1.nl")
146
147    @unittest.skipUnless(Executable("ampl").available(), 'ampl required')
148    def test_mod_nl2(self):
149        #""" Convert from MOD+DAT to NL """
150        ans = convert_problem(
151            (join(currdir, "test5.mod"), join(currdir, "test5.dat")),
152            None, [ProblemFormat.nl])
153        self.assertTrue(ans[0][0].endswith('.nl'))
154        #self.assertTrue(cmp(ans[0][0], join(currdir, "test_mod_nl2.nl")
155
156    def test_mock_lp1(self):
157        #""" Convert from Pyomo to LP """
158        arg=MockArg()
159        ans = convert_problem( (arg, ProblemFormat.cpxlp,arg), None, [ProblemFormat.cpxlp])
160        self.assertNotEqual(re.match(".*tmp.*pyomo.lp$",ans[0][0]), None)
161
162    def test_pyomo_lp1(self):
163        #""" Convert from Pyomo to LP with file"""
164        ans = convert_problem( (join(currdir, 'model.py'), ProblemFormat.cpxlp,), None, [ProblemFormat.cpxlp])
165        self.assertNotEqual(re.match(".*tmp.*pyomo.lp$",ans[0][0]), None)
166
167    def test_mock_lp2(self):
168        #""" Convert from NL to LP """
169        arg=MockArg2()
170        try:
171            ans = convert_problem( (arg,), None, [ProblemFormat.cpxlp])
172        except ConverterError:
173            err = sys.exc_info()[1]
174            if not Executable("pico_convert"):
175                return
176            else:
177                self.fail("Expected ApplicationError because pico_convert "
178                          "is not available: '%s'" % str(err))
179        self.assertEqual(ans[0][0][-15:],"pico_convert.lp")
180        os.remove(ans[0][0])
181
182    # Note sure what to do with this test now that we
183    # have a native MPS converter
184    def Xtest_mock_mps1(self):
185        #""" Convert from Pyomo to MPS """
186        arg=MockArg4()
187        try:
188            ans = convert_problem((arg, ProblemFormat.mps,arg), None, [ProblemFormat.mps])
189        except ConverterError:
190            err = sys.exc_info()[1]
191            if not Executable("pico_convert"):
192                return
193            else:
194                self.fail("Expected ApplicationError because pico_convert "
195                          "is not available: '%s'" % str(err))
196        self.assertEqual(ans[0][0][-16:],"pico_convert.mps")
197        os.remove(ans[0][0])
198
199    def test_pyomo_mps1(self):
200        #""" Convert from Pyomo to MPS with file"""
201        try:
202            ans = convert_problem( (join(currdir, 'model.py'), ProblemFormat.mps,), None, [ProblemFormat.mps])
203        except ConverterError:
204            err = sys.exc_info()[1]
205            if not Executable("pico_convert"):
206                return
207            else:
208                self.fail("Expected ApplicationError because pico_convert "
209                          "is not available: '%s'" % str(err))
210        self.assertEqual(ans[0][0][-16:],"pico_convert.mps")
211        os.remove(ans[0][0])
212
213    def test_mock_nl1(self):
214        #""" Convert from Pyomo to NL """
215        arg = MockArg4()
216        ans = convert_problem( (arg, ProblemFormat.nl,arg), None, [ProblemFormat.nl])
217        self.assertNotEqual(re.match(".*tmp.*pyomo.nl$",ans[0][0]), None)
218        os.remove(ans[0][0])
219
220    def test_pyomo_nl1(self):
221        #""" Convert from Pyomo to NL with file"""
222        ans = convert_problem( (join(currdir, 'model.py'), ProblemFormat.nl,), None, [ProblemFormat.nl])
223        self.assertNotEqual(re.match(".*tmp.*pyomo.nl$",ans[0][0]), None)
224        os.remove(ans[0][0])
225
226    def test_error1(self):
227        #""" No valid problem types """
228        try:
229            convert_problem( ("test4.nl","tmp.nl"), ProblemFormat.nl, [])
230            self.fail("Expected ConverterError exception")
231        except ConverterError:
232            err = sys.exc_info()[1]
233            pass
234
235    def test_error2(self):
236        #""" Target problem type is not valid """
237        try:
238            convert_problem( ("test4.nl","tmp.nl"), ProblemFormat.nl, [ProblemFormat.mps])
239            self.fail("Expected ConverterError exception")
240        except ConverterError:
241            pass
242
243    def test_error3(self):
244        #""" Empty argument list """
245        try:
246            convert_problem( (), None, [ProblemFormat.mps])
247            self.fail("Expected ConverterError exception")
248        except  ConverterError:
249            pass
250
251    def test_error4(self):
252        #""" Unknown source type """
253        try:
254            convert_problem( ("prob.foo",), None, [ProblemFormat.mps])
255            self.fail("Expected ConverterError exception")
256        except  ConverterError:
257            pass
258
259    def test_error5(self):
260        #""" Unknown source type """
261        try:
262            convert_problem( ("prob.lp",), ProblemFormat.nl, [ProblemFormat.nl])
263            self.fail("Expected ConverterError exception")
264        except  ConverterError:
265            pass
266
267    def test_error6(self):
268        #""" Cannot use pico_convert with more than one file """
269        try:
270            ans = convert_problem( (join(currdir, "test4.nl"), "foo"), None, [ProblemFormat.cpxlp])
271            self.fail("Expected ConverterError exception")
272        except ConverterError:
273            pass
274
275    def test_error8(self):
276        #""" Error when source file cannot be found """
277        try:
278            ans = convert_problem( (join(currdir, "unknown.nl"),), None, [ProblemFormat.cpxlp])
279            self.fail("Expected ConverterError exception")
280        except ApplicationError:
281            err = sys.exc_info()[1]
282            if not Executable("pico_convert"):
283                self.fail("Expected ApplicationError because pico_convert "
284                          "is not available: '%s'" % str(err))
285            return
286        except ConverterError:
287            pass
288
289    def test_error9(self):
290        #""" The Opt configuration has not been initialized """
291        cmd = Executable("pico_convert").disable()
292        try:
293            ans = convert_problem( (join(currdir, "test4.nl"),), None, [ProblemFormat.cpxlp])
294            self.fail("This test didn't fail, but pico_convert should not be defined.")
295        except ConverterError:
296            pass
297        cmd = Executable("pico_convert").rehash()
298
299    def test_error10(self):
300        #""" GLPSOL can only convert file data """
301        try:
302            arg = MockArg3()
303            ans = convert_problem( (arg, ProblemFormat.cpxlp,arg), None, [ProblemFormat.cpxlp])
304            self.fail("This test didn't fail, but glpsol cannot handle objects.")
305        except ConverterError:
306            pass
307
308    def test_error11(self):
309        #""" Cannot convert MOD that contains data """
310        try:
311            ans = convert_problem( (join(currdir, "test3.mod"),join(currdir, "test5.dat")), None, [ProblemFormat.cpxlp])
312            self.fail("Expected ConverterError exception because we provided a MOD file with a 'data;' declaration")
313        except ApplicationError:
314            err = sys.exc_info()[1]
315            if Executable("glpsol"):
316                self.fail("Expected ApplicationError because glpsol "
317                          "is not available: '%s'" % str(err))
318            return
319        except ConverterError:
320            pass
321
322if __name__ == "__main__":
323    deleteFiles = False
324    unittest.main()
325