diff --git a/.pylintdict b/.pylintdict
index 9230a0687..75685b4ae 100644
--- a/.pylintdict
+++ b/.pylintdict
@@ -81,6 +81,8 @@ gurobi
gurobioptimizer
gurobipy
gutmann
+gzip'ed
+gz
hamilton
hamiltonian
hamiltonians
@@ -125,6 +127,7 @@ minimizer
minimumeigenoptimizer
mmp
mpm
+mps
multiset
mypy
nannicini
@@ -233,6 +236,7 @@ transpiling
travelling
troyer
tsplib
+uncompress
undirected
upperbound
variational
diff --git a/docs/tutorials/01_quadratic_program.ipynb b/docs/tutorials/01_quadratic_program.ipynb
index 4eaf3f628..4921fdd0c 100644
--- a/docs/tutorials/01_quadratic_program.ipynb
+++ b/docs/tutorials/01_quadratic_program.ipynb
@@ -390,7 +390,7 @@
"\n",
"quadratic dict w/ index:\t {(0, 1): 2, (2, 2): -1}\n",
"quadratic dict w/ name:\t\t {('x', 'y'): 2, ('z', 'z'): -1}\n",
- "symmetric quadratic dict w/ name:\t {('y', 'x'): 1, ('x', 'y'): 1, ('z', 'z'): -1}\n",
+ "symmetric quadratic dict w/ name:\t {('x', 'y'): 1, ('y', 'x'): 1, ('z', 'z'): -1}\n",
"quadratic matrix:\n",
" [[ 0 2 0]\n",
" [ 0 0 0]\n",
@@ -786,12 +786,15 @@
}
],
"source": [
+ "from qiskit_optimization.translators import export_as_lp_string\n",
+ "\n",
"mod = QuadraticProgram()\n",
"mod.binary_var(name=\"e\")\n",
"mod.binary_var(name=\"f\")\n",
"mod.continuous_var(name=\"g\")\n",
"mod.minimize(linear=[1, 2, 3])\n",
- "print(mod.export_as_lp_string())"
+ "\n",
+ "print(export_as_lp_string(mod))"
]
},
{
@@ -802,7 +805,7 @@
{
"data": {
"text/html": [
- "
Version Information
Qiskit Software | Version |
---|
qiskit-terra | 0.21.0.dev0+dbd3961 |
qiskit-aer | 0.10.4 |
qiskit-ibmq-provider | 0.19.1 |
qiskit-optimization | 0.4.0 |
System information |
---|
Python version | 3.10.4 |
Python compiler | GCC 11.2.0 |
Python build | main, Apr 2 2022 09:04:19 |
OS | Linux |
CPUs | 4 |
Memory (Gb) | 14.577545166015625 |
Wed May 18 16:03:27 2022 JST |
"
+ "Version Information
Software | Version |
---|
qiskit | 0.45.1 |
qiskit_optimization | 0.7.0 |
System information |
---|
Python version | 3.10.13 |
Python compiler | GCC 13.2.1 20231205 (Red Hat 13.2.1-6) |
Python build | main, Dec 18 2023 00:00:00 |
OS | Linux |
CPUs | 12 |
Memory (Gb) | 31.056407928466797 |
Mon Feb 05 10:25:02 2024 CET |
"
],
"text/plain": [
""
@@ -814,7 +817,7 @@
{
"data": {
"text/html": [
- "This code is a part of Qiskit
© Copyright IBM 2017, 2022.
This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.
"
+ "This code is a part of Qiskit
© Copyright IBM 2017, 2024.
This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.
"
],
"text/plain": [
""
@@ -840,8 +843,22 @@
}
],
"metadata": {
+ "kernelspec": {
+ "display_name": "venv",
+ "language": "python",
+ "name": "python3"
+ },
"language_info": {
- "name": "python"
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.13"
}
},
"nbformat": 4,
diff --git a/qiskit_optimization/algorithms/admm_optimizer.py b/qiskit_optimization/algorithms/admm_optimizer.py
index e837ee278..7e6ad8b9d 100644
--- a/qiskit_optimization/algorithms/admm_optimizer.py
+++ b/qiskit_optimization/algorithms/admm_optimizer.py
@@ -25,6 +25,7 @@
from ..problems.linear_expression import LinearExpression
from ..problems.quadratic_program import QuadraticProgram
from ..problems.variable import Variable, VarType
+from ..translators import export_as_lp_string
from .minimum_eigen_optimizer import MinimumEigenOptimizer
from .optimization_algorithm import (
OptimizationAlgorithm,
@@ -282,7 +283,7 @@ def solve(self, problem: QuadraticProgram) -> ADMMOptimizationResult:
self._verify_compatibility(problem)
# debug
- self._log.debug("Initial problem: %s", problem.export_as_lp_string())
+ self._log.debug("Initial problem: %s", export_as_lp_string(problem))
# map integer variables to binary variables
from ..converters.integer_to_binary import IntegerToBinary
@@ -321,7 +322,7 @@ def solve(self, problem: QuadraticProgram) -> ADMMOptimizationResult:
op1 = self._create_step1_problem()
self._state.x0 = self._update_x0(op1)
# debug
- self._log.debug("Step 1 sub-problem: %s", op1.export_as_lp_string())
+ self._log.debug("Step 1 sub-problem: %s", export_as_lp_string(op1))
# else, no binary variables exist, and no update to be done in this case.
# debug
self._log.debug("x0=%s", self._state.x0)
@@ -329,7 +330,7 @@ def solve(self, problem: QuadraticProgram) -> ADMMOptimizationResult:
op2 = self._create_step2_problem()
self._state.u, self._state.z = self._update_x1(op2)
# debug
- self._log.debug("Step 2 sub-problem: %s", op2.export_as_lp_string())
+ self._log.debug("Step 2 sub-problem: %s", export_as_lp_string(op2))
self._log.debug("u=%s", self._state.u)
self._log.debug("z=%s", self._state.z)
@@ -338,7 +339,7 @@ def solve(self, problem: QuadraticProgram) -> ADMMOptimizationResult:
op3 = self._create_step3_problem()
self._state.y = self._update_y(op3)
# debug
- self._log.debug("Step 3 sub-problem: %s", op3.export_as_lp_string())
+ self._log.debug("Step 3 sub-problem: %s", export_as_lp_string(op3))
# debug
self._log.debug("y=%s", self._state.y)
diff --git a/qiskit_optimization/problems/quadratic_program.py b/qiskit_optimization/problems/quadratic_program.py
index bcbfc5d10..ee0fd05e8 100644
--- a/qiskit_optimization/problems/quadratic_program.py
+++ b/qiskit_optimization/problems/quadratic_program.py
@@ -1,6 +1,6 @@
# This code is part of a Qiskit project.
#
-# (C) Copyright IBM 2019, 2023.
+# (C) Copyright IBM 2019, 2024.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -20,8 +20,8 @@
from warnings import warn
import numpy as np
-from docplex.mp.model_reader import ModelReader
from numpy import ndarray
+from qiskit.utils.deprecation import deprecate_func
from qiskit.quantum_info import SparsePauliOp
from qiskit.quantum_info.operators.base_operator import BaseOperator
from scipy.sparse import spmatrix
@@ -911,52 +911,58 @@ def _copy_from(self, other: "QuadraticProgram", include_name: bool) -> None:
elem.quadratic_program = self
setattr(self, attr, val)
+ @deprecate_func(
+ additional_msg=(
+ "Please use export_as_lp_string from qiskit_optimization.translators.file_io instead."
+ ),
+ since="0.6.1",
+ pending=True,
+ )
def export_as_lp_string(self) -> str:
"""Returns the quadratic program as a string of LP format.
Returns:
A string representing the quadratic program.
"""
+
# pylint: disable=cyclic-import
- from ..translators.docplex_mp import to_docplex_mp
+ from ..translators.file_io import export_as_lp_string
- return to_docplex_mp(self).export_as_lp_string()
+ return export_as_lp_string(self)
@_optionals.HAS_CPLEX.require_in_call
+ @deprecate_func(
+ additional_msg=(
+ "Please use read_from_lp_file from qiskit_optimization.translators.file_io instead."
+ ),
+ since="0.6.1",
+ pending=True,
+ )
def read_from_lp_file(self, filename: str) -> None:
- """Loads the quadratic program from a LP file.
+ """Loads the quadratic program from a LP file (may be gzip'ed).
Args:
filename: The filename of the file to be loaded.
Raises:
- FileNotFoundError: If the file does not exist.
+ IOError: If the file type is not recognized, not supported or the file is not found.
Note:
This method requires CPLEX to be installed and present in ``PYTHONPATH``.
"""
- def _parse_problem_name(filename: str) -> str:
- # Because docplex model reader uses the base name as model name,
- # we parse the model name in the LP file manually.
- # https://ibmdecisionoptimization.github.io/docplex-doc/mp/docplex.mp.model_reader.html
- prefix = "\\Problem name:"
- model_name = ""
- with open(filename, encoding="utf8") as file:
- for line in file:
- if line.startswith(prefix):
- model_name = line[len(prefix) :].strip()
- if not line.startswith("\\"):
- break
- return model_name
-
# pylint: disable=cyclic-import
- from ..translators.docplex_mp import from_docplex_mp
+ from ..translators.file_io import read_from_lp_file
- model = ModelReader().read(filename, model_name=_parse_problem_name(filename))
- other = from_docplex_mp(model)
- self._copy_from(other, include_name=True)
+ self._copy_from(read_from_lp_file(filename), include_name=True)
+ @deprecate_func(
+ additional_msg=(
+ "Please use write_to_lp_file from qiskit_optimization.translators.file_io instead."
+ ),
+ since="0.6.1",
+ pending=True,
+ )
def write_to_lp_file(self, filename: str) -> None:
"""Writes the quadratic program to an LP file.
@@ -969,11 +975,11 @@ def write_to_lp_file(self, filename: str) -> None:
OSError: If this cannot open a file.
DOcplexException: If filename is an empty string
"""
+
# pylint: disable=cyclic-import
- from ..translators.docplex_mp import to_docplex_mp
+ from ..translators.file_io import write_to_lp_file
- mdl = to_docplex_mp(self)
- mdl.export_as_lp(filename)
+ write_to_lp_file(self, filename)
def substitute_variables(
self,
diff --git a/qiskit_optimization/translators/__init__.py b/qiskit_optimization/translators/__init__.py
index 913c23922..c747e5b2a 100644
--- a/qiskit_optimization/translators/__init__.py
+++ b/qiskit_optimization/translators/__init__.py
@@ -1,6 +1,6 @@
# This code is part of a Qiskit project.
#
-# (C) Copyright IBM 2021, 2023.
+# (C) Copyright IBM 2021, 2024.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -17,7 +17,7 @@
.. currentmodule:: qiskit_optimization.translators
Translators between :class:`~qiskit_optimization.problems.QuadraticProgram` and
-other optimization models or other objects.
+other optimization models or other objects, including loading and writing to/from files.
Translators
----------------------
@@ -31,9 +31,30 @@
to_gurobipy
from_ising
to_ising
+
+File loading and saving
+-------------------------
+.. autosummary::
+ :toctree: ../stubs/
+ :nosignatures:
+
+ export_as_lp_string
+ export_as_mps_string
+ read_from_lp_file
+ read_from_mps_file
+ write_to_lp_file
+ write_to_mps_file
"""
from .docplex_mp import from_docplex_mp, to_docplex_mp
+from .file_io import (
+ export_as_lp_string,
+ export_as_mps_string,
+ read_from_lp_file,
+ read_from_mps_file,
+ write_to_lp_file,
+ write_to_mps_file,
+)
from .gurobipy import from_gurobipy, to_gurobipy
from .ising import from_ising, to_ising
@@ -44,4 +65,10 @@
"to_gurobipy",
"from_ising",
"to_ising",
+ "export_as_lp_string",
+ "export_as_mps_string",
+ "read_from_lp_file",
+ "read_from_mps_file",
+ "write_to_lp_file",
+ "write_to_mps_file",
]
diff --git a/qiskit_optimization/translators/file_io.py b/qiskit_optimization/translators/file_io.py
new file mode 100644
index 000000000..1d1c88ebd
--- /dev/null
+++ b/qiskit_optimization/translators/file_io.py
@@ -0,0 +1,224 @@
+# This code is part of a Qiskit project.
+#
+# (C) Copyright IBM 2024
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+"""LP/MPS-file import / export for Quadratic Programs."""
+
+import os
+import logging
+from typing import List, Callable
+from gzip import open as gzip_open
+from pathlib import Path
+from tempfile import NamedTemporaryFile
+
+from docplex.mp.model_reader import ModelReader
+
+import qiskit_optimization.optionals as _optionals
+from ..problems.quadratic_program import QuadraticProgram
+
+logger = logging.getLogger(__name__)
+
+
+def export_as_lp_string(q_p: QuadraticProgram) -> str:
+ """Returns the quadratic program as a string of LP format.
+
+ Args:
+ q_p: The quadratic program to be exported.
+
+ Returns:
+ A string representing the quadratic program.
+ """
+ from .docplex_mp import to_docplex_mp
+
+ return to_docplex_mp(q_p).export_as_lp_string()
+
+
+@_optionals.HAS_CPLEX.require_in_call
+def export_as_mps_string(q_p: QuadraticProgram) -> str:
+ """Returns the quadratic program as a string of MPS format.
+
+ Args:
+ q_p: The quadratic program to be exported.
+
+ Returns:
+ A string representing the quadratic program.
+ """
+ from .docplex_mp import to_docplex_mp
+
+ return to_docplex_mp(q_p).export_as_mps_string()
+
+
+@_optionals.HAS_CPLEX.require_in_call
+def _read_from_file(
+ filename: str, extensions: List[str], name_parse_fun: Callable
+) -> QuadraticProgram:
+ """Loads a quadratic program from an LP or MPS file. Also deals with
+ gzip'ed files.
+
+ Args:
+ filename: The filename of the file to be loaded.
+ name_parse_fun: Function that parses the model name from the input file.
+
+ Raises:
+ IOError: If the file type is not recognized, not supported or the file is not found.
+
+ Note:
+ This method requires CPLEX to be installed and present in ``PYTHONPATH``.
+ """
+
+ # check whether this file type is supported
+ extension = "".join(Path(filename).suffixes)
+ main_extension = extension
+ if main_extension.endswith(".gz"):
+ main_extension = main_extension[:-3]
+ if main_extension not in extensions:
+ raise IOError("File type not supported for model reading.")
+
+ # uncompress and parse
+ if extension.endswith(".gz"):
+ with gzip_open(filename, "rb") as compressed:
+ # requires delete=False to avoid permission issues under windows
+ with NamedTemporaryFile(suffix=extension[:-3], delete=False) as uncompressed:
+ uncompressed.write(compressed.read())
+ uncompressed.seek(0)
+ uncompressed.flush()
+
+ model = ModelReader().read(
+ uncompressed.name,
+ model_name=name_parse_fun(uncompressed.name)
+ if name_parse_fun is not None
+ else None,
+ )
+ uncompressed.close()
+ os.unlink(uncompressed.name)
+
+ else:
+ model = ModelReader().read(
+ filename,
+ model_name=name_parse_fun(filename) if name_parse_fun is not None else None,
+ )
+
+ # pylint: disable=cyclic-import
+ from ..translators.docplex_mp import from_docplex_mp
+
+ return from_docplex_mp(model)
+
+
+@_optionals.HAS_CPLEX.require_in_call
+def read_from_lp_file(filename: str) -> QuadraticProgram:
+ """Loads the quadratic program from a LP file ('.lp' or compressed '.lp.gz').
+
+ Args:
+ filename: The filename of the file to be loaded.
+
+ Raises:
+ IOError: If the file type is not recognized, not supported or the file is not found.
+
+ Note:
+ This method requires CPLEX to be installed and present in ``PYTHONPATH``.
+ """
+
+ def _parse_problem_name(filename: str) -> str:
+ # Because docplex model reader uses the base name as model name,
+ # we parse the model name in the LP file manually.
+ # https://ibmdecisionoptimization.github.io/docplex-doc/mp/docplex.mp.model_reader.html
+ prefix = "\\Problem name:"
+ model_name = ""
+ with open(filename, encoding="utf8") as file:
+ for line in file:
+ if line.startswith(prefix):
+ model_name = line[len(prefix) :].strip()
+ if not line.startswith("\\"):
+ break
+ return model_name
+
+ return _read_from_file(filename, [".lp"], _parse_problem_name)
+
+
+@_optionals.HAS_CPLEX.require_in_call
+def read_from_mps_file(filename: str) -> QuadraticProgram:
+ """Loads the quadratic program from a MPS file ('.mps' or compressed '.mps.gz').
+
+ Args:
+ filename: The filename of the file to be loaded.
+
+ Raises:
+ FileNotFoundError: If the file does not exist.
+ IOError: If the file type is not recognized or not supported.
+
+ Note:
+ This method requires CPLEX to be installed and present in ``PYTHONPATH``.
+ """
+
+ def _parse_problem_name(filename: str) -> str:
+ # Because docplex model reader uses the base name as model name,
+ # we parse the model name in the LP file manually.
+ # https://ibmdecisionoptimization.github.io/docplex-doc/mp/docplex.mp.model_reader.html
+ prefix = "NAME "
+ model_name = ""
+ with open(filename, encoding="utf8") as file:
+ for line in file:
+ if line.startswith(prefix):
+ model_name = line[len(prefix) :].strip()
+ break
+ return model_name
+
+ return _read_from_file(filename, [".mps"], _parse_problem_name)
+
+
+def write_to_lp_file(q_p: QuadraticProgram, filename: str):
+ """Writes the quadratic program to an LP file.
+
+ Args:
+ q_p: The quadratic program to be exported.
+ filename: The filename of the file the model is written to.
+ If filename is a directory, file name 'my_problem.lp' is appended.
+ If filename does not end with '.lp', suffix '.lp' is appended.
+
+ Raises:
+ OSError: If this cannot open a file.
+ DOcplexException: If filename is an empty string
+ """
+ from .docplex_mp import to_docplex_mp
+
+ mdl = to_docplex_mp(q_p)
+ mdl.export_as_lp(filename)
+
+
+@_optionals.HAS_CPLEX.require_in_call
+def write_to_mps_file(q_p: QuadraticProgram, filename: str):
+ """Writes the quadratic program to an MPS file.
+
+ Args:
+ q_p: The quadratic program to be exported.
+ filename: The filename of the file the model is written to.
+ If filename is a directory, file name 'my_problem.mps' is appended.
+ If filename does not end with '.mps', suffix '.mps' is appended.
+
+ Raises:
+ OSError: If this cannot open a file.
+ DOcplexException: If filename is an empty string
+ """
+ from .docplex_mp import to_docplex_mp
+
+ mdl = to_docplex_mp(q_p)
+ full_path = mdl.export_as_mps(filename)
+
+ # docplex does not write the model's name out, so we do this here manually
+ with open(full_path, "r", encoding="utf8") as mps_file:
+ txt = mps_file.read()
+
+ with open(full_path, "w", encoding="utf8") as mps_file:
+ for line in txt.splitlines():
+ if line.startswith("NAME"):
+ mps_file.write(f"NAME {q_p.name}\n")
+ else:
+ mps_file.write(line + "\n")
diff --git a/releasenotes/notes/add-mps-gz-translator-support-e9d6750cb401e3c9.yaml b/releasenotes/notes/add-mps-gz-translator-support-e9d6750cb401e3c9.yaml
new file mode 100644
index 000000000..d8682c220
--- /dev/null
+++ b/releasenotes/notes/add-mps-gz-translator-support-e9d6750cb401e3c9.yaml
@@ -0,0 +1,16 @@
+---
+features:
+ - |
+ Support for reading and writing `mps` and `lp` files has moved to functions
+ :func:`translators.file_io.read_from_mps_file` and :func:`translators.file_io.read_from_lp_file`,
+ returning `QuadraticProgram` instances. In addition, both functions accept gzip'ed files as
+ `lp.gz` and `mps.gz`. In the same unit, :func:`translators.file_io.write_to_mps_file` and
+ :func:`translators.file_io.write_to_lp_file` have been added to write `QuadraticPrograms` to
+ `mps` and `lp` files.
+deprecations:
+ - |
+ Due to the new unit in `translators`, the functions
+ :meth:`~qiskit_optimization.problems.QuadraticProgram.export_as_lp_string`,
+ :meth:`~qiskit_optimization.problems.QuadraticProgram.read_from_lp_file`,
+ :meth:`~qiskit_optimization.problems.QuadraticProgram.write_to_lp_file`,
+ have been deprecated.
diff --git a/test/algorithms/test_cplex_optimizer.py b/test/algorithms/test_cplex_optimizer.py
index ecbba6d2f..01e37e798 100644
--- a/test/algorithms/test_cplex_optimizer.py
+++ b/test/algorithms/test_cplex_optimizer.py
@@ -21,6 +21,7 @@
import qiskit_optimization.optionals as _optionals
from qiskit_optimization.algorithms import CplexOptimizer, OptimizationResultStatus
from qiskit_optimization.problems import QuadraticProgram
+from qiskit_optimization.translators import read_from_lp_file
@ddt
@@ -44,7 +45,7 @@ def test_cplex_optimizer(self, config):
# load optimization problem
problem = QuadraticProgram()
lp_file = self.get_resource_path(filename, "algorithms/resources")
- problem.read_from_lp_file(lp_file)
+ problem = read_from_lp_file(lp_file)
# solve problem with cplex
result = cplex_optimizer.solve(problem)
@@ -69,7 +70,7 @@ def test_cplex_optimizer_no_solution(self, config):
# load optimization problem
problem = QuadraticProgram()
lp_file = self.get_resource_path(filename, "algorithms/resources")
- problem.read_from_lp_file(lp_file)
+ problem = read_from_lp_file(lp_file)
# solve problem with cplex
result = cplex_optimizer.solve(problem)
diff --git a/test/algorithms/test_gurobi_optimizer.py b/test/algorithms/test_gurobi_optimizer.py
index 713a5f3f2..4c6b3fd81 100644
--- a/test/algorithms/test_gurobi_optimizer.py
+++ b/test/algorithms/test_gurobi_optimizer.py
@@ -21,6 +21,7 @@
import qiskit_optimization.optionals as _optionals
from qiskit_optimization.algorithms import GurobiOptimizer
from qiskit_optimization.problems import QuadraticProgram
+from qiskit_optimization.translators import read_from_lp_file
@ddt
@@ -43,7 +44,7 @@ def test_gurobi_optimizer(self, config):
# load optimization problem
problem = QuadraticProgram()
lp_file = self.get_resource_path(filename, "algorithms/resources")
- problem.read_from_lp_file(lp_file)
+ problem = read_from_lp_file(lp_file)
# solve problem with gurobi
result = gurobi_optimizer.solve(problem)
diff --git a/test/algorithms/test_min_eigen_optimizer.py b/test/algorithms/test_min_eigen_optimizer.py
index c1251d6bb..1abba87a2 100644
--- a/test/algorithms/test_min_eigen_optimizer.py
+++ b/test/algorithms/test_min_eigen_optimizer.py
@@ -35,6 +35,7 @@
)
from qiskit_optimization.exceptions import QiskitOptimizationError
from qiskit_optimization.problems import QuadraticProgram
+from qiskit_optimization.translators import read_from_lp_file
@ddt
@@ -94,7 +95,7 @@ def test_min_eigen_optimizer(self, min_eigen_solver_name, shots, filename):
# load optimization problem
problem = QuadraticProgram()
lp_file = self.get_resource_path(filename, "algorithms/resources")
- problem.read_from_lp_file(lp_file)
+ problem = read_from_lp_file(lp_file)
# solve problem with cplex
cplex = CplexOptimizer(cplex_parameters={"threads": 1, "randomseed": 1})
@@ -137,7 +138,7 @@ def filter_criterion(x, v, aux):
# load optimization problem
problem = QuadraticProgram()
lp_file = self.get_resource_path(filename, "algorithms/resources")
- problem.read_from_lp_file(lp_file)
+ problem = read_from_lp_file(lp_file)
# solve problem
result = min_eigen_optimizer.solve(problem)
diff --git a/test/algorithms/test_recursive_optimization.py b/test/algorithms/test_recursive_optimization.py
index b3970563a..68c90792e 100644
--- a/test/algorithms/test_recursive_optimization.py
+++ b/test/algorithms/test_recursive_optimization.py
@@ -37,6 +37,7 @@
QuadraticProgramToQubo,
)
from qiskit_optimization.problems import QuadraticProgram
+from qiskit_optimization.translators import read_from_lp_file
class TestRecursiveMinEigenOptimizer(QiskitOptimizationTestCase):
@@ -58,7 +59,7 @@ def test_recursive_min_eigen_optimizer(self):
# load optimization problem
problem = QuadraticProgram()
lp_file = self.get_resource_path(filename, "algorithms/resources")
- problem.read_from_lp_file(lp_file)
+ problem = read_from_lp_file(lp_file)
# solve problem with cplex
cplex = CplexOptimizer()
@@ -78,7 +79,7 @@ def test_recursive_history(self):
# load optimization problem
problem = QuadraticProgram()
lp_file = self.get_resource_path(filename, "algorithms/resources")
- problem.read_from_lp_file(lp_file)
+ problem = read_from_lp_file(lp_file)
# get minimum eigen solver
min_eigen_solver = NumPyMinimumEigensolver()
@@ -144,7 +145,7 @@ def test_recursive_warm_qaoa(self):
# load optimization problem
problem = QuadraticProgram()
lp_file = self.get_resource_path("op_ip1.lp", "algorithms/resources")
- problem.read_from_lp_file(lp_file)
+ problem = read_from_lp_file(lp_file)
# solve problem with cplex
cplex = CplexOptimizer(cplex_parameters={"threads": 1, "randomseed": 1})
diff --git a/test/problems/resources/test_quadratic_program.lp.gz b/test/problems/resources/test_quadratic_program.lp.gz
new file mode 100644
index 000000000..93980b0ba
Binary files /dev/null and b/test/problems/resources/test_quadratic_program.lp.gz differ
diff --git a/test/problems/resources/test_quadratic_program.mps b/test/problems/resources/test_quadratic_program.mps
new file mode 100644
index 000000000..927c4023b
--- /dev/null
+++ b/test/problems/resources/test_quadratic_program.mps
@@ -0,0 +1,62 @@
+* ENCODING=ISO-8859-1
+NAME my problem
+ROWS
+ N obj1
+ E lin_eq
+ L lin_leq
+ G lin_geq
+ E quad_eq
+ L quad_leq
+ G quad_geq
+COLUMNS
+ MARK0000 'MARKER' 'INTORG'
+ x obj1 1
+ x lin_eq 1
+ x lin_leq 1
+ x lin_geq 1
+ x quad_eq 1
+ x quad_leq 1
+ x quad_geq 1
+ y obj1 -1
+ y lin_eq 2
+ y lin_leq 2
+ y lin_geq 2
+ y quad_eq 1
+ y quad_leq 1
+ y quad_geq 1
+ MARK0001 'MARKER' 'INTEND'
+ z obj1 10
+RHS
+ rhs obj1 -1
+ rhs lin_eq 1
+ rhs lin_leq 1
+ rhs lin_geq 1
+ rhs quad_eq 1
+ rhs quad_leq 1
+ rhs quad_geq 1
+BOUNDS
+ BV bnd x
+ LO bnd y -1
+ UP bnd y 5
+ LO bnd z -1
+ UP bnd z 5
+QMATRIX
+ x x 1
+ y z -1
+ z y -1
+QCMATRIX quad_eq
+ x x 1
+ y z -0.5
+ z y -0.5
+ z z 2
+QCMATRIX quad_leq
+ x x 1
+ y z -0.5
+ z y -0.5
+ z z 2
+QCMATRIX quad_geq
+ x x 1
+ y z -0.5
+ z y -0.5
+ z z 2
+ENDATA
diff --git a/test/problems/resources/test_quadratic_program.mps.gz b/test/problems/resources/test_quadratic_program.mps.gz
new file mode 100644
index 000000000..d7a8b4276
Binary files /dev/null and b/test/problems/resources/test_quadratic_program.mps.gz differ
diff --git a/test/problems/test_quadratic_program.py b/test/problems/test_quadratic_program.py
index a98aba9e8..cd2833968 100644
--- a/test/problems/test_quadratic_program.py
+++ b/test/problems/test_quadratic_program.py
@@ -740,12 +740,15 @@ def test_read_from_lp_file(self):
"""test read lp file"""
try:
q_p = QuadraticProgram()
- with self.assertRaises(FileNotFoundError):
+ with self.assertRaises(IOError):
q_p.read_from_lp_file("")
- with self.assertRaises(FileNotFoundError):
+ with self.assertRaises(IOError):
q_p.read_from_lp_file("no_file.txt")
+ with self.assertRaises(IOError):
+ q_p.read_from_lp_file("no_file.lp")
lp_file = self.get_resource_path("test_quadratic_program.lp", "problems/resources")
q_p.read_from_lp_file(lp_file)
+
self.assertEqual(q_p.name, "my problem")
self.assertEqual(q_p.get_num_vars(), 3)
self.assertEqual(q_p.get_num_binary_vars(), 1)
@@ -791,36 +794,36 @@ def test_read_from_lp_file(self):
self.assertEqual(cst[2].sense, Constraint.Sense.GE)
self.assertEqual(cst[2].rhs, 1)
- cst = q_p.quadratic_constraints
- self.assertEqual(cst[0].name, "quad_eq")
- self.assertDictEqual(cst[0].linear.to_dict(use_name=True), {"x": 1, "y": 1})
+ qst = q_p.quadratic_constraints
+ self.assertEqual(qst[0].name, "quad_eq")
+ self.assertDictEqual(qst[0].linear.to_dict(use_name=True), {"x": 1, "y": 1})
self.assertDictEqual(
- cst[0].quadratic.to_dict(use_name=True),
+ qst[0].quadratic.to_dict(use_name=True),
{("x", "x"): 1, ("y", "z"): -1, ("z", "z"): 2},
)
- self.assertEqual(cst[0].sense, Constraint.Sense.EQ)
- self.assertEqual(cst[0].rhs, 1)
- self.assertEqual(cst[1].name, "quad_leq")
- self.assertDictEqual(cst[1].linear.to_dict(use_name=True), {"x": 1, "y": 1})
+ self.assertEqual(qst[0].sense, Constraint.Sense.EQ)
+ self.assertEqual(qst[0].rhs, 1)
+ self.assertEqual(qst[1].name, "quad_leq")
+ self.assertDictEqual(qst[1].linear.to_dict(use_name=True), {"x": 1, "y": 1})
self.assertDictEqual(
- cst[1].quadratic.to_dict(use_name=True),
+ qst[1].quadratic.to_dict(use_name=True),
{("x", "x"): 1, ("y", "z"): -1, ("z", "z"): 2},
)
- self.assertEqual(cst[1].sense, Constraint.Sense.LE)
- self.assertEqual(cst[1].rhs, 1)
- self.assertEqual(cst[2].name, "quad_geq")
- self.assertDictEqual(cst[2].linear.to_dict(use_name=True), {"x": 1, "y": 1})
+ self.assertEqual(qst[1].sense, Constraint.Sense.LE)
+ self.assertEqual(qst[1].rhs, 1)
+ self.assertEqual(qst[2].name, "quad_geq")
+ self.assertDictEqual(qst[2].linear.to_dict(use_name=True), {"x": 1, "y": 1})
self.assertDictEqual(
- cst[2].quadratic.to_dict(use_name=True),
+ qst[2].quadratic.to_dict(use_name=True),
{("x", "x"): 1, ("y", "z"): -1, ("z", "z"): 2},
)
- self.assertEqual(cst[2].sense, Constraint.Sense.GE)
- self.assertEqual(cst[2].rhs, 1)
+ self.assertEqual(qst[2].sense, Constraint.Sense.GE)
+ self.assertEqual(qst[2].rhs, 1)
except RuntimeError as ex:
self.fail(str(ex))
def test_write_to_lp_file(self):
- """test write problem"""
+ """test write problem to lp file"""
q_p = QuadraticProgram("my problem")
q_p.binary_var("x")
q_p.integer_var(-1, 5, "y")
diff --git a/test/translators/test_docplex_mp.py b/test/translators/test_docplex_mp.py
index 3dbd7c6b2..87a3f5161 100644
--- a/test/translators/test_docplex_mp.py
+++ b/test/translators/test_docplex_mp.py
@@ -16,15 +16,17 @@
from docplex.mp.model import Model
+import qiskit_optimization.optionals as _optionals
from qiskit_optimization.exceptions import QiskitOptimizationError
from qiskit_optimization.problems import Constraint, QuadraticProgram
from qiskit_optimization.translators.docplex_mp import from_docplex_mp, to_docplex_mp
+from qiskit_optimization.translators import export_as_lp_string
class TestDocplexMpTranslator(QiskitOptimizationTestCase):
"""Test from_docplex_mp and to_docplex_mp"""
- def test_from_and_to(self):
+ def test_from_and_to_lp(self):
"""test from_docplex_mp and to_docplex_mp"""
q_p = QuadraticProgram("test")
q_p.binary_var(name="x")
@@ -38,7 +40,7 @@ def test_from_and_to(self):
q_p.linear_constraint({"x": 2, "z": -1}, "==", 1)
q_p.quadratic_constraint({"x": 2, "z": -1}, {("y", "z"): 3}, "==", 1)
q_p2 = from_docplex_mp(to_docplex_mp(q_p))
- self.assertEqual(q_p.export_as_lp_string(), q_p2.export_as_lp_string())
+ self.assertEqual(export_as_lp_string(q_p), export_as_lp_string(q_p2))
mod = Model("test")
x = mod.binary_var("x")
@@ -47,7 +49,7 @@ def test_from_and_to(self):
mod.minimize(1 + x + 2 * y - x * y + 2 * z * z)
mod.add(2 * x - z == 1, "c0")
mod.add(2 * x - z + 3 * y * z == 1, "q0")
- self.assertEqual(q_p.export_as_lp_string(), mod.export_as_lp_string())
+ self.assertEqual(export_as_lp_string(q_p), mod.export_as_lp_string())
def test_from_without_variable_names(self):
"""test from_docplex_mp without explicit variable names"""
diff --git a/test/translators/test_file_io.py b/test/translators/test_file_io.py
new file mode 100644
index 000000000..b02edc371
--- /dev/null
+++ b/test/translators/test_file_io.py
@@ -0,0 +1,326 @@
+# This code is part of a Qiskit project.
+#
+# (C) Copyright IBM 2024
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+"""Test file_io's LP/MPS import/export functions"""
+
+import tempfile
+import unittest
+from os import path
+
+from test.optimization_test_case import QiskitOptimizationTestCase
+
+from docplex.mp.model import DOcplexException
+
+import qiskit_optimization.optionals as _optionals
+from qiskit_optimization.problems import QuadraticProgram, Constraint, QuadraticObjective, Variable
+from qiskit_optimization.translators import (
+ read_from_lp_file,
+ read_from_mps_file,
+ write_to_lp_file,
+ write_to_mps_file,
+)
+
+
+class TestFileIOTranslator(QiskitOptimizationTestCase):
+ """Test Ex/Importers for LP and MPS files (including compressed forms)."""
+
+ def helper_test_read_problem_file(self, q_p: QuadraticProgram):
+ """evaluates the quadratic program read in file reading tests"""
+ self.assertEqual(q_p.name, "my problem")
+ self.assertEqual(q_p.get_num_vars(), 3)
+ self.assertEqual(q_p.get_num_binary_vars(), 1)
+ self.assertEqual(q_p.get_num_integer_vars(), 1)
+ self.assertEqual(q_p.get_num_continuous_vars(), 1)
+ self.assertEqual(q_p.get_num_linear_constraints(), 3)
+ self.assertEqual(q_p.get_num_quadratic_constraints(), 3)
+
+ self.assertEqual(q_p.variables[0].name, "x")
+ self.assertEqual(q_p.variables[0].vartype, Variable.Type.BINARY)
+ self.assertEqual(q_p.variables[0].lowerbound, 0)
+ self.assertEqual(q_p.variables[0].upperbound, 1)
+ self.assertEqual(q_p.variables[1].name, "y")
+ self.assertEqual(q_p.variables[1].vartype, Variable.Type.INTEGER)
+ self.assertEqual(q_p.variables[1].lowerbound, -1)
+ self.assertEqual(q_p.variables[1].upperbound, 5)
+ self.assertEqual(q_p.variables[2].name, "z")
+ self.assertEqual(q_p.variables[2].vartype, Variable.Type.CONTINUOUS)
+ self.assertEqual(q_p.variables[2].lowerbound, -1)
+ self.assertEqual(q_p.variables[2].upperbound, 5)
+
+ self.assertEqual(q_p.objective.sense, QuadraticObjective.Sense.MINIMIZE)
+ self.assertEqual(q_p.objective.constant, 1)
+ self.assertDictEqual(
+ q_p.objective.linear.to_dict(use_name=True), {"x": 1, "y": -1, "z": 10}
+ )
+ self.assertDictEqual(
+ q_p.objective.quadratic.to_dict(use_name=True),
+ {("x", "x"): 0.5, ("y", "z"): -1},
+ )
+
+ cst = q_p.linear_constraints
+ self.assertEqual(cst[0].name, "lin_eq")
+ self.assertDictEqual(cst[0].linear.to_dict(use_name=True), {"x": 1, "y": 2})
+ self.assertEqual(cst[0].sense, Constraint.Sense.EQ)
+ self.assertEqual(cst[0].rhs, 1)
+ self.assertEqual(cst[1].name, "lin_leq")
+ self.assertDictEqual(cst[1].linear.to_dict(use_name=True), {"x": 1, "y": 2})
+ self.assertEqual(cst[1].sense, Constraint.Sense.LE)
+ self.assertEqual(cst[1].rhs, 1)
+ self.assertEqual(cst[2].name, "lin_geq")
+ self.assertDictEqual(cst[2].linear.to_dict(use_name=True), {"x": 1, "y": 2})
+ self.assertEqual(cst[2].sense, Constraint.Sense.GE)
+ self.assertEqual(cst[2].rhs, 1)
+
+ qst = q_p.quadratic_constraints
+ self.assertEqual(qst[0].name, "quad_eq")
+ self.assertDictEqual(qst[0].linear.to_dict(use_name=True), {"x": 1, "y": 1})
+ self.assertDictEqual(
+ qst[0].quadratic.to_dict(use_name=True),
+ {("x", "x"): 1, ("y", "z"): -1, ("z", "z"): 2},
+ )
+ self.assertEqual(qst[0].sense, Constraint.Sense.EQ)
+ self.assertEqual(qst[0].rhs, 1)
+ self.assertEqual(qst[1].name, "quad_leq")
+ self.assertDictEqual(qst[1].linear.to_dict(use_name=True), {"x": 1, "y": 1})
+ self.assertDictEqual(
+ qst[1].quadratic.to_dict(use_name=True),
+ {("x", "x"): 1, ("y", "z"): -1, ("z", "z"): 2},
+ )
+ self.assertEqual(qst[1].sense, Constraint.Sense.LE)
+ self.assertEqual(qst[1].rhs, 1)
+ self.assertEqual(qst[2].name, "quad_geq")
+ self.assertDictEqual(qst[2].linear.to_dict(use_name=True), {"x": 1, "y": 1})
+ self.assertDictEqual(
+ qst[2].quadratic.to_dict(use_name=True),
+ {("x", "x"): 1, ("y", "z"): -1, ("z", "z"): 2},
+ )
+ self.assertEqual(qst[2].sense, Constraint.Sense.GE)
+ self.assertEqual(qst[2].rhs, 1)
+
+ def helper_test_write_problem_file(self):
+ """creates the quadratic program used in file write tests"""
+ q_p = QuadraticProgram("my problem")
+ q_p.binary_var("x")
+ q_p.integer_var(-1, 5, "y")
+ q_p.continuous_var(-1, 5, "z")
+ q_p.minimize(1, {"x": 1, "y": -1, "z": 10}, {("x", "x"): 0.5, ("y", "z"): -1})
+ q_p.linear_constraint({"x": 1, "y": 2}, "==", 1, "lin_eq")
+ q_p.linear_constraint({"x": 1, "y": 2}, "<=", 1, "lin_leq")
+ q_p.linear_constraint({"x": 1, "y": 2}, ">=", 1, "lin_geq")
+ q_p.quadratic_constraint(
+ {"x": 1, "y": 1},
+ {("x", "x"): 1, ("y", "z"): -1, ("z", "z"): 2},
+ "==",
+ 1,
+ "quad_eq",
+ )
+ q_p.quadratic_constraint(
+ {"x": 1, "y": 1},
+ {("x", "x"): 1, ("y", "z"): -1, ("z", "z"): 2},
+ "<=",
+ 1,
+ "quad_leq",
+ )
+ q_p.quadratic_constraint(
+ {"x": 1, "y": 1},
+ {("x", "x"): 1, ("y", "z"): -1, ("z", "z"): 2},
+ ">=",
+ 1,
+ "quad_geq",
+ )
+
+ return q_p
+
+ @unittest.skipIf(not _optionals.HAS_CPLEX, "CPLEX not available.")
+ def test_qp_read_from_lp_file(self):
+ """test read lp file"""
+ q_p = QuadraticProgram()
+
+ try:
+ with self.assertRaises(IOError):
+ q_p.read_from_lp_file("")
+ with self.assertRaises(IOError):
+ q_p.read_from_lp_file("no_file.txt")
+ with self.assertRaises(IOError):
+ q_p.read_from_lp_file("no_file.lp")
+ lp_file = self.get_resource_path("test_quadratic_program.lp", "problems/resources")
+ q_p.read_from_lp_file(lp_file)
+
+ self.helper_test_read_problem_file(q_p)
+ except RuntimeError as ex:
+ self.fail(str(ex))
+
+ @unittest.skipIf(not _optionals.HAS_CPLEX, "CPLEX not available.")
+ def test_read_from_lp_file(self):
+ """test read lp file"""
+ try:
+ with self.assertRaises(IOError):
+ read_from_lp_file("")
+ with self.assertRaises(IOError):
+ read_from_lp_file("no_file.txt")
+ with self.assertRaises(IOError):
+ read_from_lp_file("no_file.lp")
+ lp_file = self.get_resource_path("test_quadratic_program.lp", "problems/resources")
+ q_p = read_from_lp_file(lp_file)
+
+ self.helper_test_read_problem_file(q_p)
+ except RuntimeError as ex:
+ self.fail(str(ex))
+
+ @unittest.skipIf(not _optionals.HAS_CPLEX, "CPLEX not available.")
+ def test_read_from_mps_file(self):
+ """test read mps file"""
+ try:
+ with self.assertRaises(IOError):
+ read_from_mps_file("")
+ with self.assertRaises(IOError):
+ read_from_mps_file("no_file.txt")
+ with self.assertRaises(IOError):
+ read_from_mps_file("no_file.mps")
+ mps_file = self.get_resource_path("test_quadratic_program.mps", "problems/resources")
+ q_p = read_from_mps_file(mps_file)
+
+ self.helper_test_read_problem_file(q_p)
+ except RuntimeError as ex:
+ self.fail(str(ex))
+
+ @unittest.skipIf(not _optionals.HAS_CPLEX, "CPLEX not available.")
+ def test_read_from_lp_gz_file(self):
+ """test read compressed lp file"""
+ q_p = QuadraticProgram()
+ try:
+ with self.assertRaises(IOError):
+ read_from_lp_file("")
+ with self.assertRaises(IOError):
+ read_from_lp_file("no_file.txt")
+ with self.assertRaises(IOError):
+ read_from_lp_file("no_file.lp.gz")
+ mps_file = self.get_resource_path("test_quadratic_program.lp.gz", "problems/resources")
+ q_p = read_from_lp_file(mps_file)
+
+ self.helper_test_read_problem_file(q_p)
+ except RuntimeError as ex:
+ self.fail(str(ex))
+
+ @unittest.skipIf(not _optionals.HAS_CPLEX, "CPLEX not available.")
+ def test_read_from_mps_gz_file(self):
+ """test read compressed mps file"""
+ try:
+ with self.assertRaises(IOError):
+ read_from_mps_file("")
+ with self.assertRaises(IOError):
+ read_from_mps_file("no_file.txt")
+ with self.assertRaises(IOError):
+ read_from_mps_file("no_file.mps.gz")
+ mps_file = self.get_resource_path("test_quadratic_program.mps.gz", "problems/resources")
+ q_p = read_from_mps_file(mps_file)
+
+ self.helper_test_read_problem_file(q_p)
+ except RuntimeError as ex:
+ self.fail(str(ex))
+
+ def test_qp_write_to_lp_file(self):
+ """test write problem to lp file"""
+ q_p = self.helper_test_write_problem_file()
+
+ reference_file_name = self.get_resource_path(
+ "test_quadratic_program.lp", "problems/resources"
+ )
+ with tempfile.TemporaryDirectory() as tmp:
+ temp_output_path = path.join(tmp, "temp.lp")
+ q_p.write_to_lp_file(temp_output_path)
+ with open(reference_file_name, encoding="utf8") as reference, open(
+ temp_output_path, encoding="utf8"
+ ) as temp_output_file:
+ lines1 = temp_output_file.readlines()
+ lines2 = reference.readlines()
+ self.assertListEqual(lines1, lines2)
+
+ with tempfile.TemporaryDirectory() as temp_problem_dir:
+ q_p.write_to_lp_file(temp_problem_dir)
+ with open(path.join(temp_problem_dir, "my_problem.lp"), encoding="utf8") as file1, open(
+ reference_file_name, encoding="utf8"
+ ) as file2:
+ lines1 = file1.readlines()
+ lines2 = file2.readlines()
+ self.assertListEqual(lines1, lines2)
+
+ with self.assertRaises(OSError):
+ q_p.write_to_lp_file("/cannot/write/this/file.lp")
+
+ with self.assertRaises(DOcplexException):
+ q_p.write_to_lp_file("")
+
+ def test_write_to_lp_file(self):
+ """test write problem to lp file"""
+ q_p = self.helper_test_write_problem_file()
+
+ reference_file_name = self.get_resource_path(
+ "test_quadratic_program.lp", "problems/resources"
+ )
+ with tempfile.TemporaryDirectory() as tmp:
+ temp_output_path = path.join(tmp, "temp.lp")
+ write_to_lp_file(q_p, temp_output_path)
+ with open(reference_file_name, encoding="utf8") as reference, open(
+ temp_output_path, encoding="utf8"
+ ) as temp_output_file:
+ lines1 = temp_output_file.readlines()
+ lines2 = reference.readlines()
+ self.assertListEqual(lines1, lines2)
+
+ with tempfile.TemporaryDirectory() as temp_problem_dir:
+ write_to_lp_file(q_p, temp_problem_dir)
+ with open(path.join(temp_problem_dir, "my_problem.lp"), encoding="utf8") as file1, open(
+ reference_file_name, encoding="utf8"
+ ) as file2:
+ lines1 = file1.readlines()
+ lines2 = file2.readlines()
+ self.assertListEqual(lines1, lines2)
+
+ with self.assertRaises(OSError):
+ write_to_lp_file(q_p, "/cannot/write/this/file.lp")
+
+ with self.assertRaises(DOcplexException):
+ write_to_lp_file(q_p, "")
+
+ @unittest.skipIf(not _optionals.HAS_CPLEX, "CPLEX not available.")
+ def test_write_to_mps_file(self):
+ """test write problem to mps file"""
+ q_p = self.helper_test_write_problem_file()
+
+ reference_file_name = self.get_resource_path(
+ "test_quadratic_program.mps", "problems/resources"
+ )
+ with tempfile.TemporaryDirectory() as tmp:
+ temp_output_path = path.join(tmp, "temp.mps")
+ write_to_mps_file(q_p, temp_output_path)
+ with open(reference_file_name, encoding="utf8") as reference, open(
+ temp_output_path, encoding="utf8"
+ ) as temp_output_file:
+ lines1 = temp_output_file.readlines()
+ lines2 = reference.readlines()
+ self.assertListEqual(lines1, lines2)
+
+ with tempfile.TemporaryDirectory() as temp_problem_dir:
+ write_to_mps_file(q_p, temp_problem_dir)
+ with open(
+ path.join(temp_problem_dir, "my_problem.mps"), encoding="utf8"
+ ) as file1, open(reference_file_name, encoding="utf8") as file2:
+ lines1 = file1.readlines()
+ lines2 = file2.readlines()
+ self.assertListEqual(lines1, lines2)
+
+ with self.assertRaises(OSError):
+ write_to_mps_file(q_p, "/cannot/write/this/file.mps")
+
+ with self.assertRaises(DOcplexException):
+ write_to_mps_file(q_p, "")