Source code for qiskit.optimization.converters.ising_to_quadratic_program

# -*- 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``."""

import copy

import numpy as np

from qiskit.aqua.operators.legacy import WeightedPauliOperator
from ..problems.quadratic_program import QuadraticProgram
from ..exceptions import QiskitOptimizationError


[docs]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 self._num_qubits = 0 self._qubo_matrix = None self._qp = None self._linear = linear
[docs] def encode(self, qubit_op: 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 """ # Set properties self._qubit_op = qubit_op self._offset = copy.deepcopy(offset) self._num_qubits = qubit_op.num_qubits # Create `QuadraticProgram` self._qp = QuadraticProgram() for i in range(self._num_qubits): self._qp.binary_var(name='x_{0}'.format(i)) # Create QUBO matrix self._create_qubo_matrix() # 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(self._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 self._qubo_matrix[i, j] -= weight # Sub the weight of the linear pauli term from the QUBO matrix self._qubo_matrix[i, i] += weight self._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(self._num_qubits): weight = self._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 self._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 self._qubo_matrix[i, i] -= weight offset += weight # Set the objective function self._qp.minimize(constant=offset, linear=linear_terms, quadratic=quadratic_terms) offset -= offset return self._qp
def _create_qubo_matrix(self): """Create a QUBO matrix from the qubit operator Raises: QiskitOptimizationError: If there are Pauli Xs in any Pauli term QiskitOptimizationError: If there are more than 2 Pauli Zs in any Pauli term """ # Set properties # The Qubo matrix is an upper triangular matrix. # Diagonal elements in the QUBO matrix is for linear terms of the qubit operator # The other elements in the QUBO matrix is for quadratic terms of the qubit operator self._qubo_matrix = np.zeros((self._num_qubits, self._num_qubits)) for pauli in self._qubit_op.paulis: # Count the number of Pauli Zs in a Pauli term lst_z = pauli[1].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: self._qubo_matrix[z_index[0], z_index[0]] = pauli[0].real elif num_z == 2: self._qubo_matrix[z_index[0], z_index[1]] = pauli[0].real else: raise QiskitOptimizationError( 'There are more than 2 Pauli Zs in the Pauli term {}'.format(pauli[1].z) ) # If there are Pauli Xs in the Pauli term, raise an error lst_x = pauli[1].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[1].x))