From 61ecadb84720267e6f0cc8dfe08ef295f5fd7f31 Mon Sep 17 00:00:00 2001 From: Donny Greenberg Date: Thu, 23 Jan 2020 16:44:15 -0500 Subject: [PATCH 1/4] Move Operator redesign doc from Aqua branch. --- ####-0.7_operator_refactor.ipynb | 1233 ++++++++++++++++++++++++++++++ 1 file changed, 1233 insertions(+) create mode 100644 ####-0.7_operator_refactor.ipynb diff --git a/####-0.7_operator_refactor.ipynb b/####-0.7_operator_refactor.ipynb new file mode 100644 index 0000000..6c4fd22 --- /dev/null +++ b/####-0.7_operator_refactor.ipynb @@ -0,0 +1,1233 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Aqua 0.7 Operator Redesign\n", + "_17-Jan-19, donny@_" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Purpose\n", + "To improve the transparency, ease of understanding, and programming power of Aqua’s operator logic and usage. Specifically, to reconcile with the Terra operator hierarchy and make the Aqua algorithmic flow more visible, explicit, and extensible.\n", + "\n", + "Throughout this doc, we rely on definitions of Operators roughly derived from the first chapter of John Watrous's \"The Theory of Quantum Information,\" with a focus on Square Operators over binary alphabets." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Background: Motivation and Opportunities\n", + "The representation of matrices sparsely as linear combinations of Pauli operators is critical in many quantum algorithms. As such, the Operator classes are the workhorses of Aqua today (0.6.2), containing both the expectation value and evolution logic used by most of its algorithms.\n", + "\n", + "However, there are several opportunities for improvement:\n", + "* **Basic Construction & Rapid Protoyping:** Aqua's Operators were initially built as procedural infrastructure rather than first-class programming primitives. Improvements to syntax and interfaces can enable the succinctness and power typical of mathematical Operator language\n", + "* **Separation of Operator Math and Operator Algorithms**\n", + " * Ease of understanding: The \"Operator algorithm\" logic - the ExpectationValue, Evolution, grouping, and symmetry analysis - is mostly spread across the 3000-line operator hierarchy, and is very branchy for different modes of execution\n", + " * Ease of extension: Modification to the expectation value, evolution, grouping, and symmetry logic is a core use case (e.g. the [CVaR expectation](https://arxiv.org/abs/1907.04769), [linear combination evolution](https://arxiv.org/abs/1202.5822), or the many recent papers on [Pauli grouping](https://www.nature.com/articles/nature23879)), but not explicitly supported today\n", + "* **Smooth Borders with Broader Qiskit**\n", + " * Terra's `quantum_info` module also supports operator math, but is mostly matrix-based\n", + " * **Remote Operator Algorithms:** Aer's fast ExpectationValue is not transparently or cleanly interchangeable with Aqua's local ExpectationValue today. The concept of an Algorithm not provided by Aqua is not yet defined to support this type of interchangeability cleanly" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Present State of Operators in Qiskit\n", + "\n", + "Both Aqua and Terra include suites of modules to support Operator math, but do so very differently.\n", + "\n", + "* Aqua\n", + " * Operators are focused primarily on the procedural requirements of algorithmic execution\n", + " * Modules are very large and include hundreds of lines of procedural algorithm code\n", + " * Interfaces were not initial built for end-user usage as a programming primitive, and are therefore wordy and difficult for users to understand\n", + " * Syntax is not built for rapid prototyping and lacks syntactic power of mathematical Operator language\n", + " * Primarily focused on Pauli-basis Operators\n", + " * WeightedPauli - $2^n\\times 2^n$ Operators sparsely represented as complex combination of Paulis\n", + " * MatrixOperator in the standard basis with $2^n\\times 2^n$ elements was initially built for performance improvements which are no longer relevant\n", + " * Only dependency on Terra is through Pauli module, but this is largely symbolic (not an inexorable component)\n", + "* Terra\n", + " * Operator math is mostly built around QCVV (Quantum Characterization Verification & Validation) and open Quantum systems modelling use cases\n", + " * Support for Channel, Choi, Superoperator, Kraus, etc.\n", + " * Operators are largely matrix-based and therefore do not support the Pauli-basis operations necessary to non-exponentially execute quantum algorithms\n", + " * Used by: \n", + " * Aqua, 29 dependencies - Only Pauli module\n", + " * Aer, 10 dependencies\n", + " * Ignis, 2 dependencies\n", + "* Ignis includes a `clifford.py` module somewhat specific to characterization needs." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Aqua Present Usage (0.6.2)\n", + "\n", + "Within Aqua, the primary uses of Operators are:\n", + "* Qubit Observable (Hamiltonian, Cost Function, etc.) Construction\n", + " * Used as sparse representations of large observables when constructing problems in Chemistry, Physics, Optimization, and Finance today\n", + " * Also often a translation step between domain-specific problems and Quantum hardware-addressable equivalents\n", + "* ExpectationValues\n", + " * Primarily used in VQE (and derivatives QAOA, UCCSD, etc.) as a device-executable cost function of the ansatz state\n", + " * Expectation values can only be taken of Operators in the Pauli basis on Quantum hardware\n", + " * Also present in the \"Evolution of Hamiltonian\" algorithm, which is simply state evolution by one operator followed by an expectation value by another operator\n", + "* State Evolution\n", + " * Used in QPE (and derivatives HHL, iQPE, etc.) as a Quantum circuit-representable matrix exponentiation\n", + " * Used in UCCSD and QAOA ansatze and EOH algorithm as representation of system dynamics to simulate time evolution of a system on quantum hardware\n", + " * Evolution can only be taken by Operators in the Pauli basis on Quantum hardware" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Other Important Aqua Operator Features\n", + "\n", + "* __Grouping__ - Grouping is a technique to reduce the number of circuit evaluations required to compute an ExpectationValue based on mutually commuting Paulis in the Operator decomposition.\n", + "* __Tapering__ - Tapering is a technique to remove qubits from a Hamiltonian of interest by identifying Z2 symmetries in the Hamiltonian.\n", + "* __Gradients__ - Many variational algorithms are improved dramatically when exact gradients of gate parameters with respect to the cost function observable are computed analytically rather than numerically. Aqua can compute these gradients and provide them to the optimizer directly." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Aqua Present (0.6.2) Operator Object Model and Hierarchy\n", + "\n", + "Aqua's Operators are organized as follows:\n", + "* `qiskit.aqua.operators`\n", + " * base_operator.py: `BaseOperator(ABC)`\n", + " * matrix_operator.py: `MatrixOperator(BaseOperator)`\n", + " * weighted_pauli_operator.py: `WeightedPauliOperator(BaseOperator)`, __and__ `Z2Symmetries`\n", + " * tpb_grouped_weighted_pauli_operator.py: `TPBGroupedWeightedPauliOperator(WeightedPauliOperator)`, essentially a wrapper around `WeightedPauliOperator` for backward compatibility.\n", + " * pauli_graph: `PauliGraph`\n", + " * op_converter.py: `to_weighted_pauli_operator(operator)`, `to_matrix_operator(operator)`, `to_tpb_grouped_weighted_pauli_operator(operator, grouping_func, **kwargs)`\n", + " * common.py: Utility functions, inc. `evolution_instruction`, `pauli_measurement(circuit, pauli, qr, cr, barrier=False)`, `measure_pauli_z(data, pauli)`, `covariance(data, pauli_1, pauli_2, avg_1, avg_2)`, etc.\n", + "* `qiskit.chemistry` __- OUT OF SCOPE OF THIS DOC__\n", + " * fermionic_operator.py: `FermionicOperator`, contains `jordan_wigner`, `parity`, `bravyi_kitaev` Fermion-to-qubit operator mappings.\n", + " * bksf.py: Another mapping\n", + " * `.core`\n", + " * chemistry_operator.py: `ChemistryOperator(ABC)`\n", + " * hamiltonian.py: `Hamiltonian(ChemistryOperator)`" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Terra Present (0.11.0) Operator Object Model and Hierarchy\n", + "\n", + "Terra's Operators are organized as follows:\n", + "* `qiskit.quantum_info`\n", + " * `.operators`\n", + " * base_operator.py, pauli.py, operator.py (matrix operator), measures.py (`process_fidelity`), predicates.py (`is_unitary_matrix`, `is_hermitian_matrix`, `matrix_equal`, etc.), quaternion.py\n", + " * `.channel`\n", + " * quantum_channel.py (base), chi.py, choi.py, kraus.py, ptm.py, stinespring.py, superop.py, transformations.py\n", + " * `.states`\n", + " * quantum_state.py (base), densitymatrix.py, statevector.py, measures.py (`state_fidelity`), states.py (`basis_state`, `projector`, `purity`)\n", + " * `.analysis`\n", + " * average.py - ExpectationValue of diagonal operator\n", + " * make_observable.py - Convert an observable in matrix form to dictionary form\n", + " \n", + "#### WeightedPauliOperator Not Available in Terra\n", + "\n", + "Terra does not contain any of the logic for working in the Pauli-basis implemented in Aqua today, and is not interoptable with Aqua's operator algorithms. As such, these utilities are only accessible to Aqua users." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Operator Construction and Manipulation Present State\n", + "\n", + "The center of Qiskit's algorithmic Operator logic is the WeightedPauli, being the only non-exponential scaling operator basis available today (the only other being the standard basis).\n", + "\n", + "Qiskit supports several methods of WeightedPauli operator construction, none of which are self explanatory to a new user:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2020-01-17T17:33:32.562434Z", + "start_time": "2020-01-17T17:33:26.186996Z" + }, + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "# from qiskit.quantum_info.operators import WeightedPauliOperator\n", + "from qiskit.aqua.operators import WeightedPauliOperator, MatrixOperator, op_converter\n", + "from qiskit.quantum_info.operators import Pauli" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2020-01-17T17:33:32.568447Z", + "start_time": "2020-01-17T17:33:32.564898Z" + } + }, + "outputs": [], + "source": [ + "pauli_op = WeightedPauliOperator([\n", + " [.5, Pauli.from_label('IX')],\n", + " [.2, Pauli.from_label('ZY')],\n", + " [.1j, Pauli.from_label('ZZ')],\n", + "])" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2020-01-02T19:47:24.277537Z", + "start_time": "2020-01-02T19:47:24.273779Z" + } + }, + "outputs": [], + "source": [ + "pauli_op = WeightedPauliOperator.from_list(\n", + " paulis=[Pauli.from_label('IX'),\n", + " Pauli.from_label('ZY'),\n", + " Pauli.from_label('ZZ')],\n", + " weights=[.5, .2, .1j])" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2020-01-02T19:47:25.369049Z", + "start_time": "2020-01-02T19:47:25.210110Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mat = [[0. +0.1j, 0.5-0.2j, 0. +0.j , 0. +0.j ],\n", + " [0.5+0.2j, 0. -0.1j, 0. +0.j , 0. +0.j ],\n", + " [0. +0.j , 0. +0.j , 0. -0.1j, 0.5+0.2j],\n", + " [0. +0.j , 0. +0.j , 0.5-0.2j, 0. +0.1j]]\n", + "mat_op = MatrixOperator(mat)\n", + "pauli_op_from_mat = op_converter.to_weighted_pauli_operator(mat_op)\n", + "pauli_op == pauli_op_from_mat" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Classical matrices can be exported for classical usage, again if the user already knows the Operator hierarchy somewhat well:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2020-01-02T19:48:03.232154Z", + "start_time": "2020-01-02T19:48:03.223098Z" + }, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[0. +0.1j, 0.5-0.2j, 0. +0.j , 0. +0.j ],\n", + " [0.5+0.2j, 0. -0.1j, 0. +0.j , 0. +0.j ],\n", + " [0. +0.j , 0. +0.j , 0. -0.1j, 0.5+0.2j],\n", + " [0. +0.j , 0. +0.j , 0.5-0.2j, 0. +0.1j]])" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "op_converter.to_matrix_operator(pauli_op).matrix.toarray()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Composition uses the `*` operator, while Terra's operators and Python use `@`." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "ExecuteTime": { + "end_time": "2020-01-02T19:53:55.013620Z", + "start_time": "2020-01-02T19:53:55.009046Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "3*pauli_op + .2j*pauli_op == (3+.2j)*pauli_op" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "ExecuteTime": { + "end_time": "2020-01-02T19:59:04.863490Z", + "start_time": "2020-01-02T19:59:04.858997Z" + }, + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "II\t(0.28+0j)\n", + "ZZ\t0j\n", + "ZY\t0j\n", + "IX\t0j\n", + "\n" + ] + } + ], + "source": [ + "print((pauli_op * pauli_op).print_details())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Aqua's ExpectationValue is Procedural and Inextensible\n", + "\n", + "Aqua's ExpectationValue is not contained within a single function or module, but rather split into several functions without a clear interface or flow for user usage. This is due to structural constraints in Aqua which are no longer present, where the algorithm requiring the expectation value held the backend object and could run circuits, but the operator could not. We encourage the reader to scan lines [361-395 of Aqua 6.1 VQE’s](https://github.com/Qiskit/qiskit-aqua/blob/stable/qiskit/aqua/algorithms/adaptive/vqe/vqe.py#L361) ExpectationValue calculation to try to understand where and how the expectation is computed. We’ve been asked by numerous Aqua users to explain how this code works, and most do not attempt to use it on their own.\n", + "\n", + "The following is the shortest possible way to write an expectation value in Aqua. Note that it fundamentally requires the user to understand a certain execution flow, the correct functions to use to do this, and how those functions work with their execution mode. This takes a few hours to understand at least, often days. Further, there are no hints that a change from the Z basis for each Pauli is being performed here, or matrix multiplication if the system chooses to do that instead." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2019-12-26T21:34:45.903765Z", + "start_time": "2019-12-26T21:34:45.899854Z" + }, + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "from qiskit.aqua.operators import WeightedPauliOperator\n", + "from qiskit.aqua.components.variational_forms import RY\n", + "from qiskit.quantum_info import Pauli\n", + "from qiskit import BasicAer, execute, QuantumCircuit\n", + "from qiskit.circuit import Parameter\n", + "qasm_sim = BasicAer.get_backend('qasm_simulator')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2019-12-26T20:03:09.762509Z", + "start_time": "2019-12-26T20:03:09.452240Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(0.5+0.005078125j)" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "op = WeightedPauliOperator([\n", + " [.5, Pauli.from_label('IX')],\n", + " [.2j, Pauli.from_label('ZY')],\n", + "])\n", + "circuit = QuantumCircuit(2)\n", + "circuit.h([0,1])\n", + "\n", + "evaluation_circuits = op.construct_evaluation_circuit(wave_function=circuit, statevector_mode=False)\n", + "result = execute(evaluation_circuits, qasm_sim).result()\n", + "expect, std = op.evaluate_with_result(result=result, statevector_mode=False)\n", + "expect" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Alternative Expectation Values and the Aer Expectation Value\n", + "\n", + "Because the ExpectationValue logic is embedded directly in the Operator, modifications to the ExpectationValue (e.g. CVaR) are impossible without editing the Operator directly with heavy branching or duplicating the entire Operator. This branching is already in effect within Aqua, automatically choosing between several execution modes mostly opaquely to the user. This is also the case for grouping, evolution, and symmetry logic.\n", + "\n", + "The most dramatic example of this is the Aer-provided fast ExpectationValue simulation, which is so buried into the Operator it is effectively a super-superuser feature today. It was introduced quickly to achieve critical performance gains, but must be formalized to become a true first-class feature.\n", + "* In Aqua, there is no simple way to specify which ExpectationValue algorithm the user wants, Aer or otherwise, and most users do not know that the Aer Expectation Exists\n", + "* Aer's ExpectationValue is woven throughout the core operator code in a way that is branchy, inexorable, and difficult for users to understand and control\n", + "* A new ExpectationValue, such as one provided by BasicAer or IBMQProvider, would simply introduce additional branches following the existing style" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Aqua's State Evolution is Inextensible and Difficult to Navigate\n", + "\n", + "Evolution is somewhat more succinct, but more difficult to navigate in code. The logic for evolution is distributed over several branchy static modules, and the evolution is pre-compiled as a CNOT-chain circuit, which is often not the ideal evaluation format (e.g. matrix multiplication if simulating, or Swap Networks)." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2019-12-26T20:03:23.794305Z", + "start_time": "2019-12-26T20:03:23.775959Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ┌─────────────────┐\n", + "q_0: |0>┤0 ├\n", + " │ Evolution^1(θ) │\n", + "q_1: |0>┤1 ├\n", + " └─────────────────┘\n", + "Decomposed:\n" + ] + }, + { + "data": { + "text/html": [ + "
        ┌─────────────────────┐                       ┌──────────────────────┐┌──────────┐┌───────────┐┌──────────┐\n",
+       "q_0: |0>┤ U3(pi/2,-pi/2,pi/2) ├──■─────────────────■──┤ U3(-pi/2,-pi/2,pi/2) ├┤ U2(0,pi) ├┤ U1(1.0*θ) ├┤ U2(0,pi) ├\n",
+       "        └─────────────────────┘┌─┴─┐┌───────────┐┌─┴─┐└──────────────────────┘└──────────┘└───────────┘└──────────┘\n",
+       "q_1: |0>───────────────────────┤ X ├┤ U1(0.4*θ) ├┤ X ├─────────────────────────────────────────────────────────────\n",
+       "                               └───┘└───────────┘└───┘                                                             
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit.circuit import Parameter\n", + "\n", + "op = WeightedPauliOperator([\n", + " [.5, Pauli.from_label('IX')],\n", + " [.2, Pauli.from_label('ZY')],\n", + "])\n", + "circuit = QuantumCircuit(2)\n", + "\n", + "θ = Parameter('θ')\n", + "instr = op.evolve_instruction(evo_time=θ)\n", + "circuit.append(instr, [0,1])\n", + "print(circuit.draw(fold=4000))\n", + "print('Decomposed:')\n", + "circuit.decompose().draw(fold=4000)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## Requirements and Design\n", + "\n", + "1. Location and Ownership\n", + " 1. Operators\n", + " 1. Provider-specific Algorithms\n", + "1. Object Model\n", + " 1. Operator Definition - Primitives and Composites\n", + " 1. Algorithms Definition - Primitives and Composite Operations\n", + " 1. Parameterization and Eagerness\n", + "1. Changes to Terra\n", + "1. Changes to Aqua\n", + " 1. Algorithms as composite Operations\n", + " 1. Circuit Execution Algorithms\n", + " 1. Expectation Algorithms\n", + " 1. Evolution Algorithms\n", + " 1. Other Primitive Algorithms" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Location and Ownership in Qiskit\n", + "\n", + "Given the presence of Operator logic in both Aqua and Terra, there are several options for their placement within Qiskit. The primary considerations here relate to which master branch tests them, who owns what in the case of breakage, and who owns what in the case of design.\n", + "\n", + "In addition, some remote Operator algorithms are being discussed, with one already in production - the Aer Expectation Value. The location of these algorithms is also an important question." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Operator Location Considerations\n", + "\n", + "* The Operator's centrality to Aqua means relying on an external library is a big overhead\n", + " * Reliance on Terra has created frequent firedrills because behavior and interfaces change without integration testing\n", + " * Firedrills are very difficult to troubleshoot because presently there is no integration testing between Terra and Aqua or design review to check whether a change will have downstream implications\n", + " * Operator is so central to Aqua that it will require strong ownership by the Aqua team, constant maintenance and changes\n", + "* Centralized Operator primitives can simplify interfaces across Qiskit\n", + " * By accepting a common Operator format derived from Terra, methods in different areas of Qiskit can communicate in a consistent format without dependencies\n", + " * For example, Aer's expectation value can take a circuit and an Operator, rather than depend on Aqua to define its interface, or rely on an informal interface (e.g. lists) which must be validated\n", + "* Terra and Aqua's respective Operators can be delineated somewhat cleanly\n", + " * Aqua and Terra's operators are seemingly used by completely different users for very different tasks (QA&A vs. QCVV or circuit analysis)\n", + " * Terra's Operators are primarily matrix-based, while Aqua's are primarily composites of sparse representations (e.g. sums of Paulis or Circuits)\n", + " * Though some are definitely shared, such as Pauli\n", + "* Operators and Gates may need to be reconciled at some point\n", + " * The X, Y, and Z Paulis are not different from the X, Y, and Z Gates\n", + " * Both the gate and operator models include functionality for converting unitary matrices to circuit operations" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Operator Location Options\n", + "\n", + "**A.** Move Aqua Operators into Terra, with:\n", + "1. Joint ownership by Aqua team\n", + "2. Aqua integration tests run on Terra's master branch (e.g. pulling in Aqua's master branch to execute tests). _Unit tests alone are not sufficient, as they are usually modified along with breaking changes to pass._\n", + "3. Aligned release cycles so Aqua does not need to scramble to release when Terra does\n", + "\n", + "**Big-A.** Combine Aqua and Terra into a single repo and jointly own Operators\n", + "\n", + "**B.** Move all operators and states into Aqua, jointly owned by Terra team\n", + "\n", + "**C.** Leave Operators split between Aqua and Terra, with dependency on Terra for some primitives (QuantumCircuit, Pauli), with joint ownership and Aqua integration testing\n", + "\n", + "##### **Decision:** Following a discussion in Aqua Design Review, option **A** will be pursued for the remainder of this doc." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Provider-Specific Algorithm Location Options (Decision)\n", + "\n", + "**A.** Remote algorithms live in provider repo, and are tested and released at provider’s discretion\n", + "\n", + "**B.** Remote algorithms live in Aqua, with Aqua integration testing of functionality in provider repo\n", + "\n", + "**C.** Remote algorithms live in Aqua, with agreed upon interface to enforce consistency, and data interchange (e.g. an Operator format defined in Terra) tested in provider repo" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Object Model and Hierarchy \n", + "\n", + "What is an Operator _to a QA&A (Quantum Algorithms & Applications) programmer?_\n", + "\n", + "Ignoring the Physical definition of an Operator for a moment, as a _Quantum programming primitive,_ the Operator is:\n", + "\n", + "* __Recursively defined__ - Operators can be one of several _primitives_ - e.g. Matrix, Pauli, Clifford, QuantumCircuit, or an arbitrary combination of these primitives, e.g. Addition, Tensor, Composition. \n", + " * It makes complete mathematical sense to add two primitives together, e.g. `(my_matrix+my_circuit)@my_pauli`. In classical programming, this would be like `5.7 + \"pickle\"`.\n", + "* __Both code and data__ - The Operator encodes both data (e.g. a matrix for eigensolution or a wavefunction being prepared) and computation (measure my wavefunction in this basis). There is little distinction between the two in Quantum programming.\n", + "* __Linear__ - The Operator is a recursively powerful construct, allowing algorithmic rearrangement not typically allowed in classical computation. \n", + " * `op1(op2(A,B)) == op1(op2(A)), op2(B))` in many cases, e.g. Expectation(A+B). \n", + " * The idea that `program(a*circuita + b*circuitb)` gives a mathematically valid result is highly surprising.\n", + "* __Algorithmically ubiquitous__ - Every quantum algorithm uses Operators. Algorithms are nearly always defined in literature by Operator operations. This language is rigorous, accepted, and compact. \n", + "* __Eagerly Computable__ - In most cases, Operator computation can be partially compiled as parameters become available, allowing improved performance, functional modularity (e.g. passing a ready-to-run algorithm), and inspection transparency. For example:\n", + " * A circuit can be compiled to a Qobj with parameters missing, to be filled in later\n", + " * The full list of circuits necessary to execute an algorithm can be prepared pending some operator coefficients\n", + " * A full algorithm can be prepared and passed to a user pending the insertion of some subcomponent (a choice of ExpectationValue algorithm) or parameters" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Operator Definition: Primitives and Combinations\n", + "\n", + "Operators can be _primitives_ or _combinations._ Primitives are base-level Operator representations which are not defined in terms of other primitives, but can be converted into one another with some computational work. Combinations are Operators which are constructed from functions of multiple primitives, such as sums and tensors. Combinations store the primitives from which they are constructed. Note that many Gates are present in other classes of primitives, and this must be reconciled as a follow-on to this redesign. The following should all be modules in the Operator hierarchy:\n", + "\n", + "* Primitives\n", + " * Matrix\n", + " * Pauli - X, Y, Z, I\n", + " * QuantumCircuit, Gate\n", + " * Clifford\n", + " * Projector - Ze, O, P, M\n", + " * Stabilizer\n", + " * Graph State - Stored as a graph\n", + " * QuantumCircuit - Implicitly starts from |0⟩⟨0|\n", + " * Others (follow-on): ZX, MPS, Dirac Matrix, Gell-Mann matrix\n", + "* Combinations\n", + " * OpSum - Generalization of WeightedPauli. Stores a list of Operators of equal dimension and complex weights\n", + " * OpComposition - Stores a list of Operators which are all of equal dimension\n", + " * OpKron - Stores a list of Operators of any size\n", + " * OpVec - Stores a list of Operators of any size\n", + " * OpExp - Stores a single Operator, acting as a placeholder for some Evolution algorithm to replace later\n", + " * OpCombo - custom, user-defined recombination function\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2019-12-26T21:44:56.965368Z", + "start_time": "2019-12-26T21:44:56.961373Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit.aqua.operators.pauli import X, Y, Z, I\n", + "op_new = .5*(I^X) + .2*(Z^Y) + .1j*(Z^Z)\n", + "op_new == pauli_op" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Note that to support the above, the existing Pauli in Terra would need to support Tensor, sum, and scalar multiplication which can return an OpSum and OpKron.\n", + "\n", + "The following overload operations are also desirable:\n", + "* Operator composition using `@` overload\n", + " * __Decision:__ deprecate the `*` overload for composition?\n", + "* Power (`**3`), kronpower (`^3`)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "ExecuteTime": { + "end_time": "2020-01-02T20:07:37.857560Z", + "start_time": "2020-01-02T20:07:37.854325Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(pauli_op^2)**2 == (pauli_op^pauli_op)@(pauli_op^pauli_op)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit.aqua.ansatz import Ry\n", + "from qiskit.aqua.operators.projectors import Ze, O, P\n", + "\n", + "ansatz = Ry(qubits=2, depth=3) @ (P^(-.1*O + 3*Ze))\n", + " # This is an OpSum of two circuits!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Algorithms Definition: Primitives and Composites\n", + "\n", + "Operations on Operators also can be described as primitives or combinations of such. Primitives are computations which can be performed directly on some available computation engine, such as Numpy or Quantum Hardware, while composites are constructed from piping primitives together. Algorithms accept only _specific primitives,_ so an algorithm taking a Pauli vs. one taking a matrix are fundamentally different, but are also defined over certain combinations of their input primitives. For example, a Change-of-Basis Expectation Value is defined to accept a Pauli and a Projector (or QuantumCircuit acting as one from Zero implicitly), but can also accept sums, tensors, and vectorizations of Paulis and Projectors. If an unsupported primitive, such as Matrix or OpComposition were passed in, an exception would be thrown.\n", + "\n", + "* Primitives\n", + " * Classical sum, product, tensor, trace, etc.\n", + " * Z-Basis QuantumCircuit measurement / Trace (traditional QASM backend)\n", + " * Primitive Conversion - Pauli to matrix, matrix to Pauli, etc.\n", + " * Evolution Conversion - Trotter, Suzuki, etc.\n", + " * Pauli Sum, Composition, Tensor\n", + " * Change of Basis - Pauli, Fourier\n", + " * Optimizers\n", + " * External functions, such as Drivers or imports\n", + "* Composites\n", + " * ExpectationValue\n", + " * Existing Aqua Algorithms: VQE, QPE, HHL, etc.\n", + " * Gradients\n", + " \n", + "Over time, we have found that it is easiest to describe the behavior of Algorithms in terms of the flow of Operators through various components and subroutines. This description is naturally recursive, and considerably easier to understand than the present presentation of algorithmic flow in Aqua." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "To demonstrate this, consider the following VQE coded from scratch in this model:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2020-01-17T17:16:29.164503Z", + "start_time": "2020-01-17T17:16:29.100888Z" + } + }, + "outputs": [], + "source": [ + "ansatz = Ry(qubits=2, depth=3) @ (P^P)\n", + " # Ansatz state = Ry(θ)|++⟩\n", + "hamiltonian = 3*(I^Z) + .4j*(X^Z)\n", + "expectation = PauliExpectation(ansatz, hamiltonian, backend)\n", + "print(expectation.run({ansatz.params: np.zeroes(len(ansatz.params))})) \n", + " # Print starting expectation\n", + "\n", + "gradient = ParamShiftGradient(expectation)\n", + "optimizer = AQGD(initial_point=np.zeroes(len(ansatz.params)))\n", + "my_vqe = AQGD(cost_fn=expectation.run, grad_fn=gradient.run)\n", + "min_eig = my_vqe.run()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Parameterization and Eagerness\n", + "\n", + "Operators and algorithms can be _parameterized,_ or missing some key information in order to execute. For Operators these may be sum coefficients, evolution times, QuantumCircuit parameters, and more. For Algorithms these may be input operators, execution parameters, or instances of algorithms used in computation which cannot be inferred by default (e.g. backend on which to execute, optimizer, etc.).\n", + "\n", + "##### Eager Parameterization+Execution Interface Options:\n", + "\n", + "An algorithm should execute as soon as it has filled the parameters necessary to do so. This is called **Eager Execution.** In a similar vein, OpSum can be seen as eagerly waiting for the contained operators to be summable, e.g. replaced with scalars by an expectation value. (**Decision**) Some interface options for eagerness:\n", + "\n", + "**Option A**: Algorithms should be **callable** with a parameter dictionary, triggering a breadth-first search to parameterize any sub-objects with the parameter dictionary. This may be too much hocus pocus and difficult for implementers of algorithms to understand. A user may want to parameterize without executing, so an `execute` parameter should be available in the parameterization function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2020-01-16T01:08:01.116018Z", + "start_time": "2020-01-16T01:08:00.856363Z" + } + }, + "outputs": [], + "source": [ + "my_op = Parameter('t1')*(Z^Z) + .6*(X^I)\n", + "my_vqe = VQE(backend=Parameter('backend'), \n", + " operator=my_op, \n", + " ansatz=Ry(qubits=2, reps=3), \n", + " optimizer=SLSQP(initial_point=Parameter('initial_rotations')))\n", + "my_vqe({'t1': .2j, 'backend': Aer.get_backend('qasm_simulator')}) \n", + " # Didn't return anything yet\n", + "rots = np.zeros(len(my_vqe.ansatz.params))\n", + "min_eig = my_vqe({'initial_rotations': rots})\n", + " # Now a value is returned, and other execution information can be found inside the object" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + " # Alternatively\n", + "my_vqe({'initial_rotations': rots}, execute=False)\n", + "min_eig = my_vqe()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Option B:** Algorithms should have a `.run(param_dict)` method which accepts parameters and performs the breadth-first parameterization. The form factor of this would be similar to the above, but with `run()` instead of direct function calls. This has the benefit of some backward compatibility. \n", + "\n", + "**Option C:** Algorithms should support separate parameterization and execution functions. This is the most explicit, but is clunky in an eager execution regime, where execution is automatic if the algorithm is sufficiently parameterized.\n", + "\n", + "All of an Algorithm or Operator's pending Parameters should be recursively returned by a `.params` function. _(Tentative)_ A `deepcopy` option should be available to return a deep copy of the algorithm with the desired parameterization, rather than parameterize the algorithm in-place (this is evaluated with `execute=False` by default)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "##### Eager Partial Computation\n", + "\n", + "Aqua should be **eager** in partial computation while some parameters necessary for execution are not yet available, to allow for inspection transparency and performance. For example, once backend information is available, circuits should be transpiled for the backend or otherwise prepared for execution. This can avoid many transpilations or preparations later if the circuits are duplicated for Operator composition, as in Change-of-Basis expectation values or gradients.\n", + "\n", + "The choice of which partial computation to perform is left to the algorithm, so only worthwhile partial computations are performed. If parameters change, re-preparing the partial computation can be expensive, so a `lazy` parameter should be available in the callable function." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Changes to Terra\n", + "\n", + "The `quantum_info` directory should be organized as follows:\n", + "\n", + " * channel\n", + " * ...\n", + " * matrix.py **- Decision: Rename operator.py to matrix.py or matrix_op.py?**\n", + " * pauli.py\n", + " * clifford.py **- Decision: Use the Ignis's Clifford?**\n", + " * projector.py\n", + " * stabilizer.py\n", + " * Composites\n", + " * op_sum.py, op_composite.py, op_kron.py, op_vec.py, op_exp.py\n", + "\n", + "In addition to the functionality detailed in [Object Model and Hierarchy](#Object-Model-and-Hierarchy) above, Terra should support the following for all of the above Non-matrix-based operators:\n", + "* `to_matrix()` - Method to allow quick access to unscalable classical tools, e.g. numpy eigensolution\n", + "* `to_quantum_circuits()` - returns a single or list of quantum circuits and coefficients representing the full Operator, including any distributive composition, tensors, etc.\n", + "* Trace, Partial Trace, Determinant, Norms, Adjoints - Where possible, linear algebra should be easily accessible " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "##### Follow-on: Terra Reconciliation Between Operators and Gates\n", + "\n", + "Terra's Operators and Gates are currently fully distinct from one another. The X, Y, Z, Clifford Gates, Evolution by a matrix-specified Unitary (UnitaryGate), and more are direct overlaps between the two, but not interoperable. At some point, Terra should address this difference to allow Operators to be inserted onto a circuit, maintain only a single set of primitive unitaries, allow Gates to be composed with Operators, etc." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Changes to Aqua\n", + "\n", + "The changes to Aqua are basically just to \n", + "* deprecate the Operators after moving their logic into Terra, \n", + "* change the Aqua algorithms to rely on the new Terra operators,\n", + "* break up the Expectation, Evolution, circuit execution, and gradient code to be first-class algorithms users can extend and understand,\n", + "* and change the exsting Aqua algorithms to rely on these new algorithms." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "##### Change Algorithms to rely on Terra operators and new Operator algorithms\n", + "\n", + "In particular, algorithms should be accessible with only Terra-defined inputs (meaning constructed using Terra alone) to provide a seamless experience between Terra and Aqua usage, and extensible interfaces. For example, a VQE should be runnable by passing only a parameterized QuantumCircuit and Terra-defined Operator, allowing a provider or collaborator to share a custom VQE without an unnecessary dependency on Aqua. In particular, this allows the Aer Expectation Value to be defined with the same interface as Aqua's Pauli Expectation, without a dependency on Aqua." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "##### Circuit Execution Algorithms - **Decision: Name - CircuitExecution? QCExecute? QuantumMeasureZ? RunCircuit?**\n", + "\n", + "Circuit execution is a utility in Aqua today, mediated by the QuantumInstance, which most users do not understand, and growing increasingly branchy to accommodate more and more execution variants. Measurement error mitigation, noisy simulation setup, hardware API fault handling, and more all fall into the same execution flow in various branches. \n", + "\n", + "Circuit execution is an algorithm for sampling a circuit's expectation in exponentially many ${Z, I}^{\\otimes n}$ bases, but is not reflected an an algorithm today. It should be promoted to be a first-class algorithm to be more transparent and compartmental, wherein for example, code for simulation and code for execution on hardware can be kept distinct. A CircuitExecution Algorithm accepts a backend and interacts with it in some well-defined way - in way breaking up and organizing of the functionality of the QuantumInstance. Some examples of CircuitExecution algorithms are:\n", + "1. QuantumHardware - An Execution algorithm tailored for execution on remote hardware, including fault handling, slicing to limit job sizes, etc. Can stack up a queue of circuits for batch execution, or accept a list of jobids to use as the first n results objects, allowing the user to reuse results from a terminated execution.\n", + "1. IdealSimulator - Algorithm tailored for execution in ideal simulation.\n", + "1. NoisySimulator - Utility for querying a Hardware backend's properties and providing a noisy simulator using Aer's \"noise config from device\" functionality.\n", + "1. ErrorMitigatedExecutor - OUT OF SCOPE, BEING COVERED IN ANOTHER DOC.\n", + "\n", + "If none is explicitly specified, Aqua should aggressively guess the preferred execution algorithm for the user given the backend and other execution parameters." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "##### Expectation Algorithms\n", + "\n", + "Aqua should support the following ExpectationValue algorithms. An `ExpectationBase` class should allow automatic selection of an evolution algorithm by default if none is specified - e.g. if the user has Aer installed, VQE will use the AerExpectation by default instead of QASM execution. Other possible expectation values include:\n", + "1. PauliExpectation (Change-of-Basis)\n", + "1. CVaRExpectation\n", + "1. AerExpectation - relies on Aer's fast expectation feature\n", + "1. MatrixExpectation\n", + "1. (Tentative) BasicAerExpectation\n", + "1. RichardsonExpectation - OUT OF SCOPE, BEING COVERED IN ANOTHER DOC.\n", + "\n", + "##### Grouping\n", + "\n", + "Grouping is an important feature within the PauliExpectation in Aqua today, but is not used by default, and has an interface which is not obvious. Grouping should be moved into the PauliExpectation, with a simple interface for the user to specify whether to group the Paulis, or how aggresively to do so. By default, the PauliExpectation should group Paulis as aggressively as is performant on the given execution backend." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "##### Circuit Evolution Algorithms\n", + "\n", + "And similarly for Evolution, a variety of algorithms should be available for converting a OpExp composite operator into a sum, composition, etc. More specifically, circuit evolution algorithms take an OpExp placeholder and return operators which approximate the value of the exponentiation. For example, the PauliEvolution accepts a Pauli and returns a QuantumCircuit representing the unitary evolution of that Pauli. An `EvolutionBase` class should allow automatic selection of an evolution algorithm by default if none is specified.\n", + "\n", + "1. PauliEvolution (Change-of-Basis)\n", + "1. SumEvolution\n", + " 1. Trotter\n", + " 1. Suzuki\n", + "1. MatrixEvolution\n", + "1. (Tentative) [LinCombEvolution](https://arxiv.org/abs/1202.5822)\n", + "1. (Tentative) AerEvolution\n", + "1. (Tentative) BasicAerEvolution\n", + "\n", + "##### Other algorithms to build out into first-class Algorithm groups\n", + "1. Converters - convert lazily between Operator types\n", + "1. Gradient\n", + "1. Optimization" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Timeline and Gameplan\n", + "\n", + "Stage 1: Implement new Operators in Terra with thorough unit and integration tests.\n", + "\n", + "Stage 2: Implement Operator algorithms in Aqua, relying on Terra Operators\n", + "\n", + "Stage 3: Migrate Aqua algorithms to rely on new Operator algorithms and new Terra Operators\n", + "\n", + "Stage 4: Deprecate Present Aqua Operators (0.7 release)\n", + "\n", + "Stage 5: Delete Present Aqua Operators (0.8 release)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## ⚰️⚰️⚰️⚰️⚰️⚰️ Graveyard ⚰️⚰️⚰️⚰️⚰️⚰️" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Other Benefits of OperatorFlow\n", + "\n", + "* Obedient Eager Evaluation - Best of Eager and Lazy evaluation:\n", + " * Partially evaluate whatever you can with the parameters you have\n", + " * Allows transparency, inspection, rapid prototyping (e.g. Users couldn't find circuits or operator when working through JSON dictionaries)\n", + " * Performance - partially compiled algorithms save massive amounts of compilation and deepcopy time\n", + " * But not too early, not compiling preemptively for a possible parameter value\n", + " * Objects can be returned without being totally incorrectly constructed for the next step or engine (e.g. building massive CNOT chains for UCCSD simulations)\n", + " * Intractable but possible computations (e.g. convert to matrix and solve) are avoided\n", + "* Natural, Powerful, and Self-defining Programming Interfaces\n", + " * __An algorithm's behavior is simply defined by the operator primitives it accepts and returns__\n", + " * Nesting of algorithms is identical to user algorithm execution\n", + "* Ubiquitous parameters, and obvious interface for Optimization\n", + " * OpCombo coefficients, primitive parameters, and algorithm parameters can all be parameterized\n", + " * Algorithms of any level of completeness can be returned\n", + " * Optimization is universal - simply pass a nearly-complete algorithm to an optimizer and the callable interface executes when the optimizer provides the parameters" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Grouping\n", + "\n", + "Aqua's grouping functionality is only relevant to ExpectationValues today." + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "metadata": { + "ExecuteTime": { + "end_time": "2020-01-03T06:23:10.774540Z", + "start_time": "2020-01-03T06:23:10.761737Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[(Pauli(z=[True, True, True], x=[False, False, False]), [0, 1, 2])]" + ] + }, + "execution_count": 92, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qaoa_cost_op = WeightedPauliOperator([\n", + " [.5, Pauli.from_label('ZIZ')],\n", + " [.2, Pauli.from_label('ZZI')],\n", + " [.1j, Pauli.from_label('IZZ')],\n", + "])\n", + "\n", + "grouped_cost_op = TPBGroupedWeightedPauliOperator.sorted_grouping(qaoa_cost_op)\n", + "grouped_cost_op._basis" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class VQE(QuantumAlgorithm):\n", + " def __init__(self, operator, var_form, optimizer,\n", + " initial_point=None, backend=backend, callback=None, ...):\n", + " ...\n", + " self._expectation_value = ExpectationValue(self._operator, self._backend)\n", + " \n", + " def _energy_evaluation(self, params):\n", + " circuits = self._var_form.construct_circuit(params)\n", + " energy, stdev = self._expectation_value.run(circuits)\n", + " return energy" + ] + } + ], + "metadata": { + "celltoolbar": "Slideshow", + "kernelspec": { + "display_name": "code", + "language": "python", + "name": "code" + }, + "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.7.4" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "328.594px" + }, + "toc_section_display": true, + "toc_window_display": true + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} From 2ae94fbdd42ef1a7701b3830194f5de84e3f2db4 Mon Sep 17 00:00:00 2001 From: Donny Greenberg Date: Thu, 23 Jan 2020 16:48:40 -0500 Subject: [PATCH 2/4] Add RfC header, rename following acceptance --- ...b => 0003-Aqua_0.7_operator_redesign.ipynb | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) rename ####-0.7_operator_refactor.ipynb => 0003-Aqua_0.7_operator_redesign.ipynb (98%) diff --git a/####-0.7_operator_refactor.ipynb b/0003-Aqua_0.7_operator_redesign.ipynb similarity index 98% rename from ####-0.7_operator_refactor.ipynb rename to 0003-Aqua_0.7_operator_redesign.ipynb index 6c4fd22..cb98c9b 100644 --- a/####-0.7_operator_refactor.ipynb +++ b/0003-Aqua_0.7_operator_redesign.ipynb @@ -20,6 +20,14 @@ } }, "source": [ + "| **Status** | **Accepted** |\n", + "|:------------------|:----------------------------------------------|\n", + "| **RFC #** | 0003 |\n", + "| **Authors** | Donny Greenberg (donny@ibm.com) |\n", + "| **Deprecates** | NA |\n", + "| **Submitted** | 2020-01-17 |\n", + "| **Updated** | 2020-01-23 |\n", + "\n", "## Purpose\n", "To improve the transparency, ease of understanding, and programming power of Aqua’s operator logic and usage. Specifically, to reconcile with the Terra operator hierarchy and make the Aqua algorithmic flow more visible, explicit, and extensible.\n", "\n", @@ -1226,8 +1234,17 @@ }, "toc_section_display": true, "toc_window_display": true + }, + "pycharm": { + "stem_cell": { + "cell_type": "raw", + "source": [], + "metadata": { + "collapsed": false + } + } } }, "nbformat": 4, "nbformat_minor": 1 -} +} \ No newline at end of file From fdc71a7dbfcc94efaa7dfd89032f35453e6b9b00 Mon Sep 17 00:00:00 2001 From: Donny Greenberg Date: Thu, 23 Jan 2020 16:53:22 -0500 Subject: [PATCH 3/4] Move header and delete old info --- 0003-Aqua_0.7_operator_redesign.ipynb | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/0003-Aqua_0.7_operator_redesign.ipynb b/0003-Aqua_0.7_operator_redesign.ipynb index cb98c9b..fed408b 100644 --- a/0003-Aqua_0.7_operator_redesign.ipynb +++ b/0003-Aqua_0.7_operator_redesign.ipynb @@ -9,7 +9,14 @@ }, "source": [ "# Aqua 0.7 Operator Redesign\n", - "_17-Jan-19, donny@_" + "\n", + "| **Status** | **Accepted** |\n", + "|:------------------|:----------------------------------------------|\n", + "| **RFC #** | 0003 |\n", + "| **Authors** | Donny Greenberg (donny@ibm.com) |\n", + "| **Deprecates** | NA |\n", + "| **Submitted** | 2020-01-17 |\n", + "| **Updated** | 2020-01-23 |" ] }, { @@ -20,14 +27,6 @@ } }, "source": [ - "| **Status** | **Accepted** |\n", - "|:------------------|:----------------------------------------------|\n", - "| **RFC #** | 0003 |\n", - "| **Authors** | Donny Greenberg (donny@ibm.com) |\n", - "| **Deprecates** | NA |\n", - "| **Submitted** | 2020-01-17 |\n", - "| **Updated** | 2020-01-23 |\n", - "\n", "## Purpose\n", "To improve the transparency, ease of understanding, and programming power of Aqua’s operator logic and usage. Specifically, to reconcile with the Terra operator hierarchy and make the Aqua algorithmic flow more visible, explicit, and extensible.\n", "\n", From 5aa83a7cf620800648776318a34d2b90ee146253 Mon Sep 17 00:00:00 2001 From: Donny Greenberg Date: Thu, 23 Jan 2020 22:04:41 -0500 Subject: [PATCH 4/4] Update following design review. --- 0003-Aqua_0.7_operator_redesign.ipynb | 334 ++++++++++++-------------- 1 file changed, 155 insertions(+), 179 deletions(-) diff --git a/0003-Aqua_0.7_operator_redesign.ipynb b/0003-Aqua_0.7_operator_redesign.ipynb index fed408b..bb9b447 100644 --- a/0003-Aqua_0.7_operator_redesign.ipynb +++ b/0003-Aqua_0.7_operator_redesign.ipynb @@ -30,7 +30,9 @@ "## Purpose\n", "To improve the transparency, ease of understanding, and programming power of Aqua’s operator logic and usage. Specifically, to reconcile with the Terra operator hierarchy and make the Aqua algorithmic flow more visible, explicit, and extensible.\n", "\n", - "Throughout this doc, we rely on definitions of Operators roughly derived from the first chapter of John Watrous's \"The Theory of Quantum Information,\" with a focus on Square Operators over binary alphabets." + "Throughout this doc, we rely on definitions of Operators roughly derived from the first chapter of John Watrous's \"The Theory of Quantum Information,\" with a focus on Square Operators over binary alphabets.\n", + "\n", + "Items which are intended for the March 17th 2020 Aqua release have been labeled **[0.7]**." ] }, { @@ -601,8 +603,8 @@ "* Centralized Operator primitives can simplify interfaces across Qiskit\n", " * By accepting a common Operator format derived from Terra, methods in different areas of Qiskit can communicate in a consistent format without dependencies\n", " * For example, Aer's expectation value can take a circuit and an Operator, rather than depend on Aqua to define its interface, or rely on an informal interface (e.g. lists) which must be validated\n", - "* Terra and Aqua's respective Operators can be delineated somewhat cleanly\n", - " * Aqua and Terra's operators are seemingly used by completely different users for very different tasks (QA&A vs. QCVV or circuit analysis)\n", + "* Terra and Aqua's respective Operators can be delineated somewhat cleanly and used quite differently\n", + " * Aqua and Terra's operators are seemingly used by completely different users for very different tasks - Aqua primarily uses Operators for lazy algorithmic manipulation while Terra primarily uses them for numerical computation.\n", " * Terra's Operators are primarily matrix-based, while Aqua's are primarily composites of sparse representations (e.g. sums of Paulis or Circuits)\n", " * Though some are definitely shared, such as Pauli\n", "* Operators and Gates may need to be reconciled at some point\n", @@ -629,9 +631,9 @@ "\n", "**B.** Move all operators and states into Aqua, jointly owned by Terra team\n", "\n", - "**C.** Leave Operators split between Aqua and Terra, with dependency on Terra for some primitives (QuantumCircuit, Pauli), with joint ownership and Aqua integration testing\n", + "**C.** Leave Operators split between Aqua and Terra, with dependency on Terra for primitives (QuantumCircuit, MatrixOperator, Pauli), with joint ownership and Aqua integration testing\n", "\n", - "##### **Decision:** Following a discussion in Aqua Design Review, option **A** will be pursued for the remainder of this doc." + "##### **Decision:** Following a discussion in Aqua Design Review, option **C** will be pursued." ] }, { @@ -648,7 +650,9 @@ "\n", "**B.** Remote algorithms live in Aqua, with Aqua integration testing of functionality in provider repo\n", "\n", - "**C.** Remote algorithms live in Aqua, with agreed upon interface to enforce consistency, and data interchange (e.g. an Operator format defined in Terra) tested in provider repo" + "**C.** Remote algorithms live in Aqua, with agreed upon interface to enforce consistency, and data interchange (e.g. an Operator format defined in Terra) tested in provider repo\n", + "\n", + "##### **Decision:** Following a discussion in Aqua Design Review, option **C** will be pursued." ] }, { @@ -688,24 +692,22 @@ "source": [ "#### Operator Definition: Primitives and Combinations\n", "\n", - "Operators can be _primitives_ or _combinations._ Primitives are base-level Operator representations which are not defined in terms of other primitives, but can be converted into one another with some computational work. Combinations are Operators which are constructed from functions of multiple primitives, such as sums and tensors. Combinations store the primitives from which they are constructed. Note that many Gates are present in other classes of primitives, and this must be reconciled as a follow-on to this redesign. The following should all be modules in the Operator hierarchy:\n", + "Operators can be _primitives_ or _combinations._ Primitives are base-level Operator representations which have unique data structures and are not defined in terms of other primitives, but can be converted into one another with some computational work. Often a unique data structure is used to achieve better performance even when other representations are available, such as a Clifford tableau or Pauli representation as X and Z bits. Combinations are Operators which are constructed from functions of multiple primitives, such as sums and tensors. Combinations store the primitives from which they are constructed. Note that many Gates are present in other classes of primitives, and this must be reconciled as a follow-on to this redesign. The following should all be available in the Operator hierarchy:\n", "\n", "* Primitives\n", " * Matrix\n", - " * Pauli - X, Y, Z, I\n", + " * Pauli\n", + " * **[0.7]** Singletons for easy importing: X, Y, Z, I\n", " * QuantumCircuit, Gate\n", - " * Clifford\n", - " * Projector - Ze, O, P, M\n", - " * Stabilizer\n", - " * Graph State - Stored as a graph\n", - " * QuantumCircuit - Implicitly starts from |0⟩⟨0|\n", - " * Others (follow-on): ZX, MPS, Dirac Matrix, Gell-Mann matrix\n", + " * Clifford\n", + " * WeightedPauli - A matrix of X And Z binary strings\n", + " * Others: ZX Calculus, MPS, Dirac Matrix, Gell-Mann matrix\n", "* Combinations\n", - " * OpSum - Generalization of WeightedPauli. Stores a list of Operators of equal dimension and complex weights\n", - " * OpComposition - Stores a list of Operators which are all of equal dimension\n", + " * **[0.7]** OpSum - Generalization of WeightedPauli. Stores a list of Operators of equal dimension and complex weights\n", + " * **[0.7]** OpVec - Stores a list of Operators of any size\n", + " * **[0.7]** OpEvo - Stores a single Operator and time parameter, acting as a placeholder for some Evolution algorithm to replace later\n", + " * **[0.7]** OpCompose - Stores a list of Operators which are all of equal dimension\n", " * OpKron - Stores a list of Operators of any size\n", - " * OpVec - Stores a list of Operators of any size\n", - " * OpExp - Stores a single Operator, acting as a placeholder for some Evolution algorithm to replace later\n", " * OpCombo - custom, user-defined recombination function\n" ] }, @@ -731,7 +733,7 @@ } ], "source": [ - "from qiskit.aqua.operators.pauli import X, Y, Z, I\n", + "from qiskit.aqua.operators import X, Y, Z, I\n", "op_new = .5*(I^X) + .2*(Z^Y) + .1j*(Z^Z)\n", "op_new == pauli_op" ] @@ -744,12 +746,14 @@ } }, "source": [ - "Note that to support the above, the existing Pauli in Terra would need to support Tensor, sum, and scalar multiplication which can return an OpSum and OpKron.\n", - "\n", - "The following overload operations are also desirable:\n", + "The following overload operations may also be desirable:\n", "* Operator composition using `@` overload\n", - " * __Decision:__ deprecate the `*` overload for composition?\n", - "* Power (`**3`), kronpower (`^3`)" + "* Power (`**3`), kronpower (`^3`)\n", + "\n", + "__Decision:__ Following a discussion in design review, the following was agreed:\n", + "* Aqua should support `op1.compose(op2)` for circuit-direction matrix multiplication and `op1.dot(op2)` for linear algebra direction matrix multiplication.\n", + "* Usage of this syntactic sugar should generally be avoided in the Aqua source code.\n", + "* The composition, kron, power, and kronpower overload should be explored after the 0.7 release" ] }, { @@ -777,19 +781,6 @@ "(pauli_op^2)**2 == (pauli_op^pauli_op)@(pauli_op^pauli_op)" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from qiskit.aqua.ansatz import Ry\n", - "from qiskit.aqua.operators.projectors import Ze, O, P\n", - "\n", - "ansatz = Ry(qubits=2, depth=3) @ (P^(-.1*O + 3*Ze))\n", - " # This is an OpSum of two circuits!" - ] - }, { "cell_type": "markdown", "metadata": { @@ -800,7 +791,7 @@ "source": [ "#### Algorithms Definition: Primitives and Composites\n", "\n", - "Operations on Operators also can be described as primitives or combinations of such. Primitives are computations which can be performed directly on some available computation engine, such as Numpy or Quantum Hardware, while composites are constructed from piping primitives together. Algorithms accept only _specific primitives,_ so an algorithm taking a Pauli vs. one taking a matrix are fundamentally different, but are also defined over certain combinations of their input primitives. For example, a Change-of-Basis Expectation Value is defined to accept a Pauli and a Projector (or QuantumCircuit acting as one from Zero implicitly), but can also accept sums, tensors, and vectorizations of Paulis and Projectors. If an unsupported primitive, such as Matrix or OpComposition were passed in, an exception would be thrown.\n", + "Operations on Operators also can be described as primitives or combinations of such. Primitives are computations which can be performed directly on some available computation engine, such as Numpy or Quantum Hardware, while composites are constructed from piping primitives together. Algorithms accept only _specific primitives,_ so an algorithm taking a Pauli vs. one taking a matrix are fundamentally different, but are also defined over certain combinations of their input primitives. For example, a Change-of-Basis Expectation Value is defined to accept a Pauli and a Projector (or QuantumCircuit acting as one from Zero implicitly), but can also accept sums, tensors, and vectorizations of Paulis and Projectors. If an unsupported primitive, such as Matrix or OpCompose were passed in, an exception would be thrown.\n", "\n", "* Primitives\n", " * Classical sum, product, tensor, trace, etc.\n", @@ -841,8 +832,8 @@ }, "outputs": [], "source": [ - "ansatz = Ry(qubits=2, depth=3) @ (P^P)\n", - " # Ansatz state = Ry(θ)|++⟩\n", + "ansatz = Ry(qubits=2, depth=3)\n", + " # Ansatz state = Ry(θ)|00⟩\n", "hamiltonian = 3*(I^Z) + .4j*(X^Z)\n", "expectation = PauliExpectation(ansatz, hamiltonian, backend)\n", "print(expectation.run({ansatz.params: np.zeroes(len(ansatz.params))})) \n", @@ -862,60 +853,47 @@ } }, "source": [ - "#### Parameterization and Eagerness\n", - "\n", - "Operators and algorithms can be _parameterized,_ or missing some key information in order to execute. For Operators these may be sum coefficients, evolution times, QuantumCircuit parameters, and more. For Algorithms these may be input operators, execution parameters, or instances of algorithms used in computation which cannot be inferred by default (e.g. backend on which to execute, optimizer, etc.).\n", - "\n", - "##### Eager Parameterization+Execution Interface Options:\n", + "##### Eager Partial Computation\n", "\n", - "An algorithm should execute as soon as it has filled the parameters necessary to do so. This is called **Eager Execution.** In a similar vein, OpSum can be seen as eagerly waiting for the contained operators to be summable, e.g. replaced with scalars by an expectation value. (**Decision**) Some interface options for eagerness:\n", + "Aqua should be **eager** in partial computation while some parameters necessary for execution are not yet available, to allow for inspection transparency and performance. For example, once backend information is available, circuits should be transpiled for the backend or otherwise prepared for execution. This can avoid many transpilations or preparations later if the circuits are duplicated for Operator composition, as in Change-of-Basis expectation values or gradients.\n", "\n", - "**Option A**: Algorithms should be **callable** with a parameter dictionary, triggering a breadth-first search to parameterize any sub-objects with the parameter dictionary. This may be too much hocus pocus and difficult for implementers of algorithms to understand. A user may want to parameterize without executing, so an `execute` parameter should be available in the parameterization function." + "The choice of which partial computation to perform is left to the algorithm, so only worthwhile partial computations are performed. If parameters change, re-preparing the partial computation can be expensive, so a `lazy` parameter should be available in the callable function." ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": { - "ExecuteTime": { - "end_time": "2020-01-16T01:08:01.116018Z", - "start_time": "2020-01-16T01:08:00.856363Z" + "slideshow": { + "slide_type": "slide" } }, - "outputs": [], "source": [ - "my_op = Parameter('t1')*(Z^Z) + .6*(X^I)\n", - "my_vqe = VQE(backend=Parameter('backend'), \n", - " operator=my_op, \n", - " ansatz=Ry(qubits=2, reps=3), \n", - " optimizer=SLSQP(initial_point=Parameter('initial_rotations')))\n", - "my_vqe({'t1': .2j, 'backend': Aer.get_backend('qasm_simulator')}) \n", - " # Didn't return anything yet\n", - "rots = np.zeros(len(my_vqe.ansatz.params))\n", - "min_eig = my_vqe({'initial_rotations': rots})\n", - " # Now a value is returned, and other execution information can be found inside the object" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - " # Alternatively\n", - "my_vqe({'initial_rotations': rots}, execute=False)\n", - "min_eig = my_vqe()" + "### Desirable Changes in Terra\n", + "\n", + "In parallel with the work described in this doc, the following changes in Terra would improve the Aqua's Operator interfaces and functionality:\n", + "* Clifford, WeightedPauli (as a matrix of X and Z bitstrings) primitives\n", + "* `to_matrix()` - Method to allow quick access to unscalable classical tools, e.g. numpy eigensolution, with adequate warnings for large matrices.\n", + "* Kron, Kronpower, Trace, Partial Trace, Determinant, Norms, Adjoints - Where possible, linear algebra should be easily accessible for any applicable Operator primitives\n", + "* Rename `operator.py` to `matrix.py`\n", + "* Reconciliation Between Operators and Gates - Terra's Operators and Gates are currently fully distinct from one another. The X, Y, Z, Clifford Gates, Evolution by a matrix-specified Unitary (UnitaryGate), and more are direct overlaps between the two, but not interoperable. At some point, Terra should address this difference to allow Operators to be inserted onto a circuit, maintain only a single set of primitive unitaries, allow Gates to be composed with Operators, etc." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "source": [ - "**Option B:** Algorithms should have a `.run(param_dict)` method which accepts parameters and performs the breadth-first parameterization. The form factor of this would be similar to the above, but with `run()` instead of direct function calls. This has the benefit of some backward compatibility. \n", - "\n", - "**Option C:** Algorithms should support separate parameterization and execution functions. This is the most explicit, but is clunky in an eager execution regime, where execution is automatic if the algorithm is sufficiently parameterized.\n", - "\n", - "All of an Algorithm or Operator's pending Parameters should be recursively returned by a `.params` function. _(Tentative)_ A `deepcopy` option should be available to return a deep copy of the algorithm with the desired parameterization, rather than parameterize the algorithm in-place (this is evaluated with `execute=False` by default)." + "### [0.7] Changes to Aqua\n", + "\n", + "The changes needed for the 0.7 release are:\n", + "* Introduction of OpSum, OpVec, OpEvo, OpCompose modules\n", + "* Migrate Aqua usage of MatrixOperator to rely on Terra's Operator and WeightedPauliOperator to rely on OpSum\n", + "* Implement the new Expectation and Evolution algorithms\n", + "* Migrate Aqua algos to rely on the new Expectation and Evolution algorithms (including replicating grouping functionality from the TPBGroupedWeightedPauliOperator)\n", + "* Deprecate the Aqua Operators" ] }, { @@ -926,11 +904,23 @@ } }, "source": [ - "##### Eager Partial Computation\n", + "##### Expectation Algorithms\n", "\n", - "Aqua should be **eager** in partial computation while some parameters necessary for execution are not yet available, to allow for inspection transparency and performance. For example, once backend information is available, circuits should be transpiled for the backend or otherwise prepared for execution. This can avoid many transpilations or preparations later if the circuits are duplicated for Operator composition, as in Change-of-Basis expectation values or gradients.\n", + "Aqua should support the following ExpectationValue algorithms. \n", "\n", - "The choice of which partial computation to perform is left to the algorithm, so only worthwhile partial computations are performed. If parameters change, re-preparing the partial computation can be expensive, so a `lazy` parameter should be available in the callable function." + "1. **[0.7]** A base Expectation factory module which automatically selects and returns an evolution algorithm based on the backend and operator given - e.g. if the user passes an Aer backend and Pauli OpSum, VQE will use the AerExpectation by default instead of QASM execution.\n", + " 1. The logic for Expectation's handling of OpSum, OpVec, OpCompose, and OpEvo are all contained in this base. When an Expectation encounters an Operator type which is not the primitive (or list thereof) it was built to handle, it calls `super.run` on the Operator, which will rearrange Operator combinations into an OpSum or OpVec of primitives, call the Operator's `.run` on the primitives, and then recombine the results by summation or vectorization. OpKron and OpCompose are handled by attempting to kron or compose each of the constituents. OpEvo will return an error, as the user must convert them with an Evolution algorithm before taking an expectation.\n", + "1. **[0.7]** PauliExpectation (Change-of-Basis)\n", + "1. **[0.7]** AerExpectation - Takes Pauli expectations using on Aer's fast expectation feature\n", + "1. **[0.7]** MatrixExpectation\n", + "1. **[0.7]** ProjectorOverlap - Takes the overlap with respect to the operator composed as-is with $|0\\rangle\\langle 0|$ (i.e. as a state-preparation), rather than changing into the basis of the operator.\n", + "1. CVaRExpectation\n", + "1. (tentative) BasicAerExpectation\n", + "1. RichardsonExpectation - OUT OF SCOPE, BEING COVERED IN ANOTHER DOC.\n", + "\n", + "##### Grouping\n", + "\n", + "Grouping is an important feature within the PauliExpectation in Aqua today, but has an interface which is not obvious. Grouping should be moved into the PauliExpectation, with a simple interface for the user to specify whether to group the Paulis, or how aggresively to do so. By default, the PauliExpectation should group Paulis as aggressively as is performant on the given execution backend." ] }, { @@ -941,24 +931,24 @@ } }, "source": [ - "### Changes to Terra\n", - "\n", - "The `quantum_info` directory should be organized as follows:\n", - "\n", - " * channel\n", - " * ...\n", - " * matrix.py **- Decision: Rename operator.py to matrix.py or matrix_op.py?**\n", - " * pauli.py\n", - " * clifford.py **- Decision: Use the Ignis's Clifford?**\n", - " * projector.py\n", - " * stabilizer.py\n", - " * Composites\n", - " * op_sum.py, op_composite.py, op_kron.py, op_vec.py, op_exp.py\n", - "\n", - "In addition to the functionality detailed in [Object Model and Hierarchy](#Object-Model-and-Hierarchy) above, Terra should support the following for all of the above Non-matrix-based operators:\n", - "* `to_matrix()` - Method to allow quick access to unscalable classical tools, e.g. numpy eigensolution\n", - "* `to_quantum_circuits()` - returns a single or list of quantum circuits and coefficients representing the full Operator, including any distributive composition, tensors, etc.\n", - "* Trace, Partial Trace, Determinant, Norms, Adjoints - Where possible, linear algebra should be easily accessible " + "##### Circuit Evolution Algorithms\n", + "\n", + "And similarly for Evolution, a variety of algorithms should be available for converting a OpEvo composite operator into a sum, composition, etc. More specifically, circuit evolution algorithms take an OpEvo placeholder and return operators which approximate the value of the exponentiation. For example, the PauliEvolution accepts a Pauli and returns a QuantumCircuit representing the unitary evolution of that Pauli. \n", + "\n", + "1. **[0.7]** A base Evolution factory module which automatically selects and returns an evolution algorithm based on the backend and operator given - e.g. if a MatrixOperator is passed, the MatrixEvolution module will be instantiated. A similar flow to the Expectation base will encapsulate the handling of combinations on behalf of the Evolution submodules below, but with evolution-specific rules. For example, OpCompose and OpVec can be handled trivially, but OpSum must be trotterized, and OpKron's components must be kroned together.\n", + "1. **[0.7]** PauliEvolution (Change-of-Basis)\n", + "1. **[0.7]** SumEvolution\n", + " 1. Trotter\n", + " 1. Suzuki\n", + "1. **[0.7]** MatrixEvolution\n", + "1. (tentative) [LinCombEvolution](https://arxiv.org/abs/1202.5822)\n", + "1. (tentative) AerEvolution\n", + "1. (tentative) BasicAerEvolution\n", + "\n", + "##### Other algorithms to build out into first-class Algorithm groups\n", + "1. **[0.7]** Converters - convert lazily between Operator types\n", + "1. **[0.7]** Gradients\n", + "1. Optimizers" ] }, { @@ -969,9 +959,9 @@ } }, "source": [ - "##### Follow-on: Terra Reconciliation Between Operators and Gates\n", + "## ⚰️⚰️⚰️⚰️⚰️⚰️ Graveyard ⚰️⚰️⚰️⚰️⚰️⚰️\n", "\n", - "Terra's Operators and Gates are currently fully distinct from one another. The X, Y, Z, Clifford Gates, Evolution by a matrix-specified Unitary (UnitaryGate), and more are direct overlaps between the two, but not interoperable. At some point, Terra should address this difference to allow Operators to be inserted onto a circuit, maintain only a single set of primitive unitaries, allow Gates to be composed with Operators, etc." + "The following were deemed out of scope or no longer necessary for inclusion in this doc, but preserved here for posterity." ] }, { @@ -982,47 +972,60 @@ } }, "source": [ - "### Changes to Aqua\n", + "#### Parameterization and Eagerness\n", "\n", - "The changes to Aqua are basically just to \n", - "* deprecate the Operators after moving their logic into Terra, \n", - "* change the Aqua algorithms to rely on the new Terra operators,\n", - "* break up the Expectation, Evolution, circuit execution, and gradient code to be first-class algorithms users can extend and understand,\n", - "* and change the exsting Aqua algorithms to rely on these new algorithms." + "Operators and algorithms can be _parameterized,_ or missing some key information in order to execute. For Operators these may be sum coefficients, evolution times, QuantumCircuit parameters, and more. For Algorithms these may be input operators, execution parameters, or instances of algorithms used in computation which cannot be inferred by default (e.g. backend on which to execute, optimizer, etc.).\n", + "\n", + "##### Eager Parameterization+Execution Interface Options:\n", + "\n", + "An algorithm should execute as soon as it has filled the parameters necessary to do so. This is called **Eager Execution.** In a similar vein, OpSum can be seen as eagerly waiting for the contained operators to be summable, e.g. replaced with scalars by an expectation value. (**Decision**) Some interface options for eagerness:\n", + "\n", + "**Option A**: Algorithms should be **callable** with a parameter dictionary, triggering a breadth-first search to parameterize any sub-objects with the parameter dictionary. This may be too much hocus pocus and difficult for implementers of algorithms to understand. A user may want to parameterize without executing, so an `execute` parameter should be available in the parameterization function." ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "metadata": { - "slideshow": { - "slide_type": "slide" + "ExecuteTime": { + "end_time": "2020-01-16T01:08:01.116018Z", + "start_time": "2020-01-16T01:08:00.856363Z" } }, + "outputs": [], "source": [ - "##### Change Algorithms to rely on Terra operators and new Operator algorithms\n", - "\n", - "In particular, algorithms should be accessible with only Terra-defined inputs (meaning constructed using Terra alone) to provide a seamless experience between Terra and Aqua usage, and extensible interfaces. For example, a VQE should be runnable by passing only a parameterized QuantumCircuit and Terra-defined Operator, allowing a provider or collaborator to share a custom VQE without an unnecessary dependency on Aqua. In particular, this allows the Aer Expectation Value to be defined with the same interface as Aqua's Pauli Expectation, without a dependency on Aqua." + "my_op = Parameter('t1')*(Z^Z) + .6*(X^I)\n", + "my_vqe = VQE(backend=Parameter('backend'), \n", + " operator=my_op, \n", + " ansatz=Ry(qubits=2, reps=3), \n", + " optimizer=SLSQP(initial_point=Parameter('initial_rotations')))\n", + "my_vqe({'t1': .2j, 'backend': Aer.get_backend('qasm_simulator')}) \n", + " # Didn't return anything yet\n", + "rots = np.zeros(len(my_vqe.ansatz.params))\n", + "min_eig = my_vqe({'initial_rotations': rots})\n", + " # Now a value is returned, and other execution information can be found inside the object" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + " # Alternatively\n", + "my_vqe({'initial_rotations': rots}, execute=False)\n", + "min_eig = my_vqe()" ] }, { "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, + "metadata": {}, "source": [ - "##### Circuit Execution Algorithms - **Decision: Name - CircuitExecution? QCExecute? QuantumMeasureZ? RunCircuit?**\n", - "\n", - "Circuit execution is a utility in Aqua today, mediated by the QuantumInstance, which most users do not understand, and growing increasingly branchy to accommodate more and more execution variants. Measurement error mitigation, noisy simulation setup, hardware API fault handling, and more all fall into the same execution flow in various branches. \n", + "**Option B:** Algorithms should have a `.run(param_dict)` method which accepts parameters and performs the breadth-first parameterization. The form factor of this would be similar to the above, but with `run()` instead of direct function calls. This has the benefit of some backward compatibility. \n", "\n", - "Circuit execution is an algorithm for sampling a circuit's expectation in exponentially many ${Z, I}^{\\otimes n}$ bases, but is not reflected an an algorithm today. It should be promoted to be a first-class algorithm to be more transparent and compartmental, wherein for example, code for simulation and code for execution on hardware can be kept distinct. A CircuitExecution Algorithm accepts a backend and interacts with it in some well-defined way - in way breaking up and organizing of the functionality of the QuantumInstance. Some examples of CircuitExecution algorithms are:\n", - "1. QuantumHardware - An Execution algorithm tailored for execution on remote hardware, including fault handling, slicing to limit job sizes, etc. Can stack up a queue of circuits for batch execution, or accept a list of jobids to use as the first n results objects, allowing the user to reuse results from a terminated execution.\n", - "1. IdealSimulator - Algorithm tailored for execution in ideal simulation.\n", - "1. NoisySimulator - Utility for querying a Hardware backend's properties and providing a noisy simulator using Aer's \"noise config from device\" functionality.\n", - "1. ErrorMitigatedExecutor - OUT OF SCOPE, BEING COVERED IN ANOTHER DOC.\n", + "**Option C:** Algorithms should support separate parameterization and execution functions. This is the most explicit, but is clunky in an eager execution regime, where execution is automatic if the algorithm is sufficiently parameterized.\n", "\n", - "If none is explicitly specified, Aqua should aggressively guess the preferred execution algorithm for the user given the backend and other execution parameters." + "All of an Algorithm or Operator's pending Parameters should be recursively returned by a `.params` function. _(Tentative)_ A `deepcopy` option should be available to return a deep copy of the algorithm with the desired parameterization, rather than parameterize the algorithm in-place (this is evaluated with `execute=False` by default)." ] }, { @@ -1033,19 +1036,9 @@ } }, "source": [ - "##### Expectation Algorithms\n", - "\n", - "Aqua should support the following ExpectationValue algorithms. An `ExpectationBase` class should allow automatic selection of an evolution algorithm by default if none is specified - e.g. if the user has Aer installed, VQE will use the AerExpectation by default instead of QASM execution. Other possible expectation values include:\n", - "1. PauliExpectation (Change-of-Basis)\n", - "1. CVaRExpectation\n", - "1. AerExpectation - relies on Aer's fast expectation feature\n", - "1. MatrixExpectation\n", - "1. (Tentative) BasicAerExpectation\n", - "1. RichardsonExpectation - OUT OF SCOPE, BEING COVERED IN ANOTHER DOC.\n", - "\n", - "##### Grouping\n", + "##### Change Algorithms to rely on Terra operators and new Operator algorithms\n", "\n", - "Grouping is an important feature within the PauliExpectation in Aqua today, but is not used by default, and has an interface which is not obvious. Grouping should be moved into the PauliExpectation, with a simple interface for the user to specify whether to group the Paulis, or how aggresively to do so. By default, the PauliExpectation should group Paulis as aggressively as is performant on the given execution backend." + "In particular, algorithms should be accessible with only Terra-defined inputs (meaning constructed using Terra alone) to provide a seamless experience between Terra and Aqua usage, and extensible interfaces. For example, a VQE should be runnable by passing only a parameterized QuantumCircuit and Terra-defined Operator, allowing a provider or collaborator to share a custom VQE without an unnecessary dependency on Aqua. In particular, this allows the Aer Expectation Value to be defined with the same interface as Aqua's Pauli Expectation, without a dependency on Aqua." ] }, { @@ -1056,23 +1049,17 @@ } }, "source": [ - "##### Circuit Evolution Algorithms\n", + "##### Circuit Execution Algorithms - **Decision: Name - CircuitExecution? QCExecute? QuantumMeasureZ? RunCircuit?**\n", "\n", - "And similarly for Evolution, a variety of algorithms should be available for converting a OpExp composite operator into a sum, composition, etc. More specifically, circuit evolution algorithms take an OpExp placeholder and return operators which approximate the value of the exponentiation. For example, the PauliEvolution accepts a Pauli and returns a QuantumCircuit representing the unitary evolution of that Pauli. An `EvolutionBase` class should allow automatic selection of an evolution algorithm by default if none is specified.\n", + "Circuit execution is a utility in Aqua today, mediated by the QuantumInstance, which most users do not understand, and growing increasingly branchy to accommodate more and more execution variants. Measurement error mitigation, noisy simulation setup, hardware API fault handling, and more all fall into the same execution flow in various branches. \n", "\n", - "1. PauliEvolution (Change-of-Basis)\n", - "1. SumEvolution\n", - " 1. Trotter\n", - " 1. Suzuki\n", - "1. MatrixEvolution\n", - "1. (Tentative) [LinCombEvolution](https://arxiv.org/abs/1202.5822)\n", - "1. (Tentative) AerEvolution\n", - "1. (Tentative) BasicAerEvolution\n", + "Circuit execution is an algorithm for sampling a circuit's expectation in exponentially many ${Z, I}^{\\otimes n}$ bases, but is not reflected an an algorithm today. It should be promoted to be a first-class algorithm to be more transparent and compartmental, wherein for example, code for simulation and code for execution on hardware can be kept distinct. A CircuitExecution Algorithm accepts a backend and interacts with it in some well-defined way - in way breaking up and organizing of the functionality of the QuantumInstance. Some examples of CircuitExecution algorithms are:\n", + "1. QuantumHardware - An Execution algorithm tailored for execution on remote hardware, including fault handling, slicing to limit job sizes, etc. Can stack up a queue of circuits for batch execution, or accept a list of jobids to use as the first n results objects, allowing the user to reuse results from a terminated execution.\n", + "1. IdealSimulator - Algorithm tailored for execution in ideal simulation.\n", + "1. NoisySimulator - Utility for querying a Hardware backend's properties and providing a noisy simulator using Aer's \"noise config from device\" functionality.\n", + "1. ErrorMitigatedExecutor - OUT OF SCOPE, BEING COVERED IN ANOTHER DOC.\n", "\n", - "##### Other algorithms to build out into first-class Algorithm groups\n", - "1. Converters - convert lazily between Operator types\n", - "1. Gradient\n", - "1. Optimization" + "If none is explicitly specified, Aqua should aggressively guess the preferred execution algorithm for the user given the backend and other execution parameters." ] }, { @@ -1083,7 +1070,7 @@ } }, "source": [ - "## Timeline and Gameplan\n", + "#### Timeline and Gameplan\n", "\n", "Stage 1: Implement new Operators in Terra with thorough unit and integration tests.\n", "\n", @@ -1104,18 +1091,7 @@ } }, "source": [ - "## ⚰️⚰️⚰️⚰️⚰️⚰️ Graveyard ⚰️⚰️⚰️⚰️⚰️⚰️" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "### Other Benefits of OperatorFlow\n", + "### Other Benefits of Operator Combinations\n", "\n", "* Obedient Eager Evaluation - Best of Eager and Lazy evaluation:\n", " * Partially evaluate whatever you can with the parameters you have\n", @@ -1216,6 +1192,15 @@ "pygments_lexer": "ipython3", "version": "3.7.4" }, + "pycharm": { + "stem_cell": { + "cell_type": "raw", + "metadata": { + "collapsed": false + }, + "source": [] + } + }, "toc": { "base_numbering": 1, "nav_menu": {}, @@ -1232,18 +1217,9 @@ "width": "328.594px" }, "toc_section_display": true, - "toc_window_display": true - }, - "pycharm": { - "stem_cell": { - "cell_type": "raw", - "source": [], - "metadata": { - "collapsed": false - } - } + "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 1 -} \ No newline at end of file +}