diff --git a/docs/build/_toc.json b/docs/build/_toc.json index f1a74dd1463..672d8c379fd 100644 --- a/docs/build/_toc.json +++ b/docs/build/_toc.json @@ -46,10 +46,14 @@ "title": "Operators module overview", "url": "/build/operators-overview" }, - { - "title": "Specify observables in the Pauli basis", - "url": "/build/specify-observables-pauli" - } + { + "title": "Specify observables in the Pauli basis", + "url": "/build/specify-observables-pauli" + }, + { + "title": "The Operator class", + "url": "/build/operator-class" + } ] }, { diff --git a/docs/build/operator-class.ipynb b/docs/build/operator-class.ipynb new file mode 100644 index 00000000000..2bb547c299a --- /dev/null +++ b/docs/build/operator-class.ipynb @@ -0,0 +1,718 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b88d9242-a6f2-47b6-8e0a-daf60d0c4374", + "metadata": {}, + "source": [ + "# The Operator class\n", + "\n", + "This page shows how to use the [`Operator`](/api/qiskit/qiskit.quantum_info.Operator) class. For a high-level overview of operator representations in Qiskit, including the `Operator` class and others, see [Overview of operator classes](/build/operators-overview)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "549bc166-9c0e-4e89-b7e2-8adafd7110c3", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from qiskit.circuit import QuantumCircuit\n", + "from qiskit.circuit.library import CXGate, RXGate, XGate\n", + "from qiskit.quantum_info import Operator, Pauli, process_fidelity" + ] + }, + { + "cell_type": "markdown", + "id": "8292bbef-0a61-44db-a096-cee8c9b23827", + "metadata": {}, + "source": [ + "## Converting classes to Operators\n", + "\n", + "Several other classes in Qiskit can be directly converted to an `Operator` object using the operator initialization method. For example:\n", + "\n", + "* `Pauli` objects\n", + "* `Gate` and `Instruction` objects\n", + "* `QuantumCircuit` objects\n", + "\n", + "Note that the last point means we can use the `Operator` class as a unitary simulator to compute the final unitary matrix for a quantum circuit, without having to call a simulator backend. If the circuit contains any unsupported operations, an exception will be raised. Unsupported operations are: measure, reset, conditional operations, or a gate that does not have a matrix definition or decomposition in terms of gate with matrix definitions." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "cb6e9d7a-8dc9-4b32-81f0-278d2e7e64b5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Operator([[0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],\n", + " [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n", + " [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],\n", + " [1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]],\n", + " input_dims=(2, 2), output_dims=(2, 2))\n" + ] + } + ], + "source": [ + "# Create an Operator from a Pauli object\n", + "\n", + "pauliXX = Pauli(\"XX\")\n", + "Operator(pauliXX)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "ffdda3ff-75ca-486c-8e52-be80244b969f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Operator([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", + " [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],\n", + " [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n", + " [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j]],\n", + " input_dims=(2, 2), output_dims=(2, 2))\n" + ] + } + ], + "source": [ + "# Create an Operator for a Gate object\n", + "Operator(CXGate())" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b8cbc1d9-4957-4b2d-b59c-32c1efbfbeed", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Operator([[0.70710678+0.j , 0. -0.70710678j],\n", + " [0. -0.70710678j, 0.70710678+0.j ]],\n", + " input_dims=(2,), output_dims=(2,))\n" + ] + } + ], + "source": [ + "# Create an operator from a parameterized Gate object\n", + "Operator(RXGate(np.pi / 2))" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "e60c3079-84bf-40cd-ace7-bae419118c99", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Operator([[ 0.70710678+0.j, 0.70710678+0.j, 0. +0.j, ...,\n", + " 0. +0.j, 0. +0.j, 0. +0.j],\n", + " [ 0. +0.j, 0. +0.j, 0.70710678+0.j, ...,\n", + " 0. +0.j, 0. +0.j, 0. +0.j],\n", + " [ 0. +0.j, 0. +0.j, 0. +0.j, ...,\n", + " 0. +0.j, 0. +0.j, 0. +0.j],\n", + " ...,\n", + " [ 0. +0.j, 0. +0.j, 0. +0.j, ...,\n", + " 0. +0.j, 0. +0.j, 0. +0.j],\n", + " [ 0. +0.j, 0. +0.j, 0.70710678+0.j, ...,\n", + " 0. +0.j, 0. +0.j, 0. +0.j],\n", + " [ 0.70710678+0.j, -0.70710678+0.j, 0. +0.j, ...,\n", + " 0. +0.j, 0. +0.j, 0. +0.j]],\n", + " input_dims=(2, 2, 2, 2, 2, 2, 2, 2, 2, 2), output_dims=(2, 2, 2, 2, 2, 2, 2, 2, 2, 2))\n" + ] + } + ], + "source": [ + "# Create an operator from a QuantumCircuit object\n", + "circ = QuantumCircuit(10)\n", + "circ.h(0)\n", + "for j in range(1, 10):\n", + " circ.cx(j - 1, j)\n", + "\n", + "# Convert circuit to an operator by implicit unitary simulation\n", + "Operator(circ)" + ] + }, + { + "cell_type": "markdown", + "id": "693e4885-bd79-4a2f-8e2b-8e4d81892c05", + "metadata": {}, + "source": [ + "## Using Operators in circuits\n", + "\n", + "Unitary `Operators` can be directly inserted into a `QuantumCircuit` using the `QuantumCircuit.append` method. This converts the `Operator` into a `UnitaryGate` object, which is added to the circuit.\n", + "\n", + "If the operator is not unitary, an exception will be raised. This can be checked using the `Operator.is_unitary()` function, which will return `True` if the operator is unitary and `False` otherwise." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "223c07c9-1b1d-4a33-a397-091cac2ec9e3", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Create an operator\n", + "XX = Operator(Pauli(\"XX\"))\n", + "\n", + "# Add to a circuit\n", + "circ = QuantumCircuit(2, 2)\n", + "circ.append(XX, [0, 1])\n", + "circ.measure([0, 1], [0, 1])\n", + "circ.draw(\"mpl\")" + ] + }, + { + "cell_type": "markdown", + "id": "04a90485-7b03-4ae3-a37d-0ae87feee6a4", + "metadata": {}, + "source": [ + "Note that in the above example we initialize the operator from a `Pauli` object. However, the `Pauli` object may also be directly inserted into the circuit itself and will be converted into a sequence of single-qubit Pauli gates:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "85f37f4a-078e-401f-b222-ec230da6ae78", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
     ┌────────────┐┌─┐   \n",
+       "q_0: ┤0           ├┤M├───\n",
+       "     │  Pauli(XX) │└╥┘┌─┐\n",
+       "q_1: ┤1           ├─╫─┤M├\n",
+       "     └────────────┘ ║ └╥┘\n",
+       "c: 2/═══════════════╩══╩═\n",
+       "                    0  1 
" + ], + "text/plain": [ + " ┌────────────┐┌─┐ \n", + "q_0: ┤0 ├┤M├───\n", + " │ Pauli(XX) │└╥┘┌─┐\n", + "q_1: ┤1 ├─╫─┤M├\n", + " └────────────┘ ║ └╥┘\n", + "c: 2/═══════════════╩══╩═\n", + " 0 1 " + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Add to a circuit\n", + "circ2 = QuantumCircuit(2, 2)\n", + "circ2.append(Pauli(\"XX\"), [0, 1])\n", + "circ2.measure([0, 1], [0, 1])\n", + "circ2.draw()" + ] + }, + { + "cell_type": "markdown", + "id": "59bf4972-731f-40c4-9d26-ca0110827cdc", + "metadata": {}, + "source": [ + "## Combining Operators\n", + "\n", + "Operators may be combined using several methods.\n", + "\n", + "### Tensor Product\n", + "\n", + "Two operators $A$ and $B$ may be combined into a tensor product operator $A\\otimes B$ using the `Operator.tensor` function. Note that if both $A$ and $B$ are single-qubit operators, then `A.tensor(B)` = $A\\otimes B$ will have the subsystems indexed as matrix $B$ on subsystem 0, and matrix $A$ on subsystem 1." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "0fcf1f68-8af1-417f-b6c3-313846ec5b0d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Operator([[ 0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n", + " [ 0.+0.j, -0.+0.j, 0.+0.j, -1.+0.j],\n", + " [ 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", + " [ 0.+0.j, -1.+0.j, 0.+0.j, -0.+0.j]],\n", + " input_dims=(2, 2), output_dims=(2, 2))\n" + ] + } + ], + "source": [ + "A = Operator(Pauli(\"X\"))\n", + "B = Operator(Pauli(\"Z\"))\n", + "A.tensor(B)" + ] + }, + { + "cell_type": "markdown", + "id": "bca7c8a7-5f19-405c-9f27-3e75db53cb5e", + "metadata": {}, + "source": [ + "### Tensor Expansion\n", + "\n", + "A closely related operation is `Operator.expand`, which acts like a tensor product but in the reverse order. Hence, for two operators $A$ and $B$ we have `A.expand(B)` = $B\\otimes A$ where the subsystems indexed as matrix $A$ on subsystem 0, and matrix $B$ on subsystem 1." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "a38a8588-f0a5-4533-95dc-23ff383501d6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Operator([[ 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],\n", + " [ 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", + " [ 0.+0.j, 0.+0.j, -0.+0.j, -1.+0.j],\n", + " [ 0.+0.j, 0.+0.j, -1.+0.j, -0.+0.j]],\n", + " input_dims=(2, 2), output_dims=(2, 2))\n" + ] + } + ], + "source": [ + "A = Operator(Pauli(\"X\"))\n", + "B = Operator(Pauli(\"Z\"))\n", + "A.expand(B)" + ] + }, + { + "cell_type": "markdown", + "id": "6a33cf17-68f8-442d-b4e0-6b899d1d280a", + "metadata": {}, + "source": [ + "### Composition\n", + "\n", + "We can also compose two operators $A$ and $B$ to implement matrix multiplication using the `Operator.compose` method. We have that `A.compose(B)` returns the operator with matrix $B.A$:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "a45e1ef6-09b4-4d70-a9e2-0eb5f1b59a83", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Operator([[ 0.+0.j, 1.+0.j],\n", + " [-1.+0.j, 0.+0.j]],\n", + " input_dims=(2,), output_dims=(2,))\n" + ] + } + ], + "source": [ + "A = Operator(Pauli(\"X\"))\n", + "B = Operator(Pauli(\"Z\"))\n", + "A.compose(B)" + ] + }, + { + "cell_type": "markdown", + "id": "f54c6b4e-e8ac-4ae7-ac02-60300a46c88c", + "metadata": {}, + "source": [ + "We can also compose in the reverse order by applying $B$ in front of $A$ using the `front` kwarg of `compose`: `A.compose(B, front=True)` = $A.B$:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "30b6a60d-11fd-4b19-a673-790aab891554", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Operator([[ 0.+0.j, -1.+0.j],\n", + " [ 1.+0.j, 0.+0.j]],\n", + " input_dims=(2,), output_dims=(2,))\n" + ] + } + ], + "source": [ + "A = Operator(Pauli(\"X\"))\n", + "B = Operator(Pauli(\"Z\"))\n", + "A.compose(B, front=True)" + ] + }, + { + "cell_type": "markdown", + "id": "f08c91f5-88f0-408f-ae9e-03cec65de305", + "metadata": {}, + "source": [ + "### Subsystem Composition\n", + "\n", + "Note that the previous compose requires that the total output dimension of the first operator $A$ is equal to total input dimension of the composed operator $B$ (and similarly, the output dimension of $B$ must be equal to the input dimension of $A$ when composing with `front=True`).\n", + "\n", + "We can also compose a smaller operator with a selection of subsystems on a larger operator using the `qargs` kwarg of `compose`, either with or without `front=True`. In this case, the relevant input and output dimensions of the subsystems being composed must match. *Note that the smaller operator must always be the argument of* `compose` *method.*\n", + "\n", + "For example, to compose a two-qubit gate with a three-qubit Operator:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "e604f408-3ec3-49fa-830a-eaf960fdd40d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Operator([[ 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j,\n", + " 0.+0.j],\n", + " [ 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, -1.+0.j, 0.+0.j,\n", + " 0.+0.j],\n", + " [ 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j,\n", + " 0.+0.j],\n", + " [ 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,\n", + " -1.+0.j],\n", + " [ 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,\n", + " 0.+0.j],\n", + " [ 0.+0.j, -1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,\n", + " 0.+0.j],\n", + " [ 0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,\n", + " 0.+0.j],\n", + " [ 0.+0.j, 0.+0.j, 0.+0.j, -1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,\n", + " 0.+0.j]],\n", + " input_dims=(2, 2, 2), output_dims=(2, 2, 2))\n" + ] + } + ], + "source": [ + "# Compose XZ with a 3-qubit identity operator\n", + "op = Operator(np.eye(2**3))\n", + "XZ = Operator(Pauli(\"XZ\"))\n", + "op.compose(XZ, qargs=[0, 2])" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "d2024a14-891b-46e4-93e7-4dc73e33e928", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Operator([[0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j, 0.+0.j],\n", + " [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", + " [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j],\n", + " [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j],\n", + " [0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", + " [0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", + " [0.+0.j, 0.+0.j, 0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", + " [0.+0.j, 0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]],\n", + " input_dims=(2, 2, 2), output_dims=(2, 2, 2))\n" + ] + } + ], + "source": [ + "# Compose YX in front of the previous operator\n", + "op = Operator(np.eye(2**3))\n", + "YX = Operator(Pauli(\"YX\"))\n", + "op.compose(YX, qargs=[0, 2], front=True)" + ] + }, + { + "cell_type": "markdown", + "id": "5e362de3-866d-42b3-8d3c-a3905d9d4005", + "metadata": {}, + "source": [ + "### Linear combinations\n", + "\n", + "Operators may also be combined using standard linear operators for addition, subtraction and scalar multiplication by complex numbers." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "79b9ccd5-a002-4499-9439-7c553177abb8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Operator([[-1.5+0.j, 0. +0.j, 0. +0.j, 0. +0.j],\n", + " [ 0. +0.j, 1.5+0.j, 1. +0.j, 0. +0.j],\n", + " [ 0. +0.j, 1. +0.j, 1.5+0.j, 0. +0.j],\n", + " [ 0. +0.j, 0. +0.j, 0. +0.j, -1.5+0.j]],\n", + " input_dims=(2, 2), output_dims=(2, 2))\n" + ] + } + ], + "source": [ + "XX = Operator(Pauli(\"XX\"))\n", + "YY = Operator(Pauli(\"YY\"))\n", + "ZZ = Operator(Pauli(\"ZZ\"))\n", + "\n", + "op = 0.5 * (XX + YY - 3 * ZZ)\n", + "op" + ] + }, + { + "cell_type": "markdown", + "id": "235d9549-a7f6-4c8c-8592-de57e5e45fc6", + "metadata": {}, + "source": [ + "An important point is that while `tensor`, `expand` and `compose` will preserve the unitarity of unitary operators, linear combinations will not; hence, adding two unitary operators will, in general, result in a non-unitary operator:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "97615df2-f6dc-4367-8463-f5c0c1ef94eb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "op.is_unitary()" + ] + }, + { + "cell_type": "markdown", + "id": "1a2bf1cd-6f0f-446f-a993-ae9c2d22984e", + "metadata": {}, + "source": [ + "### Implicit Conversion to Operators\n", + "\n", + "Note that for all the following methods, if the second object is not already an `Operator` object, it will be implicitly converted into one by the method. This means that matrices can be passed in directly without being explicitly converted to an `Operator` first. If the conversion is not possible, an exception will be raised." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "9d72e40f-8d72-4cd7-8350-1896e88528c0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Operator([[0.+0.j, 1.+0.j],\n", + " [1.+0.j, 0.+0.j]],\n", + " input_dims=(2,), output_dims=(2,))\n" + ] + } + ], + "source": [ + "# Compose with a matrix passed as a list\n", + "Operator(np.eye(2)).compose([[0, 1], [1, 0]])" + ] + }, + { + "cell_type": "markdown", + "id": "0ee93f02-c3ea-44b0-8b87-f79e3107749e", + "metadata": {}, + "source": [ + "## Comparison of Operators\n", + "\n", + "Operators implement an equality method that can be used to check if two operators are approximately equal." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "ce9983d1-e20a-432d-af22-d6a1011f412c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Operator(Pauli(\"X\")) == Operator(XGate())" + ] + }, + { + "cell_type": "markdown", + "id": "36bbbbf9-3ed7-4aab-bc9e-f542325372f1", + "metadata": {}, + "source": [ + "Note that this checks that each matrix element of the operators is approximately equal; two unitaries that differ by a global phase will not be considered equal:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "a42ba8a1-5ab5-44d0-bd54-4147f4f28e53", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Operator(XGate()) == np.exp(1j * 0.5) * Operator(XGate())" + ] + }, + { + "cell_type": "markdown", + "id": "c06612c0-be6e-41b5-9ba5-92a9d7755c2f", + "metadata": {}, + "source": [ + "### Process Fidelity\n", + "\n", + "We may also compare operators using the `process_fidelity` function from the *Quantum Information* module. This is an information theoretic quantity for how close two quantum channels are to each other, and in the case of unitary operators it does not depend on global phase." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "813d97d1-94d3-4c65-aca0-324b09cebc6a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Process fidelity = 1.0\n" + ] + } + ], + "source": [ + "# Two operators which differ only by phase\n", + "op_a = Operator(XGate())\n", + "op_b = np.exp(1j * 0.5) * Operator(XGate())\n", + "\n", + "# Compute process fidelity\n", + "F = process_fidelity(op_a, op_b)\n", + "print(\"Process fidelity =\", F)" + ] + }, + { + "cell_type": "markdown", + "id": "993bab6d-7f37-4363-a17e-8b8e3b7de30a", + "metadata": {}, + "source": [ + "Note that process fidelity is generally only a valid measure of closeness if the input operators are unitary (or CP in the case of quantum channels), and an exception will be raised if the inputs are not CP." + ] + }, + { + "cell_type": "markdown", + "id": "52ef24af-888c-4c76-ab6f-4e1e8323cf2a", + "metadata": {}, + "source": [ + "## Next steps\n", + "\n", + "\n", + " - Explore the [Operator API](/api/qiskit/qiskit.quantum_info.Operator#operator) reference.\n", + "" + ] + } + ], + "metadata": { + "anaconda-cloud": {}, + "celltoolbar": "Tags", + "description": "In-depth explanation of using the Operator class in Qiskit.", + "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" + }, + "title": "The Operator class", + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/build/operators-overview.ipynb b/docs/build/operators-overview.ipynb index d767aa18817..ae385a80cf0 100644 --- a/docs/build/operators-overview.ipynb +++ b/docs/build/operators-overview.ipynb @@ -1,29 +1,21 @@ { "cells": [ { + "attachments": {}, "cell_type": "markdown", - "id": "77fe28b1-cfb3-4984-80f2-ab3a05d718c2", + "id": "434beca8-b15d-4f42-a887-4636fb692789", "metadata": {}, "source": [ - "# Operators module overview" - ] - }, - { - "cell_type": "markdown", - "id": "8a16a455-4530-4f2e-be6c-8c0da255b1c5", - "metadata": {}, - "source": [ - "The `Operator` class is used in the Qiskit SDK to represent matrix operators acting on a quantum system. It has several methods to build composite operators using tensor products of smaller operators, and to compose operators.\n", + "# Overview of operator classes\n", "\n", - "### Creating Operators\n", "\n", - "The easiest way to create an operator object is to initialize it with a matrix given as a list or a Numpy array. For example, to create a two-qubit Pauli-XX operator:" + "In Qiskit, quantum operators are represented using classes from the [`quantum_info`](/api/qiskit/quantum_info) module. The most important operator class is [`SparsePauliOp`](/api/qiskit/qiskit.quantum_info.SparsePauliOp), which represents a general quantum operator as a linear combination of Pauli strings. `SparsePauliOp` is the class most commonly used to represent quantum observables. The rest of this page explains how to use `SparsePauliOp` and other operator classes." ] }, { "cell_type": "code", "execution_count": 1, - "id": "fc51e532-a85f-40bc-b333-e622f39bc6b3", + "id": "6fc6f151-f2ca-44f6-811b-d15c71ee1fa6", "metadata": { "ExecuteTime": { "end_time": "2019-08-21T09:02:56.554914Z", @@ -33,304 +25,196 @@ "outputs": [], "source": [ "import numpy as np\n", - "from qiskit_aer import Aer\n", - "\n", - "from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister\n", - "from qiskit.quantum_info.operators import Operator, Pauli\n", - "from qiskit.quantum_info import process_fidelity\n", - "\n", - "from qiskit.circuit.library import RXGate, XGate, CXGate" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "250dfa1d-e3b5-4308-9ddb-488a2af828d9", - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:02:56.572857Z", - "start_time": "2019-08-21T09:02:56.566140Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],\n", - " [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n", - " [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],\n", - " [1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]],\n", - " input_dims=(2, 2), output_dims=(2, 2))\n" - ] - } - ], - "source": [ - "XX = Operator([[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]])\n", - "XX" + "from qiskit.quantum_info.operators import Operator, Pauli, SparsePauliOp" ] }, { "cell_type": "markdown", - "id": "6dbe8dec-e9a0-46aa-91a6-86e24a9ab945", + "id": "23a940b3-fce0-4f84-8461-5a72c0ce9aa7", "metadata": {}, "source": [ - "### Operator Properties\n", - "\n", - "The operator object stores the underlying matrix, and the input and output dimension of subsystems.\n", + "## SparsePauliOp\n", "\n", - "* `data`: To access the underlying Numpy array, we may use the `Operator.data` property.\n", - "* `dims`: To return the total input and output dimension of the operator, we may use the `Operator.dim` property. *Note: the output is returned as a tuple* `(input_dim, output_dim)`, *which is the reverse of the shape of the underlying matrix.*" + "The [`SparsePauliOp`](/api/qiskit/qiskit.quantum_info.SparsePauliOp) class represents a linear combination of Pauli strings. There are several ways to initialize a `SparsePauliOp`, but the most flexible way is to use the [`from_sparse_list`](/api/qiskit/qiskit.quantum_info.SparsePauliOp#from_sparse_list) method, as demonstrated in the following code cell. The `from_sparse_list` accepts a list of `(pauli_string, qubit_indices, coefficient)` triplets." ] }, { "cell_type": "code", - "execution_count": 3, - "id": "548bb147-213e-43f1-b4a6-e1ae3c09f755", - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:02:56.589962Z", - "start_time": "2019-08-21T09:02:56.585681Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],\n", - " [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n", - " [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],\n", - " [1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]])" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "XX.data" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "d7e3b756-6a77-4c77-b7c3-4dacda1a3807", - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:02:56.615497Z", - "start_time": "2019-08-21T09:02:56.611146Z" - } - }, + "execution_count": 2, + "id": "90053827-6a1f-4fb4-8188-808afe3ddd4f", + "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "(4, 4)" + "SparsePauliOp(['XIIZI', 'IYIIY'],\n", + " coeffs=[ 1.+0.j, -1.+1.j])" ] }, - "execution_count": 4, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "input_dim, output_dim = XX.dim\n", - "input_dim, output_dim" + "op1 = SparsePauliOp.from_sparse_list(\n", + " [(\"ZX\", [1, 4], 1.0), (\"YY\", [0, 3], -1 + 1j)], num_qubits=5\n", + ")\n", + "op1" ] }, { "cell_type": "markdown", - "id": "b77139bc-a4f2-4894-86c4-6c71bc13bcce", + "id": "fc7ffabb-5245-4687-b84b-25176a64dc07", "metadata": {}, "source": [ - "### Input and Output Dimensions\n", - "\n", - "The operator class also keeps track of subsystem dimensions, which can be used for composing operators together. These can be accessed using the `input_dims` and `output_dims` functions.\n", - "\n", - "For $2^N$ by $2^M$ operators, the input and output dimension will be automatically assumed to be M-qubit and N-qubit:" + "`SparsePauliOp` supports arithmetic operations, as demonstrated in the following code cell." ] }, { "cell_type": "code", - "execution_count": 5, - "id": "214a4159-4913-49c4-aad9-05bbbd08e806", - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:02:56.804167Z", - "start_time": "2019-08-21T09:02:56.798857Z" - } - }, + "execution_count": 3, + "id": "2e21467f-b6fe-4ac5-9654-66dec83a97fb", + "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Input dimensions: (2, 2)\n", - "Output dimensions: (2,)\n" + "op1 + op2:\n", + "SparsePauliOp(['XIIZI', 'IYIIY', 'ZIIXX', 'IIZZI'],\n", + " coeffs=[ 1.+0.j, -1.+1.j, 1.+2.j, -1.+1.j])\n", + "\n", + "2 * op1:\n", + "SparsePauliOp(['XIIZI', 'IYIIY'],\n", + " coeffs=[ 2.+0.j, -2.+2.j])\n", + "\n", + "op1 @ op2:\n", + "SparsePauliOp(['YIIYX', 'XIZII', 'ZYIXZ', 'IYZZY'],\n", + " coeffs=[ 1.+2.j, -1.+1.j, -1.+3.j, 0.-2.j])\n", + "\n", + "op1.tensor(op2):\n", + "SparsePauliOp(['XIIZIZIIXX', 'XIIZIIIZZI', 'IYIIYZIIXX', 'IYIIYIIZZI'],\n", + " coeffs=[ 1.+2.j, -1.+1.j, -3.-1.j, 0.-2.j])\n" ] } ], "source": [ - "op = Operator(np.random.rand(2 ** 1, 2 ** 2))\n", - "print('Input dimensions:', op.input_dims())\n", - "print('Output dimensions:', op.output_dims())" + "op2 = SparsePauliOp.from_sparse_list(\n", + " [(\"XXZ\", [0, 1, 4], 1 + 2j), (\"ZZ\", [1, 2], -1 + 1j)], num_qubits=5\n", + ")\n", + "\n", + "# Addition\n", + "print(\"op1 + op2:\")\n", + "print(op1 + op2)\n", + "print()\n", + "# Multiplication by a scalar\n", + "print(\"2 * op1:\")\n", + "print(2 * op1)\n", + "print()\n", + "# Operator multiplication (composition)\n", + "print(\"op1 @ op2:\")\n", + "print(op1 @ op2)\n", + "print()\n", + "# Tensor product\n", + "print(\"op1.tensor(op2):\")\n", + "print(op1.tensor(op2))" ] }, { "cell_type": "markdown", - "id": "a6ed7631-2f8a-488a-9b3e-73ca54f9c5c3", + "id": "85fb16f2-5296-4c4d-9836-249358fd7e12", "metadata": {}, "source": [ - "If the input matrix is not divisible into qubit subsystems, then it will be stored as a single-qubit operator. For example, if we have a $6\\times6$ matrix:" + "## Pauli\n", + "\n", + "The [`Pauli`](/api/qiskit/qiskit.quantum_info.Pauli) class represents a single Pauli string with an optional phase coefficient from the set $\\set{+1, i, -1, -i}$. A `Pauli` can be initialized by passing a string of characters from the set `{\"I\", \"X\", \"Y\", \"Z\"}`, optionally prefixed by one of `{\"\", \"i\", \"-\", \"-i\"}` to represent the phase coefficient." ] }, { "cell_type": "code", - "execution_count": 6, - "id": "30fcfeb4-898a-43c5-9cc8-fd1944aac6c6", - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:02:57.764881Z", - "start_time": "2019-08-21T09:02:57.760401Z" - } - }, + "execution_count": 4, + "id": "edc33fe7-989e-489b-92f6-81975b20bd05", + "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Input dimensions: (6,)\n", - "Output dimensions: (6,)\n" - ] + "data": { + "text/plain": [ + "Pauli('iXX')" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "op = Operator(np.random.rand(6, 6))\n", - "print('Input dimensions:', op.input_dims())\n", - "print('Output dimensions:', op.output_dims())" + "op1 = Pauli(\"iXX\")\n", + "op1" ] }, { "cell_type": "markdown", - "id": "0644ba22-890a-49b5-a39e-1a6af9a6851a", + "id": "82c19270-98d0-4543-bad5-5bfaacee406f", "metadata": {}, "source": [ - "The input and output dimension can also be manually specified when initializing a new operator:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "93210e11-7671-4c6e-a736-54419242e3b5", - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:02:58.292849Z", - "start_time": "2019-08-21T09:02:58.287354Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Input dimensions: (4,)\n", - "Output dimensions: (2,)\n" - ] - } - ], - "source": [ - "# Force input dimension to be (4,) rather than (2, 2)\n", - "op = Operator(np.random.rand(2 ** 1, 2 ** 2), input_dims=[4])\n", - "print('Input dimensions:', op.input_dims())\n", - "print('Output dimensions:', op.output_dims())" + "The following code cell demonstrates the use of some attributes and methods." ] }, { "cell_type": "code", - "execution_count": 8, - "id": "a615f9dd-76ca-43f4-baae-644291b15ae5", - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:02:58.779572Z", - "start_time": "2019-08-21T09:02:58.774878Z" - } - }, + "execution_count": 5, + "id": "69b105cd-e978-45be-a3e9-bab344d9111f", + "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Input dimensions: (2, 3)\n", - "Output dimensions: (2, 3)\n" + "Dimension of iXX: (4, 4)\n", + "Phase of iXX: 3\n", + "Matrix representation of iXX: \n", + " [[0.+0.j 0.+0.j 0.+0.j 0.+1.j]\n", + " [0.+0.j 0.+0.j 0.+1.j 0.+0.j]\n", + " [0.+0.j 0.+1.j 0.+0.j 0.+0.j]\n", + " [0.+1.j 0.+0.j 0.+0.j 0.+0.j]]\n" ] } ], "source": [ - "# Specify system is a qubit and qutrit\n", - "op = Operator(np.random.rand(6, 6),\n", - " input_dims=[2, 3], output_dims=[2, 3])\n", - "print('Input dimensions:', op.input_dims())\n", - "print('Output dimensions:', op.output_dims())" + "print(f\"Dimension of {op1}: {op1.dim}\")\n", + "print(f\"Phase of {op1}: {op1.phase}\")\n", + "print(f\"Matrix representation of {op1}: \\n {op1.to_matrix()}\")" ] }, { "cell_type": "markdown", - "id": "f1a8f582-0735-4d3a-875d-aff0aa24747b", + "id": "4c4b1efe-4025-4b88-bbce-d0b3060e0ab1", "metadata": {}, "source": [ - "We can also extract just the input or output dimensions of a subset of subsystems using the `input_dims` and `output_dims` functions:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "8824780d-881f-4203-b4a5-38d383ce2f99", - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:03:02.187313Z", - "start_time": "2019-08-21T09:03:02.183719Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dimension of input system 0: (2,)\n", - "Dimension of input system 1: (3,)\n" - ] - } - ], - "source": [ - "print('Dimension of input system 0:', op.input_dims([0]))\n", - "print('Dimension of input system 1:', op.input_dims([1]))" + "`Pauli` objects possess a number of other methods to manipulate the operators such as determining its adjoint, if it (anti)commutes with another `Pauli`, and compute the dot product with another `Pauli`. Refer to the [api documentation](/api/qiskit/qiskit.quantum_info.Pauli) for more info." ] }, { "cell_type": "markdown", - "id": "83647e0f-6edf-4a13-9540-78acb578d17c", + "id": "0f02f8da-fab6-4ba6-b4ec-80d577cf8914", "metadata": {}, "source": [ - "## Converting classes to Operators\n", - "\n", - "Several other classes in Qiskit can be directly converted to an `Operator` object using the operator initialization method. For example:\n", + "## Operator\n", "\n", - "* `Pauli` objects\n", - "* `Gate` and `Instruction` objects\n", - "* `QuantumCircuit` objects\n", + "The [`Operator`](/api/qiskit/qiskit.quantum_info.Operator) class represents a general linear operator. Unlike `SparsePauliOp`, `Operator` stores the linear operator as a dense matrix. Because the memory required to store a dense matrix scales exponentially with the number of qubits, the `Operator` class is only suitable for use with a small number of qubits.\n", "\n", - "Note that the last point means we can use the `Operator` class as a unitary simulator to compute the final unitary matrix for a quantum circuit, without having to call a simulator backend. If the circuit contains any unsupported operations, an exception will be raised. Unsupported operations are: measure, reset, conditional operations, or a gate that does not have a matrix definition or decomposition in terms of gate with matrix definitions." + "You can initialize an `Operator` by directly passing a Numpy array storing the matrix of the operator. For example, the following code cell creates a two-qubit Pauli XX operator:" ] }, { "cell_type": "code", - "execution_count": 10, - "id": "c84c7f7d-292a-4507-a3de-b1b4156d8ee9", + "execution_count": 6, + "id": "f8d9d183-586c-44ac-aa93-317a2adb40ad", "metadata": { "ExecuteTime": { - "end_time": "2019-08-21T09:03:02.854419Z", - "start_time": "2019-08-21T09:03:02.842387Z" + "end_time": "2019-08-21T09:02:56.572857Z", + "start_time": "2019-08-21T09:02:56.566140Z" } }, "outputs": [ @@ -347,373 +231,104 @@ } ], "source": [ - "# Create an Operator from a Pauli object\n", - "\n", - "pauliXX = Pauli('XX')\n", - "Operator(pauliXX)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "08265ba9-db26-4573-9a36-2687f6d58f2c", - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:03:03.064145Z", - "start_time": "2019-08-21T09:03:03.058953Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],\n", - " [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n", - " [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j]],\n", - " input_dims=(2, 2), output_dims=(2, 2))\n" - ] - } - ], - "source": [ - "# Create an Operator for a Gate object\n", - "Operator(CXGate())" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "7b51c2df-14b2-430e-8319-5162420ebc4c", - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:03:03.353613Z", - "start_time": "2019-08-21T09:03:03.345462Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[0.70710678+0.j , 0. -0.70710678j],\n", - " [0. -0.70710678j, 0.70710678+0.j ]],\n", - " input_dims=(2,), output_dims=(2,))\n" - ] - } - ], - "source": [ - "# Create an operator from a parameterized Gate object\n", - "Operator(RXGate(np.pi / 2))" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "4d090045-7eef-4709-8ad3-272001de56f7", - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:03:47.550069Z", - "start_time": "2019-08-21T09:03:47.408126Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[ 0.70710678+0.j, 0.70710678+0.j, 0. +0.j, ...,\n", - " 0. +0.j, 0. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 0. +0.j, 0.70710678+0.j, ...,\n", - " 0. +0.j, 0. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 0. +0.j, 0. +0.j, ...,\n", - " 0. +0.j, 0. +0.j, 0. +0.j],\n", - " ...,\n", - " [ 0. +0.j, 0. +0.j, 0. +0.j, ...,\n", - " 0. +0.j, 0. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 0. +0.j, 0.70710678+0.j, ...,\n", - " 0. +0.j, 0. +0.j, 0. +0.j],\n", - " [ 0.70710678+0.j, -0.70710678+0.j, 0. +0.j, ...,\n", - " 0. +0.j, 0. +0.j, 0. +0.j]],\n", - " input_dims=(2, 2, 2, 2, 2, 2, 2, 2, 2, 2), output_dims=(2, 2, 2, 2, 2, 2, 2, 2, 2, 2))\n" - ] - } - ], - "source": [ - "# Create an operator from a QuantumCircuit object\n", - "circ = QuantumCircuit(10)\n", - "circ.h(0)\n", - "for j in range(1, 10):\n", - " circ.cx(j-1, j)\n", - "\n", - "# Convert circuit to an operator by implicit unitary simulation\n", - "Operator(circ)" + "XX = Operator(\n", + " np.array(\n", + " [\n", + " [0, 0, 0, 1],\n", + " [0, 0, 1, 0],\n", + " [0, 1, 0, 0],\n", + " [1, 0, 0, 0],\n", + " ]\n", + " )\n", + ")\n", + "XX" ] }, { "cell_type": "markdown", - "id": "5eb2fcf7-0a83-4fef-b08b-9d6e01c4226e", + "id": "4ded9878-c239-4e79-a60e-fff048c15784", "metadata": {}, "source": [ - "## Using Operators in circuits\n", - "\n", - "Unitary `Operators` can be directly inserted into a `QuantumCircuit` using the `QuantumCircuit.append` method. This converts the `Operator` into a `UnitaryGate` object, which is added to the circuit.\n", + "The operator object stores the underlying matrix, and the input and output dimension of subsystems.\n", "\n", - "If the operator is not unitary, an exception will be raised. This can be checked using the `Operator.is_unitary()` function, which will return `True` if the operator is unitary and `False` otherwise." + "* `data`: To access the underlying Numpy array, we may use the `Operator.data` property.\n", + "* `dims`: To return the total input and output dimension of the operator, we may use the `Operator.dim` property. *Note: the output is returned as a tuple* `(input_dim, output_dim)`, *which is the reverse of the shape of the underlying matrix.*" ] }, { "cell_type": "code", - "execution_count": 14, - "id": "ab1bebc7-5af1-4bae-bac5-a12558b8bd6f", + "execution_count": 7, + "id": "d9a6fe93-00a1-498f-9110-67b022db0a4f", "metadata": { "ExecuteTime": { - "end_time": "2019-08-21T09:03:49.196556Z", - "start_time": "2019-08-21T09:03:49.161398Z" - }, - "tags": [ - "nbsphinx-thumbnail" - ] - }, - "outputs": [ - { - "data": { - "image/svg+xml": [ - "" - ], - "text/plain": [ - "
" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" + "end_time": "2019-08-21T09:02:56.589962Z", + "start_time": "2019-08-21T09:02:56.585681Z" } - ], - "source": [ - "# Create an operator\n", - "XX = Operator(Pauli('XX'))\n", - "\n", - "# Add to a circuit\n", - "circ = QuantumCircuit(2, 2)\n", - "circ.append(XX, [0, 1])\n", - "circ.measure([0,1], [0,1])\n", - "circ.draw('mpl')" - ] - }, - { - "cell_type": "markdown", - "id": "de0ad8c9-74c6-4a3c-b59c-a9c96c902aa2", - "metadata": {}, - "source": [ - "Note that in the above example we initialize the operator from a `Pauli` object. However, the `Pauli` object may also be directly inserted into the circuit itself and will be converted into a sequence of single-qubit Pauli gates:" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "5fd28c29-3047-416d-bfad-af38f003433f", - "metadata": {}, + }, "outputs": [ { "data": { "text/plain": [ - "{'11': 1024}" + "array([[0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],\n", + " [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n", + " [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],\n", + " [1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]])" ] }, - "execution_count": 15, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n", - "backend = Aer.get_backend('qasm_simulator')\n", - "circ = generate_preset_pass_manager(optimization_level=1, backend=backend).run(circ)\n", - "job = backend.run(circ)\n", - "job.result().get_counts(0)" + "XX.data" ] }, { "cell_type": "code", - "execution_count": 16, - "id": "b697f771-d0e1-4b8f-b98c-82a19d8d3cfe", + "execution_count": 8, + "id": "784a657a-1731-4ecd-9bc0-aa0b96bc27e8", "metadata": { "ExecuteTime": { - "end_time": "2019-08-21T09:04:12.017240Z", - "start_time": "2019-08-21T09:04:11.989825Z" + "end_time": "2019-08-21T09:02:56.615497Z", + "start_time": "2019-08-21T09:02:56.611146Z" } }, "outputs": [ { "data": { - "text/html": [ - "
     ┌────────────┐┌─┐   \n",
-       "q_0: ┤0           ├┤M├───\n",
-       "     │  Pauli(XX) │└╥┘┌─┐\n",
-       "q_1: ┤1           ├─╫─┤M├\n",
-       "     └────────────┘ ║ └╥┘\n",
-       "c: 2/═══════════════╩══╩═\n",
-       "                    0  1 
" - ], "text/plain": [ - " ┌────────────┐┌─┐ \n", - "q_0: ┤0 ├┤M├───\n", - " │ Pauli(XX) │└╥┘┌─┐\n", - "q_1: ┤1 ├─╫─┤M├\n", - " └────────────┘ ║ └╥┘\n", - "c: 2/═══════════════╩══╩═\n", - " 0 1 " + "(4, 4)" ] }, - "execution_count": 16, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "# Add to a circuit\n", - "circ2 = QuantumCircuit(2, 2)\n", - "circ2.append(Pauli('XX'), [0, 1])\n", - "circ2.measure([0,1], [0,1])\n", - "circ2.draw()" - ] - }, - { - "cell_type": "markdown", - "id": "682526da-17a3-40c1-8c02-cb1deb083198", - "metadata": {}, - "source": [ - "## Combining Operators\n", - "\n", - "Operators may be combined using several methods.\n", - "\n", - "### Tensor Product\n", - "\n", - "Two operators $A$ and $B$ may be combined into a tensor product operator $A\\otimes B$ using the `Operator.tensor` function. Note that if both $A$ and $B$ are single-qubit operators, then `A.tensor(B)` = $A\\otimes B$ will have the subsystems indexed as matrix $B$ on subsystem 0, and matrix $A$ on subsystem 1." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "fc397a0b-52a9-41da-85c2-e465749be4a5", - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:14.208734Z", - "start_time": "2019-08-21T09:04:14.201058Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[ 0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n", - " [ 0.+0.j, -0.+0.j, 0.+0.j, -1.+0.j],\n", - " [ 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [ 0.+0.j, -1.+0.j, 0.+0.j, -0.+0.j]],\n", - " input_dims=(2, 2), output_dims=(2, 2))\n" - ] - } - ], - "source": [ - "A = Operator(Pauli('X'))\n", - "B = Operator(Pauli('Z'))\n", - "A.tensor(B)" - ] - }, - { - "cell_type": "markdown", - "id": "97285327-6adc-43c3-b858-d2baae6fe258", - "metadata": {}, - "source": [ - "### Tensor Expansion\n", - "\n", - "A closely related operation is `Operator.expand`, which acts like a tensor product but in the reverse order. Hence, for two operators $A$ and $B$ we have `A.expand(B)` = $B\\otimes A$ where the subsystems indexed as matrix $A$ on subsystem 0, and matrix $B$ on subsystem 1." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "64ca5769-56ad-49d5-a95a-eb0054739dab", - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:14.899024Z", - "start_time": "2019-08-21T09:04:14.891072Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[ 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],\n", - " [ 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [ 0.+0.j, 0.+0.j, -0.+0.j, -1.+0.j],\n", - " [ 0.+0.j, 0.+0.j, -1.+0.j, -0.+0.j]],\n", - " input_dims=(2, 2), output_dims=(2, 2))\n" - ] - } - ], - "source": [ - "A = Operator(Pauli('X'))\n", - "B = Operator(Pauli('Z'))\n", - "A.expand(B)" + "input_dim, output_dim = XX.dim\n", + "input_dim, output_dim" ] }, { "cell_type": "markdown", - "id": "98bd95da-6e31-4805-8ece-dd87b0d60020", + "id": "0f84bf31-ce41-454a-9e2c-11c0ec4a04dc", "metadata": {}, "source": [ - "### Composition\n", + "The operator class also keeps track of subsystem dimensions, which can be used for composing operators together. These can be accessed using the `input_dims` and `output_dims` functions.\n", "\n", - "We can also compose two operators $A$ and $B$ to implement matrix multiplication using the `Operator.compose` method. We have that `A.compose(B)` returns the operator with matrix $B.A$:" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "id": "bcf18f94-fe96-44f0-b455-bb42e2962a1f", - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:15.655155Z", - "start_time": "2019-08-21T09:04:15.648295Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[ 0.+0.j, 1.+0.j],\n", - " [-1.+0.j, 0.+0.j]],\n", - " input_dims=(2,), output_dims=(2,))\n" - ] - } - ], - "source": [ - "A = Operator(Pauli('X'))\n", - "B = Operator(Pauli('Z'))\n", - "A.compose(B)" - ] - }, - { - "cell_type": "markdown", - "id": "5f16ace4-045a-47f5-bb1e-6ae45833fffc", - "metadata": {}, - "source": [ - "We can also compose in the reverse order by applying $B$ in front of $A$ using the `front` kwarg of `compose`: `A.compose(B, front=True)` = $A.B$:" + "For $2^N$ by $2^M$ operators, the input and output dimension will be automatically assumed to be M-qubit and N-qubit:" ] }, { "cell_type": "code", - "execution_count": 20, - "id": "ccdf52a6-0cd9-4a50-af3d-0350e7db7425", + "execution_count": 9, + "id": "b4576985-9ae2-46f0-a29d-9ca3bea53bf0", "metadata": { "ExecuteTime": { - "end_time": "2019-08-21T09:04:16.460560Z", - "start_time": "2019-08-21T09:04:16.452319Z" + "end_time": "2019-08-21T09:02:56.804167Z", + "start_time": "2019-08-21T09:02:56.798857Z" } }, "outputs": [ @@ -721,82 +336,33 @@ "name": "stdout", "output_type": "stream", "text": [ - "Operator([[ 0.+0.j, -1.+0.j],\n", - " [ 1.+0.j, 0.+0.j]],\n", - " input_dims=(2,), output_dims=(2,))\n" + "Input dimensions: (2, 2)\n", + "Output dimensions: (2,)\n" ] } ], "source": [ - "A = Operator(Pauli('X'))\n", - "B = Operator(Pauli('Z'))\n", - "A.compose(B, front=True)" + "op = Operator(np.random.rand(2**1, 2**2))\n", + "print(\"Input dimensions:\", op.input_dims())\n", + "print(\"Output dimensions:\", op.output_dims())" ] }, { "cell_type": "markdown", - "id": "48ba1e74-6ccc-4981-a2aa-58cf1ee50840", + "id": "9bae9b8c-b7f5-4e1e-ad8b-4482f19af531", "metadata": {}, "source": [ - "### Subsystem Composition\n", - "\n", - "Note that the previous compose requires that the total output dimension of the first operator $A$ is equal to total input dimension of the composed operator $B$ (and similarly, the output dimension of $B$ must be equal to the input dimension of $A$ when composing with `front=True`).\n", - "\n", - "We can also compose a smaller operator with a selection of subsystems on a larger operator using the `qargs` kwarg of `compose`, either with or without `front=True`. In this case, the relevant input and output dimensions of the subsystems being composed must match. *Note that the smaller operator must always be the argument of* `compose` *method.*\n", - "\n", - "For example, to compose a two-qubit gate with a three-qubit Operator:" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "1fa1ebb3-c56d-49cf-9f16-6baf1a81bc56", - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:17.113510Z", - "start_time": "2019-08-21T09:04:17.105398Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[ 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j,\n", - " 0.+0.j],\n", - " [ 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, -1.+0.j, 0.+0.j,\n", - " 0.+0.j],\n", - " [ 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j,\n", - " 0.+0.j],\n", - " [ 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,\n", - " -1.+0.j],\n", - " [ 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,\n", - " 0.+0.j],\n", - " [ 0.+0.j, -1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,\n", - " 0.+0.j],\n", - " [ 0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,\n", - " 0.+0.j],\n", - " [ 0.+0.j, 0.+0.j, 0.+0.j, -1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,\n", - " 0.+0.j]],\n", - " input_dims=(2, 2, 2), output_dims=(2, 2, 2))\n" - ] - } - ], - "source": [ - "# Compose XZ with a 3-qubit identity operator\n", - "op = Operator(np.eye(2 ** 3))\n", - "XZ = Operator(Pauli('XZ'))\n", - "op.compose(XZ, qargs=[0, 2])" + "If the input matrix is not divisible into qubit subsystems, then it will be stored as a single-qubit operator. For example, if we have a $6\\times6$ matrix:" ] }, { "cell_type": "code", - "execution_count": 22, - "id": "1fef1d8c-b331-4d32-8bf3-6834da726cf4", + "execution_count": 10, + "id": "186c1ac5-0bf2-4a86-84d8-bffa3f1763a1", "metadata": { "ExecuteTime": { - "end_time": "2019-08-21T09:04:17.324353Z", - "start_time": "2019-08-21T09:04:17.315952Z" + "end_time": "2019-08-21T09:02:57.764881Z", + "start_time": "2019-08-21T09:02:57.760401Z" } }, "outputs": [ @@ -804,43 +370,33 @@ "name": "stdout", "output_type": "stream", "text": [ - "Operator([[0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j],\n", - " [0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]],\n", - " input_dims=(2, 2, 2), output_dims=(2, 2, 2))\n" + "Input dimensions: (6,)\n", + "Output dimensions: (6,)\n" ] } ], "source": [ - "# Compose YX in front of the previous operator\n", - "op = Operator(np.eye(2 ** 3))\n", - "YX = Operator(Pauli('YX'))\n", - "op.compose(YX, qargs=[0, 2], front=True)" + "op = Operator(np.random.rand(6, 6))\n", + "print(\"Input dimensions:\", op.input_dims())\n", + "print(\"Output dimensions:\", op.output_dims())" ] }, { "cell_type": "markdown", - "id": "64cd39c4-1927-4d7f-acb9-4155afc22cfb", + "id": "e813709d-4ac3-4abc-863f-9d5d262ba753", "metadata": {}, "source": [ - "### Linear combinations\n", - "\n", - "Operators may also be combined using standard linear operators for addition, subtraction and scalar multiplication by complex numbers." + "The input and output dimension can also be manually specified when initializing a new operator:" ] }, { "cell_type": "code", - "execution_count": 23, - "id": "c32d348b-02d7-4c84-91e7-021e8fe5a54e", + "execution_count": 11, + "id": "e14faef6-3b05-4447-9960-3b140ddff1a1", "metadata": { "ExecuteTime": { - "end_time": "2019-08-21T09:04:18.829988Z", - "start_time": "2019-08-21T09:04:18.812834Z" + "end_time": "2019-08-21T09:02:58.292849Z", + "start_time": "2019-08-21T09:02:58.287354Z" } }, "outputs": [ @@ -848,75 +404,26 @@ "name": "stdout", "output_type": "stream", "text": [ - "Operator([[-1.5+0.j, 0. +0.j, 0. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 1.5+0.j, 1. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 1. +0.j, 1.5+0.j, 0. +0.j],\n", - " [ 0. +0.j, 0. +0.j, 0. +0.j, -1.5+0.j]],\n", - " input_dims=(2, 2), output_dims=(2, 2))\n" + "Input dimensions: (4,)\n", + "Output dimensions: (2,)\n" ] } ], "source": [ - "XX = Operator(Pauli('XX'))\n", - "YY = Operator(Pauli('YY'))\n", - "ZZ = Operator(Pauli('ZZ'))\n", - "\n", - "op = 0.5 * (XX + YY - 3 * ZZ)\n", - "op" - ] - }, - { - "cell_type": "markdown", - "id": "e9089a49-a346-420e-a2dc-37024498c8b4", - "metadata": {}, - "source": [ - "An important point is that while `tensor`, `expand` and `compose` will preserve the unitarity of unitary operators, linear combinations will not; hence, adding two unitary operators will, in general, result in a non-unitary operator:" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "e407a9bf-0429-4528-8ade-c37fac1371fc", - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:19.151814Z", - "start_time": "2019-08-21T09:04:19.147497Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "op.is_unitary()" - ] - }, - { - "cell_type": "markdown", - "id": "85d84c97-c596-4696-9563-209ef3524654", - "metadata": {}, - "source": [ - "### Implicit Conversion to Operators\n", - "\n", - "Note that for all the following methods, if the second object is not already an `Operator` object, it will be implicitly converted into one by the method. This means that matrices can be passed in directly without being explicitly converted to an `Operator` first. If the conversion is not possible, an exception will be raised." + "# Force input dimension to be (4,) rather than (2, 2)\n", + "op = Operator(np.random.rand(2**1, 2**2), input_dims=[4])\n", + "print(\"Input dimensions:\", op.input_dims())\n", + "print(\"Output dimensions:\", op.output_dims())" ] }, { "cell_type": "code", - "execution_count": 25, - "id": "db28f1c8-899c-48c2-8db3-02c7cd41a2c4", + "execution_count": 12, + "id": "41845475-f6e9-4057-b21e-89cf1a62f672", "metadata": { "ExecuteTime": { - "end_time": "2019-08-21T09:04:20.045005Z", - "start_time": "2019-08-21T09:04:20.039841Z" + "end_time": "2019-08-21T09:02:58.779572Z", + "start_time": "2019-08-21T09:02:58.774878Z" } }, "outputs": [ @@ -924,105 +431,34 @@ "name": "stdout", "output_type": "stream", "text": [ - "Operator([[0.+0.j, 1.+0.j],\n", - " [1.+0.j, 0.+0.j]],\n", - " input_dims=(2,), output_dims=(2,))\n" + "Input dimensions: (2, 3)\n", + "Output dimensions: (2, 3)\n" ] } ], "source": [ - "# Compose with a matrix passed as a list\n", - "Operator(np.eye(2)).compose([[0, 1], [1, 0]])" - ] - }, - { - "cell_type": "markdown", - "id": "70e3d5ef-d23b-4c22-b8aa-c910216085e0", - "metadata": {}, - "source": [ - "## Comparison of Operators\n", - "\n", - "Operators implement an equality method that can be used to check if two operators are approximately equal." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "id": "7da2a904-fa1d-4c06-9a6a-0e3c2595827c", - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:20.821642Z", - "start_time": "2019-08-21T09:04:20.815611Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "Operator(Pauli('X')) == Operator(XGate())" - ] - }, - { - "cell_type": "markdown", - "id": "60d461e1-40eb-4043-9035-33f303b714f5", - "metadata": {}, - "source": [ - "Note that this checks that each matrix element of the operators is approximately equal; two unitaries that differ by a global phase will not be considered equal:" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "id": "4161780a-64ab-4f86-9526-62303a681839", - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:21.146256Z", - "start_time": "2019-08-21T09:04:21.141242Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "Operator(XGate()) == np.exp(1j * 0.5) * Operator(XGate())" + "# Specify system is a qubit and qutrit\n", + "op = Operator(np.random.rand(6, 6), input_dims=[2, 3], output_dims=[2, 3])\n", + "print(\"Input dimensions:\", op.input_dims())\n", + "print(\"Output dimensions:\", op.output_dims())" ] }, { "cell_type": "markdown", - "id": "0eb8a6cf-492d-43d1-908f-379980bb64e4", + "id": "1ed55128-5b97-4733-ac0f-6b762e48691b", "metadata": {}, "source": [ - "### Process Fidelity\n", - "\n", - "We may also compare operators using the `process_fidelity` function from the *Quantum Information* module. This is an information theoretic quantity for how close two quantum channels are to each other, and in the case of unitary operators it does not depend on global phase." + "We can also extract just the input or output dimensions of a subset of subsystems using the `input_dims` and `output_dims` functions:" ] }, { "cell_type": "code", - "execution_count": 28, - "id": "b75e9c20-5596-4697-bb0d-25874cf6ffb0", + "execution_count": 13, + "id": "c9fe7460-8e93-47aa-acc1-425bfe797c17", "metadata": { "ExecuteTime": { - "end_time": "2019-08-21T09:04:22.171481Z", - "start_time": "2019-08-21T09:04:22.147477Z" + "end_time": "2019-08-21T09:03:02.187313Z", + "start_time": "2019-08-21T09:03:02.183719Z" } }, "outputs": [ @@ -1030,38 +466,28 @@ "name": "stdout", "output_type": "stream", "text": [ - "Process fidelity = 1.0\n" + "Dimension of input system 0: (2,)\n", + "Dimension of input system 1: (3,)\n" ] } ], "source": [ - "# Two operators which differ only by phase\n", - "op_a = Operator(XGate())\n", - "op_b = np.exp(1j * 0.5) * Operator(XGate())\n", - "\n", - "# Compute process fidelity\n", - "F = process_fidelity(op_a, op_b)\n", - "print('Process fidelity =', F)" - ] - }, - { - "cell_type": "markdown", - "id": "4452e087-eb5b-4630-8732-76cba1fec582", - "metadata": {}, - "source": [ - "Note that process fidelity is generally only a valid measure of closeness if the input operators are unitary (or CP in the case of quantum channels), and an exception will be raised if the inputs are not CP." + "print(\"Dimension of input system 0:\", op.input_dims([0]))\n", + "print(\"Dimension of input system 1:\", op.input_dims([1]))" ] }, { "cell_type": "markdown", - "id": "58ba5dc7-05d5-49a2-a5a9-6d6102c75bd9", + "id": "3e8538f5-77d4-4683-838f-abff122cd3f7", "metadata": {}, "source": [ "## Next steps\n", "\n", "\n", - " - See an example of using operators in the [Grover's Algorithm](https://learning.quantum.ibm.com/tutorial/grovers-algorithm) tutorial.\n", - " - Explore the [Operator API](/api/qiskit/qiskit.quantum_info.Operator#operator) reference.\n", + " - Learn how to [specify observables in the Pauli basis](/build/specify-observables-pauli)\n", + " - See an example of using operators in the [Combine error mitigation options with the estimator primitive](https://learning.quantum.ibm.com/tutorial/combine-error-mitigation-options-with-the-estimator-primitive) tutorial.\n", + " - Read more [in-depth coverage of the Operator class](/build/operator-class)\n", + " - Explore the [Operator API](/api/qiskit/qiskit.quantum_info.Operator#operator) reference.\n", "" ] } diff --git a/qiskit_bot.yaml b/qiskit_bot.yaml index 9dec54e4ca7..1a163f9aaf1 100644 --- a/qiskit_bot.yaml +++ b/qiskit_bot.yaml @@ -55,6 +55,8 @@ notifications: - "@Cryoris" "docs/build/operators_overview": - "`@mtreinish`" + "docs/build/operator-class": + - "`@mtreinish`" "docs/build/pulse": - "`@nkanazawa1989`" - "@abbycross" diff --git a/scripts/nb-tester/notebooks.toml b/scripts/nb-tester/notebooks.toml index 0f4aaf00ae1..efdc302c913 100644 --- a/scripts/nb-tester/notebooks.toml +++ b/scripts/nb-tester/notebooks.toml @@ -4,6 +4,7 @@ notebooks_normal_test = [ "docs/build/circuit-library.ipynb", "docs/build/circuit-visualization.ipynb", "docs/build/classical-feedforward-and-control-flow.ipynb", + "docs/build/operator-class.ipynb", "docs/build/operators-overview.ipynb", "docs/build/pulse.ipynb", "docs/build/save-circuits.ipynb",