SimoneGasperini / qiskit-symb

Python package for the symbolic evaluation of parameterized quantum circuits in Qiskit
https://pypi.org/project/qiskit-symb/
Apache License 2.0
26 stars 2 forks source link

`XXMinusYYGate`, `XXPlusYYGate` and `DCXGate` are not supported #2

Closed SimoneGasperini closed 7 months ago

SimoneGasperini commented 1 year ago

The following Qiskit gates are not supported and have to be implemented from scratch:

As shown in the code below, trying to use qiskit-symb on a circuit containing one of these gates raises a NotImplementedError. At the moment, the best workaround is to decompose the circuit just before its symbolic evaluation.

from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.circuit.library import XXMinusYYGate
from qiskit_symb import Operator

qc = QuantumCircuit(2)
theta = Parameter('theta')
gate = XXMinusYYGate(theta)
qc.append(gate, [0, 1])

#qc = qc.decompose()
op = Operator(qc)

To successfully implement a gate acting on $N$ qubits in qiskit-symb, the first step is to find its mathematical representation as a linear combination of tensor products between $N$ complex 2 $\times$ 2 matrices (see here for further details and suggestions).

SimoneGasperini commented 1 year ago

The following utility function can be used to get the Pauli decomposition (linear combination of tensor products between Pauli matrices) of any valid quantum gate represented as a sympy matrix, including parametric gates such as XXMinusYYGate and XXPlusYYGate (see here for the mathematical formulation).

import itertools
import numpy as np
from sympy.physics.quantum import TensorProduct
from qiskit_symb.circuit.library import IGate, XGate, YGate, ZGate

paulis = {
    'I': IGate().to_sympy(),
    'X': XGate().to_sympy(),
    'Y': YGate().to_sympy(),
    'Z': ZGate().to_sympy()
}

def pauli_decomposition(matrix):
    num_qubits = int(np.log2(matrix.shape[0]))
    factor = 1 / 2**num_qubits
    pauli_terms = {}
    for pauli_tuple in itertools.product('IXYZ', repeat=num_qubits):
        trace = (TensorProduct(*[paulis[p] for p in pauli_tuple]) * matrix).trace()
        if trace:
            pauli_string = ''.join(pauli_tuple)
            pauli_terms[pauli_string] = factor * trace
    return pauli_terms
SimoneGasperini commented 1 year ago

Here are the Pauli terms decomposition of the XXMinusYYGate, XXPlusYYGate, and DCXGate computed by calling the pauli_decomposition function defined above and passing the corresponding sympy matrix.

from sympy import Symbol, cos, sin, exp, I
from sympy.matrices import Matrix

theta = Symbol('θ')
beta = Symbol('β')

xx_minus_yy = Matrix([[cos(theta/2), 0, 0, -I*sin(theta/2)*exp(-I*beta)],
                      [0, 1, 0, 0],
                      [0, 0, 1, 0],
                      [-I*sin(theta/2)*exp(I*beta), 0, 0, cos(theta/2)]])

xx_plus_yy = Matrix([[1, 0, 0, 0],
                     [0, cos(theta/2), -I*sin(theta/2)*exp(-I*beta), 0],
                     [0, -I*sin(theta/2)*exp(I*beta), cos(theta/2), 0],
                     [0, 0, 0, 1]])

dcx = Matrix([[1, 0, 0, 0],
              [0, 0, 0, 1],
              [0, 1, 0, 0],
              [0, 0, 1, 0]])
########## XXMinusYYGate ##########
{'II': 0.5*cos(θ/2) + 0.5,
 'XX': -0.25*I*exp(I*β)*sin(θ/2) - 0.25*I*exp(-I*β)*sin(θ/2),
 'XY': -0.25*exp(I*β)*sin(θ/2) + 0.25*exp(-I*β)*sin(θ/2),
 'YX': -0.25*exp(I*β)*sin(θ/2) + 0.25*exp(-I*β)*sin(θ/2),
 'YY': 0.25*I*exp(I*β)*sin(θ/2) + 0.25*I*exp(-I*β)*sin(θ/2),
 'ZZ': 0.5*cos(θ/2) - 0.5}

########## XXPlusYYGate ##########
{'II': 0.5*cos(θ/2) + 0.5,
 'XX': -0.25*I*exp(I*β)*sin(θ/2) - 0.25*I*exp(-I*β)*sin(θ/2),
 'XY': 0.25*exp(I*β)*sin(θ/2) - 0.25*exp(-I*β)*sin(θ/2),
 'YX': -0.25*exp(I*β)*sin(θ/2) + 0.25*exp(-I*β)*sin(θ/2),
 'YY': -0.25*I*exp(I*β)*sin(θ/2) - 0.25*I*exp(-I*β)*sin(θ/2),
 'ZZ': 0.5 - 0.5*cos(θ/2)}

########## DCXGate ##########
{'II': 0.25,
 'IX': 0.25,
 'IY': -0.25*I,
 'IZ': 0.25,
 'XI': 0.25,
 'XX': 0.25,
 'XY': 0.25*I,
 'XZ': -0.25,
 'YI': 0.25*I,
 'YX': -0.25*I,
 'YY': 0.25,
 'YZ': -0.25*I,
 'ZI': 0.25,
 'ZX': -0.25,
 'ZY': 0.25*I,
 'ZZ': 0.25}