Skip to content
1 change: 1 addition & 0 deletions .pylintdict
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ wavefunction
wecker
williamson
xs
ys
zemlin
zi
zj
Expand Down
198 changes: 11 additions & 187 deletions qiskit_optimization/problems/quadratic_program.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,10 @@
import numpy as np
from docplex.mp.model import Model
from docplex.mp.model_reader import ModelReader
from numpy import ndarray, zeros
from scipy.sparse import spmatrix

from numpy import ndarray
from qiskit.exceptions import MissingOptionalLibraryError
from qiskit.opflow import I, ListOp, OperatorBase, PauliOp, PauliSumOp, SummedOp
from qiskit.quantum_info import Pauli
from qiskit.opflow import OperatorBase, PauliSumOp
from scipy.sparse import spmatrix

from ..exceptions import QiskitOptimizationError
from ..infinity import INFINITY
Expand Down Expand Up @@ -1015,94 +1013,10 @@ def to_ising(self) -> Tuple[OperatorBase, float]:
QiskitOptimizationError: If a variable type is not binary.
QiskitOptimizationError: If constraints exist in the problem.
"""
# if problem has variables that are not binary, raise an error
if self.get_num_vars() > self.get_num_binary_vars():
raise QiskitOptimizationError(
"The type of variable must be a binary variable. "
"Use a QuadraticProgramToQubo converter to convert "
"integer variables to binary variables. "
"If the problem contains continuous variables, "
"currently we can not apply VQE/QAOA directly. "
"you might want to use an ADMM optimizer "
"for the problem. "
)

# if constraints exist, raise an error
if self.linear_constraints or self.quadratic_constraints:
raise QiskitOptimizationError(
"An constraint exists. "
"The method supports only model with no constraints. "
"Use a QuadraticProgramToQubo converter. "
"It converts inequality constraints to equality "
"constraints, and then, it converters equality "
"constraints to penalty terms of the object function."
)

# initialize Hamiltonian.
num_nodes = self.get_num_vars()
pauli_list = []
offset = 0.0
zero = zeros(num_nodes, dtype=bool)

# set a sign corresponding to a maximized or minimized problem.
# sign == 1 is for minimized problem. sign == -1 is for maximized problem.
sense = self.objective.sense.value

# convert a constant part of the object function into Hamiltonian.
offset += self.objective.constant * sense

# convert linear parts of the object function into Hamiltonian.
for idx, coef in self.objective.linear.to_dict().items():
z_p = zeros(num_nodes, dtype=bool)
weight = coef * sense / 2
z_p[idx] = True

pauli_list.append([-weight, Pauli((z_p, zero))])
offset += weight

# convert quadratic parts of the object function into Hamiltonian.
# first merge coefficients (i, j) and (j, i)
coeffs = {} # type: Dict
for (i, j), coeff in self.objective.quadratic.to_dict().items():
if j < i: # type: ignore
coeffs[(j, i)] = coeffs.get((j, i), 0.0) + coeff
else:
coeffs[(i, j)] = coeffs.get((i, j), 0.0) + coeff

# create Pauli terms
for (i, j), coeff in coeffs.items():

weight = coeff * sense / 4

if i == j:
offset += weight
else:
z_p = zeros(num_nodes, dtype=bool)
z_p[i] = True
z_p[j] = True
pauli_list.append([weight, Pauli((z_p, zero))])

z_p = zeros(num_nodes, dtype=bool)
z_p[i] = True
pauli_list.append([-weight, Pauli((z_p, zero))])

z_p = zeros(num_nodes, dtype=bool)
z_p[j] = True
pauli_list.append([-weight, Pauli((z_p, zero))])

offset += weight

# Remove paulis whose coefficients are zeros.
qubit_op = sum(PauliOp(pauli, coeff=coeff) for coeff, pauli in pauli_list)

# qubit_op could be the integer 0, in this case return an identity operator of
# appropriate size
if isinstance(qubit_op, OperatorBase):
qubit_op = qubit_op.reduce()
else:
qubit_op = I ^ num_nodes
# pylint: disable=cyclic-import
from ..translators.ising import to_ising

return qubit_op, offset
return to_ising(self)
Comment thread
woodsp-ibm marked this conversation as resolved.

def from_ising(
self,
Expand All @@ -1129,102 +1043,12 @@ def from_ising(
QiskitOptimizationError: If there are more than 2 Pauli Zs in any Pauli term
NotImplementedError: If the input operator is a ListOp
"""
if isinstance(qubit_op, PauliSumOp):
qubit_op = qubit_op.to_pauli_op()

# No support for ListOp yet, this can be added in future
# pylint: disable=unidiomatic-typecheck
if type(qubit_op) == ListOp:
raise NotImplementedError(
"Conversion of a ListOp is not supported, convert each "
"operator in the ListOp separately."
)

# add binary variables
for i in range(qubit_op.num_qubits):
self.binary_var(name="x_{0}".format(i))

# Create a QUBO matrix
# The Qubo matrix is an upper triangular matrix.
# Diagonal elements in the QUBO matrix are for linear terms of the qubit operator.
# The other elements in the QUBO matrix are for quadratic terms of the qubit operator.
qubo_matrix = zeros((qubit_op.num_qubits, qubit_op.num_qubits))

if not isinstance(qubit_op, SummedOp):
pauli_list = [qubit_op.to_pauli_op()]
else:
pauli_list = qubit_op.to_pauli_op()

for pauli_op in pauli_list:
pauli_op = pauli_op.to_pauli_op()
pauli = pauli_op.primitive
coeff = pauli_op.coeff
# Count the number of Pauli Zs in a Pauli term
lst_z = pauli.z.tolist()
z_index = [i for i, z in enumerate(lst_z) if z is True]
num_z = len(z_index)

# Add its weight of the Pauli term to the corresponding element of QUBO matrix
if num_z == 1:
qubo_matrix[z_index[0], z_index[0]] = coeff.real
elif num_z == 2:
qubo_matrix[z_index[0], z_index[1]] = coeff.real
else:
raise QiskitOptimizationError(
"There are more than 2 Pauli Zs in the Pauli term {}".format(pauli.z)
)
# pylint: disable=cyclic-import
from ..translators.ising import from_ising

# If there are Pauli Xs in the Pauli term, raise an error
lst_x = pauli.x.tolist()
x_index = [i for i, x in enumerate(lst_x) if x is True]
if len(x_index) > 0:
raise QiskitOptimizationError("Pauli Xs exist in the Pauli {}".format(pauli.x))

# Initialize dicts for linear terms and quadratic terms
linear_terms = {}
quadratic_terms = {}

# For quadratic pauli terms of operator
# x_i * x_ j = (1 - Z_i - Z_j + Z_i * Z_j)/4
for i, row in enumerate(qubo_matrix):
for j, weight in enumerate(row):
# Focus on the upper triangular matrix
if j <= i:
continue
# Add a quadratic term to the object function of `QuadraticProgram`
# The coefficient of the quadratic term in `QuadraticProgram` is
# 4 * weight of the pauli
coef = weight * 4
quadratic_terms[i, j] = coef
# Sub the weight of the quadratic pauli term from the QUBO matrix
qubo_matrix[i, j] -= weight
# Sub the weight of the linear pauli term from the QUBO matrix
qubo_matrix[i, i] += weight
qubo_matrix[j, j] += weight
# Sub the weight from offset
offset -= weight

# After processing quadratic pauli terms, only linear paulis are left
# x_i = (1 - Z_i)/2
for i in range(qubit_op.num_qubits):
weight = qubo_matrix[i, i]
# Add a linear term to the object function of `QuadraticProgram`
# The coefficient of the linear term in `QuadraticProgram` is
# 2 * weight of the pauli
coef = weight * 2
if linear:
# If the linear option is True, add it into linear_terms
linear_terms[i] = -coef
else:
# Else, add it into quadratic_terms as a diagonal element.
quadratic_terms[i, i] = -coef
# Sub the weight of the linear pauli term from the QUBO matrix
qubo_matrix[i, i] -= weight
offset += weight

# Set the objective function
self.minimize(constant=offset, linear=linear_terms, quadratic=quadratic_terms)
offset -= offset
other = from_ising(qubit_op, offset, linear)
for attr, val in vars(other).items():
setattr(self, attr, val)

def get_feasibility_info(
self, x: Union[List[float], np.ndarray]
Expand Down
12 changes: 11 additions & 1 deletion qiskit_optimization/translators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,19 @@
to_docplex_mp
from_gurobipy
to_gurobipy
from_ising
to_ising
"""

from .docplex_mp import from_docplex_mp, to_docplex_mp
from .gurobipy import from_gurobipy, to_gurobipy
from .ising import from_ising, to_ising

_all = ["from_docplex_mp", "to_docplex_mp", "from_gurobipy", "to_gurobipy"]
_all = [
"from_docplex_mp",
"to_docplex_mp",
"from_gurobipy",
"to_gurobipy",
"from_ising",
"to_ising",
]
Loading