Skip to content

SimoneGasperini/qiskit-symb

Repository files navigation


Qiskit DemoDays presentation $\rightarrow$ 🔗link, password: Demoday20230615 (15th June 2023)

Jupyter notebook demo $\rightarrow$ 🔗link (15th June 2023)

Qiskit Medium blog $\rightarrow$ 🔗link (28th June 2023)


Table of contents

Introduction

The qiskit-symb package is meant to be a Python tool to enable the symbolic evaluation of parametric quantum states and operators defined in Qiskit by parameterized quantum circuits.

A Parameterized Quantum Circuit (PQC) is a quantum circuit where we have at least one free parameter (e.g. a rotation angle $\theta$). PQCs are particularly relevant in Quantum Machine Learning (QML) models, where the values of these parameters can be learned during training to reach the desired output.

In particular, qiskit-symb can be used to create a symbolic representation of a parametric quantum statevector, density matrix, or unitary operator directly from the Qiskit quantum circuit. This has been achieved through the re-implementation of some basic classes defined in the qiskit/quantum_info/ module by using sympy as a backend for symbolic expressions manipulation.

Installation

User-mode

pip install qiskit-symb

⚠️ The package requires qiskit>=1. See the official Migration guides if you are used to a prevoius Qiskit version.

Dev-mode

git clone https://github.com/SimoneGasperini/qiskit-symb.git
cd qiskit-symb
pip install -e .

Usage examples

Sympify a Qiskit circuit

Let's get started on how to use qiskit-symb to get the symbolic representation of a given Qiskit circuit. In particular, in this first basic example, we consider the following quantum circuit:

from qiskit import QuantumCircuit
from qiskit.circuit import Parameter, ParameterVector

y = Parameter('y')
p = ParameterVector('p', length=2)

pqc = QuantumCircuit(2)
pqc.ry(y, 0)
pqc.cx(0, 1)
pqc.u(0, *p, 1)

pqc.draw('mpl')

To get the sympy representation of the unitary matrix corresponding to the parameterized circuit, we just have to create the symbolic Operator instance and call the to_sympy() method:

from qiskit_symb.quantum_info import Operator

op = Operator(pqc)
op.to_sympy()
$$\left[\begin{matrix}\cos{\left(\frac{y}{2} \right)} & - \sin{\left(\frac{y}{2} \right)} & 0 & 0\\0 & 0 & \sin{\left(\frac{y}{2} \right)} & \cos{\left(\frac{y}{2} \right)}\\0 & 0 & e^{i \left(p[0] + p[1]\right)} \cos{\left(\frac{y}{2} \right)} & - e^{i \left(p[0] + p[1]\right)} \sin{\left(\frac{y}{2} \right)}\\e^{i \left(p[0] + p[1]\right)} \sin{\left(\frac{y}{2} \right)} & e^{i \left(p[0] + p[1]\right)} \cos{\left(\frac{y}{2} \right)} & 0 & 0\end{matrix}\right]$$

If you want then to assign a value to some specific parameter, you can use the subs(<dict>) method passing a dictionary that maps each parameter to the desired corresponding value:

new_op = op.subs({p: [-1, 2]})
new_op.to_sympy()
$$\left[\begin{matrix}\cos{\left(\frac{y}{2} \right)} & - \sin{\left(\frac{y}{2} \right)} & 0 & 0\\0 & 0 & \sin{\left(\frac{y}{2} \right)} & \cos{\left(\frac{y}{2} \right)}\\0 & 0 & e^{i} \cos{\left(\frac{y}{2} \right)} & - e^{i} \sin{\left(\frac{y}{2} \right)}\\e^{i} \sin{\left(\frac{y}{2} \right)} & e^{i} \cos{\left(\frac{y}{2} \right)} & 0 & 0\end{matrix}\right]$$

Lambdify a Qiskit circuit

Given a Qiskit circuit, qiskit-symb also allows to generate a Python lambda function with actual arguments matching the Qiskit unbound parameters. Let's consider the following example starting from a ZZFeatureMap circuit, commonly used as a data embedding ansatz in QML applications:

from qiskit.circuit.library import ZZFeatureMap

pqc = ZZFeatureMap(feature_dimension=3, reps=1)
pqc.draw('mpl')

To get the Python function representing the final parameteric statevector, we just have to create the symbolic Statevector instance and call the to_lambda() method:

from qiskit_symb.quantum_info import Statevector

pqc = pqc.decompose()
statevec = Statevector(pqc).to_lambda()

We can now call the lambda-generated function statevec passing the x values we want to assign to each parameter. The returned object will be a numpy 2D-array (with shape=(8,1) in this case) representing the final output statevector psi.

x = [1.24, 2.27, 0.29]
psi = statevec(*x)

This feature can be useful when, given a Qiskit PQC, we want to run it multiple times with different parameters values. Indeed, we can perform a single symbolic evalutation and then call the lambda generated function as many times as needed, passing different values of the parameters at each iteration.

Contributors


Simone Gasperini