From fe3bffbc85a4fb57dc62ba04fa7f11bcefb5d75f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20Pe=C3=B1a=20Tapia?= <57907331+ElePT@users.noreply.github.com> Date: Mon, 23 Oct 2023 15:49:47 +0200 Subject: [PATCH 1/4] Remove deprecated tutorials (#11084) --- docs/tutorials.rst | 14 - .../operators/01_operator_flow.ipynb | 2822 ----------------- .../operators/02_gradients_framework.ipynb | 1562 --------- .../operators/images/gradient_framework.png | Bin 43645 -> 0 bytes 4 files changed, 4398 deletions(-) delete mode 100644 docs/tutorials/operators/01_operator_flow.ipynb delete mode 100644 docs/tutorials/operators/02_gradients_framework.ipynb delete mode 100644 docs/tutorials/operators/images/gradient_framework.png diff --git a/docs/tutorials.rst b/docs/tutorials.rst index 8ba7a73df89d..d6189e1ce0b4 100644 --- a/docs/tutorials.rst +++ b/docs/tutorials.rst @@ -34,17 +34,3 @@ Advanced circuits :glob: tutorials/circuits_advanced/* - -Operators -========= - -.. deprecated:: 0.24.0 - The operators tutorials rely on the ``opflow`` module, which has been deprecated since - Qiskit 0.43 (aka Qiskit Terra 0.24). Refer to the - `Opflow migration guide `_. - These tutorials will be removed in the future. - -.. nbgallery:: - :glob: - - tutorials/operators/* diff --git a/docs/tutorials/operators/01_operator_flow.ipynb b/docs/tutorials/operators/01_operator_flow.ipynb deleted file mode 100644 index 0a6fb2254517..000000000000 --- a/docs/tutorials/operators/01_operator_flow.ipynb +++ /dev/null @@ -1,2822 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Operator Flow" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Introduction\n", - "\n", - "Qiskit provides classes representing states and operators and sums, tensor products, and compositions thereof. These algebraic constructs allow us to build expressions representing operators.\n", - "\n", - "We introduce expressions by building them from Pauli operators. In subsequent sections we explore in more detail operators and states, how they are represented, and what we can do with them. In the last section we construct a state, evolve it with a Hamiltonian, and compute expectation values of an observable.\n", - "\n", - "### Pauli operators, sums, compositions, and tensor products\n", - "\n", - "The most important base operators are the Pauli operators.\n", - "The Pauli operators are represented like this.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:55.220124Z", - "iopub.status.busy": "2023-08-25T18:25:55.218526Z", - "iopub.status.idle": "2023-08-25T18:25:55.802288Z", - "shell.execute_reply": "2023-08-25T18:25:55.801595Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "I X Y Z\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10214/1460376431.py:1: DeprecationWarning: The ``qiskit.opflow`` module is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " from qiskit.opflow import I, X, Y, Z\n" - ] - } - ], - "source": [ - "from qiskit.opflow import I, X, Y, Z\n", - "print(I, X, Y, Z)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "These operators may also carry a coefficient." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:55.807526Z", - "iopub.status.busy": "2023-08-25T18:25:55.806057Z", - "iopub.status.idle": "2023-08-25T18:25:55.812872Z", - "shell.execute_reply": "2023-08-25T18:25:55.812293Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1.5 * I\n", - "2.5 * X\n" - ] - } - ], - "source": [ - "print(1.5 * I)\n", - "print(2.5 * X)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "These coefficients allow the operators to be used as terms in a sum." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:55.817408Z", - "iopub.status.busy": "2023-08-25T18:25:55.816257Z", - "iopub.status.idle": "2023-08-25T18:25:55.823245Z", - "shell.execute_reply": "2023-08-25T18:25:55.822662Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1.0 * X\n", - "+ 2.0 * Y\n" - ] - } - ], - "source": [ - "print(X + 2.0 * Y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Tensor products are denoted with a caret, like this." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:55.827766Z", - "iopub.status.busy": "2023-08-25T18:25:55.826620Z", - "iopub.status.idle": "2023-08-25T18:25:55.833020Z", - "shell.execute_reply": "2023-08-25T18:25:55.832438Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "XYZ\n" - ] - } - ], - "source": [ - "print(X^Y^Z)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Composition is denoted by the `@` symbol." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:55.837753Z", - "iopub.status.busy": "2023-08-25T18:25:55.836589Z", - "iopub.status.idle": "2023-08-25T18:25:55.843676Z", - "shell.execute_reply": "2023-08-25T18:25:55.843080Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "iI\n" - ] - } - ], - "source": [ - "print(X @ Y @ Z)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the preceding two examples, the tensor product and composition of Pauli operators were immediately reduced to the equivalent (possibly multi-qubit) Pauli operator. If we tensor or compose more complicated objects, the result is objects representing the unevaluated operations. That is, algebraic expressions." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For example, composing two sums gives" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:55.847477Z", - "iopub.status.busy": "2023-08-25T18:25:55.847011Z", - "iopub.status.idle": "2023-08-25T18:25:55.854313Z", - "shell.execute_reply": "2023-08-25T18:25:55.853730Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1j * Z\n", - "+ -1j * Y\n", - "+ 1.0 * I\n", - "+ 1j * X\n" - ] - } - ], - "source": [ - "print((X + Y) @ (Y + Z))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And tensoring two sums gives" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:55.858119Z", - "iopub.status.busy": "2023-08-25T18:25:55.857660Z", - "iopub.status.idle": "2023-08-25T18:25:55.865015Z", - "shell.execute_reply": "2023-08-25T18:25:55.864335Z" - }, - "scrolled": true - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1.0 * XY\n", - "+ 1.0 * XZ\n", - "+ 1.0 * YY\n", - "+ 1.0 * YZ\n" - ] - } - ], - "source": [ - "print((X + Y) ^ (Y + Z))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's take a closer look at the types introduced above. First the Pauli operators." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:55.868723Z", - "iopub.status.busy": "2023-08-25T18:25:55.868250Z", - "iopub.status.idle": "2023-08-25T18:25:55.878351Z", - "shell.execute_reply": "2023-08-25T18:25:55.877739Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "(PauliOp(Pauli('I'), coeff=1.0), PauliOp(Pauli('X'), coeff=1.0))" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(I, X)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Each Pauli operator is an instance of `PauliOp`, which wraps an instance of `qiskit.quantum_info.Pauli`, and adds a coefficient `coeff`. In general, a `PauliOp` represents a weighted tensor product of Pauli operators." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:55.882960Z", - "iopub.status.busy": "2023-08-25T18:25:55.881794Z", - "iopub.status.idle": "2023-08-25T18:25:55.894792Z", - "shell.execute_reply": "2023-08-25T18:25:55.894185Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "PauliOp(Pauli('XYZ'), coeff=2.0)" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "2.0 * X^Y^Z" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For the encoding of the Pauli operators as pairs of Boolean values, see the documentation for `qiskit.quantum_info.Pauli`.\n", - "\n", - "All of the objects representing operators, whether as \"primitive\"s such as `PauliOp`, or algebraic expressions carry a coefficient" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:55.900504Z", - "iopub.status.busy": "2023-08-25T18:25:55.898811Z", - "iopub.status.idle": "2023-08-25T18:25:55.909636Z", - "shell.execute_reply": "2023-08-25T18:25:55.909017Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1.2 * (\n", - " 1.1 * XY\n", - " + 1.4300000000000002 * XZ\n", - ")\n" - ] - } - ], - "source": [ - "print(1.1 * ((1.2 * X)^(Y + (1.3 * Z))))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the following we take a broader and deeper look at Qiskit's operators, states, and the building blocks of quantum algorithms." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## Part I: State Functions and Measurements\n", - "\n", - "Quantum states are represented by subclasses of the class `StateFn`. There are four representations of quantum states: `DictStateFn` is a sparse representation in the computational basis, backed by a `dict`. `VectorStateFn` is a dense representation in the computational basis backed by a numpy array. `CircuitStateFn` is backed by a circuit and represents the state obtained by executing the circuit on the all-zero computational-basis state. `OperatorStateFn` represents mixed states via a density matrix. (As we will see later, `OperatorStateFn` is also used to represent observables.)\n", - "\n", - "Several `StateFn` instances are provided for convenience. For example `Zero, One, Plus, Minus`." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:55.914460Z", - "iopub.status.busy": "2023-08-25T18:25:55.913286Z", - "iopub.status.idle": "2023-08-25T18:25:55.918398Z", - "shell.execute_reply": "2023-08-25T18:25:55.917810Z" - } - }, - "outputs": [], - "source": [ - "from qiskit.opflow import (StateFn, Zero, One, Plus, Minus, H,\n", - " DictStateFn, VectorStateFn, CircuitStateFn, OperatorStateFn)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`Zero` and `One` represent the quantum states $|0\\rangle$ and $|1\\rangle$. They are represented via `DictStateFn`." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:55.922902Z", - "iopub.status.busy": "2023-08-25T18:25:55.921567Z", - "iopub.status.idle": "2023-08-25T18:25:55.928619Z", - "shell.execute_reply": "2023-08-25T18:25:55.928008Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "DictStateFn({'0': 1}) DictStateFn({'1': 1})\n" - ] - } - ], - "source": [ - "print(Zero, One)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`Plus` and `Minus`, representing states $(|0\\rangle + |1\\rangle)/\\sqrt{2}$ and $(|0\\rangle - |1\\rangle)/\\sqrt{2}$ are represented via circuits. `H` is a synonym for `Plus`." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:55.933273Z", - "iopub.status.busy": "2023-08-25T18:25:55.932223Z", - "iopub.status.idle": "2023-08-25T18:25:55.989875Z", - "shell.execute_reply": "2023-08-25T18:25:55.988998Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CircuitStateFn(\n", - " ┌───┐\n", - "q: ┤ H ├\n", - " └───┘\n", - ") CircuitStateFn(\n", - " ┌───┐┌───┐\n", - "q: ┤ X ├┤ H ├\n", - " └───┘└───┘\n", - ")\n" - ] - } - ], - "source": [ - "print(Plus, Minus)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Indexing into quantum states is done with the `eval` method. These examples return the coefficients of the `0` and `1` basis states. (Below, we will see that the `eval` method is used for other computations, as well.)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:55.993947Z", - "iopub.status.busy": "2023-08-25T18:25:55.993499Z", - "iopub.status.idle": "2023-08-25T18:25:56.036114Z", - "shell.execute_reply": "2023-08-25T18:25:56.035247Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1.0\n", - "0.0\n", - "1.0\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(0.7071067811865475+0j)\n", - "(-0.7071067811865475+8.7e-17j)\n" - ] - } - ], - "source": [ - "print(Zero.eval('0'))\n", - "print(Zero.eval('1'))\n", - "print(One.eval('1'))\n", - "print(Plus.eval('0'))\n", - "print(Minus.eval('1'))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The dual vector of a quantum state, that is the *bra* corresponding to a *ket* is obtained via the `adjoint` method. The `StateFn` carries a flag `is_measurement`, which is `False` if the object is a ket and `True` if it is a bra." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here, we construct $\\langle 1 |$." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.040370Z", - "iopub.status.busy": "2023-08-25T18:25:56.039585Z", - "iopub.status.idle": "2023-08-25T18:25:56.044683Z", - "shell.execute_reply": "2023-08-25T18:25:56.044082Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "DictStateFn({'1': 1}, coeff=1.0, is_measurement=True)" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "One.adjoint()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For convenience, one may obtain the dual vector with a tilde, like this" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.047797Z", - "iopub.status.busy": "2023-08-25T18:25:56.047303Z", - "iopub.status.idle": "2023-08-25T18:25:56.052085Z", - "shell.execute_reply": "2023-08-25T18:25:56.051460Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "DictStateFn({'1': 1}, coeff=1.0, is_measurement=True)" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "~One" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Algebraic operations and predicates\n", - "\n", - "Many algebraic operations and predicates between `StateFn`s are supported, including:\n", - "\n", - "* `+` - addition\n", - "* `-` - subtraction, negation (scalar multiplication by -1)\n", - "* `*` - scalar multiplication\n", - "* `/` - scalar division\n", - "* `@` - composition\n", - "* `^` - tensor product or tensor power (tensor with self n times)\n", - "* `**` - composition power (compose with self n times)\n", - "* `==` - equality\n", - "* `~` - adjoint, alternating between a State Function and Measurement\n", - "\n", - "Be very aware that these operators obey the [Python rules for operator precedence](https://docs.python.org/3/reference/expressions.html#operator-precedence), which might not be what you expect mathematically. For example, `I^X + X^I` will actually be parsed as `I ^ (X + X) ^ I == 2 * (I^X^I)` because Python evaluates `+` before `^`. In these cases, you can use the methods (`.tensor()`, etc) or parentheses." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`StateFn`s carry a coefficient. This allows us to multiply states by a scalar, and so to construct sums." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here, we construct $(2 + 3i)|0\\rangle$." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.057090Z", - "iopub.status.busy": "2023-08-25T18:25:56.056276Z", - "iopub.status.idle": "2023-08-25T18:25:56.062034Z", - "shell.execute_reply": "2023-08-25T18:25:56.061369Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "DictStateFn({'0': 1}, coeff=(2+3j), is_measurement=False)" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(2.0 + 3.0j) * Zero" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here, we see that adding two `DictStateFn`s returns an object of the same type. We construct $|0\\rangle + |1\\rangle$." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.066444Z", - "iopub.status.busy": "2023-08-25T18:25:56.065959Z", - "iopub.status.idle": "2023-08-25T18:25:56.069846Z", - "shell.execute_reply": "2023-08-25T18:25:56.069265Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "DictStateFn({'0': 1.0, '1': 1.0})\n" - ] - } - ], - "source": [ - "print(Zero + One)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that you must normalize states by hand. For example, to construct $(|0\\rangle + |1\\rangle)/\\sqrt{2}$, we write" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.074307Z", - "iopub.status.busy": "2023-08-25T18:25:56.072919Z", - "iopub.status.idle": "2023-08-25T18:25:56.079817Z", - "shell.execute_reply": "2023-08-25T18:25:56.079220Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "DictStateFn({'0': 1.0, '1': 1.0}) * 0.7071067811865475\n" - ] - } - ], - "source": [ - "import math\n", - "\n", - "v_zero_one = (Zero + One) / math.sqrt(2)\n", - "print(v_zero_one)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In other cases, the result is a symbolic representation of a sum. For example, here is a representation of $|+\\rangle + |-\\rangle$." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.083384Z", - "iopub.status.busy": "2023-08-25T18:25:56.082941Z", - "iopub.status.idle": "2023-08-25T18:25:56.087976Z", - "shell.execute_reply": "2023-08-25T18:25:56.087388Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "SummedOp([\n", - " CircuitStateFn(\n", - " ┌───┐\n", - " q: ┤ H ├\n", - " └───┘\n", - " ),\n", - " CircuitStateFn(\n", - " ┌───┐┌───┐\n", - " q: ┤ X ├┤ H ├\n", - " └───┘└───┘\n", - " )\n", - "])\n" - ] - } - ], - "source": [ - "print(Plus + Minus)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The composition operator is used to perform an inner product, which by default is held in an unevaluated form. Here is a representation of $\\langle 1 | 1 \\rangle$." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.094699Z", - "iopub.status.busy": "2023-08-25T18:25:56.094237Z", - "iopub.status.idle": "2023-08-25T18:25:56.100560Z", - "shell.execute_reply": "2023-08-25T18:25:56.099970Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ComposedOp([\n", - " DictMeasurement({'1': 1}),\n", - " DictStateFn({'1': 1})\n", - "])\n" - ] - } - ], - "source": [ - "print(~One @ One)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that the `is_measurement` flag causes the (bra) state `~One` to be printed `DictMeasurement`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Symbolic expressions may be evaluated with the `eval` method." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.103964Z", - "iopub.status.busy": "2023-08-25T18:25:56.103504Z", - "iopub.status.idle": "2023-08-25T18:25:56.111143Z", - "shell.execute_reply": "2023-08-25T18:25:56.110543Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "1.0" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(~One @ One).eval()" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.114578Z", - "iopub.status.busy": "2023-08-25T18:25:56.114126Z", - "iopub.status.idle": "2023-08-25T18:25:56.121360Z", - "shell.execute_reply": "2023-08-25T18:25:56.120728Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "0.9999999999999998" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(~v_zero_one @ v_zero_one).eval()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here is $\\langle - | 1 \\rangle = \\langle (\\langle 0| - \\langle 1|)/\\sqrt{2} | 1\\rangle$." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.124828Z", - "iopub.status.busy": "2023-08-25T18:25:56.124349Z", - "iopub.status.idle": "2023-08-25T18:25:56.136495Z", - "shell.execute_reply": "2023-08-25T18:25:56.135880Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "(-0.7071067811865475-8.7e-17j)" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(~Minus @ One).eval()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The composition operator `@` is equivalent to calling the `compose` method." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.141205Z", - "iopub.status.busy": "2023-08-25T18:25:56.139980Z", - "iopub.status.idle": "2023-08-25T18:25:56.146565Z", - "shell.execute_reply": "2023-08-25T18:25:56.145974Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ComposedOp([\n", - " DictMeasurement({'1': 1}),\n", - " DictStateFn({'1': 1})\n", - "])\n" - ] - } - ], - "source": [ - "print((~One).compose(One))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Inner products may also be computed using the `eval` method directly, without constructing a `ComposedOp`." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.151201Z", - "iopub.status.busy": "2023-08-25T18:25:56.150056Z", - "iopub.status.idle": "2023-08-25T18:25:56.157474Z", - "shell.execute_reply": "2023-08-25T18:25:56.156893Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "1.0" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(~One).eval(One)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Symbolic tensor products are constructed as follows. Here is $|0\\rangle \\otimes |+\\rangle$." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.162108Z", - "iopub.status.busy": "2023-08-25T18:25:56.160970Z", - "iopub.status.idle": "2023-08-25T18:25:56.167828Z", - "shell.execute_reply": "2023-08-25T18:25:56.167237Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "TensoredOp([\n", - " DictStateFn({'0': 1}),\n", - " CircuitStateFn(\n", - " ┌───┐\n", - " q: ┤ H ├\n", - " └───┘\n", - " )\n", - "])\n" - ] - } - ], - "source": [ - "print(Zero^Plus)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This may be represented as a simple (not compound) `CircuitStateFn`." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.171497Z", - "iopub.status.busy": "2023-08-25T18:25:56.171041Z", - "iopub.status.idle": "2023-08-25T18:25:56.183381Z", - "shell.execute_reply": "2023-08-25T18:25:56.182766Z" - }, - "scrolled": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CircuitStateFn(\n", - " ┌───┐\n", - "q_0: ┤ H ├\n", - " └───┘\n", - "q_1: ─────\n", - " \n", - ")\n" - ] - } - ], - "source": [ - "print((Zero^Plus).to_circuit_op())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Tensor powers are constructed using the caret `^` as follows. Here are $600 (|11111\\rangle + |00000\\rangle)$, and $|10\\rangle^{\\otimes 3}$." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.189118Z", - "iopub.status.busy": "2023-08-25T18:25:56.187406Z", - "iopub.status.idle": "2023-08-25T18:25:56.197144Z", - "shell.execute_reply": "2023-08-25T18:25:56.196543Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "DictStateFn({'11111': 1.0, '00000': 1.0}) * 600.0\n", - "DictStateFn({'101010': 1})\n" - ] - } - ], - "source": [ - "print(600 * ((One^5) + (Zero^5)))\n", - "print((One^Zero)^3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The method `to_matrix_op` converts to `VectorStateFn`." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.202791Z", - "iopub.status.busy": "2023-08-25T18:25:56.201106Z", - "iopub.status.idle": "2023-08-25T18:25:56.271843Z", - "shell.execute_reply": "2023-08-25T18:25:56.271157Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "VectorStateFn(Statevector([ 0.25-6.1e-17j, -0.25+6.1e-17j, 0.25-6.1e-17j,\n", - " -0.25+6.1e-17j, -0.25+6.1e-17j, 0.25-6.1e-17j,\n", - " -0.25+6.1e-17j, 0.25-6.1e-17j, 0.25-6.1e-17j,\n", - " -0.25+6.1e-17j, 0.25-6.1e-17j, -0.25+6.1e-17j,\n", - " -0.25+6.1e-17j, 0.25-6.1e-17j, -0.25+6.1e-17j,\n", - " 0.25-6.1e-17j],\n", - " dims=(2, 2, 2, 2)))" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "CircuitStateFn(\n", - " ┌───┐\n", - "q_0: ┤ X ├\n", - " ├───┤\n", - "q_1: ┤ H ├\n", - " ├───┤\n", - "q_2: ┤ X ├\n", - " ├───┤\n", - "q_3: ┤ H ├\n", - " └───┘\n", - ")\n", - "{'1111': 0.2724609375, '1101': 0.2626953125, '0101': 0.2490234375, '0111': 0.2158203125}\n" - ] - } - ], - "source": [ - "print(((Plus^Minus)^2).to_matrix_op())\n", - "print(((Plus^One)^2).to_circuit_op())\n", - "print(((Plus^One)^2).to_matrix_op().sample())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Constructing a StateFn is easy. The `StateFn` class also serves as a factory, and can take any applicable primitive in its constructor and return the correct StateFn subclass. Currently the following primitives can be passed into the constructor, listed alongside the `StateFn` subclass they produce:\n", - "\n", - "* str (equal to some basis bitstring) -> DictStateFn\n", - "* dict -> DictStateFn\n", - "* Qiskit Result object -> DictStateFn\n", - "* list -> VectorStateFn\n", - "* np.ndarray -> VectorStateFn\n", - "* Statevector -> VectorStateFn\n", - "* QuantumCircuit -> CircuitStateFn\n", - "* Instruction -> CircuitStateFn\n", - "* OperatorBase -> OperatorStateFn" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.276809Z", - "iopub.status.busy": "2023-08-25T18:25:56.275581Z", - "iopub.status.idle": "2023-08-25T18:25:56.629683Z", - "shell.execute_reply": "2023-08-25T18:25:56.628999Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "DictStateFn({'0': 1})\n", - "True\n", - "VectorStateFn(Statevector([0.+0.j, 1.+0.j, 1.+0.j, 0.+0.j],\n", - " dims=(2, 2)))\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10214/1137983803.py:1: DeprecationWarning: The class ``qiskit.opflow.state_fns.dict_state_fn.DictStateFn`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " print(StateFn({'0':1}))\n", - "/tmp/ipykernel_10214/1137983803.py:2: DeprecationWarning: The class ``qiskit.opflow.state_fns.dict_state_fn.DictStateFn`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " print(StateFn({'0':1}) == Zero)\n", - "/tmp/ipykernel_10214/1137983803.py:4: DeprecationWarning: The class ``qiskit.opflow.state_fns.vector_state_fn.VectorStateFn`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " print(StateFn([0,1,1,0]))\n", - "/tmp/ipykernel_10214/1137983803.py:7: DeprecationWarning: The class ``qiskit.opflow.state_fns.circuit_state_fn.CircuitStateFn`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " print(StateFn(RealAmplitudes(2)))\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CircuitStateFn(\n", - " ┌──────────────────────────────────────────────────────────┐\n", - "q_0: ┤0 ├\n", - " │ RealAmplitudes(θ[0],θ[1],θ[2],θ[3],θ[4],θ[5],θ[6],θ[7]) │\n", - "q_1: ┤1 ├\n", - " └──────────────────────────────────────────────────────────┘\n", - ")\n" - ] - } - ], - "source": [ - "print(StateFn({'0':1}))\n", - "print(StateFn({'0':1}) == Zero)\n", - "\n", - "print(StateFn([0,1,1,0]))\n", - "\n", - "from qiskit.circuit.library import RealAmplitudes\n", - "print(StateFn(RealAmplitudes(2)))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part II: `PrimitiveOp`s\n", - "\n", - "The basic Operators are subclasses of `PrimitiveOp`. Just like `StateFn`, `PrimitiveOp` is also a factory for creating the correct type of `PrimitiveOp` for a given primitive. Currently, the following primitives can be passed into the constructor, listed alongside the `PrimitiveOp` subclass they produce:\n", - "\n", - "* Terra's Pauli -> PauliOp\n", - "* Instruction -> CircuitOp\n", - "* QuantumCircuit -> CircuitOp\n", - "* 2d List -> MatrixOp\n", - "* np.ndarray -> MatrixOp\n", - "* spmatrix -> MatrixOp\n", - "* Terra's quantum_info.Operator -> MatrixOp" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.634723Z", - "iopub.status.busy": "2023-08-25T18:25:56.633405Z", - "iopub.status.idle": "2023-08-25T18:25:56.638555Z", - "shell.execute_reply": "2023-08-25T18:25:56.637972Z" - } - }, - "outputs": [], - "source": [ - "from qiskit.opflow import X, Y, Z, I, CX, T, H, S, PrimitiveOp" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Matrix elements" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `eval` method returns a column from an operator. For example, the Pauli $X$ operator is represented by a `PauliOp`. Asking for a column returns an instance of the sparse representation, a `DictStateFn`." - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.643193Z", - "iopub.status.busy": "2023-08-25T18:25:56.642046Z", - "iopub.status.idle": "2023-08-25T18:25:56.649256Z", - "shell.execute_reply": "2023-08-25T18:25:56.648680Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "PauliOp(Pauli('X'), coeff=1.0)" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "X" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.653652Z", - "iopub.status.busy": "2023-08-25T18:25:56.652514Z", - "iopub.status.idle": "2023-08-25T18:25:56.659131Z", - "shell.execute_reply": "2023-08-25T18:25:56.658557Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "DictStateFn({'1': (1+0j)})\n" - ] - } - ], - "source": [ - "print(X.eval('0'))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It follows that indexing into an operator, that is obtaining a matrix element, is performed with two calls to the `eval` method." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We have $X = \\left(\\begin{matrix} 0 & 1 \\\\\n", - " 1 & 0\n", - " \\end{matrix} \\right)$. And the matrix element $\\left\\{X \\right\\}_{0,1}$ is" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.663573Z", - "iopub.status.busy": "2023-08-25T18:25:56.662447Z", - "iopub.status.idle": "2023-08-25T18:25:56.669990Z", - "shell.execute_reply": "2023-08-25T18:25:56.669417Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "(1+0j)" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "X.eval('0').eval('1')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here is an example using the two qubit operator `CX`, the controlled `X`, which is represented by a circuit." - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.674396Z", - "iopub.status.busy": "2023-08-25T18:25:56.673268Z", - "iopub.status.idle": "2023-08-25T18:25:56.681289Z", - "shell.execute_reply": "2023-08-25T18:25:56.680710Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " \n", - "q_0: ──■──\n", - " ┌─┴─┐\n", - "q_1: ┤ X ├\n", - " └───┘\n", - "[[1. 0. 0. 0.]\n", - " [0. 0. 0. 1.]\n", - " [0. 0. 1. 0.]\n", - " [0. 1. 0. 0.]]\n" - ] - } - ], - "source": [ - "print(CX)\n", - "print(CX.to_matrix().real) # The imaginary part vanishes." - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.685652Z", - "iopub.status.busy": "2023-08-25T18:25:56.684482Z", - "iopub.status.idle": "2023-08-25T18:25:56.692980Z", - "shell.execute_reply": "2023-08-25T18:25:56.692401Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "VectorStateFn(Statevector([0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],\n", - " dims=(2, 2)), coeff=1.0, is_measurement=False)" - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "CX.eval('01') # 01 is the one in decimal. We get the first column." - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.697419Z", - "iopub.status.busy": "2023-08-25T18:25:56.696288Z", - "iopub.status.idle": "2023-08-25T18:25:56.704529Z", - "shell.execute_reply": "2023-08-25T18:25:56.703961Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "(1+0j)" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "CX.eval('01').eval('11') # This returns element with (zero-based) index (1, 3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Applying an operator to a state vector" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Applying an operator to a state vector may be done with the `compose` method (equivalently, `@` operator). Here is a representation of $X | 1 \\rangle = |0\\rangle$." - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.708965Z", - "iopub.status.busy": "2023-08-25T18:25:56.707826Z", - "iopub.status.idle": "2023-08-25T18:25:56.713940Z", - "shell.execute_reply": "2023-08-25T18:25:56.713382Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ComposedOp([\n", - " X,\n", - " DictStateFn({'1': 1})\n", - "])\n" - ] - } - ], - "source": [ - "print(X @ One)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A simpler representation, the `DictStateFn` representation of $|0\\rangle$, is obtained with `eval`." - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.718292Z", - "iopub.status.busy": "2023-08-25T18:25:56.717168Z", - "iopub.status.idle": "2023-08-25T18:25:56.724395Z", - "shell.execute_reply": "2023-08-25T18:25:56.723815Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "DictStateFn({'0': (1+0j)}, coeff=(1+0j), is_measurement=False)" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(X @ One).eval()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The intermediate `ComposedOp` step may be avoided by using `eval` directly." - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.728722Z", - "iopub.status.busy": "2023-08-25T18:25:56.727581Z", - "iopub.status.idle": "2023-08-25T18:25:56.734661Z", - "shell.execute_reply": "2023-08-25T18:25:56.734099Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "DictStateFn({'0': (1+0j)}, coeff=(1+0j), is_measurement=False)" - ] - }, - "execution_count": 41, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "X.eval(One)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Composition and tensor products of operators are effected with `@` and `^`. Here are some examples." - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.738961Z", - "iopub.status.busy": "2023-08-25T18:25:56.737846Z", - "iopub.status.idle": "2023-08-25T18:25:56.802032Z", - "shell.execute_reply": "2023-08-25T18:25:56.801300Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(1+0j)\n", - " ┌───┐ ┌───┐ \n", - "q_0: ──■──┤ H ├───────■──┤ H ├─────\n", - " ┌─┴─┐└───┘┌───┐┌─┴─┐└───┘┌───┐\n", - "q_1: ┤ X ├──■──┤ H ├┤ X ├──■──┤ H ├\n", - " └───┘┌─┴─┐├───┤└───┘┌─┴─┐├───┤\n", - "q_2: ──■──┤ X ├┤ H ├──■──┤ X ├┤ H ├\n", - " ┌─┴─┐└───┘├───┤┌─┴─┐└───┘├───┤\n", - "q_3: ┤ X ├──■──┤ H ├┤ X ├──■──┤ H ├\n", - " └───┘┌─┴─┐├───┤└───┘┌─┴─┐├───┤\n", - "q_4: ─────┤ X ├┤ H ├─────┤ X ├┤ H ├\n", - " └───┘└───┘ └───┘└───┘\n", - "CircuitStateFn(\n", - " ┌───┐┌───┐ ┌───┐ ┌───┐ \n", - "q_0: ┤ X ├┤ H ├──■──┤ H ├───────■──┤ H ├─────\n", - " ├───┤├───┤┌─┴─┐└───┘┌───┐┌─┴─┐└───┘┌───┐\n", - "q_1: ┤ X ├┤ H ├┤ X ├──■──┤ H ├┤ X ├──■──┤ H ├\n", - " ├───┤├───┤└───┘┌─┴─┐├───┤└───┘┌─┴─┐├───┤\n", - "q_2: ┤ X ├┤ H ├──■──┤ X ├┤ H ├──■──┤ X ├┤ H ├\n", - " ├───┤├───┤┌─┴─┐└───┘├───┤┌─┴─┐└───┘├───┤\n", - "q_3: ┤ X ├┤ H ├┤ X ├──■──┤ H ├┤ X ├──■──┤ H ├\n", - " ├───┤├───┤└───┘┌─┴─┐├───┤└───┘┌─┴─┐├───┤\n", - "q_4: ┤ X ├┤ H ├─────┤ X ├┤ H ├─────┤ X ├┤ H ├\n", - " └───┘└───┘ └───┘└───┘ └───┘└───┘\n", - ")\n", - "CircuitStateFn(\n", - " ┌─────────────┐ \n", - "q_0: ┤0 ├─────\n", - " │ │ \n", - "q_1: ┤1 Pauli(XII) ├─────\n", - " │ │┌───┐\n", - "q_2: ┤2 ├┤ H ├\n", - " └─────────────┘└───┘\n", - ")\n" - ] - } - ], - "source": [ - "print(((~One^2) @ (CX.eval('01'))).eval())\n", - "\n", - "print(((H^5) @ ((CX^2)^I) @ (I^(CX^2)))**2)\n", - "print((((H^5) @ ((CX^2)^I) @ (I^(CX^2)))**2) @ (Minus^5))\n", - "print(((H^I^I)@(X^I^I)@Zero))" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.807042Z", - "iopub.status.busy": "2023-08-25T18:25:56.805863Z", - "iopub.status.idle": "2023-08-25T18:25:56.812886Z", - "shell.execute_reply": "2023-08-25T18:25:56.812321Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ComposedOp([\n", - " DictMeasurement({'1': 1}),\n", - " CircuitStateFn(\n", - " ┌───┐┌───┐\n", - " q: ┤ X ├┤ H ├\n", - " └───┘└───┘\n", - " )\n", - "])\n" - ] - } - ], - "source": [ - "print(~One @ Minus)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part III: `ListOp` and subclasses" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### `ListOp`\n", - "\n", - "`ListOp` is a container for effectively vectorizing operations over a list of operators and states." - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.817440Z", - "iopub.status.busy": "2023-08-25T18:25:56.816254Z", - "iopub.status.idle": "2023-08-25T18:25:56.824392Z", - "shell.execute_reply": "2023-08-25T18:25:56.823779Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ComposedOp([\n", - " ListOp([\n", - " DictMeasurement({'1': 1}),\n", - " DictMeasurement({'0': 1})\n", - " ]),\n", - " ListOp([\n", - " DictStateFn({'1': 1}),\n", - " DictStateFn({'0': 1})\n", - " ])\n", - "])\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10214/1512571236.py:3: DeprecationWarning: The class ``qiskit.opflow.list_ops.list_op.ListOp`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " print((~ListOp([One, Zero]) @ ListOp([One, Zero])))\n" - ] - } - ], - "source": [ - "from qiskit.opflow import ListOp\n", - "\n", - "print((~ListOp([One, Zero]) @ ListOp([One, Zero])))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For example, the composition above is distributed over the lists (`ListOp`) using the simplification method `reduce`." - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.828945Z", - "iopub.status.busy": "2023-08-25T18:25:56.827797Z", - "iopub.status.idle": "2023-08-25T18:25:56.836469Z", - "shell.execute_reply": "2023-08-25T18:25:56.835868Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ListOp([\n", - " ListOp([\n", - " ComposedOp([\n", - " DictMeasurement({'1': 1}),\n", - " DictStateFn({'1': 1})\n", - " ]),\n", - " ComposedOp([\n", - " DictMeasurement({'1': 1}),\n", - " DictStateFn({'0': 1})\n", - " ])\n", - " ]),\n", - " ListOp([\n", - " ComposedOp([\n", - " DictMeasurement({'0': 1}),\n", - " DictStateFn({'1': 1})\n", - " ]),\n", - " ComposedOp([\n", - " DictMeasurement({'0': 1}),\n", - " DictStateFn({'0': 1})\n", - " ])\n", - " ])\n", - "])\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10214/2216466513.py:1: DeprecationWarning: The class ``qiskit.opflow.list_ops.list_op.ListOp`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " print((~ListOp([One, Zero]) @ ListOp([One, Zero])).reduce())\n" - ] - } - ], - "source": [ - "print((~ListOp([One, Zero]) @ ListOp([One, Zero])).reduce())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### `ListOp`s: `SummedOp`, `ComposedOp`, `TensoredOp`\n", - "\n", - "`ListOp`, introduced above, is useful for vectorizing operations. But, it also serves as the superclass for list-like composite classes.\n", - "If you've already played around with the above, you'll notice that you can easily perform operations between `OperatorBase`s which we may not know how to perform efficiently in general (or simply haven't implemented an efficient procedure for yet), such as addition between `CircuitOp`s. In those cases, you may receive a `ListOp` result (or subclass thereof) from your operation representing the lazy execution of the operation. For example, if you attempt to add together a `DictStateFn` and a `CircuitStateFn`, you'll receive a `SummedOp` representing the sum of the two. This composite State function still has a working `eval` (but may need to perform a non-scalable computation under the hood, such as converting both to vectors).\n", - "\n", - "These composite `OperatorBase`s are how we construct increasingly complex and rich computation out of `PrimitiveOp` and `StateFn` building blocks.\n", - "\n", - "Every `ListOp` has four properties:\n", - "\n", - "* `oplist` - The list of `OperatorBase`s which may represent terms, factors, etc.\n", - "* `combo_fn` - The function taking a list of complex numbers to an output value which defines how to combine the outputs of the `oplist` items. For broadcasting simplicity, this function is defined over NumPy arrays.\n", - "* `coeff` - A coefficient multiplying the primitive. Note that `coeff` can be int, float, complex or a free `Parameter` object (from `qiskit.circuit` in Terra) to be bound later using `my_op.bind_parameters`.\n", - "* `abelian` - Indicates whether the Operators in `oplist` are known to mutually commute (usually set after being converted by the `AbelianGrouper` converter).\n", - "\n", - "Note that `ListOp` supports typical sequence overloads, so you can use indexing like `my_op[4]` to access the `OperatorBase`s in `oplist`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### `OperatorStateFn`\n", - "\n", - "We mentioned above that `OperatorStateFn` represents a density operator. But, if the `is_measurement` flag is `True`, then `OperatorStateFn` represents an observable. The expectation value of this observable can then be constructed via `ComposedOp`. Or, directly, using `eval`. Recall that the `is_measurement` flag (property) is set via the `adjoint` method." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here we construct the observable corresponding to the Pauli $Z$ operator. Note that when printing, it is called `OperatorMeasurement`." - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.841037Z", - "iopub.status.busy": "2023-08-25T18:25:56.839884Z", - "iopub.status.idle": "2023-08-25T18:25:56.851255Z", - "shell.execute_reply": "2023-08-25T18:25:56.850682Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "OperatorMeasurement(Z)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10214/2925661353.py:1: DeprecationWarning: The class ``qiskit.opflow.state_fns.operator_state_fn.OperatorStateFn`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " print(StateFn(Z).adjoint())\n", - "/tmp/ipykernel_10214/2925661353.py:2: DeprecationWarning: The class ``qiskit.opflow.state_fns.operator_state_fn.OperatorStateFn`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " StateFn(Z).adjoint()\n" - ] - }, - { - "data": { - "text/plain": [ - "OperatorStateFn(PauliOp(Pauli('Z'), coeff=1.0), coeff=1.0, is_measurement=True)" - ] - }, - "execution_count": 46, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "print(StateFn(Z).adjoint())\n", - "StateFn(Z).adjoint()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here, we compute $\\langle 0 | Z | 0 \\rangle$, $\\langle 1 | Z | 1 \\rangle$, and $\\langle + | Z | + \\rangle$, where $|+\\rangle = (|0\\rangle + |1\\rangle)/\\sqrt{2}$." - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.854777Z", - "iopub.status.busy": "2023-08-25T18:25:56.854341Z", - "iopub.status.idle": "2023-08-25T18:25:56.869160Z", - "shell.execute_reply": "2023-08-25T18:25:56.868532Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(1+0j)\n", - "(-1+0j)\n", - "(-2.2e-17+0j)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10214/2804323468.py:1: DeprecationWarning: The class ``qiskit.opflow.state_fns.operator_state_fn.OperatorStateFn`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " print(StateFn(Z).adjoint().eval(Zero))\n", - "/tmp/ipykernel_10214/2804323468.py:2: DeprecationWarning: The class ``qiskit.opflow.state_fns.operator_state_fn.OperatorStateFn`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " print(StateFn(Z).adjoint().eval(One))\n", - "/tmp/ipykernel_10214/2804323468.py:3: DeprecationWarning: The class ``qiskit.opflow.state_fns.operator_state_fn.OperatorStateFn`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " print(StateFn(Z).adjoint().eval(Plus))\n" - ] - } - ], - "source": [ - "print(StateFn(Z).adjoint().eval(Zero))\n", - "print(StateFn(Z).adjoint().eval(One))\n", - "print(StateFn(Z).adjoint().eval(Plus))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part IV: Converters\n", - "\n", - "Converters are classes that manipulate operators and states and perform building blocks of algorithms. Examples include changing the basis of operators and Trotterization.\n", - "Converters traverse an expression and perform a particular manipulation or replacement, defined by the converter's `convert()` method, of the Operators within. Typically, if a converter encounters an `OperatorBase` in the recursion which is irrelevant to its conversion purpose, that `OperatorBase` is left unchanged." - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.873880Z", - "iopub.status.busy": "2023-08-25T18:25:56.872707Z", - "iopub.status.idle": "2023-08-25T18:25:56.878114Z", - "shell.execute_reply": "2023-08-25T18:25:56.877442Z" - } - }, - "outputs": [], - "source": [ - "import numpy as np\n", - "from qiskit.opflow import I, X, Y, Z, H, CX, Zero, ListOp, PauliExpectation, PauliTrotterEvolution, CircuitSampler, MatrixEvolution, Suzuki\n", - "from qiskit.circuit import Parameter\n", - "from qiskit import Aer" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Evolutions, `exp_i()`, and the `EvolvedOp`\n", - "\n", - "Every `PrimitiveOp` and `ListOp` has an `.exp_i()` function such that `H.exp_i()` corresponds to $e^{-iH}$. In practice, only a few of these Operators have an efficiently computable exponentiation (such as MatrixOp and the PauliOps with only one non-identity single-qubit Pauli), so we need to return a placeholder, or symbolic representation, (similar to how `SummedOp` is a placeholder when we can't perform addition). This placeholder is called `EvolvedOp`, and it holds the `OperatorBase` to be exponentiated in its `.primitive` property.\n", - "\n", - "Qiskit operators fully support parameterization, so we can use a `Parameter` for our evolution time here. Notice that there's no \"evolution time\" argument in any function. The Operator flow exponentiates whatever operator we tell it to, and if we choose to multiply the operator by an evolution time, $e^{-iHt}$, that will be reflected in our exponentiation parameters." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Weighted sum of Pauli operators\n", - "A Hamiltonian expressed as a linear combination of multi-qubit Pauli operators may be constructed like this." - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.882746Z", - "iopub.status.busy": "2023-08-25T18:25:56.881594Z", - "iopub.status.idle": "2023-08-25T18:25:56.888390Z", - "shell.execute_reply": "2023-08-25T18:25:56.887704Z" - } - }, - "outputs": [], - "source": [ - "two_qubit_H2 = (-1.0523732 * I^I) + \\\n", - " (0.39793742 * I^Z) + \\\n", - " (-0.3979374 * Z^I) + \\\n", - " (-0.0112801 * Z^Z) + \\\n", - " (0.18093119 * X^X)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that `two_qubit_H2` is represented as a `SummedOp` whose terms are `PauliOp`s." - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.893005Z", - "iopub.status.busy": "2023-08-25T18:25:56.891830Z", - "iopub.status.idle": "2023-08-25T18:25:56.898482Z", - "shell.execute_reply": "2023-08-25T18:25:56.897846Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "-1.0523732 * II\n", - "+ 0.39793742 * IZ\n", - "- 0.3979374 * ZI\n", - "- 0.0112801 * ZZ\n", - "+ 0.18093119 * XX\n" - ] - } - ], - "source": [ - "print(two_qubit_H2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we multiply the Hamiltonian by a `Parameter`. This `Parameter` is stored in the `coeff` property of the `SummedOp`. Calling `exp_i()` on the result wraps it in `EvolvedOp`, representing exponentiation." - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.903041Z", - "iopub.status.busy": "2023-08-25T18:25:56.901906Z", - "iopub.status.idle": "2023-08-25T18:25:56.910883Z", - "shell.execute_reply": "2023-08-25T18:25:56.910278Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "e^(-i*1.0*θ * (\n", - " -1.0523732 * II\n", - " + 0.39793742 * IZ\n", - " - 0.3979374 * ZI\n", - " - 0.0112801 * ZZ\n", - " + 0.18093119 * XX\n", - "))\n", - "EvolvedOp(PauliSumOp(SparsePauliOp(['II', 'IZ', 'ZI', 'ZZ', 'XX'],\n", - " coeffs=[-1.0523732 +0.j, 0.39793742+0.j, -0.3979374 +0.j, -0.0112801 +0.j,\n", - " 0.18093119+0.j]), coeff=1.0*θ), coeff=1.0)\n" - ] - } - ], - "source": [ - "evo_time = Parameter('θ')\n", - "evolution_op = (evo_time*two_qubit_H2).exp_i()\n", - "print(evolution_op) # Note, EvolvedOps print as exponentiations\n", - "print(repr(evolution_op))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We construct `h2_measurement`, which represents `two_qubit_H2` as an observable." - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.915515Z", - "iopub.status.busy": "2023-08-25T18:25:56.914348Z", - "iopub.status.idle": "2023-08-25T18:25:56.923483Z", - "shell.execute_reply": "2023-08-25T18:25:56.922884Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "OperatorMeasurement(-1.0523732 * II\n", - "+ 0.39793742 * IZ\n", - "- 0.3979374 * ZI\n", - "- 0.0112801 * ZZ\n", - "+ 0.18093119 * XX)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10214/251610506.py:1: DeprecationWarning: The class ``qiskit.opflow.state_fns.operator_state_fn.OperatorStateFn`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " h2_measurement = StateFn(two_qubit_H2).adjoint()\n" - ] - } - ], - "source": [ - "h2_measurement = StateFn(two_qubit_H2).adjoint()\n", - "print(h2_measurement)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We construct a Bell state $|\\Phi_+\\rangle$ via $\\text{CX} (H\\otimes I) |00\\rangle$." - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.928037Z", - "iopub.status.busy": "2023-08-25T18:25:56.926867Z", - "iopub.status.idle": "2023-08-25T18:25:56.940800Z", - "shell.execute_reply": "2023-08-25T18:25:56.940163Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CircuitStateFn(\n", - " ┌───┐ \n", - "q_0: ┤ H ├──■──\n", - " └───┘┌─┴─┐\n", - "q_1: ─────┤ X ├\n", - " └───┘\n", - ")\n" - ] - } - ], - "source": [ - "bell = CX @ (I ^ H) @ Zero\n", - "print(bell)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here is the expression $H e^{-iHt} |\\Phi_+\\rangle$." - ] - }, - { - "cell_type": "code", - "execution_count": 54, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.946668Z", - "iopub.status.busy": "2023-08-25T18:25:56.944947Z", - "iopub.status.idle": "2023-08-25T18:25:56.957552Z", - "shell.execute_reply": "2023-08-25T18:25:56.956933Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ComposedOp([\n", - " OperatorMeasurement(-1.0523732 * II\n", - " + 0.39793742 * IZ\n", - " - 0.3979374 * ZI\n", - " - 0.0112801 * ZZ\n", - " + 0.18093119 * XX),\n", - " e^(-i*1.0*θ * (\n", - " -1.0523732 * II\n", - " + 0.39793742 * IZ\n", - " - 0.3979374 * ZI\n", - " - 0.0112801 * ZZ\n", - " + 0.18093119 * XX\n", - " )),\n", - " CircuitStateFn(\n", - " ┌───┐ \n", - " q_0: ┤ H ├──■──\n", - " └───┘┌─┴─┐\n", - " q_1: ─────┤ X ├\n", - " └───┘\n", - " )\n", - "])\n" - ] - } - ], - "source": [ - "evo_and_meas = h2_measurement @ evolution_op @ bell\n", - "print(evo_and_meas)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Typically, we want to approximate $e^{-iHt}$ using two-qubit gates. We achieve this with the `convert` method of `PauliTrotterEvolution`, which traverses expressions applying trotterization to all `EvolvedOp`s encountered. Although we use `PauliTrotterEvolution` here, there are other possibilities, such as `MatrixEvolution`, which performs the exponentiation exactly." - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.963254Z", - "iopub.status.busy": "2023-08-25T18:25:56.961595Z", - "iopub.status.idle": "2023-08-25T18:25:56.979035Z", - "shell.execute_reply": "2023-08-25T18:25:56.978364Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ComposedOp([\n", - " OperatorMeasurement(-1.0523732 * II\n", - " + 0.39793742 * IZ\n", - " - 0.3979374 * ZI\n", - " - 0.0112801 * ZZ\n", - " + 0.18093119 * XX),\n", - " CircuitStateFn(\n", - " ┌───┐ ┌───────────────────────────────────────────┐\n", - " q_0: ┤ H ├──■──┤0 ├\n", - " └───┘┌─┴─┐│ exp(-it (II + IZ + ZI + ZZ + XX))(1.0*θ) │\n", - " q_1: ─────┤ X ├┤1 ├\n", - " └───┘└───────────────────────────────────────────┘\n", - " )\n", - "])\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10214/3156331339.py:1: DeprecationWarning: The class ``qiskit.opflow.evolutions.trotterizations.suzuki.Suzuki`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " trotterized_op = PauliTrotterEvolution(trotter_mode=Suzuki(order=2, reps=1)).convert(evo_and_meas)\n", - "/tmp/ipykernel_10214/3156331339.py:1: DeprecationWarning: The class ``qiskit.opflow.evolutions.pauli_trotter_evolution.PauliTrotterEvolution`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " trotterized_op = PauliTrotterEvolution(trotter_mode=Suzuki(order=2, reps=1)).convert(evo_and_meas)\n" - ] - } - ], - "source": [ - "trotterized_op = PauliTrotterEvolution(trotter_mode=Suzuki(order=2, reps=1)).convert(evo_and_meas)\n", - "# We can also set trotter_mode='suzuki' or leave it empty to default to first order Trotterization.\n", - "print(trotterized_op)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`trotterized_op` contains a `Parameter`. The `bind_parameters` method traverses the expression binding values to parameter names as specified via a `dict`. In this case, there is only one parameter." - ] - }, - { - "cell_type": "code", - "execution_count": 56, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.983663Z", - "iopub.status.busy": "2023-08-25T18:25:56.982499Z", - "iopub.status.idle": "2023-08-25T18:25:56.987858Z", - "shell.execute_reply": "2023-08-25T18:25:56.987268Z" - } - }, - "outputs": [], - "source": [ - "bound = trotterized_op.bind_parameters({evo_time: .5})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`bound` is a `ComposedOp`. The second factor is the circuit. Let's draw it to verify that the binding has taken place." - ] - }, - { - "cell_type": "code", - "execution_count": 57, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.992373Z", - "iopub.status.busy": "2023-08-25T18:25:56.991221Z", - "iopub.status.idle": "2023-08-25T18:25:57.003384Z", - "shell.execute_reply": "2023-08-25T18:25:57.002808Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
     ┌───┐     ┌─────────────────────────────────────────┐\n",
-       "q_0: ┤ H ├──■──┤0                                        ├\n",
-       "     └───┘┌─┴─┐│  exp(-it (II + IZ + ZI + ZZ + XX))(0.5) │\n",
-       "q_1: ─────┤ X ├┤1                                        ├\n",
-       "          └───┘└─────────────────────────────────────────┘
" - ], - "text/plain": [ - " ┌───┐ ┌─────────────────────────────────────────┐\n", - "q_0: ┤ H ├──■──┤0 ├\n", - " └───┘┌─┴─┐│ exp(-it (II + IZ + ZI + ZZ + XX))(0.5) │\n", - "q_1: ─────┤ X ├┤1 ├\n", - " └───┘└─────────────────────────────────────────┘" - ] - }, - "execution_count": 57, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bound[1].to_circuit().draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Expectations\n", - "\n", - "`Expectation`s are converters that enable the computation of expectation values of observables. They traverse an Operator tree, replacing `OperatorStateFn`s (observables) with equivalent instructions which are more amenable to\n", - "computation on quantum or classical hardware. For example, if we want to measure the expectation value of an Operator `o` expressed as a sum of Paulis with respect to some state function, but can only access diagonal measurements on quantum hardware, we can create an observable `~StateFn(o)` and use a ``PauliExpectation`` to convert it to a diagonal measurement and circuit pre-rotations to append to the state.\n", - "\n", - "Another interesting `Expectation` is the `AerPauliExpectation`, which converts the observable into a `CircuitStateFn` containing a special expectation snapshot instruction which `Aer` can execute natively with high performance." - ] - }, - { - "cell_type": "code", - "execution_count": 58, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:57.008810Z", - "iopub.status.busy": "2023-08-25T18:25:57.007113Z", - "iopub.status.idle": "2023-08-25T18:25:57.038750Z", - "shell.execute_reply": "2023-08-25T18:25:57.038119Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10214/119115492.py:2: DeprecationWarning: The class ``qiskit.opflow.expectations.pauli_expectation.PauliExpectation`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " print(PauliExpectation(group_paulis=False).convert(h2_measurement))\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "SummedOp([\n", - " ComposedOp([\n", - " OperatorMeasurement(-1.0523732 * II),\n", - " II\n", - " ]),\n", - " ComposedOp([\n", - " OperatorMeasurement(0.39793742 * IZ),\n", - " II\n", - " ]),\n", - " ComposedOp([\n", - " OperatorMeasurement(-0.3979374 * ZI),\n", - " II\n", - " ]),\n", - " ComposedOp([\n", - " OperatorMeasurement(-0.0112801 * ZZ),\n", - " II\n", - " ]),\n", - " ComposedOp([\n", - " OperatorMeasurement(0.18093119 * ZZ),\n", - " ┌───┐\n", - " q_0: ┤ H ├\n", - " ├───┤\n", - " q_1: ┤ H ├\n", - " └───┘\n", - " ])\n", - "])\n" - ] - } - ], - "source": [ - "# Note that XX was the only non-diagonal measurement in our H2 Observable\n", - "print(PauliExpectation(group_paulis=False).convert(h2_measurement))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "By default `group_paulis=True`, which will use the `AbelianGrouper` to convert the `SummedOp` into groups of mutually qubit-wise commuting Paulis. This reduces circuit execution overhead, as each group can share the same circuit execution." - ] - }, - { - "cell_type": "code", - "execution_count": 59, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:57.043515Z", - "iopub.status.busy": "2023-08-25T18:25:57.042286Z", - "iopub.status.idle": "2023-08-25T18:25:57.059888Z", - "shell.execute_reply": "2023-08-25T18:25:57.059208Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "SummedOp([\n", - " ComposedOp([\n", - " OperatorMeasurement(0.18093119 * ZZ\n", - " - 1.0523732 * II),\n", - " ┌───┐\n", - " q_0: ┤ H ├\n", - " ├───┤\n", - " q_1: ┤ H ├\n", - " └───┘\n", - " ]),\n", - " ComposedOp([\n", - " OperatorMeasurement(0.39793742 * IZ\n", - " - 0.3979374 * ZI\n", - " - 0.0112801 * ZZ),\n", - " II\n", - " ])\n", - "])\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10214/2025675328.py:1: DeprecationWarning: The class ``qiskit.opflow.expectations.pauli_expectation.PauliExpectation`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " print(PauliExpectation().convert(h2_measurement))\n" - ] - } - ], - "source": [ - "print(PauliExpectation().convert(h2_measurement))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that converters act recursively, that is, they traverse an expression applying their action only where possible. So we can just convert our full evolution and measurement expression. We could have equivalently composed the converted `h2_measurement` with our evolution `CircuitStateFn`. We proceed by applying the conversion on the entire expression." - ] - }, - { - "cell_type": "code", - "execution_count": 60, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:57.064656Z", - "iopub.status.busy": "2023-08-25T18:25:57.063441Z", - "iopub.status.idle": "2023-08-25T18:25:57.085692Z", - "shell.execute_reply": "2023-08-25T18:25:57.085076Z" - }, - "scrolled": true - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "SummedOp([\n", - " ComposedOp([\n", - " OperatorMeasurement(0.18093119 * ZZ\n", - " - 1.0523732 * II),\n", - " CircuitStateFn(\n", - " ┌───┐ ┌───────────────────────────────────────────┐┌───┐\n", - " q_0: ┤ H ├──■──┤0 ├┤ H ├\n", - " └───┘┌─┴─┐│ exp(-it (II + IZ + ZI + ZZ + XX))(1.0*θ) │├───┤\n", - " q_1: ─────┤ X ├┤1 ├┤ H ├\n", - " └───┘└───────────────────────────────────────────┘└───┘\n", - " )\n", - " ]),\n", - " ComposedOp([\n", - " OperatorMeasurement(0.39793742 * IZ\n", - " - 0.3979374 * ZI\n", - " - 0.0112801 * ZZ),\n", - " CircuitStateFn(\n", - " ┌───┐ ┌───────────────────────────────────────────┐\n", - " q_0: ┤ H ├──■──┤0 ├\n", - " └───┘┌─┴─┐│ exp(-it (II + IZ + ZI + ZZ + XX))(1.0*θ) │\n", - " q_1: ─────┤ X ├┤1 ├\n", - " └───┘└───────────────────────────────────────────┘\n", - " )\n", - " ])\n", - "])\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10214/4170641321.py:1: DeprecationWarning: The class ``qiskit.opflow.expectations.pauli_expectation.PauliExpectation`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " diagonalized_meas_op = PauliExpectation().convert(trotterized_op)\n" - ] - } - ], - "source": [ - "diagonalized_meas_op = PauliExpectation().convert(trotterized_op)\n", - "print(diagonalized_meas_op)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we bind multiple parameter values into a `ListOp`, followed by `eval` to evaluate the entire expression. We could have used `eval` earlier if we bound earlier, but it would not be efficient. Here, `eval` will convert our `CircuitStateFn`s to `VectorStateFn`s through simulation internally." - ] - }, - { - "cell_type": "code", - "execution_count": 61, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:57.090356Z", - "iopub.status.busy": "2023-08-25T18:25:57.089147Z", - "iopub.status.idle": "2023-08-25T18:25:57.097072Z", - "shell.execute_reply": "2023-08-25T18:25:57.096482Z" - } - }, - "outputs": [], - "source": [ - "evo_time_points = list(range(8))\n", - "h2_trotter_expectations = diagonalized_meas_op.bind_parameters({evo_time: evo_time_points})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here are the expectation values $\\langle \\Phi_+| e^{iHt} H e^{-iHt} |\\Phi_+\\rangle$ corresponding to the different values of the parameter." - ] - }, - { - "cell_type": "code", - "execution_count": 62, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:57.101588Z", - "iopub.status.busy": "2023-08-25T18:25:57.100436Z", - "iopub.status.idle": "2023-08-25T18:25:57.411162Z", - "shell.execute_reply": "2023-08-25T18:25:57.410486Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([-0.88272211+0.j, -0.88272211+0.j, -0.88272211+0.j, -0.88272211+0.j,\n", - " -0.88272211+0.j, -0.88272211+0.j, -0.88272211+0.j, -0.88272211+0.j])" - ] - }, - "execution_count": 62, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "h2_trotter_expectations.eval()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Executing `CircuitStateFn`s with the `CircuitSampler`\n", - "\n", - "The `CircuitSampler` traverses an Operator and converts any `CircuitStateFn`s into approximations of the resulting state function by a `DictStateFn` or `VectorStateFn` using a quantum backend. Note that in order to approximate the value of the `CircuitStateFn`, it must 1) send the state function through a depolarizing channel, which will destroy all phase information and 2) replace the sampled frequencies with **square roots** of the frequency, rather than the raw probability of sampling (which would be the equivalent of sampling the **square** of the state function, per the Born rule)." - ] - }, - { - "cell_type": "code", - "execution_count": 63, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:57.416132Z", - "iopub.status.busy": "2023-08-25T18:25:57.414939Z", - "iopub.status.idle": "2023-08-25T18:25:58.273080Z", - "shell.execute_reply": "2023-08-25T18:25:58.272263Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10214/2634295565.py:1: DeprecationWarning: The class ``qiskit.opflow.converters.circuit_sampler.CircuitSampler`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " sampler = CircuitSampler(backend=Aer.get_backend('aer_simulator'))\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Sampled Trotterized energies:\n", - " [-0.88272211 -0.88272211 -0.88272211 -0.88272211 -0.88272211 -0.88272211\n", - " -0.88272211 -0.88272211]\n" - ] - } - ], - "source": [ - "sampler = CircuitSampler(backend=Aer.get_backend('aer_simulator'))\n", - "# sampler.quantum_instance.run_config.shots = 1000\n", - "sampled_trotter_exp_op = sampler.convert(h2_trotter_expectations)\n", - "sampled_trotter_energies = sampled_trotter_exp_op.eval()\n", - "print('Sampled Trotterized energies:\\n {}'.format(np.real(sampled_trotter_energies)))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note again that the circuits are replaced by dicts with ***square roots*** of the circuit sampling probabilities. Take a look at one sub-expression before and after the conversion:" - ] - }, - { - "cell_type": "code", - "execution_count": 64, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:58.277314Z", - "iopub.status.busy": "2023-08-25T18:25:58.277024Z", - "iopub.status.idle": "2023-08-25T18:25:58.297905Z", - "shell.execute_reply": "2023-08-25T18:25:58.297085Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Before:\n", - "\n", - "ComposedOp([\n", - " OperatorMeasurement(0.18093119 * ZZ\n", - " - 1.0523732 * II),\n", - " CircuitStateFn(\n", - " ┌───┐ ┌───────────────────────────────────────┐┌───┐\n", - " q_0: ┤ H ├──■──┤0 ├┤ H ├\n", - " └───┘┌─┴─┐│ exp(-it (II + IZ + ZI + ZZ + XX))(0) │├───┤\n", - " q_1: ─────┤ X ├┤1 ├┤ H ├\n", - " └───┘└───────────────────────────────────────┘└───┘\n", - " )\n", - "])\n", - "\n", - "After:\n", - "\n", - "ComposedOp([\n", - " OperatorMeasurement(0.18093119 * ZZ\n", - " - 1.0523732 * II),\n", - " DictStateFn({'11': 0.7091753573693885, '00': 0.7050321357924049})\n", - "])\n" - ] - } - ], - "source": [ - "print('Before:\\n')\n", - "print(h2_trotter_expectations.reduce()[0][0])\n", - "print('\\nAfter:\\n')\n", - "print(sampled_trotter_exp_op[0][0])" - ] - }, - { - "cell_type": "code", - "execution_count": 65, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:58.303397Z", - "iopub.status.busy": "2023-08-25T18:25:58.302115Z", - "iopub.status.idle": "2023-08-25T18:25:58.902660Z", - "shell.execute_reply": "2023-08-25T18:25:58.902010Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "

Version Information

SoftwareVersion
qiskitNone
qiskit-terra0.45.0
qiskit_aer0.12.2
System information
Python version3.8.17
Python compilerGCC 11.3.0
Python builddefault, Jun 7 2023 12:29:56
OSLinux
CPUs2
Memory (Gb)6.7694854736328125
Fri Aug 25 18:25:58 2023 UTC
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "

This code is a part of Qiskit

© Copyright IBM 2017, 2023.

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.

" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import qiskit.tools.jupyter\n", - "%qiskit_version_table\n", - "%qiskit_copyright" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.17" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": { - "0536aec4253349f0a510da687a7cb5ad": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": "0px 0px 10px 0px", - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "4462da81d1df499e82002402b0af8517": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "HTMLView", - "description": "", - "description_allow_html": false, - "layout": "IPY_MODEL_0536aec4253349f0a510da687a7cb5ad", - "placeholder": "​", - "style": "IPY_MODEL_6491e46af2f8493eb678e78d34c39e13", - "tabbable": null, - "tooltip": null, - "value": "

Circuit Properties

" - } - }, - "6491e46af2f8493eb678e78d34c39e13": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "StyleView", - "background": null, - "description_width": "", - "font_size": null, - "text_color": null - } - } - }, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/operators/02_gradients_framework.ipynb b/docs/tutorials/operators/02_gradients_framework.ipynb deleted file mode 100644 index 8c3b9f5ac29c..000000000000 --- a/docs/tutorials/operators/02_gradients_framework.ipynb +++ /dev/null @@ -1,1562 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "collapsed": true, - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "# Qiskit Gradient Framework\n", - "\n", - "The gradient framework enables the evaluation of quantum gradients as well as functions thereof.\n", - "Besides standard first order gradients of expectation values of the form\n", - "$$ \\langle\\psi\\left(\\theta\\right)|\\hat{O}\\left(\\omega\\right)|\\psi\\left(\\theta\\right)\\rangle $$\n", - "\n", - "\n", - "The gradient framework also supports the evaluation of second order gradients (Hessians), and the Quantum Fisher Information (QFI) of quantum states $|\\psi\\left(\\theta\\right)\\rangle$." - ] - }, - { - "attachments": { - "gradient_framework.png": { - "image/png": "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" - } - }, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "![gradient_framework.png](attachment:gradient_framework.png)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Imports" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:00.525679Z", - "iopub.status.busy": "2023-08-25T18:26:00.523227Z", - "iopub.status.idle": "2023-08-25T18:26:01.188952Z", - "shell.execute_reply": "2023-08-25T18:26:01.184878Z" - }, - "slideshow": { - "slide_type": "subslide" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10262/3317236272.py:5: DeprecationWarning: The ``qiskit.opflow`` module is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " from qiskit.opflow import Z, X, I, StateFn, CircuitStateFn, SummedOp\n" - ] - } - ], - "source": [ - "#General imports\n", - "import numpy as np\n", - "\n", - "#Operator Imports\n", - "from qiskit.opflow import Z, X, I, StateFn, CircuitStateFn, SummedOp\n", - "from qiskit.opflow.gradients import Gradient, NaturalGradient, QFI, Hessian\n", - "\n", - "#Circuit imports\n", - "from qiskit.circuit import QuantumCircuit, QuantumRegister, Parameter, ParameterVector, ParameterExpression\n", - "from qiskit.circuit.library import EfficientSU2" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "## First Order Gradients\n", - "\n", - "\n", - "Given a parameterized quantum state $|\\psi\\left(\\theta\\right)\\rangle = V\\left(\\theta\\right)|\\psi\\rangle$ with input state $|\\psi\\rangle$, parametrized Ansatz $V\\left(\\theta\\right)$, and observable $\\hat{O}\\left(\\omega\\right)=\\sum_{i}\\omega_i\\hat{O}_i$, we want to compute..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Gradients w.r.t. Measurement Operator Parameters\n", - "\n", - "Gradient of an expectation value w.r.t. a coefficient of the measurement operator respectively observable $\\hat{O}\\left(\\omega\\right)$, i.e.\n", - "$$ \\frac{\\partial\\langle\\psi\\left(\\theta\\right)|\\hat{O}\\left(\\omega\\right)|\\psi\\left(\\theta\\right)\\rangle}{\\partial\\omega_i} = \\langle\\psi\\left(\\theta\\right)|\\hat{O}_i\\left(\\omega\\right)|\\psi\\left(\\theta\\right)\\rangle. $$" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First of all, we define a quantum state $|\\psi\\left(\\theta\\right)\\rangle$ and a Hamiltonian $H$ acting as observable. Then, the state and the Hamiltonian are wrapped into an object defining the expectation value $$ \\langle\\psi\\left(\\theta\\right)|H|\\psi\\left(\\theta\\right)\\rangle. $$" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:01.192686Z", - "iopub.status.busy": "2023-08-25T18:26:01.192216Z", - "iopub.status.idle": "2023-08-25T18:26:01.564265Z", - "shell.execute_reply": "2023-08-25T18:26:01.563542Z" - }, - "slideshow": { - "slide_type": "subslide" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10262/1548928561.py:14: DeprecationWarning: The class ``qiskit.opflow.state_fns.operator_state_fn.OperatorStateFn`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " op = ~StateFn(H) @ CircuitStateFn(primitive=qc, coeff=1.)\n", - "/tmp/ipykernel_10262/1548928561.py:14: DeprecationWarning: The class ``qiskit.opflow.state_fns.circuit_state_fn.CircuitStateFn`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " op = ~StateFn(H) @ CircuitStateFn(primitive=qc, coeff=1.)\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ComposedOp([\n", - " OperatorMeasurement(2.0 * X\n", - " + 1.0 * Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌───────┐┌───────┐\n", - " q0: ┤ H ├┤ Rz(a) ├┤ Rx(b) ├\n", - " └───┘└───────┘└───────┘\n", - " )\n", - "])\n" - ] - } - ], - "source": [ - "# Instantiate the quantum state\n", - "a = Parameter('a')\n", - "b = Parameter('b')\n", - "q = QuantumRegister(1)\n", - "qc = QuantumCircuit(q)\n", - "qc.h(q)\n", - "qc.rz(a, q[0])\n", - "qc.rx(b, q[0])\n", - "\n", - "# Instantiate the Hamiltonian observable\n", - "H = (2 * X) + Z\n", - "\n", - "# Combine the Hamiltonian observable and the state\n", - "op = ~StateFn(H) @ CircuitStateFn(primitive=qc, coeff=1.)\n", - "\n", - "# Print the operator corresponding to the expectation value\n", - "print(op)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We construct a list of the parameters for which we aim to evaluate the gradient.\n", - "Now, this list and the expectation value operator are used to generate the operator which represents the gradient." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:01.567896Z", - "iopub.status.busy": "2023-08-25T18:26:01.567308Z", - "iopub.status.idle": "2023-08-25T18:26:01.643180Z", - "shell.execute_reply": "2023-08-25T18:26:01.642556Z" - }, - "scrolled": false, - "slideshow": { - "slide_type": "subslide" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ListOp([\n", - " SummedOp([\n", - " ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌─────────────┐┌───────┐┌───┐\n", - " q0: ┤ H ├┤ Rz(a + π/2) ├┤ Rx(b) ├┤ H ├\n", - " └───┘└─────────────┘└───────┘└───┘\n", - " )\n", - " ]),\n", - " -1.0 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌─────────────┐┌───────┐┌───┐\n", - " q0: ┤ H ├┤ Rz(a - π/2) ├┤ Rx(b) ├┤ H ├\n", - " └───┘└─────────────┘└───────┘└───┘\n", - " )\n", - " ]),\n", - " 0.5 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌─────────────┐┌───────┐\n", - " q0: ┤ H ├┤ Rz(a + π/2) ├┤ Rx(b) ├\n", - " └───┘└─────────────┘└───────┘\n", - " )\n", - " ]),\n", - " -0.5 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌─────────────┐┌───────┐\n", - " q0: ┤ H ├┤ Rz(a - π/2) ├┤ Rx(b) ├\n", - " └───┘└─────────────┘└───────┘\n", - " )\n", - " ])\n", - " ]),\n", - " SummedOp([\n", - " ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌───────┐┌─────────────┐┌───┐\n", - " q0: ┤ H ├┤ Rz(a) ├┤ Rx(b + π/2) ├┤ H ├\n", - " └───┘└───────┘└─────────────┘└───┘\n", - " )\n", - " ]),\n", - " -1.0 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌───────┐┌─────────────┐┌───┐\n", - " q0: ┤ H ├┤ Rz(a) ├┤ Rx(b - π/2) ├┤ H ├\n", - " └───┘└───────┘└─────────────┘└───┘\n", - " )\n", - " ]),\n", - " 0.5 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌───────┐┌─────────────┐\n", - " q0: ┤ H ├┤ Rz(a) ├┤ Rx(b + π/2) ├\n", - " └───┘└───────┘└─────────────┘\n", - " )\n", - " ]),\n", - " -0.5 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌───────┐┌─────────────┐\n", - " q0: ┤ H ├┤ Rz(a) ├┤ Rx(b - π/2) ├\n", - " └───┘└───────┘└─────────────┘\n", - " )\n", - " ])\n", - " ])\n", - "])\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10262/388471408.py:7: DeprecationWarning: The class ``qiskit.opflow.gradients.gradient.Gradient`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " grad = Gradient().convert(operator = op, params = params)\n" - ] - } - ], - "source": [ - "params = [a, b]\n", - "\n", - "# Define the values to be assigned to the parameters\n", - "value_dict = {a: np.pi / 4, b: np.pi}\n", - "\n", - "# Convert the operator and the gradient target params into the respective operator\n", - "grad = Gradient().convert(operator = op, params = params)\n", - "\n", - "# Print the operator corresponding to the Gradient\n", - "print(grad)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "All that is left to do is to assign values to the parameters and to evaluate the gradient operators." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:01.647475Z", - "iopub.status.busy": "2023-08-25T18:26:01.646058Z", - "iopub.status.idle": "2023-08-25T18:26:01.704502Z", - "shell.execute_reply": "2023-08-25T18:26:01.703801Z" - }, - "slideshow": { - "slide_type": "subslide" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Gradient [(-1.414213562373094+0j), (-0.7071067811865472+0j)]\n" - ] - } - ], - "source": [ - "# Assign the parameters and evaluate the gradient\n", - "grad_result = grad.assign_parameters(value_dict).eval()\n", - "print('Gradient', grad_result)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "### Gradients w.r.t. State Parameters\n", - "\n", - "Gradient of an expectation value w.r.t. a state $|\\psi\\left(\\theta\\right)\\rangle$ parameter, i.e.$$\\frac{\\partial\\langle\\psi\\left(\\theta\\right)|\\hat{O}\\left(\\omega\\right)|\\psi\\left(\\theta\\right)\\rangle}{\\partial\\theta} $$\n", - "respectively of sampling probabilities w.r.t. a state $|\\psi\\left(\\theta\\right)\\rangle$ parameter, i.e.\n", - "$$ \\frac{\\partial p_i}{\\partial\\theta} = \\frac{\\partial\\langle\\psi\\left(\\theta\\right)|i\\rangle\\langle i |\\psi\\left(\\theta\\right)\\rangle}{\\partial\\theta}.$$\n", - "A gradient w.r.t. a state parameter may be evaluated with different methods. Each method has advantages and disadvantages." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:01.709455Z", - "iopub.status.busy": "2023-08-25T18:26:01.708278Z", - "iopub.status.idle": "2023-08-25T18:26:01.719466Z", - "shell.execute_reply": "2023-08-25T18:26:01.718868Z" - }, - "slideshow": { - "slide_type": "subslide" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ComposedOp([\n", - " OperatorMeasurement(0.5 * X\n", - " - 1.0 * Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌───────┐┌───────┐\n", - " q0: ┤ H ├┤ Rz(a) ├┤ Rx(b) ├\n", - " └───┘└───────┘└───────┘\n", - " )\n", - "])\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10262/3920713381.py:9: DeprecationWarning: The class ``qiskit.opflow.state_fns.operator_state_fn.OperatorStateFn`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " op = ~StateFn(H) @ CircuitStateFn(primitive=qc, coeff=1.)\n", - "/tmp/ipykernel_10262/3920713381.py:9: DeprecationWarning: The class ``qiskit.opflow.state_fns.circuit_state_fn.CircuitStateFn`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " op = ~StateFn(H) @ CircuitStateFn(primitive=qc, coeff=1.)\n" - ] - } - ], - "source": [ - "# Define the Hamiltonian with fixed coefficients\n", - "H = 0.5 * X - 1 * Z\n", - "# Define the parameters w.r.t. we want to compute the gradients\n", - "params = [a, b]\n", - "# Define the values to be assigned to the parameters\n", - "value_dict = { a: np.pi / 4, b: np.pi}\n", - "\n", - "# Combine the Hamiltonian observable and the state into an expectation value operator\n", - "op = ~StateFn(H) @ CircuitStateFn(primitive=qc, coeff=1.)\n", - "print(op)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Parameter Shift Gradients\n", - "Given a Hermitian operator $g$ with two unique eigenvalues $\\pm r$ which acts as generator for a parameterized quantum gate $$G(\\theta)= e^{-i\\theta g}.$$\n", - "Then, quantum gradients can be computed by using eigenvalue $r$ dependent shifts to parameters. \n", - "All [standard, parameterized Qiskit gates](https://github.com/Qiskit/qiskit-terra/tree/master/qiskit/circuit/library/standard_gates) can be shifted with $\\pi/2$, i.e.,\n", - " $$ \\frac{\\partial\\langle\\psi\\left(\\theta\\right)|\\hat{O}\\left(\\omega\\right)|\\psi\\left(\\theta\\right)\\rangle}{\\partial\\theta} = \\left(\\langle\\psi\\left(\\theta+\\pi/2\\right)|\\hat{O}\\left(\\omega\\right)|\\psi\\left(\\theta+\\pi/2\\right)\\rangle -\\langle\\psi\\left(\\theta-\\pi/2\\right)|\\hat{O}\\left(\\omega\\right)|\\psi\\left(\\theta-\\pi/2\\right)\\rangle\\right) / 2.$$\n", - " Probability gradients are computed equivalently." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:01.724151Z", - "iopub.status.busy": "2023-08-25T18:26:01.722838Z", - "iopub.status.idle": "2023-08-25T18:26:01.806625Z", - "shell.execute_reply": "2023-08-25T18:26:01.805968Z" - }, - "slideshow": { - "slide_type": "subslide" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ListOp([\n", - " SummedOp([\n", - " 0.25 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌─────────────┐┌───────┐┌───┐\n", - " q0: ┤ H ├┤ Rz(a + π/2) ├┤ Rx(b) ├┤ H ├\n", - " └───┘└─────────────┘└───────┘└───┘\n", - " )\n", - " ]),\n", - " -0.25 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌─────────────┐┌───────┐┌───┐\n", - " q0: ┤ H ├┤ Rz(a - π/2) ├┤ Rx(b) ├┤ H ├\n", - " └───┘└─────────────┘└───────┘└───┘\n", - " )\n", - " ]),\n", - " -0.5 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌─────────────┐┌───────┐\n", - " q0: ┤ H ├┤ Rz(a + π/2) ├┤ Rx(b) ├\n", - " └───┘└─────────────┘└───────┘\n", - " )\n", - " ]),\n", - " 0.5 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌─────────────┐┌───────┐\n", - " q0: ┤ H ├┤ Rz(a - π/2) ├┤ Rx(b) ├\n", - " └───┘└─────────────┘└───────┘\n", - " )\n", - " ])\n", - " ]),\n", - " SummedOp([\n", - " 0.25 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌───────┐┌─────────────┐┌───┐\n", - " q0: ┤ H ├┤ Rz(a) ├┤ Rx(b + π/2) ├┤ H ├\n", - " └───┘└───────┘└─────────────┘└───┘\n", - " )\n", - " ]),\n", - " -0.25 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌───────┐┌─────────────┐┌───┐\n", - " q0: ┤ H ├┤ Rz(a) ├┤ Rx(b - π/2) ├┤ H ├\n", - " └───┘└───────┘└─────────────┘└───┘\n", - " )\n", - " ]),\n", - " -0.5 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌───────┐┌─────────────┐\n", - " q0: ┤ H ├┤ Rz(a) ├┤ Rx(b + π/2) ├\n", - " └───┘└───────┘└─────────────┘\n", - " )\n", - " ]),\n", - " 0.5 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌───────┐┌─────────────┐\n", - " q0: ┤ H ├┤ Rz(a) ├┤ Rx(b - π/2) ├\n", - " └───┘└───────┘└─────────────┘\n", - " )\n", - " ])\n", - " ])\n", - "])\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10262/2773902786.py:3: DeprecationWarning: The class ``qiskit.opflow.gradients.gradient.Gradient`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " state_grad = Gradient(grad_method='param_shift').convert(operator=op, params=params)\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "State gradient computed with parameter shift [(-0.35355339059327356+0j), (0.7071067811865471+0j)]\n" - ] - } - ], - "source": [ - "# Convert the expectation value into an operator corresponding to the gradient w.r.t. the state parameters using \n", - "# the parameter shift method.\n", - "state_grad = Gradient(grad_method='param_shift').convert(operator=op, params=params)\n", - "# Print the operator corresponding to the gradient\n", - "print(state_grad)\n", - "# Assign the parameters and evaluate the gradient\n", - "state_grad_result = state_grad.assign_parameters(value_dict).eval()\n", - "print('State gradient computed with parameter shift', state_grad_result)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "subslide" - } - }, - "source": [ - "### Linear Combination of Unitaries Gradients\n", - "Unitaries can be written as $U\\left(\\omega\\right) = e^{iM\\left(\\omega\\right)}$, where $M\\left(\\omega\\right)$ denotes a parameterized Hermitian matrix. \n", - "Further, Hermitian matrices can be decomposed into weighted sums of Pauli terms, i.e., $M\\left(\\omega\\right) = \\sum_pm_p\\left(\\omega\\right)h_p$ with $m_p\\left(\\omega\\right)\\in\\mathbb{R}$ and $h_p=\\bigotimes\\limits_{j=0}^{n-1}\\sigma_{j, p}$ for $\\sigma_{j, p}\\in\\left\\{I, X, Y, Z\\right\\}$ acting on the $j^{\\text{th}}$ qubit. Thus, the gradients of \n", - "$U_k\\left(\\omega_k\\right)$ are given by\n", - "\\begin{equation*}\n", - "\\frac{\\partial U_k\\left(\\omega_k\\right)}{\\partial\\omega_k} = \\sum\\limits_pi \\frac{\\partial m_{k,p}\\left(\\omega_k\\right)}{\\partial\\omega_k}U_k\\left(\\omega_k\\right)h_{k_p}.\n", - "\\end{equation*}\n", - "\n", - "Combining this observation with a circuit structure presented in [Simulating physical phenomena by quantum networks](https://journals.aps.org/pra/abstract/10.1103/PhysRevA.65.042323) allows us to compute the gradient with the evaluation of a single quantum circuit." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:01.811546Z", - "iopub.status.busy": "2023-08-25T18:26:01.810356Z", - "iopub.status.idle": "2023-08-25T18:26:01.886854Z", - "shell.execute_reply": "2023-08-25T18:26:01.886211Z" - }, - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ListOp([\n", - " SummedOp([\n", - " 0.5 * ComposedOp([\n", - " OperatorMeasurement(2.0 * ZZ),\n", - " CircuitStateFn(\n", - " ┌────────────┐ ┌───────┐┌───────┐┌────────────┐\n", - " q0: ┤ U(π/2,0,π) ├────────■─┤ Rz(a) ├┤ Rx(b) ├┤ U(π/2,0,π) ├\n", - " └───┬───┬────┘┌─────┐ │ └─┬───┬─┘└───────┘└────────────┘\n", - " q52: ────┤ H ├─────┤ Sdg ├─■───┤ H ├─────────────────────────\n", - " └───┘ └─────┘ └───┘ \n", - " ) * 0.7071067811865476\n", - " ]),\n", - " -1.0 * ComposedOp([\n", - " OperatorMeasurement(2.0 * ZZ),\n", - " CircuitStateFn(\n", - " ┌────────────┐ ┌───────┐┌───────┐\n", - " q0: ┤ U(π/2,0,π) ├────────■─┤ Rz(a) ├┤ Rx(b) ├\n", - " └───┬───┬────┘┌─────┐ │ └─┬───┬─┘└───────┘\n", - " q56: ────┤ H ├─────┤ Sdg ├─■───┤ H ├───────────\n", - " └───┘ └─────┘ └───┘ \n", - " ) * 0.7071067811865476\n", - " ])\n", - " ]),\n", - " SummedOp([\n", - " 0.5 * ComposedOp([\n", - " OperatorMeasurement(2.0 * ZZ),\n", - " CircuitStateFn(\n", - " ┌────────────┐┌───────┐┌───┐┌───────┐┌────────────┐\n", - " q0: ┤ U(π/2,0,π) ├┤ Rz(a) ├┤ X ├┤ Rx(b) ├┤ U(π/2,0,π) ├\n", - " └───┬───┬────┘└┬─────┬┘└─┬─┘└─┬───┬─┘└────────────┘\n", - " q60: ────┤ H ├──────┤ Sdg ├───■────┤ H ├────────────────\n", - " └───┘ └─────┘ └───┘ \n", - " ) * 0.7071067811865476\n", - " ]),\n", - " -1.0 * ComposedOp([\n", - " OperatorMeasurement(2.0 * ZZ),\n", - " CircuitStateFn(\n", - " ┌────────────┐┌───────┐┌───┐┌───────┐\n", - " q0: ┤ U(π/2,0,π) ├┤ Rz(a) ├┤ X ├┤ Rx(b) ├\n", - " └───┬───┬────┘└┬─────┬┘└─┬─┘└─┬───┬─┘\n", - " q64: ────┤ H ├──────┤ Sdg ├───■────┤ H ├──\n", - " └───┘ └─────┘ └───┘ \n", - " ) * 0.7071067811865476\n", - " ])\n", - " ])\n", - "])\n", - "State gradient computed with the linear combination method [(-0.35355339059327373+0j), (0.7071067811865471+0j)]\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10262/141190660.py:3: DeprecationWarning: The class ``qiskit.opflow.gradients.gradient.Gradient`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " state_grad = Gradient(grad_method='lin_comb').convert(operator=op, params=params)\n" - ] - } - ], - "source": [ - "# Convert the expectation value into an operator corresponding to the gradient w.r.t. the state parameter using \n", - "# the linear combination of unitaries method.\n", - "state_grad = Gradient(grad_method='lin_comb').convert(operator=op, params=params)\n", - "\n", - "# Print the operator corresponding to the gradient\n", - "print(state_grad)\n", - "\n", - "# Assign the parameters and evaluate the gradient\n", - "state_grad_result = state_grad.assign_parameters(value_dict).eval()\n", - "print('State gradient computed with the linear combination method', state_grad_result)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "subslide" - } - }, - "source": [ - "### Finite Difference Gradients\n", - "\n", - "Unlike the other methods, finite difference gradients are numerical estimations rather than analytical values.\n", - "This implementation employs a central difference approach with $\\epsilon \\ll 1$\n", - "$$ \\frac{\\partial\\langle\\psi\\left(\\theta\\right)|\\hat{O}\\left(\\omega\\right)|\\psi\\left(\\theta\\right)\\rangle}{\\partial\\theta} \\approx \\frac{1}{2\\epsilon} \\left(\\langle\\psi\\left(\\theta+\\epsilon\\right)|\\hat{O}\\left(\\omega\\right)|\\psi\\left(\\theta+\\epsilon\\right)\\rangle - \\partial\\langle\\psi\\left(\\theta-\\epsilon\\right)|\\hat{O}\\left(\\omega\\right)|\\psi\\left(\\theta-\\epsilon\\right)\\rangle\\right).$$\n", - " Probability gradients are computed equivalently." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:01.891613Z", - "iopub.status.busy": "2023-08-25T18:26:01.890434Z", - "iopub.status.idle": "2023-08-25T18:26:01.977595Z", - "shell.execute_reply": "2023-08-25T18:26:01.976861Z" - }, - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10262/1989470805.py:3: DeprecationWarning: The class ``qiskit.opflow.gradients.gradient.Gradient`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " state_grad = Gradient(grad_method='fin_diff').convert(operator=op, params=params)\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ListOp([\n", - " SummedOp([\n", - " 250000.0 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌────────────────┐┌───────┐┌───┐\n", - " q0: ┤ H ├┤ Rz(a + 1.0e-6) ├┤ Rx(b) ├┤ H ├\n", - " └───┘└────────────────┘└───────┘└───┘\n", - " )\n", - " ]),\n", - " -250000.0 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌────────────────┐┌───────┐┌───┐\n", - " q0: ┤ H ├┤ Rz(a - 1.0e-6) ├┤ Rx(b) ├┤ H ├\n", - " └───┘└────────────────┘└───────┘└───┘\n", - " )\n", - " ]),\n", - " -500000.0 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌────────────────┐┌───────┐\n", - " q0: ┤ H ├┤ Rz(a + 1.0e-6) ├┤ Rx(b) ├\n", - " └───┘└────────────────┘└───────┘\n", - " )\n", - " ]),\n", - " 500000.0 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌────────────────┐┌───────┐\n", - " q0: ┤ H ├┤ Rz(a - 1.0e-6) ├┤ Rx(b) ├\n", - " └───┘└────────────────┘└───────┘\n", - " )\n", - " ])\n", - " ]),\n", - " SummedOp([\n", - " 250000.0 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌───────┐┌────────────────┐┌───┐\n", - " q0: ┤ H ├┤ Rz(a) ├┤ Rx(b + 1.0e-6) ├┤ H ├\n", - " └───┘└───────┘└────────────────┘└───┘\n", - " )\n", - " ]),\n", - " -250000.0 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌───────┐┌────────────────┐┌───┐\n", - " q0: ┤ H ├┤ Rz(a) ├┤ Rx(b - 1.0e-6) ├┤ H ├\n", - " └───┘└───────┘└────────────────┘└───┘\n", - " )\n", - " ]),\n", - " -500000.0 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌───────┐┌────────────────┐\n", - " q0: ┤ H ├┤ Rz(a) ├┤ Rx(b + 1.0e-6) ├\n", - " └───┘└───────┘└────────────────┘\n", - " )\n", - " ]),\n", - " 500000.0 * ComposedOp([\n", - " OperatorMeasurement(Z),\n", - " CircuitStateFn(\n", - " ┌───┐┌───────┐┌────────────────┐\n", - " q0: ┤ H ├┤ Rz(a) ├┤ Rx(b - 1.0e-6) ├\n", - " └───┘└───────┘└────────────────┘\n", - " )\n", - " ])\n", - " ])\n", - "])\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "State gradient computed with finite difference [(-0.3535533905739581+0j), (0.707106781152+0j)]\n" - ] - } - ], - "source": [ - "# Convert the expectation value into an operator corresponding to the gradient w.r.t. the state parameter using \n", - "# the finite difference method.\n", - "state_grad = Gradient(grad_method='fin_diff').convert(operator=op, params=params)\n", - "\n", - "# Print the operator corresponding to the gradient\n", - "print(state_grad)\n", - "\n", - "# Assign the parameters and evaluate the gradient\n", - "state_grad_result = state_grad.assign_parameters(value_dict).eval()\n", - "print('State gradient computed with finite difference', state_grad_result)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "### Natural Gradient\n", - "\n", - "A special type of first order gradient is the natural gradient which has proven itself useful in classical machine learning and is already being studied in the quantum context. This quantity represents a gradient that is 'rescaled' with the inverse [Quantum Fisher Information matrix](#Quantum-Fisher-Information-(QFI)) (QFI)\n", - "$$ QFI ^{-1} \\frac{\\partial\\langle\\psi\\left(\\theta\\right)|\\hat{O}\\left(\\omega\\right)|\\psi\\left(\\theta\\right)\\rangle}{\\partial\\theta}.$$" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "subslide" - } - }, - "source": [ - "Instead of inverting the QFI, one can also use a least-square solver with or without regularization to solve\n", - "\n", - "$$ QFI x = \\frac{\\partial\\langle\\psi\\left(\\theta\\right)|\\hat{O}\\left(\\omega\\right)|\\psi\\left(\\theta\\right)\\rangle}{\\partial\\theta}.$$\n", - "\n", - "The implementation supports ridge and lasso regularization with automatic search for a good parameter using [L-curve corner search](https://arxiv.org/pdf/1608.04571.pdf) as well as two types of perturbations of the diagonal elements of the QFI.\n", - "\n", - "The natural gradient can be used instead of the standard gradient with any gradient-based optimizer and/or ODE solver." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:01.982494Z", - "iopub.status.busy": "2023-08-25T18:26:01.981291Z", - "iopub.status.idle": "2023-08-25T18:26:02.658286Z", - "shell.execute_reply": "2023-08-25T18:26:02.657602Z" - }, - "slideshow": { - "slide_type": "subslide" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10262/4115387702.py:4: DeprecationWarning: The class ``qiskit.opflow.gradients.natural_gradient.NaturalGradient`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " nat_grad = NaturalGradient(grad_method='lin_comb', qfi_method='lin_comb_full', regularization='ridge').convert(\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Natural gradient computed with linear combination of unitaries [-2.62902831 1.31451415]\n" - ] - } - ], - "source": [ - "# Besides the method to compute the circuit gradients resp. QFI, a regularization method can be chosen: \n", - "# `ridge` or `lasso` with automatic parameter search or `perturb_diag_elements` or `perturb_diag` \n", - "# which perturb the diagonal elements of the QFI.\n", - "nat_grad = NaturalGradient(grad_method='lin_comb', qfi_method='lin_comb_full', regularization='ridge').convert(\n", - " operator=op, params=params)\n", - "\n", - "# Assign the parameters and evaluate the gradient\n", - "nat_grad_result = nat_grad.assign_parameters(value_dict).eval()\n", - "print('Natural gradient computed with linear combination of unitaries', nat_grad_result)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "## Hessians (Second Order Gradients)\n", - "\n", - "Four types of second order gradients are supported by the gradient framework.\n", - "\n", - "1. Gradient of an expectation value w.r.t. a coefficient of the measurement operator respectively observable $\\hat{O}\\left(\\omega\\right)$, i.e.\n", - "$\\frac{\\partial^2\\langle\\psi\\left(\\theta\\right)|\\hat{O}\\left(\\omega\\right)|\\psi\\left(\\theta\\right)\\rangle}{\\partial\\omega^2}$\n", - "2. Gradient of an expectation value w.r.t. a state $|\\psi\\left(\\theta\\right)\\rangle$ parameter, i.e.\n", - "$\\frac{\\partial^2\\langle\\psi\\left(\\theta\\right)|\\hat{O}\\left(\\omega\\right)|\\psi\\left(\\theta\\right)\\rangle}{\\partial\\theta^2}$\n", - "3. Gradient of sampling probabilities w.r.t. a state $|\\psi\\left(\\theta\\right)\\rangle$ parameter, i.e.\n", - "$\\frac{\\partial^2 p_i}{\\partial\\theta^2} = \\frac{\\partial^2\\langle\\psi\\left(\\theta\\right)|i\\rangle\\langle i|\\psi\\left(\\theta\\right)\\rangle}{\\partial\\theta^2}$\n", - "4. Gradient of an expectation value w.r.t. a state $|\\psi\\left(\\theta\\right)\\rangle$ parameter and a coefficient of the measurement operator respectively observable $\\hat{O}\\left(\\omega\\right)$, i.e.\n", - "$\\frac{\\partial^2\\langle\\psi\\left(\\theta\\right)|\\hat{O}\\left(\\omega\\right)|\\psi\\left(\\theta\\right)\\rangle}{\\partial\\theta\\partial\\omega}$\n", - "\n", - "In the following examples are given for the first two Hessian types. The remaining Hessians are evaluated analogously." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Hessians w.r.t. Measurement Operator Parameters\n", - "\n", - "Again, we define a quantum state $|\\psi\\left(\\theta\\right)\\rangle$ and a Hamiltonian $H$ acting as observable. Then, the state and the Hamiltonian are wrapped into an object defining the expectation value $$ \\langle\\psi\\left(\\theta\\right)|H|\\psi\\left(\\theta\\right)\\rangle. $$" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:02.663279Z", - "iopub.status.busy": "2023-08-25T18:26:02.661991Z", - "iopub.status.idle": "2023-08-25T18:26:02.671563Z", - "shell.execute_reply": "2023-08-25T18:26:02.670966Z" - }, - "slideshow": { - "slide_type": "subslide" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10262/3187115399.py:15: DeprecationWarning: The class ``qiskit.opflow.state_fns.operator_state_fn.OperatorStateFn`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " op = ~StateFn(H) @ CircuitStateFn(primitive=qc, coeff=1.)\n", - "/tmp/ipykernel_10262/3187115399.py:15: DeprecationWarning: The class ``qiskit.opflow.state_fns.circuit_state_fn.CircuitStateFn`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " op = ~StateFn(H) @ CircuitStateFn(primitive=qc, coeff=1.)\n" - ] - } - ], - "source": [ - "# Instantiate the Hamiltonian observable\n", - "H = X\n", - "\n", - "# Instantiate the quantum state with two parameters\n", - "a = Parameter('a')\n", - "b = Parameter('b')\n", - "\n", - "q = QuantumRegister(1)\n", - "qc = QuantumCircuit(q)\n", - "qc.h(q)\n", - "qc.rz(a, q[0])\n", - "qc.rx(b, q[0])\n", - "\n", - "# Combine the Hamiltonian observable and the state\n", - "op = ~StateFn(H) @ CircuitStateFn(primitive=qc, coeff=1.)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we can choose the parameters for which we want to compute second order gradients.\n", - "- Given a tuple, the `Hessian` will evaluate the second order gradient for the two parameters. \n", - "- Given a list, the `Hessian` will evaluate the second order gradient for all possible combinations of tuples of these parameters.\n", - "\n", - "After binding parameter values to the parameters, the Hessian can be evaluated." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:02.676093Z", - "iopub.status.busy": "2023-08-25T18:26:02.674937Z", - "iopub.status.idle": "2023-08-25T18:26:02.767444Z", - "shell.execute_reply": "2023-08-25T18:26:02.766823Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hessian \n", - " [[-7.07106781e-01 0.00000000e+00]\n", - " [ 0.00000000e+00 -5.55111512e-17]]\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10262/2007550450.py:2: DeprecationWarning: The class ``qiskit.opflow.gradients.hessian.Hessian`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " hessian = Hessian().convert(operator = op, params = [a, b])\n" - ] - } - ], - "source": [ - "# Convert the operator and the hessian target coefficients into the respective operator\n", - "hessian = Hessian().convert(operator = op, params = [a, b])\n", - "\n", - "# Define the values to be assigned to the parameters\n", - "value_dict = {a: np.pi / 4, b: np.pi/4}\n", - "\n", - "# Assign the parameters and evaluate the Hessian w.r.t. the Hamiltonian coefficients\n", - "hessian_result = hessian.assign_parameters(value_dict).eval()\n", - "print('Hessian \\n', np.real(np.array(hessian_result)))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Hessians w.r.t. State Parameters" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:02.772148Z", - "iopub.status.busy": "2023-08-25T18:26:02.770982Z", - "iopub.status.idle": "2023-08-25T18:26:02.976214Z", - "shell.execute_reply": "2023-08-25T18:26:02.975541Z" - }, - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10262/1499636101.py:5: DeprecationWarning: The class ``qiskit.opflow.gradients.hessian.Hessian`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " state_hess = Hessian(hess_method='param_shift').convert(operator=op, params=params)\n", - "/tmp/ipykernel_10262/1499636101.py:11: DeprecationWarning: The class ``qiskit.opflow.gradients.hessian.Hessian`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " state_hess = Hessian(hess_method='lin_comb').convert(operator=op, params=params)\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hessian computed using the parameter shift method\n", - " [[-7.07106781e-01+0.j 0.00000000e+00+0.j]\n", - " [ 0.00000000e+00+0.j -5.55111512e-17+0.j]]\n", - "Hessian computed using the linear combination of unitaries method\n", - " [[-7.07106781e-01+0.j -1.40000000e-17+0.j]\n", - " [-1.40000000e-17+0.j 5.60000000e-17+0.j]]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hessian computed with finite difference\n", - " [[-7.07122803e-01+0.j -3.05175781e-05+0.j]\n", - " [-3.05175781e-05+0.j -6.10351562e-05+0.j]]\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10262/1499636101.py:17: DeprecationWarning: The class ``qiskit.opflow.gradients.hessian.Hessian`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " state_hess = Hessian(hess_method='fin_diff').convert(operator=op, params=params)\n" - ] - } - ], - "source": [ - "# Define parameters\n", - "params = [a, b]\n", - "\n", - "# Get the operator object representing the Hessian\n", - "state_hess = Hessian(hess_method='param_shift').convert(operator=op, params=params)\n", - "# Assign the parameters and evaluate the Hessian\n", - "hessian_result = state_hess.assign_parameters(value_dict).eval()\n", - "print('Hessian computed using the parameter shift method\\n', (np.array(hessian_result)))\n", - "\n", - "# Get the operator object representing the Hessian\n", - "state_hess = Hessian(hess_method='lin_comb').convert(operator=op, params=params)\n", - "# Assign the parameters and evaluate the Hessian\n", - "hessian_result = state_hess.assign_parameters(value_dict).eval()\n", - "print('Hessian computed using the linear combination of unitaries method\\n', (np.array(hessian_result)))\n", - "\n", - "# Get the operator object representing the Hessian using finite difference\n", - "state_hess = Hessian(hess_method='fin_diff').convert(operator=op, params=params)\n", - "# Assign the parameters and evaluate the Hessian\n", - "hessian_result = state_hess.assign_parameters(value_dict).eval()\n", - "print('Hessian computed with finite difference\\n', (np.array(hessian_result)))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "## Quantum Fisher Information (QFI)\n", - "The Quantum Fisher Information is a metric tensor which is representative for the representation capacity of a \n", - "parameterized quantum state $|\\psi\\left(\\theta\\right)\\rangle = V\\left(\\theta\\right)|\\psi\\rangle$ with input state $|\\psi\\rangle$, parametrized Ansatz $V\\left(\\theta\\right)$.\n", - "\n", - "The entries of the QFI for a pure state reads\n", - "\n", - "$$QFI_{kl} = 4 * \\text{Re}\\left[\\langle\\partial_k\\psi|\\partial_l\\psi\\rangle-\\langle\\partial_k\\psi|\\psi\\rangle\\langle\\psi|\\partial_l\\psi\\rangle \\right].$$" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "### Circuit QFIs\n", - "\n", - "The evaluation of the QFI corresponding to a quantum state that is generated by a parameterized quantum circuit can be conducted in different ways.\n", - "\n", - "### Linear Combination Full QFI\n", - "To compute the full QFI, we use a working qubit as well as intercepting controlled gates. See e.g. [Variational ansatz-based quantum simulation of imaginary time evolution ](https://www.nature.com/articles/s41534-019-0187-2)." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:02.981125Z", - "iopub.status.busy": "2023-08-25T18:26:02.979957Z", - "iopub.status.idle": "2023-08-25T18:26:03.064720Z", - "shell.execute_reply": "2023-08-25T18:26:03.064083Z" - }, - "slideshow": { - "slide_type": "subslide" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "full QFI \n", - " [[ 1.00000000e+00 -3.40575685e-16]\n", - " [-3.40575685e-16 5.00000000e-01]]\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10262/1911751681.py:2: DeprecationWarning: The class ``qiskit.opflow.state_fns.circuit_state_fn.CircuitStateFn`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " state = CircuitStateFn(primitive=qc, coeff=1.)\n", - "/tmp/ipykernel_10262/1911751681.py:5: DeprecationWarning: The class ``qiskit.opflow.gradients.qfi.QFI`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " qfi = QFI(qfi_method='lin_comb_full').convert(operator=state, params=params)\n" - ] - } - ], - "source": [ - "# Wrap the quantum circuit into a CircuitStateFn\n", - "state = CircuitStateFn(primitive=qc, coeff=1.)\n", - "\n", - "# Convert the state and the parameters into the operator object that represents the QFI \n", - "qfi = QFI(qfi_method='lin_comb_full').convert(operator=state, params=params)\n", - "# Define the values for which the QFI is to be computed\n", - "values_dict = {a: np.pi / 4, b: 0.1}\n", - "\n", - "# Assign the parameters and evaluate the QFI\n", - "qfi_result = qfi.assign_parameters(values_dict).eval()\n", - "print('full QFI \\n', np.real(np.array(qfi_result)))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "### Block-diagonal and Diagonal Approximation\n", - "A block-diagonal resp. diagonal approximation of the QFI can be computed without additional working qubits.\n", - "This implementation requires the unrolling into Pauli rotations and unparameterized Gates." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:03.069421Z", - "iopub.status.busy": "2023-08-25T18:26:03.068259Z", - "iopub.status.idle": "2023-08-25T18:26:03.108201Z", - "shell.execute_reply": "2023-08-25T18:26:03.107580Z" - }, - "slideshow": { - "slide_type": "subslide" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10262/1343277097.py:3: DeprecationWarning: The class ``qiskit.opflow.gradients.qfi.QFI`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " qfi = QFI('overlap_block_diag').convert(operator=state, params=params)\n", - "/tmp/ipykernel_10262/1343277097.py:11: DeprecationWarning: The class ``qiskit.opflow.gradients.qfi.QFI`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " qfi = QFI('overlap_diag').convert(operator=state, params=params)\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Block-diagonal QFI \n", - " [[1. 0. ]\n", - " [0. 0.5]]\n", - "Diagonal QFI \n", - " [[1. 0. ]\n", - " [0. 0.5]]\n" - ] - } - ], - "source": [ - "# Convert the state and the parameters into the operator object that represents the QFI \n", - "# and set the approximation to 'block_diagonal'\n", - "qfi = QFI('overlap_block_diag').convert(operator=state, params=params)\n", - "\n", - "# Assign the parameters and evaluate the QFI\n", - "qfi_result = qfi.assign_parameters(values_dict).eval()\n", - "print('Block-diagonal QFI \\n', np.real(np.array(qfi_result)))\n", - "\n", - "# Convert the state and the parameters into the operator object that represents the QFI \n", - "# and set the approximation to 'diagonal'\n", - "qfi = QFI('overlap_diag').convert(operator=state, params=params)\n", - "\n", - "# Assign the parameters and evaluate the QFI\n", - "qfi_result = qfi.assign_parameters(values_dict).eval()\n", - "print('Diagonal QFI \\n', np.real(np.array(qfi_result)))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "## Application Example: VQE with gradient-based optimization" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Additional Imports" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:03.112835Z", - "iopub.status.busy": "2023-08-25T18:26:03.111685Z", - "iopub.status.idle": "2023-08-25T18:26:03.164582Z", - "shell.execute_reply": "2023-08-25T18:26:03.163939Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10262/514832364.py:6: DeprecationWarning: ``qiskit.algorithms`` has been migrated to an independent package: https://github.com/qiskit-community/qiskit-algorithms. The ``qiskit.algorithms`` import path is deprecated as of qiskit-terra 0.25.0 and will be removed no earlier than 3 months after the release date. Please run ``pip install qiskit_algorithms`` and use ``import qiskit_algorithms`` instead.\n", - " from qiskit.algorithms import VQE\n" - ] - } - ], - "source": [ - "# Execution Imports\n", - "from qiskit import Aer\n", - "from qiskit.utils import QuantumInstance\n", - "\n", - "# Algorithm Imports\n", - "from qiskit.algorithms import VQE\n", - "from qiskit.algorithms.optimizers import CG" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "The Gradient Framework can also be used for a gradient-based `VQE`.\n", - "First, the Hamiltonian and wavefunction ansatz are initialized." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:03.169325Z", - "iopub.status.busy": "2023-08-25T18:26:03.168170Z", - "iopub.status.idle": "2023-08-25T18:26:03.180694Z", - "shell.execute_reply": "2023-08-25T18:26:03.180115Z" - }, - "slideshow": { - "slide_type": "subslide" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10262/1163076158.py:26: DeprecationWarning: The class ``qiskit.opflow.state_fns.operator_state_fn.OperatorStateFn`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " op = ~StateFn(h2_hamiltonian) @ StateFn(wavefunction)\n", - "/tmp/ipykernel_10262/1163076158.py:26: DeprecationWarning: The class ``qiskit.opflow.state_fns.circuit_state_fn.CircuitStateFn`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " op = ~StateFn(h2_hamiltonian) @ StateFn(wavefunction)\n" - ] - } - ], - "source": [ - "from qiskit.opflow import I, X, Z\n", - "from qiskit.circuit import QuantumCircuit, ParameterVector\n", - "from scipy.optimize import minimize\n", - "\n", - "# Instantiate the system Hamiltonian\n", - "h2_hamiltonian = -1.05 * (I ^ I) + 0.39 * (I ^ Z) - 0.39 * (Z ^ I) - 0.01 * (Z ^ Z) + 0.18 * (X ^ X)\n", - "\n", - "# This is the target energy\n", - "h2_energy = -1.85727503\n", - "\n", - "# Define the Ansatz\n", - "wavefunction = QuantumCircuit(2)\n", - "params = ParameterVector('theta', length=8)\n", - "it = iter(params)\n", - "wavefunction.ry(next(it), 0)\n", - "wavefunction.ry(next(it), 1)\n", - "wavefunction.rz(next(it), 0)\n", - "wavefunction.rz(next(it), 1)\n", - "wavefunction.cx(0, 1)\n", - "wavefunction.ry(next(it), 0)\n", - "wavefunction.ry(next(it), 1)\n", - "wavefunction.rz(next(it), 0)\n", - "wavefunction.rz(next(it), 1)\n", - "\n", - "# Define the expectation value corresponding to the energy\n", - "op = ~StateFn(h2_hamiltonian) @ StateFn(wavefunction)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, we can choose whether the `VQE` should use a `Gradient` or `NaturalGradient`, define a `QuantumInstance` to execute the quantum circuits and run the algorithm." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:03.185309Z", - "iopub.status.busy": "2023-08-25T18:26:03.184139Z", - "iopub.status.idle": "2023-08-25T18:26:08.868302Z", - "shell.execute_reply": "2023-08-25T18:26:08.866717Z" - }, - "slideshow": { - "slide_type": "subslide" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_10262/3278035267.py:1: DeprecationWarning: The class ``qiskit.opflow.gradients.gradient.Gradient`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/opflow_migration.\n", - " grad = Gradient(grad_method='lin_comb')\n", - "/tmp/ipykernel_10262/3278035267.py:3: DeprecationWarning: The class ``qiskit.utils.quantum_instance.QuantumInstance`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. For code migration guidelines, visit https://qisk.it/qi_migration.\n", - " qi_sv = QuantumInstance(Aer.get_backend('aer_simulator_statevector'),\n", - "/tmp/ipykernel_10262/3278035267.py:12: DeprecationWarning: The class ``qiskit.algorithms.minimum_eigen_solvers.vqe.VQE`` is deprecated as of qiskit-terra 0.24.0. It will be removed no earlier than 3 months after the release date. Instead, use the class ``qiskit.algorithms.minimum_eigensolvers.VQE``. See https://qisk.it/algo_migration for a migration guide.\n", - " vqe = VQE(wavefunction, optimizer=optimizer, gradient=grad, quantum_instance=qi_sv)\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Result: -1.8404998434683832 Reference: -1.85727503\n" - ] - } - ], - "source": [ - "grad = Gradient(grad_method='lin_comb')\n", - "\n", - "qi_sv = QuantumInstance(Aer.get_backend('aer_simulator_statevector'),\n", - " shots=1,\n", - " seed_simulator=2,\n", - " seed_transpiler=2)\n", - "\n", - "#Conjugate Gradient algorithm\n", - "optimizer = CG(maxiter=50)\n", - "\n", - "# Gradient callable\n", - "vqe = VQE(wavefunction, optimizer=optimizer, gradient=grad, quantum_instance=qi_sv)\n", - "\n", - "result = vqe.compute_minimum_eigenvalue(h2_hamiltonian)\n", - "print('Result:', result.optimal_value, 'Reference:', h2_energy)" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:08.871785Z", - "iopub.status.busy": "2023-08-25T18:26:08.871300Z", - "iopub.status.idle": "2023-08-25T18:26:09.312168Z", - "shell.execute_reply": "2023-08-25T18:26:09.311334Z" - }, - "slideshow": { - "slide_type": "notes" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "

Version Information

SoftwareVersion
qiskitNone
qiskit-terra0.45.0
qiskit_aer0.12.2
System information
Python version3.8.17
Python compilerGCC 11.3.0
Python builddefault, Jun 7 2023 12:29:56
OSLinux
CPUs2
Memory (Gb)6.7694854736328125
Fri Aug 25 18:26:09 2023 UTC
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "

This code is a part of Qiskit

© Copyright IBM 2017, 2023.

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.

" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import qiskit.tools.jupyter\n", - "%qiskit_version_table\n", - "%qiskit_copyright" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.17" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": { - "4f507656df55472a9d8cd8614c47ac47": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": "0px 0px 10px 0px", - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "8f88aaab3cb54c6cb31e9c363a91d292": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "HTMLView", - "description": "", - "description_allow_html": false, - "layout": "IPY_MODEL_4f507656df55472a9d8cd8614c47ac47", - "placeholder": "​", - "style": "IPY_MODEL_ae351d7645f04106b5d21c9ae94f09f4", - "tabbable": null, - "tooltip": null, - "value": "

Circuit Properties

" - } - }, - "ae351d7645f04106b5d21c9ae94f09f4": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "StyleView", - "background": null, - "description_width": "", - "font_size": null, - "text_color": null - } - } - }, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/docs/tutorials/operators/images/gradient_framework.png b/docs/tutorials/operators/images/gradient_framework.png deleted file mode 100644 index ca17b48263d57a846aa55d48c1e5306808fd95e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43645 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 s{W#ysn}zO0-eR~yjH8Yn(C&9f Date: Tue, 24 Oct 2023 18:41:08 +0300 Subject: [PATCH 2/4] Qiskit Pulse - Introduce logical elements and frames (#10694) * Introduce logical elements and frames. * test fix * Update qiskit/pulse/logical_elements_frames.py Co-authored-by: Naoki Kanazawa * Update qiskit/pulse/logical_elements_frames.py Co-authored-by: Naoki Kanazawa * Update qiskit/pulse/logical_elements_frames.py Co-authored-by: Naoki Kanazawa * Update qiskit/pulse/logical_elements_frames.py Co-authored-by: Naoki Kanazawa * Split files * Remove name from MixedFrame * fixes * Update qiskit/pulse/model/__init__.py Co-authored-by: Naoki Kanazawa * Update qiskit/pulse/model/frames.py Co-authored-by: Naoki Kanazawa * Update qiskit/pulse/model/logical_elements.py Co-authored-by: Naoki Kanazawa * Update qiskit/pulse/model/logical_elements.py Co-authored-by: Naoki Kanazawa * Update qiskit/pulse/model/logical_elements.py Co-authored-by: Naoki Kanazawa * Update qiskit/pulse/model/mixed_frames.py Co-authored-by: Will Shanks * Update qiskit/pulse/model/mixed_frames.py Co-authored-by: Will Shanks * Update qiskit/pulse/model/mixed_frames.py Co-authored-by: Will Shanks * Update qiskit/pulse/model/frames.py Co-authored-by: Will Shanks * Update qiskit/pulse/model/frames.py Co-authored-by: Will Shanks * Update qiskit/pulse/model/logical_elements.py Co-authored-by: Will Shanks * Update qiskit/pulse/model/logical_elements.py Co-authored-by: Will Shanks * Update qiskit/pulse/model/logical_elements.py Co-authored-by: Will Shanks * Update qiskit/pulse/model/logical_elements.py Co-authored-by: Will Shanks * Update qiskit/pulse/model/__init__.py Co-authored-by: Will Shanks * Update qiskit/pulse/model/__init__.py Co-authored-by: Will Shanks * fixes * doc mod name * doc fix * doc fix * doc fix * Qubit ref fix * Qubit ref fix * Qubit ref fix * Qubit ref fix legacy release notes * Update qiskit/circuit/quantumcircuit.py Co-authored-by: Naoki Kanazawa * Update qiskit/pulse/model/__init__.py Co-authored-by: Naoki Kanazawa * Update qiskit/pulse/model/__init__.py Co-authored-by: Naoki Kanazawa * Update qiskit/pulse/model/mixed_frames.py Co-authored-by: Naoki Kanazawa * Update qiskit/pulse/model/mixed_frames.py Co-authored-by: Naoki Kanazawa * Update qiskit/pulse/model/mixed_frames.py Co-authored-by: Naoki Kanazawa * minor fixes * Qubit ref * last doc fixes * last doc fixes * lynt --------- Co-authored-by: Naoki Kanazawa Co-authored-by: Will Shanks --- docs/legacy_release_notes.rst | 24 +-- qiskit/circuit/bit.py | 2 +- .../circuit/library/generalized_gates/mcmt.py | 8 +- qiskit/circuit/quantumcircuit.py | 6 +- qiskit/converters/circuit_to_dag.py | 4 +- qiskit/dagcircuit/dagcircuit.py | 18 +- qiskit/dagcircuit/dagdependency.py | 6 +- qiskit/pulse/__init__.py | 13 ++ qiskit/pulse/model/__init__.py | 91 ++++++++++ qiskit/pulse/model/frames.py | 162 ++++++++++++++++++ qiskit/pulse/model/logical_elements.py | 140 +++++++++++++++ qiskit/pulse/model/mixed_frames.py | 78 +++++++++ qiskit/transpiler/__init__.py | 2 +- qiskit/transpiler/layout.py | 11 +- .../passes/optimization/normalize_rx_angle.py | 3 +- .../transpiler/passes/routing/sabre_swap.py | 6 +- .../0.20/bit-slots-17d6649872da0440.yaml | 2 +- ...truction-data-scalar-81f2066ca2435933.yaml | 2 +- ...th-registerless-bits-6d17597b99640fb0.yaml | 2 +- ...prepare-single-qubit-e25dacc8f873bc01.yaml | 2 +- ...nselayout-loose-bits-3e66011432bc6232.yaml | 2 +- .../fix-qpy-loose-bits-5283dc4ad3823ce3.yaml | 2 +- ...ed-bit-qpy-roundtrip-9a23a795aa677c71.yaml | 4 +- .../0.25/fix-bit-copy-4b2f7349683f616a.yaml | 2 +- ...recated-code-in-0.19-a97ccfec62405b9a.yaml | 4 +- test/python/pulse/test_frames.py | 77 +++++++++ test/python/pulse/test_logical_elements.py | 66 +++++++ test/python/pulse/test_mixed_frames.py | 55 ++++++ 28 files changed, 739 insertions(+), 55 deletions(-) create mode 100644 qiskit/pulse/model/__init__.py create mode 100644 qiskit/pulse/model/frames.py create mode 100644 qiskit/pulse/model/logical_elements.py create mode 100644 qiskit/pulse/model/mixed_frames.py create mode 100644 test/python/pulse/test_frames.py create mode 100644 test/python/pulse/test_logical_elements.py create mode 100644 test/python/pulse/test_mixed_frames.py diff --git a/docs/legacy_release_notes.rst b/docs/legacy_release_notes.rst index b8a64228c0f9..13b557bcfef1 100644 --- a/docs/legacy_release_notes.rst +++ b/docs/legacy_release_notes.rst @@ -520,7 +520,7 @@ Transpiler Features - A new method :meth:`~qiskit.dagcircuit.DAGCircuit.find_bit` has been added to the :class:`~qiskit.dagcircuit.DAGCircuit` class, - which returns the bit locations of the given :class:`.Qubit` or + which returns the bit locations of the given :class:`~.circuit.Qubit` or :class:`.Clbit` as a tuple of the positional index of the bit within the circuit and a list of tuples which locate the bit in the circuit's registers. @@ -1597,7 +1597,7 @@ Bug Fixes .. releasenotes/notes/fix-bit-copy-4b2f7349683f616a.yaml @ b'163d1bd7835d58eaf8842c594b3696fb99c8442f' - Fixed an issue with copying circuits with new-style :class:`.Clbit`\ s and - :class:`.Qubit`\ s (bits without registers) where references to these bits + :class:`~.circuit.Qubit`\ s (bits without registers) where references to these bits from the containing circuit could be broken, causing issues with serialization and circuit visualization. Fixed `#10409 `__ @@ -4326,8 +4326,8 @@ Bug Fixes .. releasenotes/notes/fix-deprecated-bit-qpy-roundtrip-9a23a795aa677c71.yaml @ b'3dbbb32e762850db265c7bb40787a36351aad917' -- The deprecated :class:`.Qubit` and :class:`.Clbit` properties :attr:`~.Qubit.register` and - :attr:`~.Qubit.index` will now be correctly round-tripped by QPY (:mod:`qiskit.qpy`) in all +- The deprecated :class:`~.circuit.Qubit` and :class:`.Clbit` properties :attr:`~.circuit.Qubit.register` and + :attr:`~.circuit.Qubit.index` will now be correctly round-tripped by QPY (:mod:`qiskit.qpy`) in all valid usages of :class:`.QuantumRegister` and :class:`.ClassicalRegister`. In earlier releases in the Terra 0.23 series, this information would be lost. In versions before 0.23.0, this information was partially reconstructed but could be incorrect or produce invalid circuits for @@ -7031,7 +7031,7 @@ Bug Fixes - QPY deserialisation will no longer add extra :class:`.Clbit` instances to the circuit if there are both loose :class:`.Clbit`\ s in the circuit and more - :class:`.Qubit`\ s than :class:`.Clbit`\ s. + :class:`~.circuit.Qubit`\ s than :class:`.Clbit`\ s. .. releasenotes/notes/fix-qpy-loose-bits-5283dc4ad3823ce3.yaml @ b'e0befd769fc54e9f50cdc4b355983b9d1eda6f31' @@ -9062,7 +9062,7 @@ Bug Fixes .. releasenotes/notes/0.22/denselayout-loose-bits-3e66011432bc6232.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - Fixed an issue in the :class:`~.DenseLayout` transpiler pass where any - loose :class:`~.Qubit` objects (i.e. not part of a :class:`~.QuantumRegister`) + loose :class:`~.circuit.Qubit` objects (i.e. not part of a :class:`~.QuantumRegister`) that were part of a :class:`~.QuantumCircuit` would not be included in the output :class:`~.Layout` that was generated by the pass. @@ -9071,7 +9071,7 @@ Bug Fixes - The :meth:`.Operator.from_circuit` constructor method has been updated so that it can handle the layout output from :func:`~.transpile` and correctly reverse the qubit permutation caused by layout in all cases. - Previously, if your transpiled circuit used loose :class:`~.Qubit` objects, + Previously, if your transpiled circuit used loose :class:`~.circuit.Qubit` objects, multiple :class:`~.QuantumRegister` objects, or a single :class:`~.QuantumRegister` with a name other than ``"q"`` the constructor would have failed to create an :class:`~.Operator` from the circuit. @@ -10274,7 +10274,7 @@ Upgrade Notes - The data type of each element in :attr:`.QuantumCircuit.data` has changed. It used to be a simple 3-tuple of an :class:`~.circuit.Instruction`, a list - of :class:`.Qubit`\ s, and a list of :class:`.Clbit`\ s, whereas it is now + of :class:`~.circuit.Qubit`\ s, and a list of :class:`.Clbit`\ s, whereas it is now an instance of :class:`.CircuitInstruction`. The attributes of this new class are :attr:`~.CircuitInstruction.operation`, @@ -10651,7 +10651,7 @@ Bug Fixes .. releasenotes/notes/0.21/fix-reverse_bits-with-registerless-bits-6d17597b99640fb0.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - Fixed :meth:`.QuantumCircuit.reverse_bits` with circuits containing registerless - :class:`.Qubit` and :class:`.Clbit`. For example, the following will now work:: + :class:`~.circuit.Qubit` and :class:`.Clbit`. For example, the following will now work:: from qiskit.circuit import QuantumCircuit, Qubit, Clbit @@ -10954,7 +10954,7 @@ Bug Fixes - Fixed an issue with the visualization function :func:`~.dag_drawer` and method :meth:`.DAGCircuit.draw` where previously the drawer would fail when attempting to generate a visualization for a :class:`~.DAGCircuit` - object that contained a :class:`~.Qubit` or :class:`~.Clbit` which wasn't + object that contained a :class:`~.circuit.Qubit` or :class:`~.Clbit` which wasn't part of a :class:`~QuantumRegister` or :class:`~ClassicalRegister`. Fixed `#7915 `__. @@ -12350,7 +12350,7 @@ Upgrade Notes .. releasenotes/notes/0.20/bit-slots-17d6649872da0440.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' -- The classes :class:`.Qubit`, :class:`.Clbit` and :class:`.AncillaQubit` now +- The classes :class:`~.circuit.Qubit`, :class:`.Clbit` and :class:`.AncillaQubit` now have the ``__slots__`` attribute. This is to reduce their memory usage. As a side effect, they can no longer have arbitrary data attached as attributes to them. This is very unlikely to have any effect on downstream code other @@ -13180,7 +13180,7 @@ Bug Fixes - Fixed QPY serialization of :class:`.QuantumCircuit` containing subsets of bits from a :class:`.QuantumRegister` or :class:`.ClassicalRegister`. Previously if you tried to serialize a circuit like this it would - incorrectly treat these bits as standalone :class:`.Qubit` or + incorrectly treat these bits as standalone :class:`~.circuit.Qubit` or :class:`.Clbit` without having a register set. For example, if you try to serialize a circuit like:: diff --git a/qiskit/circuit/bit.py b/qiskit/circuit/bit.py index c186755eff76..51996590cf5d 100644 --- a/qiskit/circuit/bit.py +++ b/qiskit/circuit/bit.py @@ -24,7 +24,7 @@ class Bit: .. note:: This class should not be instantiated directly. This is just a superclass - for :class:`~.Clbit` and :class:`~.Qubit`. + for :class:`~.Clbit` and :class:`~.circuit.Qubit`. """ diff --git a/qiskit/circuit/library/generalized_gates/mcmt.py b/qiskit/circuit/library/generalized_gates/mcmt.py index f72424ccefb4..1a529b8859e1 100644 --- a/qiskit/circuit/library/generalized_gates/mcmt.py +++ b/qiskit/circuit/library/generalized_gates/mcmt.py @@ -17,7 +17,7 @@ from collections.abc import Callable from qiskit import circuit -from qiskit.circuit import ControlledGate, Gate, Qubit, QuantumRegister, QuantumCircuit +from qiskit.circuit import ControlledGate, Gate, QuantumRegister, QuantumCircuit from qiskit.exceptions import QiskitError # pylint: disable=cyclic-import @@ -51,7 +51,7 @@ class MCMT(QuantumCircuit): def __init__( self, - gate: Gate | Callable[[QuantumCircuit, Qubit, Qubit], circuit.Instruction], + gate: Gate | Callable[[QuantumCircuit, circuit.Qubit, circuit.Qubit], circuit.Instruction], num_ctrl_qubits: int, num_target_qubits: int, ) -> None: @@ -214,8 +214,8 @@ def num_ancilla_qubits(self): def _ccx_v_chain_rule( self, - control_qubits: QuantumRegister | list[Qubit], - ancilla_qubits: QuantumRegister | list[Qubit], + control_qubits: QuantumRegister | list[circuit.Qubit], + ancilla_qubits: QuantumRegister | list[circuit.Qubit], reverse: bool = False, ) -> None: """Get the rule for the CCX V-chain. diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index fea533f2e072..ceaad5ecf43a 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -381,8 +381,8 @@ def data(self, data_input: Iterable): elements must either be instances of :class:`.CircuitInstruction` (preferred), or a 3-tuple of ``(instruction, qargs, cargs)`` (legacy). In the legacy format, ``instruction`` must be an :class:`~.circuit.Instruction`, while ``qargs`` and - ``cargs`` must be iterables of :class:`.Qubit` or :class:`.Clbit` specifiers - (similar to the allowed forms in calls to :meth:`append`). + ``cargs`` must be iterables of :class:`~.circuit.Qubit` or :class:`.Clbit` + specifiers (similar to the allowed forms in calls to :meth:`append`). """ # If data_input is QuantumCircuitData(self), clearing self._data # below will also empty data_input, so make a shallow copy first. @@ -1230,7 +1230,7 @@ def append( Args: instruction: :class:`~.circuit.Instruction` instance to append, or a :class:`.CircuitInstruction` with all its context. - qargs: specifiers of the :class:`.Qubit`\\ s to attach instruction to. + qargs: specifiers of the :class:`~.circuit.Qubit`\\ s to attach instruction to. cargs: specifiers of the :class:`.Clbit`\\ s to attach instruction to. Returns: diff --git a/qiskit/converters/circuit_to_dag.py b/qiskit/converters/circuit_to_dag.py index 24f0ddd7a855..e2612b43d3e6 100644 --- a/qiskit/converters/circuit_to_dag.py +++ b/qiskit/converters/circuit_to_dag.py @@ -28,8 +28,8 @@ def circuit_to_dag(circuit, copy_operations=True, *, qubit_order=None, clbit_ord :class:`~.DAGCircuit` will be shared instances and modifications to operations in the :class:`~.DAGCircuit` will be reflected in the :class:`~.QuantumCircuit` (and vice versa). - qubit_order (Iterable[Qubit] or None): the order that the qubits should be indexed in the - output DAG. Defaults to the same order as in the circuit. + qubit_order (Iterable[~qiskit.circuit.Qubit] or None): the order that the qubits should be + indexed in the output DAG. Defaults to the same order as in the circuit. clbit_order (Iterable[Clbit] or None): the order that the clbits should be indexed in the output DAG. Defaults to the same order as in the circuit. diff --git a/qiskit/dagcircuit/dagcircuit.py b/qiskit/dagcircuit/dagcircuit.py index 4233144c86fe..bf57964fcffe 100644 --- a/qiskit/dagcircuit/dagcircuit.py +++ b/qiskit/dagcircuit/dagcircuit.py @@ -424,10 +424,10 @@ def remove_qubits(self, *qubits): also be removed. Args: - qubits (List[Qubit]): The bits to remove. + qubits (List[~qiskit.circuit.Qubit]): The bits to remove. Raises: - DAGCircuitError: a qubit is not a :obj:`.Qubit`, is not in the circuit, + DAGCircuitError: a qubit is not a :obj:`~.circuit.Qubit`, is not in the circuit, or is not idle. """ if any(not isinstance(qubit, Qubit) for qubit in qubits): @@ -643,11 +643,11 @@ def apply_operation_back(self, op, qargs=(), cargs=(), *, check=True): Args: op (qiskit.circuit.Operation): the operation associated with the DAG node - qargs (tuple[Qubit]): qubits that op will be applied to + qargs (tuple[~qiskit.circuit.Qubit]): qubits that op will be applied to cargs (tuple[Clbit]): cbits that op will be applied to check (bool): If ``True`` (default), this function will enforce that the :class:`.DAGCircuit` data-structure invariants are maintained (all ``qargs`` are - :class:`.Qubit`\\ s, all are in the DAG, etc). If ``False``, the caller *must* + :class:`~.circuit.Qubit`\\ s, all are in the DAG, etc). If ``False``, the caller *must* uphold these invariants itself, but the cost of several checks will be skipped. This is most useful when building a new DAG from a source of known-good nodes. Returns: @@ -697,11 +697,11 @@ def apply_operation_front(self, op, qargs=(), cargs=(), *, check=True): Args: op (qiskit.circuit.Operation): the operation associated with the DAG node - qargs (tuple[Qubit]): qubits that op will be applied to + qargs (tuple[~qiskit.circuit.Qubit]): qubits that op will be applied to cargs (tuple[Clbit]): cbits that op will be applied to check (bool): If ``True`` (default), this function will enforce that the :class:`.DAGCircuit` data-structure invariants are maintained (all ``qargs`` are - :class:`.Qubit`\\ s, all are in the DAG, etc). If ``False``, the caller *must* + :class:`~.circuit.Qubit`\\ s, all are in the DAG, etc). If ``False``, the caller *must* uphold these invariants itself, but the cost of several checks will be skipped. This is most useful when building a new DAG from a source of known-good nodes. Returns: @@ -755,7 +755,7 @@ def compose(self, other, qubits=None, clbits=None, front=False, inplace=True): Args: other (DAGCircuit): circuit to compose with self - qubits (list[Qubit|int]): qubits of self to compose onto. + qubits (list[~qiskit.circuit.Qubit|int]): qubits of self to compose onto. clbits (list[Clbit|int]): clbits of self to compose onto. front (bool): If True, front composition will be performed (not implemented yet) inplace (bool): If True, modify the object. Otherwise return composed circuit. @@ -2042,10 +2042,10 @@ def quantum_causal_cone(self, qubit): classical bit wires are ignored for the purposes of building the causal cone. Args: - qubit (Qubit): The output qubit for which we want to find the causal cone. + qubit (~qiskit.circuit.Qubit): The output qubit for which we want to find the causal cone. Returns: - Set[Qubit]: The set of qubits whose interactions affect ``qubit``. + Set[~qiskit.circuit.Qubit]: The set of qubits whose interactions affect ``qubit``. """ # Retrieve the output node from the qubit output_node = self.output_map.get(qubit, None) diff --git a/qiskit/dagcircuit/dagdependency.py b/qiskit/dagcircuit/dagdependency.py index 8e4904c2b8b2..a1e24b4e0b8d 100644 --- a/qiskit/dagcircuit/dagdependency.py +++ b/qiskit/dagcircuit/dagdependency.py @@ -377,7 +377,7 @@ def _create_op_node(self, operation, qargs, cargs): Args: operation (qiskit.circuit.Operation): operation - qargs (list[Qubit]): list of qubits on which the operation acts + qargs (list[~qiskit.circuit.Qubit]): list of qubits on which the operation acts cargs (list[Clbit]): list of classical wires to attach to Returns: @@ -421,7 +421,7 @@ def add_op_node(self, operation, qargs, cargs): Args: operation (qiskit.circuit.Operation): operation as a quantum gate - qargs (list[Qubit]): list of qubits on which the operation acts + qargs (list[~qiskit.circuit.Qubit]): list of qubits on which the operation acts cargs (list[Clbit]): list of classical wires to attach to """ new_node = self._create_op_node(operation, qargs, cargs) @@ -561,7 +561,7 @@ def replace_block_with_op(self, node_block, op, wire_pos_map, cycle_check=True): node block to be replaced op (qiskit.circuit.Operation): The operation to replace the block with - wire_pos_map (Dict[Qubit, int]): The dictionary mapping the qarg to + wire_pos_map (Dict[~qiskit.circuit.Qubit, int]): The dictionary mapping the qarg to the position. This is necessary to reconstruct the qarg order over multiple gates in the combined single op node. cycle_check (bool): When set to True this method will check that diff --git a/qiskit/pulse/__init__.py b/qiskit/pulse/__init__.py index 34ef5c072020..10d150e267a1 100644 --- a/qiskit/pulse/__init__.py +++ b/qiskit/pulse/__init__.py @@ -40,6 +40,8 @@ .. automodule:: qiskit.pulse.schedule .. automodule:: qiskit.pulse.transforms .. automodule:: qiskit.pulse.builder +.. automodule:: qiskit.pulse.model + .. currentmodule:: qiskit.pulse @@ -168,3 +170,14 @@ ) from qiskit.pulse.library.samplers.decorators import functional_pulse from qiskit.pulse.schedule import Schedule, ScheduleBlock + +from qiskit.pulse.model import ( + LogicalElement, + Qubit, + Coupler, + Frame, + GenericFrame, + QubitFrame, + MeasurementFrame, + MixedFrame, +) diff --git a/qiskit/pulse/model/__init__.py b/qiskit/pulse/model/__init__.py new file mode 100644 index 000000000000..05cd4513359c --- /dev/null +++ b/qiskit/pulse/model/__init__.py @@ -0,0 +1,91 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# 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. + +r""" +========================================================== +Logical Elements & Frames (:mod:`qiskit.pulse.model`) +========================================================== + +Pulse is meant to be agnostic to the underlying hardware implementation, while still allowing +low-level control. Qiskit Pulse's logical element and frames create a flexible framework +to define where pulse instructions are applied, and what would be their carrier frequency and phase +(because typically AC pulses are used). Each :class:`.LogicalElement` represents a separate component +in the quantum computing system on which instructions could be applied. On the other hand, +each :class:`.Frame` represents a frequency and phase duo for the carrier of the pulse. + +This logical and virtual representation allows the user to write template pulse +programs without worrying about the exact details of the hardware implementation +(are the pulses to be played via the same port? Which NCO is used?), while still +allowing for effective utilization of the quantum hardware. The burden of mapping +the different combinations of :class:`.LogicalElement` and :class:`.Frame` +to hardware aware objects is left to the Pulse Compiler. + +.. _logical_elements: + +LogicalElement +================ +:class:`.LogicalElement` s are identified by their type and index. Currently, the most prominent example +is the :class:`~.pulse.Qubit`. + +.. autosummary:: + :toctree: ../stubs/ + + Qubit + Coupler + + +.. _frames: + +Frame +============= +:class:`.Frame` s are identified by their type and unique identifier. A :class:`.GenericFrame` is used to +specify custom frequency +and phase duos, while :class:`.QubitFrame` and :class:`.MeasurementFrame` are used to indicate that +backend defaults are to be used (for the qubit's driving frequency and measurement frequency +respectively). + +.. autosummary:: + :toctree: ../stubs/ + + GenericFrame + QubitFrame + MeasurementFrame + + +.. _mixed_frames: + +MixedFrame +============= +The combination of a :class:`.LogicalElement` and :class:`.Frame` is dubbed a :class:`.MixedFrame`. + +.. autosummary:: + :toctree: ../stubs/ + + MixedFrame +""" + +from .logical_elements import ( + LogicalElement, + Qubit, + Coupler, +) + +from .frames import ( + Frame, + GenericFrame, + QubitFrame, + MeasurementFrame, +) + +from .mixed_frames import ( + MixedFrame, +) diff --git a/qiskit/pulse/model/frames.py b/qiskit/pulse/model/frames.py new file mode 100644 index 000000000000..ae63fe3c7948 --- /dev/null +++ b/qiskit/pulse/model/frames.py @@ -0,0 +1,162 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# 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. + +""" +Frames +""" + +from abc import ABC + +import numpy as np + +from qiskit.pulse.exceptions import PulseError + + +class Frame(ABC): + """Base class for pulse module frame. + + Because pulses used in Quantum hardware are typically AC pulses, the carrier frequency and phase + must be defined. The :class:`Frame` is the object which identifies the frequency and phase for + the carrier. + and each pulse and most other instructions are associated with a frame. The different types of frames + dictate how the frequency and phase duo are defined. + + The default initial phase for every frame is 0. + """ + + def __init__(self, identifier): + """Create ``Frame``. + + Args: + identifier: A unique identifier used to hash the Frame. + """ + self._hash = hash((type(self), identifier)) + + def __eq__(self, other: "Frame") -> bool: + """Return True iff self and other are equal, specifically, iff they have the same type and hash. + + Args: + other: The frame to compare to this one. + + Returns: + True iff equal. + """ + return type(self) is type(other) and self._hash == other._hash + + def __hash__(self) -> int: + return self._hash + + +class GenericFrame(Frame): + """Pulse module GenericFrame. + + The :class:`GenericFrame` is used for custom user defined frames, which are not associated with any + backend defaults. It is especially useful when the frame doesn't correspond to any frame of + the typical qubit model, like qudit control for example. Because no backend defaults exist for + these frames, during compilation an initial frequency and phase will need to be provided. + + :class:`GenericFrame` objects are identified by their unique name. + """ + + def __init__(self, name: str): + """Create ``GenericFrame``. + + Args: + name: A unique identifier used to identify the frame. + """ + self._name = name + super().__init__(name) + + @property + def name(self) -> str: + """Return the name of the frame.""" + return self._name + + def __repr__(self) -> str: + return f"GenericFrame({self._name})" + + +class QubitFrame(Frame): + """A frame associated with the driving of a qubit. + + :class:`QubitFrame` is a frame associated with the driving of a specific qubit. + The initial frequency of + the frame will be taken as the default driving frequency provided by the backend + during compilation. + """ + + def __init__(self, index: int): + """Create ``QubitFrame``. + + Args: + index: The index of the qubit represented by the frame. + """ + self._validate_index(index) + self._index = index + super().__init__("QubitFrame" + str(index)) + + @property + def index(self) -> int: + """Return the qubit index of the qubit frame.""" + return self._index + + def _validate_index(self, index) -> None: + """Raise a ``PulseError`` if the qubit index is invalid. Namely, check if the index is a + non-negative integer. + + Raises: + PulseError: If ``identifier`` (index) is a negative integer. + """ + if not isinstance(index, (int, np.integer)) or index < 0: + raise PulseError("Qubit index must be a non-negative integer") + + def __repr__(self) -> str: + return f"QubitFrame({self._index})" + + +class MeasurementFrame(Frame): + """A frame associated with the measurement of a qubit. + + ``MeasurementFrame`` is a frame associated with the readout of a specific qubit, + which requires a stimulus tone driven at frequency off resonant to qubit drive. + + If not set otherwise, the initial frequency of the frame will be taken as the default + measurement frequency provided by the backend during compilation. + """ + + def __init__(self, index: int): + """Create ``MeasurementFrame``. + + Args: + index: The index of the qubit represented by the frame. + """ + self._validate_index(index) + self._index = index + super().__init__("MeasurementFrame" + str(index)) + + @property + def index(self) -> int: + """Return the qubit index of the measurement frame.""" + return self._index + + def _validate_index(self, index) -> None: + """Raise a ``PulseError`` if the qubit index is invalid. Namely, check if the index is a + non-negative integer. + + Raises: + PulseError: If ``index`` is a negative integer. + """ + if not isinstance(index, (int, np.integer)) or index < 0: + raise PulseError("Qubit index must be a non-negative integer") + + def __repr__(self) -> str: + return f"MeasurementFrame({self._index})" diff --git a/qiskit/pulse/model/logical_elements.py b/qiskit/pulse/model/logical_elements.py new file mode 100644 index 000000000000..a593030c95d7 --- /dev/null +++ b/qiskit/pulse/model/logical_elements.py @@ -0,0 +1,140 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# 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. + +""" +Logical Elements +""" +from abc import ABC, abstractmethod +from typing import Tuple +import numpy as np + +from qiskit.pulse.exceptions import PulseError + + +class LogicalElement(ABC): + """Base class of logical elements. + + A :class:`LogicalElement` is an abstraction of a quantum hardware component which can be controlled + by the user (instructions can be applied to it). + Every played pulse and most other instructions are associated with a :class:`LogicalElement` on which + they are performed. + A logical element is identified by its type and index. + + """ + + def __init__(self, index: Tuple[int, ...]): + """Create ``LogicalElement``. + + Args: + index: Tuple of indices of the logical element. + """ + self._validate_index(index) + self._index = index + self._hash = hash((self._index, type(self))) + + @property + def index(self) -> Tuple[int, ...]: + """Return the ``index`` of this logical element.""" + return self._index + + @abstractmethod + def _validate_index(self, index) -> None: + """Raise a PulseError if the logical element ``index`` is invalid. + + Raises: + PulseError: If ``index`` is not valid. + """ + pass + + def __eq__(self, other: "LogicalElement") -> bool: + """Return True iff self and other are equal, specifically, iff they have the same type + and the same ``index``. + + Args: + other: The logical element to compare to this one. + + Returns: + True iff equal. + """ + return type(self) is type(other) and self._index == other._index + + def __repr__(self) -> str: + ind_str = str(self._index) if len(self._index) > 1 else f"({self._index[0]})" + return type(self).__name__ + ind_str + + def __hash__(self) -> int: + return self._hash + + +class Qubit(LogicalElement): + """Qubit logical element. + + ``Qubit`` represents the different qubits in the system, as identified by + their (positive integer) index values. + """ + + def __init__(self, index: int): + """Qubit logical element. + + Args: + index: Qubit index. + """ + super().__init__((index,)) + + @property + def qubit_index(self): + """Index of the Qubit""" + return self.index[0] + + def _validate_index(self, index) -> None: + """Raise a ``PulseError`` if the qubit index is invalid. Namely, check if the index is a + non-negative integer. + + Raises: + PulseError: If ``index`` is a negative integer. + """ + if not isinstance(index[0], (int, np.integer)) or index[0] < 0: + raise PulseError("Qubit index must be a non-negative integer") + + +class Coupler(LogicalElement): + """Coupler logical element. + + :class:`Coupler` represents an element which couples qubits, and can be controlled on its own. + It is identified by the tuple of indices of the coupled qubits. + """ + + def __init__(self, *qubits): + """Coupler logical element. + + The coupler ``index`` is defined as the ``tuple`` (\\*qubits). + + Args: + *qubits: any number of qubit indices coupled by the coupler. + """ + super().__init__(tuple(qubits)) + + def _validate_index(self, index) -> None: + """Raise a ``PulseError`` if the coupler ``index`` is invalid. Namely, + check if coupled qubit indices are non-negative integers, at least two indices were provided, + and that the indices don't repeat. + + Raises: + PulseError: If ``index`` is invalid. + """ + if len(index) < 2: + raise PulseError("At least two qubit indices are needed for a Coupler") + for qubit_index in index: + if not isinstance(qubit_index, (int, np.integer)) or qubit_index < 0: + raise PulseError("Both indices of coupled qubits must be non-negative integers") + if len(set(index)) != len(index): + raise PulseError("Indices of a coupler can not repeat") diff --git a/qiskit/pulse/model/mixed_frames.py b/qiskit/pulse/model/mixed_frames.py new file mode 100644 index 000000000000..c268d6f496a1 --- /dev/null +++ b/qiskit/pulse/model/mixed_frames.py @@ -0,0 +1,78 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# 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. + +""" +Mixed Frames +""" + +from .frames import Frame +from .logical_elements import LogicalElement + + +class MixedFrame: + """Representation of a :class:`LogicalElement` and :class:`Frame` combination. + + Most instructions need to be associated with both a :class:`LogicalElement` and a :class:`Frame`. + The combination + of the two is called a mixed frame and is represented by a :class:`MixedFrame` object. + + In most cases the :class:`MixedFrame` is used more by the compiler, and a pulse program + can be written without :class:`MixedFrame` s, by setting :class:`LogicalElement` and + :class:`Frame` independently. However, in some cases using :class:`MixedFrame` s can + better convey the meaning of the code, and change the compilation process. One example + is the use of the shift/set frequency/phase instructions which are not broadcasted to other + :class:`MixedFrame` s if applied on a specific :class:`MixedFrame` (unlike the behavior + of :class:`Frame`). User can also use a subclass of :class:`MixedFrame` for a particular + combination of logical elements and frames as if a syntactic sugar. This might + increase the readability of a user pulse program. As an example consider the cross + resonance architecture, in which a pulse is played on a target qubit frame and applied + to a control qubit logical element. + """ + + def __init__(self, logical_element: LogicalElement, frame: Frame): + """Create ``MixedFrame``. + + Args: + logical_element: The logical element associated with the mixed frame. + frame: The frame associated with the mixed frame. + """ + self._logical_element = logical_element + self._frame = frame + self._hash = hash((self._logical_element, self._frame)) + + @property + def logical_element(self) -> LogicalElement: + """Return the ``LogicalElement`` of this mixed frame.""" + return self._logical_element + + @property + def frame(self) -> Frame: + """Return the ``Frame`` of this mixed frame.""" + return self._frame + + def __repr__(self) -> str: + return f"MixedFrame({self.logical_element},{self.frame})" + + def __eq__(self, other: "MixedFrame") -> bool: + """Return True iff self and other are equal, specifically, iff they have the same logical + element and frame. + + Args: + other: The mixed frame to compare to this one. + + Returns: + True iff equal. + """ + return self._logical_element == other._logical_element and self._frame == other._frame + + def __hash__(self) -> int: + return self._hash diff --git a/qiskit/transpiler/__init__.py b/qiskit/transpiler/__init__.py index c26963c27858..9abab3aa56a2 100644 --- a/qiskit/transpiler/__init__.py +++ b/qiskit/transpiler/__init__.py @@ -1054,7 +1054,7 @@ C ░░░░░░░░░░░░░░░░▒▒░ However, the :class:`.QuantumCircuit` representation is not accurate enough to represent -this model. In the circuit representation, the corresponding :class:`.Qubit` is occupied +this model. In the circuit representation, the corresponding :class:`.pulse.Qubit` is occupied by the stimulus microwave signal during the first half of the interval, and the :class:`.Clbit` is only occupied at the very end of the interval. diff --git a/qiskit/transpiler/layout.py b/qiskit/transpiler/layout.py index b9ef2156a551..eff9ff786a48 100644 --- a/qiskit/transpiler/layout.py +++ b/qiskit/transpiler/layout.py @@ -21,6 +21,7 @@ from typing import List from dataclasses import dataclass +from qiskit import circuit from qiskit.circuit.quantumregister import Qubit, QuantumRegister from qiskit.transpiler.exceptions import LayoutError from qiskit.converters import isinstanceint @@ -451,11 +452,11 @@ class TranspileLayout: * :attr:`initial_layout` - This attribute is used to model the permutation caused by the :ref:`layout_stage` it contains a :class:`~.Layout` object that maps the input :class:`~.QuantumCircuit`\s - :class:`~.Qubit` objects to the position in the output + :class:`~.circuit.Qubit` objects to the position in the output :class:`.QuantumCircuit.qubits` list. * :attr:`input_qubit_mapping` - This attribute is used to retain input ordering of the original :class:`~.QuantumCircuit` object. It - maps the virtual :class:`~.Qubit` object from the original circuit + maps the virtual :class:`~.circuit.Qubit` object from the original circuit (and :attr:`initial_layout`) to its corresponding position in :attr:`.QuantumCircuit.qubits` in the original circuit. This is needed when computing the permutation of the :class:`Operator` of @@ -475,7 +476,7 @@ class TranspileLayout: """ initial_layout: Layout - input_qubit_mapping: dict[Qubit, int] + input_qubit_mapping: dict[circuit.Qubit, int] final_layout: Layout | None = None _input_qubit_count: int | None = None _output_qubit_list: List[Qubit] | None = None @@ -483,7 +484,7 @@ class TranspileLayout: def initial_virtual_layout(self, filter_ancillas: bool = False) -> Layout: """Return a :class:`.Layout` object for the initial layout. - This returns a mapping of virtual :class:`~.Qubit` objects in the input + This returns a mapping of virtual :class:`~.circuit.Qubit` objects in the input circuit to the physical qubit selected during layout. This is analogous to the :attr:`.initial_layout` attribute. @@ -492,7 +493,7 @@ def initial_virtual_layout(self, filter_ancillas: bool = False) -> Layout: will be in the returned layout. Any ancilla qubits added to the output circuit will be filtered from the returned object. Returns: - A layout object mapping the input circuit's :class:`~.Qubit` + A layout object mapping the input circuit's :class:`~.circuit.Qubit` objects to the selected physical qubits. """ if not filter_ancillas: diff --git a/qiskit/transpiler/passes/optimization/normalize_rx_angle.py b/qiskit/transpiler/passes/optimization/normalize_rx_angle.py index ad39cbc229c5..c8d9619c673f 100644 --- a/qiskit/transpiler/passes/optimization/normalize_rx_angle.py +++ b/qiskit/transpiler/passes/optimization/normalize_rx_angle.py @@ -69,7 +69,8 @@ def quantize_angles(self, qubit, original_angle): that differ within a resolution provided by the user. Args: - qubit (Qubit): This will be the dict key to access the list of quantized rotation angles. + qubit (qiskit.circuit.Qubit): This will be the dict key to access the list of + quantized rotation angles. original_angle (float): Original rotation angle, before quantization. Returns: diff --git a/qiskit/transpiler/passes/routing/sabre_swap.py b/qiskit/transpiler/passes/routing/sabre_swap.py index b78bd539b99b..3f83a02237cd 100644 --- a/qiskit/transpiler/passes/routing/sabre_swap.py +++ b/qiskit/transpiler/passes/routing/sabre_swap.py @@ -339,9 +339,9 @@ def _apply_sabre_result( Rust run of the Sabre routing algorithm. initial_layout (NLayout): a Rust-space mapping of virtual indices (i.e. those of the qubits in ``in_dag``) to physical ones. - physical_qubits (list[Qubit]): an indexable sequence of :class:`.Qubit` objects representing - the physical qubits of the circuit. Note that disjoint-coupling handling can mean that - these are not strictly a "canonical physical register" in order. + physical_qubits (list[Qubit]): an indexable sequence of :class:`.circuit.Qubit` objects + representing the physical qubits of the circuit. Note that disjoint-coupling + handling can mean that these are not strictly a "canonical physical register" in order. circuit_to_dag_dict (Mapping[int, DAGCircuit]): a mapping of the Python object identity (as returned by :func:`id`) of a control-flow block :class:`.QuantumCircuit` to a :class:`.DAGCircuit` that represents the same thing. diff --git a/releasenotes/notes/0.20/bit-slots-17d6649872da0440.yaml b/releasenotes/notes/0.20/bit-slots-17d6649872da0440.yaml index 257ad056723a..dba8bdcfd9d5 100644 --- a/releasenotes/notes/0.20/bit-slots-17d6649872da0440.yaml +++ b/releasenotes/notes/0.20/bit-slots-17d6649872da0440.yaml @@ -1,7 +1,7 @@ --- upgrade: - | - The classes :class:`.Qubit`, :class:`.Clbit` and :class:`.AncillaQubit` now + The classes :class:`~qiskit.circuit.Qubit`, :class:`.Clbit` and :class:`.AncillaQubit` now have the ``__slots__`` attribute. This is to reduce their memory usage. As a side effect, they can no longer have arbitrary data attached as attributes to them. This is very unlikely to have any effect on downstream code other diff --git a/releasenotes/notes/0.21/change-instruction-data-scalar-81f2066ca2435933.yaml b/releasenotes/notes/0.21/change-instruction-data-scalar-81f2066ca2435933.yaml index 3b4b877580c0..5e9a3a8e041b 100644 --- a/releasenotes/notes/0.21/change-instruction-data-scalar-81f2066ca2435933.yaml +++ b/releasenotes/notes/0.21/change-instruction-data-scalar-81f2066ca2435933.yaml @@ -3,7 +3,7 @@ upgrade: - | The data type of each element in :attr:`.QuantumCircuit.data` has changed. It used to be a simple 3-tuple of an :class:`~.circuit.Instruction`, a list - of :class:`.Qubit`\ s, and a list of :class:`.Clbit`\ s, whereas it is now + of :class:`~qiskit.circuit.Qubit`\ s, and a list of :class:`.Clbit`\ s, whereas it is now an instance of :class:`.CircuitInstruction`. The attributes of this new class are :attr:`~.CircuitInstruction.operation`, diff --git a/releasenotes/notes/0.21/fix-reverse_bits-with-registerless-bits-6d17597b99640fb0.yaml b/releasenotes/notes/0.21/fix-reverse_bits-with-registerless-bits-6d17597b99640fb0.yaml index 6fa9789cfdbb..ba7c7d43af05 100644 --- a/releasenotes/notes/0.21/fix-reverse_bits-with-registerless-bits-6d17597b99640fb0.yaml +++ b/releasenotes/notes/0.21/fix-reverse_bits-with-registerless-bits-6d17597b99640fb0.yaml @@ -2,7 +2,7 @@ fixes: - | Fixed :meth:`.QuantumCircuit.reverse_bits` with circuits containing registerless - :class:`.Qubit` and :class:`.Clbit`. For example, the following will now work:: + :class:`~qiskit.circuit.Qubit` and :class:`.Clbit`. For example, the following will now work:: from qiskit.circuit import QuantumCircuit, Qubit, Clbit diff --git a/releasenotes/notes/0.22/circuit-initialize-and-prepare-single-qubit-e25dacc8f873bc01.yaml b/releasenotes/notes/0.22/circuit-initialize-and-prepare-single-qubit-e25dacc8f873bc01.yaml index 0648666c6147..2209c0e4398b 100644 --- a/releasenotes/notes/0.22/circuit-initialize-and-prepare-single-qubit-e25dacc8f873bc01.yaml +++ b/releasenotes/notes/0.22/circuit-initialize-and-prepare-single-qubit-e25dacc8f873bc01.yaml @@ -2,4 +2,4 @@ fixes: - | Fixed a bug in :meth:`.QuantumCircuit.initialize` and :meth:`.QuantumCircuit.prepare_state` - that caused them to not accept a single :class:`Qubit` as argument to initialize. + that caused them to not accept a single :class:`~qiskit.circuit.Qubit` as argument to initialize. diff --git a/releasenotes/notes/0.22/denselayout-loose-bits-3e66011432bc6232.yaml b/releasenotes/notes/0.22/denselayout-loose-bits-3e66011432bc6232.yaml index 5798e708eaf6..ab5fbdd36ccb 100644 --- a/releasenotes/notes/0.22/denselayout-loose-bits-3e66011432bc6232.yaml +++ b/releasenotes/notes/0.22/denselayout-loose-bits-3e66011432bc6232.yaml @@ -2,6 +2,6 @@ fixes: - | Fixed an issue in the :class:`~.DenseLayout` transpiler pass where any - loose :class:`~.Qubit` objects (i.e. not part of a :class:`~.QuantumRegister`) + loose :class:`~qiskit.circuit.Qubit` objects (i.e. not part of a :class:`~.QuantumRegister`) that were part of a :class:`~.QuantumCircuit` would not be included in the output :class:`~.Layout` that was generated by the pass. diff --git a/releasenotes/notes/0.23/fix-qpy-loose-bits-5283dc4ad3823ce3.yaml b/releasenotes/notes/0.23/fix-qpy-loose-bits-5283dc4ad3823ce3.yaml index 528b7facd6f7..a9fa4703d678 100644 --- a/releasenotes/notes/0.23/fix-qpy-loose-bits-5283dc4ad3823ce3.yaml +++ b/releasenotes/notes/0.23/fix-qpy-loose-bits-5283dc4ad3823ce3.yaml @@ -3,7 +3,7 @@ fixes: - | QPY deserialisation will no longer add extra :class:`.Clbit` instances to the circuit if there are both loose :class:`.Clbit`\ s in the circuit and more - :class:`.Qubit`\ s than :class:`.Clbit`\ s. + :class:`~qiskit.circuit.Qubit`\ s than :class:`.Clbit`\ s. - | QPY deserialisation will no longer add registers named `q` and `c` if the input circuit contained only loose bits. diff --git a/releasenotes/notes/0.24/fix-deprecated-bit-qpy-roundtrip-9a23a795aa677c71.yaml b/releasenotes/notes/0.24/fix-deprecated-bit-qpy-roundtrip-9a23a795aa677c71.yaml index a6f1a0db1f68..7ee1ab0e620e 100644 --- a/releasenotes/notes/0.24/fix-deprecated-bit-qpy-roundtrip-9a23a795aa677c71.yaml +++ b/releasenotes/notes/0.24/fix-deprecated-bit-qpy-roundtrip-9a23a795aa677c71.yaml @@ -1,8 +1,8 @@ --- fixes: - | - The deprecated :class:`.Qubit` and :class:`.Clbit` properties :attr:`~.Qubit.register` and - :attr:`~.Qubit.index` will now be correctly round-tripped by QPY (:mod:`qiskit.qpy`) in all + The deprecated :class:`~qiskit.circuit.Qubit` and :class:`.Clbit` properties :attr:`~.Qubit.register` and + :attr:`~qiskit.circuit.Qubit.index` will now be correctly round-tripped by QPY (:mod:`qiskit.qpy`) in all valid usages of :class:`.QuantumRegister` and :class:`.ClassicalRegister`. In earlier releases in the Terra 0.23 series, this information would be lost. In versions before 0.23.0, this information was partially reconstructed but could be incorrect or produce invalid circuits for diff --git a/releasenotes/notes/0.25/fix-bit-copy-4b2f7349683f616a.yaml b/releasenotes/notes/0.25/fix-bit-copy-4b2f7349683f616a.yaml index 41d76acdb609..8828796e744e 100644 --- a/releasenotes/notes/0.25/fix-bit-copy-4b2f7349683f616a.yaml +++ b/releasenotes/notes/0.25/fix-bit-copy-4b2f7349683f616a.yaml @@ -2,6 +2,6 @@ fixes: - | Fixed an issue with copying circuits with new-style :class:`.Clbit`\ s and - :class:`.Qubit`\ s (bits without registers) where references to these bits + :class:`~qiskit.circuit.Qubit`\ s (bits without registers) where references to these bits from the containing circuit could be broken, causing issues with serialization and circuit visualization. diff --git a/releasenotes/notes/0.45/remove-deprecated-code-in-0.19-a97ccfec62405b9a.yaml b/releasenotes/notes/0.45/remove-deprecated-code-in-0.19-a97ccfec62405b9a.yaml index eb62402ab3c4..3e04728590ad 100644 --- a/releasenotes/notes/0.45/remove-deprecated-code-in-0.19-a97ccfec62405b9a.yaml +++ b/releasenotes/notes/0.45/remove-deprecated-code-in-0.19-a97ccfec62405b9a.yaml @@ -1,10 +1,10 @@ --- upgrade: - | - The argument ``qubits`` in the method :meth:`qiskit.transpiler.instruction_durations.InstructionDurations.get`, does not accept :class:`.Qubit` (or a list of them) any more. This functionality was deprecated in Qiskit 0.33 (with Terra 0.19), released on Dec 2021. Instead, use an integer for the qubit indices. + The argument ``qubits`` in the method :meth:`qiskit.transpiler.instruction_durations.InstructionDurations.get`, does not accept :class:`qiskit.circuit.Qubit` (or a list of them) any more. This functionality was deprecated in Qiskit 0.33 (with Terra 0.19), released on Dec 2021. Instead, use an integer for the qubit indices. - | The argument ``channel`` in the method :meth:`qiskit.providers.models.backendconfiguration.PulseBackendConfiguration.control` is removed. It was deprecated in Qiskit 0.33 (with Terra 0.19), released on Dec 2021. Instead use the ``qubits`` argument. - | - The class ``qiskit.qobj.Qobj`` is removed. It was deprecated in Qiskit 0.33 (with Terra 0.19), released on Dec 2021. Instead, use :class:`qiskit.qobj.QasmQobj` or :class:`qiskit.qobj.PulseQobj`. \ No newline at end of file + The class ``qiskit.qobj.Qobj`` is removed. It was deprecated in Qiskit 0.33 (with Terra 0.19), released on Dec 2021. Instead, use :class:`qiskit.qobj.QasmQobj` or :class:`qiskit.qobj.PulseQobj`. diff --git a/test/python/pulse/test_frames.py b/test/python/pulse/test_frames.py new file mode 100644 index 000000000000..1a9b99e1139f --- /dev/null +++ b/test/python/pulse/test_frames.py @@ -0,0 +1,77 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# 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. + +"""Test pulse logical elements and frames""" + +from qiskit.pulse import ( + PulseError, + GenericFrame, + QubitFrame, + MeasurementFrame, +) +from qiskit.test import QiskitTestCase + + +class TestFrames(QiskitTestCase): + """Test frames.""" + + def test_generic_frame_initialization(self): + """Test that Frame objects are created correctly""" + frame = GenericFrame(name="frame1") + self.assertEqual(frame.name, "frame1") + self.assertEqual(str(frame), "GenericFrame(frame1)") + + def test_generic_frame_comparison(self): + """Test that GenericFrame objects are compared correctly""" + frame1 = GenericFrame(name="frame1") + + self.assertEqual(frame1, GenericFrame(name="frame1")) + self.assertNotEqual(frame1, GenericFrame(name="frame2")) + self.assertNotEqual(frame1, QubitFrame(3)) + + def test_qubit_frame_initialization(self): + """Test that QubitFrame type frames are created and validated correctly""" + frame = QubitFrame(2) + self.assertEqual(frame.index, 2) + self.assertEqual(str(frame), "QubitFrame(2)") + + with self.assertRaises(PulseError): + QubitFrame(0.5) + with self.assertRaises(PulseError): + QubitFrame(-0.5) + with self.assertRaises(PulseError): + QubitFrame(-1) + + def test_qubit_frame_comparison(self): + """Test the comparison of QubitFrame""" + self.assertEqual(QubitFrame(0), QubitFrame(0)) + self.assertNotEqual(QubitFrame(0), QubitFrame(1)) + self.assertNotEqual(MeasurementFrame(0), QubitFrame(0)) + + def test_measurement_frame_initialization(self): + """Test that MeasurementFrame type frames are created and validated correctly""" + frame = MeasurementFrame(2) + self.assertEqual(frame.index, 2) + self.assertEqual(str(frame), "MeasurementFrame(2)") + + with self.assertRaises(PulseError): + MeasurementFrame(0.5) + with self.assertRaises(PulseError): + MeasurementFrame(-0.5) + with self.assertRaises(PulseError): + MeasurementFrame(-1) + + def test_measurement_frame_comparison(self): + """Test the comparison of measurement frames""" + self.assertEqual(MeasurementFrame(0), MeasurementFrame(0)) + self.assertNotEqual(MeasurementFrame(0), MeasurementFrame(1)) + self.assertNotEqual(MeasurementFrame(0), QubitFrame(0)) diff --git a/test/python/pulse/test_logical_elements.py b/test/python/pulse/test_logical_elements.py new file mode 100644 index 000000000000..bea1841fff3a --- /dev/null +++ b/test/python/pulse/test_logical_elements.py @@ -0,0 +1,66 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# 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. + +"""Test pulse logical elements and frames""" + +from qiskit.pulse import ( + PulseError, + Qubit, + Coupler, +) +from qiskit.test import QiskitTestCase + + +class TestLogicalElements(QiskitTestCase): + """Test logical elements.""" + + def test_qubit_initialization(self): + """Test that Qubit type logical elements are created and validated correctly""" + qubit = Qubit(0) + self.assertEqual(qubit.index, (0,)) + self.assertEqual(qubit.qubit_index, 0) + self.assertEqual(str(qubit), "Qubit(0)") + + with self.assertRaises(PulseError): + Qubit(0.5) + with self.assertRaises(PulseError): + Qubit(-0.5) + with self.assertRaises(PulseError): + Qubit(-1) + + def test_coupler_initialization(self): + """Test that Coupler type logical elements are created and validated correctly""" + coupler = Coupler(0, 3) + self.assertEqual(coupler.index, (0, 3)) + self.assertEqual(str(coupler), "Coupler(0, 3)") + + coupler = Coupler(0, 3, 2) + self.assertEqual(coupler.index, (0, 3, 2)) + + with self.assertRaises(PulseError): + Coupler(-1, 0) + with self.assertRaises(PulseError): + Coupler(2, -0.5) + with self.assertRaises(PulseError): + Coupler(3, -1) + with self.assertRaises(PulseError): + Coupler(0, 0, 1) + with self.assertRaises(PulseError): + Coupler(0) + + def test_logical_elements_comparison(self): + """Test the comparison of various logical elements""" + self.assertEqual(Qubit(0), Qubit(0)) + self.assertNotEqual(Qubit(0), Qubit(1)) + + self.assertEqual(Coupler(0, 1), Coupler(0, 1)) + self.assertNotEqual(Coupler(0, 1), Coupler(0, 2)) diff --git a/test/python/pulse/test_mixed_frames.py b/test/python/pulse/test_mixed_frames.py new file mode 100644 index 000000000000..ea681878f1cf --- /dev/null +++ b/test/python/pulse/test_mixed_frames.py @@ -0,0 +1,55 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# 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. + +"""Test pulse logical elements and frames""" + +from qiskit.pulse import ( + Qubit, + GenericFrame, + MixedFrame, +) +from qiskit.test import QiskitTestCase + + +class TestMixedFrames(QiskitTestCase): + """Test mixed frames.""" + + def test_mixed_frame_initialization(self): + """Test that MixedFrame objects are created correctly""" + frame = GenericFrame("frame1") + qubit = Qubit(1) + mixed_frame = MixedFrame(qubit, frame) + self.assertEqual(mixed_frame.logical_element, qubit) + self.assertEqual(mixed_frame.frame, frame) + + def test_mixed_frames_comparison(self): + """Test the comparison of various mixed frames""" + self.assertEqual( + MixedFrame(Qubit(1), GenericFrame("a")), + MixedFrame(Qubit(1), GenericFrame("a")), + ) + + self.assertNotEqual( + MixedFrame(Qubit(1), GenericFrame("a")), + MixedFrame(Qubit(2), GenericFrame("a")), + ) + self.assertNotEqual( + MixedFrame(Qubit(1), GenericFrame("a")), + MixedFrame(Qubit(1), GenericFrame("b")), + ) + + def test_mixed_frame_repr(self): + """Test MixedFrame __repr__""" + frame = GenericFrame("frame1") + qubit = Qubit(1) + mixed_frame = MixedFrame(qubit, frame) + self.assertEqual(str(mixed_frame), f"MixedFrame({qubit},{frame})") From 594ae55f9457b6b7623c1152dc7e929aa5d36a67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Oct 2023 19:03:25 +0100 Subject: [PATCH 3/4] Bump ahash from 0.8.3 to 0.8.5 (#11098) Bumps [ahash](https://github.com/tkaitchuck/ahash) from 0.8.3 to 0.8.5. - [Release notes](https://github.com/tkaitchuck/ahash/releases) - [Commits](https://github.com/tkaitchuck/ahash/compare/v0.8.3...v0.8.5) --- updated-dependencies: - dependency-name: ahash dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 44 +++++++++++++++++++++++++++++++----- crates/accelerate/Cargo.toml | 2 +- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 21bcb3ca0162..83e5aadac9bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,14 +4,15 @@ version = 3 [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "cd7d5a2cecb58716e47d67d5703a249964b14c7be1ec3cad3affc295b2d1c35d" dependencies = [ "cfg-if", "getrandom", "once_cell", "version_check", + "zerocopy", ] [[package]] @@ -320,9 +321,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -377,7 +378,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -388,7 +389,7 @@ checksum = "947dc12175c254889edc0c02e399476c2f652b4b9ebd123aa655c224de259536" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -571,6 +572,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "target-lexicon" version = "0.12.11" @@ -657,3 +669,23 @@ name = "windows_x86_64_msvc" version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d419259aba16b663966e29e6d7c6ecfa0bb8425818bb96f6f1f3c3eb71a6e7b9" + +[[package]] +name = "zerocopy" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c19fae0c8a9efc6a8281f2e623db8af1db9e57852e04cde3e754dd2dc29340f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc56589e9ddd1f1c28d4b4b5c773ce232910a6bb67a70133d61c9e347585efe9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] diff --git a/crates/accelerate/Cargo.toml b/crates/accelerate/Cargo.toml index 25e90763ad34..b86d6bf102a4 100644 --- a/crates/accelerate/Cargo.toml +++ b/crates/accelerate/Cargo.toml @@ -20,7 +20,7 @@ numpy = "0.19.0" rand = "0.8" rand_pcg = "0.3" rand_distr = "0.4.3" -ahash = "0.8.3" +ahash = "0.8.5" num-complex = "0.4" num-bigint = "0.4" rustworkx-core = "0.13" From 9611df4a46c7266376d82f51797c838405ab6658 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Oct 2023 13:12:55 +0000 Subject: [PATCH 4/4] Bump ahash from 0.8.5 to 0.8.6 (#11108) Bumps [ahash](https://github.com/tkaitchuck/ahash) from 0.8.5 to 0.8.6. - [Release notes](https://github.com/tkaitchuck/ahash/releases) - [Commits](https://github.com/tkaitchuck/ahash/commits) --- updated-dependencies: - dependency-name: ahash dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 12 ++++++------ crates/accelerate/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 83e5aadac9bb..0b70585530ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "ahash" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7d5a2cecb58716e47d67d5703a249964b14c7be1ec3cad3affc295b2d1c35d" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ "cfg-if", "getrandom", @@ -672,18 +672,18 @@ checksum = "d419259aba16b663966e29e6d7c6ecfa0bb8425818bb96f6f1f3c3eb71a6e7b9" [[package]] name = "zerocopy" -version = "0.7.11" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c19fae0c8a9efc6a8281f2e623db8af1db9e57852e04cde3e754dd2dc29340f" +checksum = "69c48d63854f77746c68a5fbb4aa17f3997ece1cb301689a257af8cb80610d21" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.11" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc56589e9ddd1f1c28d4b4b5c773ce232910a6bb67a70133d61c9e347585efe9" +checksum = "c258c1040279e4f88763a113de72ce32dde2d50e2a94573f15dd534cea36a16d" dependencies = [ "proc-macro2", "quote", diff --git a/crates/accelerate/Cargo.toml b/crates/accelerate/Cargo.toml index b86d6bf102a4..7f21159eeae5 100644 --- a/crates/accelerate/Cargo.toml +++ b/crates/accelerate/Cargo.toml @@ -20,7 +20,7 @@ numpy = "0.19.0" rand = "0.8" rand_pcg = "0.3" rand_distr = "0.4.3" -ahash = "0.8.5" +ahash = "0.8.6" num-complex = "0.4" num-bigint = "0.4" rustworkx-core = "0.13"