{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Parametrized circuits\n", "\n", "This tutorial shows you can submit parametrized quantum circuits to the `POVMSampler`." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "%load_ext autoreload\n", "%autoreload 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Parametrized Circuit\n", "\n", "Let us look at a 2-qubit quantum circuit with 5 parameters." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "<pre style=\"word-wrap: normal;white-space: pre;background: #fff0;line-height: 1.1;font-family: "Courier New",Courier,monospace\"> ┌────────────────────────────────────────────────┐\n", "q_0: ┤0 ├\n", " │ RealAmplitudes(θ[0],θ[1],θ[2],θ[3],θ[4],θ[5]) │\n", "q_1: ┤1 ├\n", " └────────────────────────────────────────────────┘</pre>" ], "text/plain": [ " ┌────────────────────────────────────────────────┐\n", "q_0: ┤0 ├\n", " │ RealAmplitudes(θ[0],θ[1],θ[2],θ[3],θ[4],θ[5]) │\n", "q_1: ┤1 ├\n", " └────────────────────────────────────────────────┘" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from qiskit.circuit.library import RealAmplitudes\n", "\n", "# Prepare inputs.\n", "num_qubits = 2\n", "qc = RealAmplitudes(num_qubits=num_qubits, reps=2)\n", "qc.draw()" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "\n", "# Assume you want to run the circuit with two different sets of parameter values\n", "theta = np.array(\n", " [\n", " [0, 1, 1, 2, 3, 5], # first set of parameter values\n", " [0, 1, 1, 2, 2, 5], # second set of parameter values\n", " ]\n", ")\n", "# Shape of the resulting `BindingsArray` is (2,) with `num_param` equal to 6" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Measurement\n", "\n", "We now look at the implementation of Classical Shadows measurement" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "from povm_toolbox.library import ClassicalShadows\n", "\n", "# By default, the Classical Shadows (CS) measurement uses X,Y,Z measurements with equal probability.\n", "cs_implementation = ClassicalShadows(num_qubits=num_qubits, seed=342)\n", "# Define the default shot budget.\n", "shots = 4096" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "pub = (qc, theta, shots, cs_implementation)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "from povm_toolbox.sampler import POVMSampler\n", "from qiskit.primitives import StatevectorSampler as Sampler\n", "\n", "# Internal `BaseSampler`\n", "sampler = Sampler(seed=432)\n", "\n", "# Actual `POVMSampler` instance\n", "povm_sampler = POVMSampler(sampler=sampler)\n", "\n", "# Submit the job by specifying the list of PUBs to run, here we have a single PUB.\n", "job = povm_sampler.run([pub])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Results\n", "We submitted a single PUB, hence the `PrimitiveResult` will contain only one `POVMPubResult`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "POVMPubResult(data=DataBin<2>(povm_measurement_creg=BitArray(<shape=(2,), num_shots=4096, num_bits=2>)), metadata=RPMMetadata(povm_implementation=ClassicalShadows(num_qubits=2), composed_circuit=<qiskit.circuit.library.n_local.real_amplitudes.RealAmplitudes object at 0x177cbbd40>, pvm_keys=np.ndarray<2,4096,2>))\n", "(2,)\n" ] } ], "source": [ "pub_result = job.result()[0]\n", "print(pub_result)\n", "\n", "# Note that the pub result will contain a `BitArray` that has the same shape\n", "# as the submitted `BindingsArray`, which is (2,) in this example.\n", "print(pub_result.get_counts(loc=...).shape)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We now define our POVM post-processor, which will use the result object to estimate expectation values of some observables." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "from povm_toolbox.post_processor.povm_post_processor import POVMPostProcessor\n", "\n", "post_processor = POVMPostProcessor(pub_result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since the POVM implementation that we used is informationally complete, we can define our observables of interest after the sampling process." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "from qiskit.quantum_info import SparsePauliOp\n", "\n", "H1 = SparsePauliOp.from_list([(\"II\", 1), (\"IZ\", 2), (\"XI\", 3)])\n", "H2 = SparsePauliOp.from_list([(\"II\", 1), (\"XX\", 1), (\"YY\", -1), (\"ZZ\", 1)])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The post-processor will return two expectation values corresponding to the two different sets of parameter values that were submitted." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1.98364258 4.71411133]\n" ] } ], "source": [ "exp_value, std = post_processor.get_expectation_value(H1)\n", "print(exp_value)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we are interested to evaluate an observable only for one set of parameter values, this set can be specified through the `loc` argument." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.05297851562500033\n" ] } ], "source": [ "exp_value, std = post_processor.get_expectation_value(H2, loc=1)\n", "print(exp_value)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Further example\n", "Let us look at a `BindingsArray` instance with a more complex shape." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "bindings_array_shape = (3, 4)\n", "num_param = 6\n", "\n", "theta = np.arange(72).reshape((*bindings_array_shape, num_param))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(3, 4)\n" ] } ], "source": [ "job = povm_sampler.run([(qc, theta, shots, cs_implementation)])\n", "pub_result = job.result()[0]\n", "print(pub_result.get_counts(loc=...).shape)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[-2.65698242 -1.44775391 4.69360352 3.39575195]\n", " [-4.09399414 -2.57861328 -1.41918945 5.98339844]\n", " [ 4.74121094 5.98266602 -0.06347656 -0.11474609]]\n", "[[0.09684276 0.07191009 0.05729555 0.07203544]\n", " [0.0796096 0.09563027 0.10687004 0.07980559]\n", " [0.0973881 0.07996116 0.08056718 0.07930243]]\n" ] } ], "source": [ "post_processor = POVMPostProcessor(pub_result)\n", "exp_values, std = post_processor.get_expectation_value(H1)\n", "print(exp_values)\n", "print(std)" ] } ], "metadata": { "kernelspec": { "display_name": ".venv", "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.12.11" } }, "nbformat": 4, "nbformat_minor": 2 }