Skip to content

Commit

Permalink
Properly deprecate quadratic program ising converter classes (qiskit-…
Browse files Browse the repository at this point in the history
…community#1178)

* Properly deprecate quadratic program ising converters

In qiskit-community#1061 the ising converter classes were removed without a deprecation
period and then backported to the stable branch. This is a violation of
both the Qiskit deprecation policy [1] and the Qiskit stable branch
policy [2] and should not have been merged like that. This is preventing
the Qiskit metapackage 0.20.0 from being released because the tutorials
as written today do not work with aqua 0.7.4 because of this breaking
change. It should have been deprecated first for an appropriate period
to give users a chance to adjust their code and then removed. During
this period the tutorial could also be updated. This also should never
have been backported to stable since users expect a stable point release
to just contain bugfixes removals and deprecations should not be part of
stable releases. This commit adds back the removed classes and deprecates
them, this will need to be backported and released as 0.7.5. While
normally this deprecation would not be allowed under the backport policy
it is necessary here because we already released a breaking change.

[1] https://qiskit.org/documentation/contributing_to_qiskit.html#deprecation-policy
[2] https://qiskit.org/documentation/contributing_to_qiskit.html#stable-branch-policy

* Fix typo

* Remove broken loop

* Add missing print methods

Running through the tutorials there are more methods that have been
removed that needed to go through a proper deprecation, this commit
starts adding them back. There is still print_details() that needs to be
added back.

* add unit test

* Add print_details to SummedOp

This is needed for backwards compat with the optimziation code which
used to return a legacy WeightedPauliOp object. 'print_details()' is
used in the tutorials on the output from conversion so a deprecated
method is added to add this funcionality to gracefully move users over
to the new workflow.

* fix lint

* fix docstring

Co-authored-by: Manoel Marques <manoel.marques@ibm.com>
  • Loading branch information
mtreinish and manoelmarques committed Aug 7, 2020
1 parent baeb4ae commit 374cef9
Show file tree
Hide file tree
Showing 7 changed files with 304 additions and 2 deletions.
14 changes: 14 additions & 0 deletions qiskit/aqua/operators/list_ops/summed_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
""" SummedOp Class """

from typing import List, Union, cast
import warnings

import numpy as np

Expand Down Expand Up @@ -160,6 +161,19 @@ def to_legacy_op(self, massive: bool = False) -> LegacyBaseOperator:

return self.combo_fn(legacy_ops) * coeff

def print_details(self):
"""
Print out the operator in details.
Returns:
str: a formatted string describes the operator.
"""
warnings.warn("print_details() is deprecated and will be removed in "
"a future release. Instead you can use .to_legacy_op() "
"and call print_details() on it's output",
DeprecationWarning)
ret = self.to_legacy_op().print_details()
return ret

def equals(self, other: OperatorBase) -> bool:
"""Check if other is equal to self.
Expand Down
8 changes: 7 additions & 1 deletion qiskit/optimization/converters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
IntegerToBinary
QuadraticProgramToQubo
LinearEqualityToPenalty
QuadraticProgramToIsing
IsingToQuadraticProgram
"""

Expand All @@ -40,11 +42,15 @@
from .inequality_to_equality import InequalityToEquality
from .linear_equality_to_penalty import LinearEqualityToPenalty
from .quadratic_program_to_qubo import QuadraticProgramToQubo
from .quadratic_program_to_ising import QuadraticProgramToIsing
from .ising_to_quadratic_program import IsingToQuadraticProgram


__all__ = [
"InequalityToEquality",
"IntegerToBinary",
"QuadraticProgramToQubo",
"LinearEqualityToPenalty"
"LinearEqualityToPenalty",
"QuadraticProgramToIsing",
"IsingToQuadraticProgram"
]
70 changes: 70 additions & 0 deletions qiskit/optimization/converters/ising_to_quadratic_program.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 2020.
#
# 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.


"""The converter from a ```Operator``` to ``QuadraticProgram``."""

from typing import Optional, Union
import copy
import warnings
import numpy as np # pylint: disable=unused-import

from qiskit.aqua.operators import OperatorBase, WeightedPauliOperator
from ..problems.quadratic_program import QuadraticProgram


class IsingToQuadraticProgram:
"""Convert a qubit operator into a quadratic program"""

def __init__(self, linear: bool = False) -> None:
r"""
Args:
linear: If linear is True, :math:`x^2` is treated as a linear term
since :math:`x^2 = x` for :math:`x \in \{0,1\}`.
Else, :math:`x^2` is treat as a quadratic term.
The default value is False.
"""
self._qubit_op = None
self._offset = 0.0
self._num_qubits = 0
self._qubo_matrix = None # type: Optional[np.ndarray]
self._qp = None # type: Optional[QuadraticProgram]
self._linear = linear
warnings.warn("The IsingToQuadraticProgram class is deprecated and "
"will be removed in a future release. Use the "
".from_ising() method on the QuadraticProgram class "
"instead.", DeprecationWarning)

def encode(self, qubit_op: Union[OperatorBase, WeightedPauliOperator], offset: float = 0.0
) -> QuadraticProgram:
"""Convert a qubit operator and a shift value into a quadratic program
Args:
qubit_op: The qubit operator to be converted into a
:class:`~qiskit.optimization.problems.quadratic_program.QuadraticProgram`
offset: The shift value of the qubit operator
Returns:
QuadraticProgram converted from the input qubit operator and the shift value
Raises:
QiskitOptimizationError: If there are Pauli Xs in any Pauli term
QiskitOptimizationError: If there are more than 2 Pauli Zs in any Pauli term
NotImplementedError: If the input operator is a ListOp
"""
self._qubit_op = qubit_op
self._offset = copy.deepcopy(offset)
self._num_qubits = qubit_op.num_qubits
self._qp = QuadraticProgram()
self._qp.from_ising(qubit_op, offset,
linear=self._linear)
return self._qp
49 changes: 49 additions & 0 deletions qiskit/optimization/converters/quadratic_program_to_ising.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 2020.
#
# 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.

"""The converter from an ```QuadraticProgram``` to ``Operator``."""

from typing import Tuple, Optional
import warnings

from qiskit.aqua.operators import OperatorBase
from ..problems.quadratic_program import QuadraticProgram


class QuadraticProgramToIsing:
"""Convert an optimization problem into a qubit operator."""

def __init__(self) -> None:
"""Initialize the internal data structure."""
self._src = None # type: Optional[QuadraticProgram]
warnings.warn("The QuadraticProgramToIsing class is deprecated and "
"will be removed in a future release. Use the "
".to_ising() method on a QuadraticProgram object "
"instead.", DeprecationWarning)

def encode(self, op: QuadraticProgram) -> Tuple[OperatorBase, float]:
"""Convert a problem into a qubit operator
Args:
op: The optimization problem to be converted. Must be an unconstrained problem with
binary variables only.
Returns:
The qubit operator of the problem and the shift value.
Raises:
QiskitOptimizationError: If a variable type is not binary.
QiskitOptimizationError: If constraints exist in the problem.
"""

self._src = op
return self._src.to_ising()
25 changes: 25 additions & 0 deletions qiskit/optimization/problems/quadratic_program.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from collections import defaultdict
from enum import Enum
from math import fsum
import warnings

from docplex.mp.constr import (LinearConstraint as DocplexLinearConstraint,
QuadraticConstraint as DocplexQuadraticConstraint,
Expand Down Expand Up @@ -774,6 +775,30 @@ def export_as_lp_string(self) -> str:
"""
return self.to_docplex().export_as_lp_string()

def pprint_as_string(self) -> str:
"""Returns the quadratic program as a string in Docplex's pretty print format.
Returns:
A string representing the quadratic program.
"""
warnings.warn("The pprint_as_string method is deprecated and will be "
"removed in a future release. Instead use the"
"to_docplex() method and run pprint_as_string() on that "
"output", DeprecationWarning)
return self.to_docplex().pprint_as_string()

def prettyprint(self, out: Optional[str] = None) -> None:
"""Pretty prints the quadratic program to a given output stream (None = default).
Args:
out: The output stream or file name to print to.
if you specify a file name, the output file name is has '.mod' as suffix.
"""
warnings.warn("The prettyprint method is deprecated and will be "
"removed in a future release. Instead use the"
"to_docplex() method and run prettyprint() on that "
"output", DeprecationWarning)
self.to_docplex().prettyprint(out)

def read_from_lp_file(self, filename: str) -> None:
"""Loads the quadratic program from a LP file.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
deprecations:
- |
The ising convert classes
:class:`qiskit.optimization.converters.QuadraticProgramToIsing` and
:class:`qiskit.optimization.converters.IsingToQuadraticProgram` have
been deprecated and will be removed in a future release. Instead the
:class:`qiskit.optimization.QuadraticProgram` methods
:meth:`~qiskit.optimization.QuadraticProgram.to_ising` and
:meth:`~qiskit.optimization.QuadraticPrgraom.from_ising` should be used
instead.
- |
The ``pprint_as_string`` method for
:class:`qiskit.optimization.QuadraticProgram` has been deprecated and will
be removed in a future release. Instead you should just run
``.pprint_as_string()`` on the output from
:meth:`~qiskit.optimization.QuadraticProgram.to_docplex`
- |
The ``prettyprint`` method for
:class:`qiskit.optimization.QuadraticProgram` has been deprecated and will
be removed in a future release. Instead you should just run
``.prettyprint()`` on the output from
:meth:`~qiskit.optimization.QuadraticProgram.to_docplex`
117 changes: 116 additions & 1 deletion test/optimization/test_converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
from qiskit.optimization.algorithms.admm_optimizer import ADMMParameters
from qiskit.optimization.algorithms.optimization_algorithm import OptimizationResultStatus
from qiskit.optimization.converters import (InequalityToEquality, IntegerToBinary,
LinearEqualityToPenalty)
LinearEqualityToPenalty, QuadraticProgramToIsing,
IsingToQuadraticProgram)
from qiskit.optimization.problems import Constraint, Variable

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -559,6 +560,120 @@ def test_linear_equality_to_penalty_decode(self):
self.assertListEqual(infeasible_result.variable_names, ['x', 'y', 'z'])
self.assertDictEqual(infeasible_result.variables_dict, {'x': 1.0, 'y': 1.0, 'z': 1.0})

def test_empty_problem_deprecated(self):
""" Test empty problem """
op = QuadraticProgram()
conv = InequalityToEquality()
op = conv.encode(op)
conv = IntegerToBinary()
op = conv.encode(op)
conv = LinearEqualityToPenalty()
op = conv.encode(op)
conv = QuadraticProgramToIsing()
_, shift = conv.encode(op)
self.assertEqual(shift, 0.0)

def test_valid_variable_type_deprecated(self):
"""Validate the types of the variables for QuadraticProgramToIsing."""
# Integer variable
with self.assertRaises(QiskitOptimizationError):
op = QuadraticProgram()
op.integer_var(0, 10, "int_var")
conv = QuadraticProgramToIsing()
_ = conv.encode(op)
# Continuous variable
with self.assertRaises(QiskitOptimizationError):
op = QuadraticProgram()
op.continuous_var(0, 10, "continuous_var")
conv = QuadraticProgramToIsing()
_ = conv.encode(op)

def test_optimizationproblem_to_ising_deprecated(self):
""" Test optimization problem to operators"""
op = QuadraticProgram()
for i in range(4):
op.binary_var(name='x{}'.format(i))
linear = {}
for x in op.variables:
linear[x.name] = 1
op.maximize(0, linear, {})
linear = {}
for i, x in enumerate(op.variables):
linear[x.name] = i + 1
op.linear_constraint(linear, Constraint.Sense.EQ, 3, 'sum1')
penalize = LinearEqualityToPenalty(penalty=1e5)
op2ope = QuadraticProgramToIsing()
op2 = penalize.encode(op)
qubitop, offset = op2ope.encode(op2)

self.assertEqual(qubitop, QUBIT_OP_MAXIMIZE_SAMPLE)
self.assertEqual(offset, OFFSET_MAXIMIZE_SAMPLE)

def test_ising_to_quadraticprogram_linear_deprecated(self):
""" Test optimization problem to operators with linear=True"""
op = QUBIT_OP_MAXIMIZE_SAMPLE
offset = OFFSET_MAXIMIZE_SAMPLE

op2qp = IsingToQuadraticProgram(linear=True)
quadratic = op2qp.encode(op, offset)

self.assertEqual(len(quadratic.variables), 4)
self.assertEqual(len(quadratic.linear_constraints), 0)
self.assertEqual(len(quadratic.quadratic_constraints), 0)
self.assertEqual(quadratic.objective.sense, quadratic.objective.Sense.MINIMIZE)
self.assertAlmostEqual(quadratic.objective.constant, 900000)

linear_matrix = np.zeros((1, 4))
linear_matrix[0, 0] = -500001
linear_matrix[0, 1] = -800001
linear_matrix[0, 2] = -900001
linear_matrix[0, 3] = -800001

quadratic_matrix = np.zeros((4, 4))
quadratic_matrix[0, 1] = 400000
quadratic_matrix[0, 2] = 600000
quadratic_matrix[1, 2] = 1200000
quadratic_matrix[0, 3] = 800000
quadratic_matrix[1, 3] = 1600000
quadratic_matrix[2, 3] = 2400000

np.testing.assert_array_almost_equal(
quadratic.objective.linear.coefficients.toarray(), linear_matrix
)
np.testing.assert_array_almost_equal(
quadratic.objective.quadratic.coefficients.toarray(), quadratic_matrix
)

def test_ising_to_quadraticprogram_quadratic_deprecated(self):
""" Test optimization problem to operators with linear=False"""
op = QUBIT_OP_MAXIMIZE_SAMPLE
offset = OFFSET_MAXIMIZE_SAMPLE

op2qp = IsingToQuadraticProgram(linear=False)
quadratic = op2qp.encode(op, offset)

self.assertEqual(len(quadratic.variables), 4)
self.assertEqual(len(quadratic.linear_constraints), 0)
self.assertEqual(len(quadratic.quadratic_constraints), 0)
self.assertEqual(quadratic.objective.sense, quadratic.objective.Sense.MINIMIZE)
self.assertAlmostEqual(quadratic.objective.constant, 900000)

quadratic_matrix = np.zeros((4, 4))
quadratic_matrix[0, 0] = -500001
quadratic_matrix[0, 1] = 400000
quadratic_matrix[0, 2] = 600000
quadratic_matrix[0, 3] = 800000
quadratic_matrix[1, 1] = -800001
quadratic_matrix[1, 2] = 1200000
quadratic_matrix[1, 3] = 1600000
quadratic_matrix[2, 2] = -900001
quadratic_matrix[2, 3] = 2400000
quadratic_matrix[3, 3] = -800001

np.testing.assert_array_almost_equal(
quadratic.objective.quadratic.coefficients.toarray(), quadratic_matrix
)


if __name__ == '__main__':
unittest.main()

0 comments on commit 374cef9

Please sign in to comment.