diff --git a/notebooks/Training Ansatzes.ipynb b/notebooks/Training Ansatzes.ipynb new file mode 100644 index 0000000..dd11cac --- /dev/null +++ b/notebooks/Training Ansatzes.ipynb @@ -0,0 +1,413 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Import `quick` modules. For this demo, we will import the circuit instances." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from quick.circuit import QiskitCircuit, Ansatz\n", + "from quick.circuit.circuit_utils import flatten, reshape" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will also import some basic modules for angle calculations." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Variational Quantum Computing\n", + "\n", + "Quantum Circuits with single qubit rotation gates are known as Parameterized Quantum Circuits (PQC). If we variationally update the rotation angles of a PQC to minimize a cost function, we call the PQC an Ansatze for clarity in notation.\n", + "\n", + "In `quick.circuit`, we provide the Ansatze as a separate class for brevity and easier readability. You can use `quick.circuit.Ansatz` to \"convert\" ordinary `quick.circuit.Circuit` instances to ansatzes. This is done by simply using the `.update()` where we just change the rotation angle values and update to re-construct the updated circuit. Use-cases of variational quantum circuits include:\n", + "- Supervised QML\n", + "- Approximate Quantum Compilation (AQC)\n", + "- Quantum Approximate Optimization Algorithm (QAOA)\n", + "- Variational Quantum Eigensolver (VQE)\n", + "\n", + "For this notebook, we will mainly focus on AQC." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will use `scipy.optimize.minimize` for optimizing the parameters. Scipy provides an elegant and minimalistic interface for accessing a variety of SOTA optimization algorithms without the hassle of manually setting each one up or passing a multitude of hyper-parameters. For our use-case in this notebook, scipy is an excellet fit. It should however be noted that if the use-case requires more sophisticated optimization techniques depending on the cost landscape, users are urged to implement their own optimization routines and/or use more advanced optimization libraries." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "from scipy.optimize import minimize" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we will generate a random state to be encoded using Shende's synthesis as our initial parameter." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "from quick.random import generate_random_state\n", + "\n", + "random_state = generate_random_state(6)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "circuit = QiskitCircuit(6)\n", + "circuit.initialize(random_state, range(6))\n", + "\n", + "ansatz = Ansatz(circuit)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we will define our target state (the state which we want to prepare). The goal of our VQA is to variationally train the circuit such that the inner-product between the statevector represented by our ansatz and the target state is maximized, or in other words the infidelity is minimized.\n", + "\n", + "We can use any arbitrary statevector, but for visualization purposes let's use a MNIST image instance." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "\n", + "# Load in the resized MNIST dataset\n", + "dataset = pd.read_csv('datasets/mnist-resized.csv')\n", + "\n", + "# Convert the dataset to a numpy array\n", + "images = dataset.to_numpy()[:,1:].reshape(30018, 8, 8)\n", + "\n", + "# Get the first image\n", + "test_image = images.reshape(30018, 8, 8)[0, :]" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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tB555JH0V3bcqj777DNVV1dr7Nixqqur0/Lly7Vy5Uq98cYbXk9zZceOHbp48aKeeeYZzzb0+W9TRv8SjUZ1/PhxX7y3Lkn33XefmpubFY/H9dZbb6miokL19fW3dGRaWlq0atUq7dmzR4MGDfJ6TreVl5en/3dxcbGmTZumoqIi/eEPf9Czzz7r4bKupVIplZaWau3atZKkSZMm6fjx43rttddUUVHh8brue/3111VeXq6CggLPNvj+DubOO+9UVlaWLly40On5CxcuaOTIkR6t+nZYsWKF3n33XX3wwQfmX8HQW7Kzs3XPPfdo8uTJisViKikp0SuvvOL1rC41NTWptbVVDz74oILBoILBoOrr6/Xqq68qGAyqo6PD64ndcscdd+jee+/VqVOnvJ7Spfz8/Gv+D8f999/vi7f3vvHFF19o7969+tGPfuTpDt8HJjs7W5MnT9a+ffvSz6VSKe3bt8837637jeM4WrFihWpra/X+++9rzJgxXk/KWCqVUjKZ9HpGl2bNmqVjx46pubk5fZSWlmrJkiVqbm5WVlaW1xO75dKlS/r000+Vn5/v9ZQulZWVXfNj95988omKioo8WuReTU2N8vLyNG/ePE939Iu3yCorK1VRUaHS0lJNnTpV69evV3t7u5YuXer1tC5dunSp0/+bO336tJqbmzV06FCNGjXKw2Vdi0aj2rp1q3bu3KmcnJz0Z13hcFiDBw/2eN2NVVVVqby8XKNGjVJbW5u2bt2q/fv3q66uzutpXcrJybnm863bbrtNw4YNu6U/93rhhRc0f/58FRUV6dy5c1qzZo2ysrK0ePFir6d16fnnn9dDDz2ktWvX6qmnntLhw4e1adMmbdq0yetp3ZJKpVRTU6OKigoFgx7/J96Tn10z8Otf/9oZNWqUk52d7UydOtVpaGjwetJNffDBB46ka46Kigqvp3XpepslOTU1NV5P69KyZcucoqIiJzs72xk+fLgza9Ys57333vN6Vkb88GPKixYtcvLz853s7Gznu9/9rrNo0SLn1KlTXs/qlnfeeceZOHGiEwqFnHHjxjmbNm3yelK31dXVOZKcEydOeD3F4df1AwBM+P4zGADArYnAAABMEBgAgAkCAwAwQWAAACYIDADABIEBAJggMAAAEwQGAGCCwAAATBAYAIAJAgMAMPF/NSeMDcFGZn0AAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "# Plot the first image\n", + "plt.imshow(test_image)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "target_state = test_image.flatten() / np.linalg.norm(test_image.flatten())" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(0.24593246121529486+0.14333213936238834j)" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "target_state.conj().T @ ansatz.ansatz.get_statevector()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will then define the training routine and plot the improvements as we optimize. In case the scipy optimizer keeps going after the `max_iter` threshold, feel free to interrupt the cell." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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5x8cPHiwWue4evUqGjVqVGmdku25ubkAiicEW1lZYePGjXKdpKQknDhxQm8ez6ZNm3D//fejcePGerEHBgZCq9Xi119/1TvPU089hWbNmlUrfkONGzdO/tnZ2RkdO3aEvb09hg8fLpd37NgRzs7Oer9f1b0mInPHoSsiM2JjY1PmRti4ceO7zpMpz/nz5wEA7du3L7OtY8eOcgIBAH///TdycnLQvHnzco916dKlap+/KjIzM5GdnY1PPvkEn3zySaXnfuONN/Dzzz/Dz88P7dq1w8CBA/Hss8+ib9++lZ6jUaNGyMrKqrTO1atX5boA4OLiggEDBuDrr7/G3LlzARQPW1lZWek9+fT333/j2LFjFSYvpdutZHiorpX3e+Tk5ISWLVvqzZcqKb/z96u610Rk7pjoEJkRpVJpkvPqdDo0b94c69atK3d7XfVClExufe655zB69Ohy65Q8gdS5c2ckJydj+/btiImJwbfffosVK1Zg1qxZ5U4cLtG5c2ckJiYiNTUVrVq1KrfOsWPHAECvp+uZZ55BWFgYEhMT4e3tja+//hoDBgyAi4uLXvwPP/wwXn/99XKP26FDB73vtra2FcZZmyr6PaqoXAgh/1zdayIyd0x0iOqp1q1bAyj+P/TSkpOT9b63bdsWP//8M/r27VsrN+PSvQYVadasGRo1agStVovAwMC71re3t0dISAhCQkJQWFiIoUOH4r333kNERESFj1k//vjj+Oqrr/DFF1/g7bffLrM9NzcXW7duRadOnfTWrwkODsaECRPk4atTp04hIiJCb9+2bdsiLy+vSrFbivp4TdSwcY4OUT3l7u4Ob29vfP7558jJyZHLd+3aVeax5uHDh0Or1crDNHe6efMmsrOzq3Vue3t7ALjrfkqlEk899ZT8CHhpdz5JdvnyZb1tKpUKXl5eEEKgqKiownM8/fTT8PLywvz588usRK3T6TBx4kT8999/ZZ6wcnZ2RlBQEL7++mts2LABKpUKwcHBenWGDx+OuLg47Ny5s8x5s7OzcfPmzQrjMlf18ZqoYWOPDlE9FhkZiUGDBuG+++7D888/jytXrshr0eTl5cn1+vXrhwkTJiAyMhKJiYkYOHAgrK2t8ffff2PTpk1YunQpnn766Sqf18fHBwAwZcoUBAUFQalU4plnnim37vz587F79274+/tj/Pjx8PLywpUr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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "ename": "KeyboardInterrupt", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[11], line 52\u001b[0m\n\u001b[1;32m 49\u001b[0m bounds \u001b[38;5;241m=\u001b[39m [(\u001b[38;5;241m0\u001b[39m, \u001b[38;5;241m2\u001b[39m\u001b[38;5;241m*\u001b[39mnp\u001b[38;5;241m.\u001b[39mpi) \u001b[38;5;28;01mfor\u001b[39;00m _ \u001b[38;5;129;01min\u001b[39;00m initial_params]\n\u001b[1;32m 51\u001b[0m \u001b[38;5;66;03m# Perform the optimization\u001b[39;00m\n\u001b[0;32m---> 52\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mminimize\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcost_function\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minitial_params\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mshape\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmethod\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mBFGS\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43moptions\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m{\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mmaxiter\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m2000\u001b[39;49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 54\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", + "File \u001b[0;32m~/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/scipy/optimize/_minimize.py:726\u001b[0m, in \u001b[0;36mminimize\u001b[0;34m(fun, x0, args, method, jac, hess, hessp, bounds, constraints, tol, callback, options)\u001b[0m\n\u001b[1;32m 724\u001b[0m res \u001b[38;5;241m=\u001b[39m _minimize_cg(fun, x0, args, jac, callback, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39moptions)\n\u001b[1;32m 725\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m meth \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mbfgs\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[0;32m--> 726\u001b[0m res \u001b[38;5;241m=\u001b[39m \u001b[43m_minimize_bfgs\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfun\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mx0\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mjac\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcallback\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43moptions\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 727\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m meth \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnewton-cg\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[1;32m 728\u001b[0m res \u001b[38;5;241m=\u001b[39m _minimize_newtoncg(fun, x0, args, jac, hess, hessp, callback,\n\u001b[1;32m 729\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39moptions)\n", + "File \u001b[0;32m~/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/scipy/optimize/_optimize.py:1397\u001b[0m, in \u001b[0;36m_minimize_bfgs\u001b[0;34m(fun, x0, args, jac, callback, gtol, norm, eps, maxiter, disp, return_all, finite_diff_rel_step, xrtol, c1, c2, hess_inv0, **unknown_options)\u001b[0m\n\u001b[1;32m 1394\u001b[0m pk \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m-\u001b[39mnp\u001b[38;5;241m.\u001b[39mdot(Hk, gfk)\n\u001b[1;32m 1395\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 1396\u001b[0m alpha_k, fc, gc, old_fval, old_old_fval, gfkp1 \u001b[38;5;241m=\u001b[39m \\\n\u001b[0;32m-> 1397\u001b[0m \u001b[43m_line_search_wolfe12\u001b[49m\u001b[43m(\u001b[49m\u001b[43mf\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmyfprime\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mxk\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mpk\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mgfk\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1398\u001b[0m \u001b[43m \u001b[49m\u001b[43mold_fval\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mold_old_fval\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mamin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m1e-100\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1399\u001b[0m \u001b[43m \u001b[49m\u001b[43mamax\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m1e100\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mc1\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mc1\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mc2\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mc2\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1400\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m _LineSearchError:\n\u001b[1;32m 1401\u001b[0m \u001b[38;5;66;03m# Line search failed to find a better solution.\u001b[39;00m\n\u001b[1;32m 1402\u001b[0m warnflag \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m2\u001b[39m\n", + "File \u001b[0;32m~/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/scipy/optimize/_optimize.py:1133\u001b[0m, in \u001b[0;36m_line_search_wolfe12\u001b[0;34m(f, fprime, xk, pk, gfk, old_fval, old_old_fval, **kwargs)\u001b[0m\n\u001b[1;32m 1119\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 1120\u001b[0m \u001b[38;5;124;03mSame as line_search_wolfe1, but fall back to line_search_wolfe2 if\u001b[39;00m\n\u001b[1;32m 1121\u001b[0m \u001b[38;5;124;03msuitable step length is not found, and raise an exception if a\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 1128\u001b[0m \n\u001b[1;32m 1129\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 1131\u001b[0m extra_condition \u001b[38;5;241m=\u001b[39m kwargs\u001b[38;5;241m.\u001b[39mpop(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mextra_condition\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m)\n\u001b[0;32m-> 1133\u001b[0m ret \u001b[38;5;241m=\u001b[39m \u001b[43mline_search_wolfe1\u001b[49m\u001b[43m(\u001b[49m\u001b[43mf\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfprime\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mxk\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mpk\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mgfk\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1134\u001b[0m \u001b[43m \u001b[49m\u001b[43mold_fval\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mold_old_fval\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1135\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1137\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m ret[\u001b[38;5;241m0\u001b[39m] \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m extra_condition \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 1138\u001b[0m xp1 \u001b[38;5;241m=\u001b[39m xk \u001b[38;5;241m+\u001b[39m ret[\u001b[38;5;241m0\u001b[39m] \u001b[38;5;241m*\u001b[39m pk\n", + "File \u001b[0;32m~/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/scipy/optimize/_linesearch.py:93\u001b[0m, in \u001b[0;36mline_search_wolfe1\u001b[0;34m(f, fprime, xk, pk, gfk, old_fval, old_old_fval, args, c1, c2, amax, amin, xtol)\u001b[0m\n\u001b[1;32m 89\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m np\u001b[38;5;241m.\u001b[39mdot(gval[\u001b[38;5;241m0\u001b[39m], pk)\n\u001b[1;32m 91\u001b[0m derphi0 \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mdot(gfk, pk)\n\u001b[0;32m---> 93\u001b[0m stp, fval, old_fval \u001b[38;5;241m=\u001b[39m \u001b[43mscalar_search_wolfe1\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 94\u001b[0m \u001b[43m \u001b[49m\u001b[43mphi\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mderphi\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mold_fval\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mold_old_fval\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mderphi0\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 95\u001b[0m \u001b[43m \u001b[49m\u001b[43mc1\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mc1\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mc2\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mc2\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mamax\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mamax\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mamin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mamin\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mxtol\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mxtol\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 97\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m stp, fc[\u001b[38;5;241m0\u001b[39m], gc[\u001b[38;5;241m0\u001b[39m], fval, old_fval, gval[\u001b[38;5;241m0\u001b[39m]\n", + "File \u001b[0;32m~/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/scipy/optimize/_linesearch.py:170\u001b[0m, in \u001b[0;36mscalar_search_wolfe1\u001b[0;34m(phi, derphi, phi0, old_phi0, derphi0, c1, c2, amax, amin, xtol)\u001b[0m\n\u001b[1;32m 167\u001b[0m maxiter \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m100\u001b[39m\n\u001b[1;32m 169\u001b[0m dcsrch \u001b[38;5;241m=\u001b[39m DCSRCH(phi, derphi, c1, c2, xtol, amin, amax)\n\u001b[0;32m--> 170\u001b[0m stp, phi1, phi0, task \u001b[38;5;241m=\u001b[39m \u001b[43mdcsrch\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 171\u001b[0m \u001b[43m \u001b[49m\u001b[43malpha1\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mphi0\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mphi0\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mderphi0\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mderphi0\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmaxiter\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmaxiter\u001b[49m\n\u001b[1;32m 172\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 174\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m stp, phi1, phi0\n", + "File \u001b[0;32m~/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/scipy/optimize/_dcsrch.py:256\u001b[0m, in \u001b[0;36mDCSRCH.__call__\u001b[0;34m(self, alpha1, phi0, derphi0, maxiter)\u001b[0m\n\u001b[1;32m 254\u001b[0m alpha1 \u001b[38;5;241m=\u001b[39m stp\n\u001b[1;32m 255\u001b[0m phi1 \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mphi(stp)\n\u001b[0;32m--> 256\u001b[0m derphi1 \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mderphi\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstp\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 257\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 258\u001b[0m \u001b[38;5;28;01mbreak\u001b[39;00m\n", + "File \u001b[0;32m~/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/scipy/optimize/_linesearch.py:87\u001b[0m, in \u001b[0;36mline_search_wolfe1..derphi\u001b[0;34m(s)\u001b[0m\n\u001b[1;32m 86\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mderphi\u001b[39m(s):\n\u001b[0;32m---> 87\u001b[0m gval[\u001b[38;5;241m0\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[43mfprime\u001b[49m\u001b[43m(\u001b[49m\u001b[43mxk\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[43ms\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mpk\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 88\u001b[0m gc[\u001b[38;5;241m0\u001b[39m] \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m1\u001b[39m\n\u001b[1;32m 89\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m np\u001b[38;5;241m.\u001b[39mdot(gval[\u001b[38;5;241m0\u001b[39m], pk)\n", + "File \u001b[0;32m~/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/scipy/optimize/_differentiable_functions.py:331\u001b[0m, in \u001b[0;36mScalarFunction.grad\u001b[0;34m(self, x)\u001b[0m\n\u001b[1;32m 329\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m np\u001b[38;5;241m.\u001b[39marray_equal(x, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mx):\n\u001b[1;32m 330\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_update_x(x)\n\u001b[0;32m--> 331\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_update_grad\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 332\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mg\n", + "File \u001b[0;32m~/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/scipy/optimize/_differentiable_functions.py:306\u001b[0m, in \u001b[0;36mScalarFunction._update_grad\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 304\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_orig_grad \u001b[38;5;129;01min\u001b[39;00m FD_METHODS:\n\u001b[1;32m 305\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_update_fun()\n\u001b[0;32m--> 306\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mg \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_wrapped_grad\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mf0\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mf\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 307\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mg_updated \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m\n", + "File \u001b[0;32m~/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/scipy/optimize/_differentiable_functions.py:47\u001b[0m, in \u001b[0;36m_wrapper_grad..wrapped1\u001b[0;34m(x, f0)\u001b[0m\n\u001b[1;32m 45\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mwrapped1\u001b[39m(x, f0\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[1;32m 46\u001b[0m ncalls[\u001b[38;5;241m0\u001b[39m] \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m1\u001b[39m\n\u001b[0;32m---> 47\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mapprox_derivative\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 48\u001b[0m \u001b[43m \u001b[49m\u001b[43mfun\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mf0\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mf0\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mfinite_diff_options\u001b[49m\n\u001b[1;32m 49\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/scipy/optimize/_numdiff.py:519\u001b[0m, in \u001b[0;36mapprox_derivative\u001b[0;34m(fun, x0, method, rel_step, abs_step, f0, bounds, sparsity, as_linear_operator, args, kwargs)\u001b[0m\n\u001b[1;32m 516\u001b[0m use_one_sided \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mFalse\u001b[39;00m\n\u001b[1;32m 518\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m sparsity \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m--> 519\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_dense_difference\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfun_wrapped\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mx0\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mf0\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mh\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 520\u001b[0m \u001b[43m \u001b[49m\u001b[43muse_one_sided\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmethod\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 521\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 522\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m issparse(sparsity) \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(sparsity) \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m2\u001b[39m:\n", + "File \u001b[0;32m~/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/scipy/optimize/_numdiff.py:592\u001b[0m, in \u001b[0;36m_dense_difference\u001b[0;34m(fun, x0, f0, h, use_one_sided, method)\u001b[0m\n\u001b[1;32m 590\u001b[0m x1[i] \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m h[i]\n\u001b[1;32m 591\u001b[0m dx \u001b[38;5;241m=\u001b[39m x1[i] \u001b[38;5;241m-\u001b[39m x0[i] \u001b[38;5;66;03m# Recompute dx as exactly representable number.\u001b[39;00m\n\u001b[0;32m--> 592\u001b[0m df \u001b[38;5;241m=\u001b[39m \u001b[43mfun\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx1\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;241m-\u001b[39m f0\n\u001b[1;32m 593\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m method \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m3-point\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m use_one_sided[i]:\n\u001b[1;32m 594\u001b[0m x1[i] \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m h[i]\n", + "File \u001b[0;32m~/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/scipy/optimize/_numdiff.py:470\u001b[0m, in \u001b[0;36mapprox_derivative..fun_wrapped\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 467\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m xp\u001b[38;5;241m.\u001b[39misdtype(x\u001b[38;5;241m.\u001b[39mdtype, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mreal floating\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n\u001b[1;32m 468\u001b[0m x \u001b[38;5;241m=\u001b[39m xp\u001b[38;5;241m.\u001b[39mastype(x, x0\u001b[38;5;241m.\u001b[39mdtype)\n\u001b[0;32m--> 470\u001b[0m f \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39matleast_1d(\u001b[43mfun\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m)\n\u001b[1;32m 471\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m f\u001b[38;5;241m.\u001b[39mndim \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m1\u001b[39m:\n\u001b[1;32m 472\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mRuntimeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m`fun` return value has \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 473\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmore than 1 dimension.\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", + "File \u001b[0;32m~/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/scipy/optimize/_differentiable_functions.py:20\u001b[0m, in \u001b[0;36m_wrapper_fun..wrapped\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 16\u001b[0m ncalls[\u001b[38;5;241m0\u001b[39m] \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m1\u001b[39m\n\u001b[1;32m 17\u001b[0m \u001b[38;5;66;03m# Send a copy because the user may overwrite it.\u001b[39;00m\n\u001b[1;32m 18\u001b[0m \u001b[38;5;66;03m# Overwriting results in undefined behaviour because\u001b[39;00m\n\u001b[1;32m 19\u001b[0m \u001b[38;5;66;03m# fun(self.x) will change self.x, with the two no longer linked.\u001b[39;00m\n\u001b[0;32m---> 20\u001b[0m fx \u001b[38;5;241m=\u001b[39m \u001b[43mfun\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcopy\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 21\u001b[0m \u001b[38;5;66;03m# Make sure the function returns a true scalar\u001b[39;00m\n\u001b[1;32m 22\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m np\u001b[38;5;241m.\u001b[39misscalar(fx):\n", + "Cell \u001b[0;32mIn[11], line 12\u001b[0m, in \u001b[0;36mcost_function\u001b[0;34m(thetas, shape)\u001b[0m\n\u001b[1;32m 9\u001b[0m infidelity \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m1\u001b[39m \u001b[38;5;241m-\u001b[39m \u001b[38;5;28mabs\u001b[39m(target_state\u001b[38;5;241m.\u001b[39mconj()\u001b[38;5;241m.\u001b[39mT \u001b[38;5;241m@\u001b[39m ansatz\u001b[38;5;241m.\u001b[39mansatz\u001b[38;5;241m.\u001b[39mget_statevector())\n\u001b[1;32m 11\u001b[0m infidelities\u001b[38;5;241m.\u001b[39mappend(infidelity)\n\u001b[0;32m---> 12\u001b[0m \u001b[43mupdate_plot\u001b[49m\u001b[43m(\u001b[49m\u001b[43minfidelities\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 14\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m infidelity\n", + "Cell \u001b[0;32mIn[11], line 43\u001b[0m, in \u001b[0;36mupdate_plot\u001b[0;34m(new_data)\u001b[0m\n\u001b[1;32m 41\u001b[0m clear_output(wait\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m)\n\u001b[1;32m 42\u001b[0m display(fig)\n\u001b[0;32m---> 43\u001b[0m \u001b[43mplt\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpause\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m0.1\u001b[39;49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/matplotlib/pyplot.py:760\u001b[0m, in \u001b[0;36mpause\u001b[0;34m(interval)\u001b[0m\n\u001b[1;32m 758\u001b[0m canvas\u001b[38;5;241m.\u001b[39mstart_event_loop(interval)\n\u001b[1;32m 759\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m--> 760\u001b[0m time\u001b[38;5;241m.\u001b[39msleep(interval)\n", + "\u001b[0;31mKeyboardInterrupt\u001b[0m: " + ] + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "from IPython.display import display, clear_output\n", + "\n", + "infidelities = []\n", + "\n", + "def cost_function(thetas, shape) -> complex:\n", + " ansatz.thetas = reshape(thetas, shape)\n", + "\n", + " infidelity = 1 - abs(target_state.conj().T @ ansatz.ansatz.get_statevector())\n", + "\n", + " infidelities.append(infidelity)\n", + " update_plot(infidelities)\n", + "\n", + " return infidelity\n", + "\n", + "# Create a figure and axis for dynamic plotting\n", + "fig, ax = plt.subplots()\n", + "line, = ax.plot([], [], 'r-', label='Infidelity')\n", + "smooth_line, = ax.plot([], [], 'b-', label='Smoothed Infidelity')\n", + "ax.set_xlim(0, 1)\n", + "ax.set_ylim(0, 1)\n", + "ax.set_title('Infidelities Over Time')\n", + "ax.set_xlabel('Iteration')\n", + "ax.set_ylabel('Infidelity')\n", + "ax.legend()\n", + "\n", + "# Function to update the plot\n", + "def update_plot(new_data):\n", + " line.set_xdata(range(len(new_data)))\n", + " line.set_ydata(new_data)\n", + "\n", + " # Calculate the moving average\n", + " window_size = 5\n", + " if len(new_data) >= window_size:\n", + " smoothed_data = np.convolve(new_data, np.ones(window_size)/window_size, mode='valid')\n", + " smooth_line.set_xdata(range(window_size-1, len(new_data)))\n", + " smooth_line.set_ydata(smoothed_data)\n", + "\n", + " ax.set_xlim(0, len(new_data))\n", + " ax.set_ylim(0, max(new_data) + 0.1)\n", + " clear_output(wait=True)\n", + " display(fig)\n", + " plt.pause(0.1)\n", + "\n", + "# Initial parameters for the ansatz\n", + "initial_params, shape = flatten(ansatz.thetas)\n", + "\n", + "# Define bounds for each parameter to be between 0 and 2*pi\n", + "bounds = [(0, 2*np.pi) for _ in initial_params]\n", + "\n", + "# Perform the optimization\n", + "result = minimize(cost_function, initial_params, args=(shape), method=\"BFGS\", options={'maxiter': 2000})\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that we performed the training with the absolute of the probability amplitudes as the image encoding does not require the inner product to be specifically 1+0j. For general state preparation, you need to use\n", + "```py\n", + "def cost_function(thetas, shape) -> complex:\n", + " ansatz.thetas = reshape(thetas, shape)\n", + "\n", + " infidelity = 1 - target_state.conj().T @ ansatz.ansatz.get_statevector()\n", + "\n", + " infidelities.append(infidelity)\n", + " update_plot(infidelities)\n", + "\n", + " return infidelity\n", + "```\n", + "Which will push the fidelity to be exactly 1+0j. However, note that would limit the good parameter space to a very tiny space, whereas for the absolute approach there are many more possible parameter states to choose from. This is how we reach convergence easier compared to a general state. With that being said, pay close attention to the task and what the cost function really needs to do." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(0.5896664357293144+0.807564869009721j)" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.inner(target_state.conj().T, ansatz.ansatz.get_statevector())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Whilst the inner-product is a fundamental measure of similarity, let us use our own eyes to assess the quality of the approximation." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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fx6TJ0+O+fPnx/r16+PYY49Nnatr164HdCwioiiK9l//+Mc/jquuuirGjh0bX/va16KhoSG6du0aN910Uzz77LNlz9HW1hYREdddd100NTX923PevgAA+GDspneXuZvmzp0bt99+e8ybNy8mTZpU9iwcPYQGZTv11FPjF7/4RYwcOTJqamre8/zhw4fH8OHD4zvf+U488MADMWHChFiyZElcc801UVVVdRAmfnfLly+PAQMGxMqVK0vm2f8Kz379+/ePNWvWxM6dO0teOdq8eXPJeQMGDIiIiOrq6rj44os7cXIA9rObDs5uWrBgQcyZMye+8pWvxNe//vX3/XU4OvjWKco2bty42Lt3b3z729/u8Ht79uyJV155JSIitm/fXvLqTES0v0vG/kvU+/9Q3H+bStj/ytJbZ/31r38d69atKzmvqakpdu/eHXfddVf7sba2tliwYEHJeQ0NDXHBBRfEwoUL429/+1uH+9u6dWvm+ACE3XQwdtPSpUtjxowZMWHChLj11lvLejwcnVzRoGyNjY0xderUuOmmm+L3v/99jB49Oqqrq2PTpk2xbNmy+N73vheXX3553HfffXH77bdHc3NznHrqqbFjx4646667onfv3nHppZdGRERNTU0MGjQoli5dGh/96EejT58+cdZZZx3Uf6RuzJgxsXLlymhubo5PfepT8dxzz8UPf/jDGDRoULz22mvt540dOzY+8YlPxFe/+tXYvHlznHHGGfHwww/Htm3bIiJKXnFasGBBfPKTn4whQ4bElClTYsCAAfH3v/891q1bF3/5y1/i6aeffteZnnjiiXjiiSciYt8f/v/617/ihhtuiIiIUaNGtb/9IQD72E2du5s2bNgQV155ZRx33HFx0UUXdfgWtREjRrRfNYF2lXq7Kw4fb38Lwf3uvPPOYujQoUVNTU3Rq1evYsiQIcX1119f/PWvfy2KoiieeuqpYvz48cXJJ59cdO/evWhoaCjGjBlTbNy4seTrrF27thg6dGhxzDHHvOfbCb7bWwhu3bq15NyWlpaiZ8+eHb5GY2NjMXjw4PbP29raihtvvLHo379/0b179+LjH/948cgjjxQtLS1F//79S267devW4oorrih69epV1NbWFldddVXx5JNPFhFRLFmypOTcZ599trjyyiuLE044oaiuri769etXjBkzpli+fPk7Pr63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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Create a figure with two subplots\n", + "fig, axes = plt.subplots(1, 2, figsize=(10, 5))\n", + "\n", + "# Plot the first image\n", + "axes[0].imshow(abs(ansatz.ansatz.get_statevector()).reshape(8, 8))\n", + "axes[0].set_title('Test Image 1')\n", + "axes[0].axis('off')\n", + "\n", + "# Plot the second image\n", + "axes[1].imshow(test_image)\n", + "axes[1].set_title('Test Image 2')\n", + "axes[1].axis('off')\n", + "\n", + "# Display the plot\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For AQC, we often would use a much smaller ansatz so as to justify the training cost. However, for ease in demonstration and better fidelity we used an exact circuit and simply updated the parameters to what Shende's synthesis would have gotten to.\n", + "\n", + "It should also be noted that when we attempt a global optimization of the circuit like we did here, we are prone to the **\"orthogonality catastrophe\"**. Orthogonality catastrophe in quantum machine learning refers to a situation where a small change in the parameters of a quantum model causes the quantum state to change drastically, becoming almost completely different (orthogonal) from the original state. This causes an exponential decay in the fidelity measure as the size of the system (number of qubits) increases. That is why for the 6 qubit instance we have 94 fidelity, but for 3 or 4 qubits we can reach 99.999.\n", + "\n", + "This can cause problems during training because:\n", + "- **Vanishing Gradients:** The changes in the cost function become very small, making it hard to update the parameters.\n", + "- **Optimization Instability:** The training process can become unstable and not converge properly.\n", + "\n", + "In Layman terms, the larger the circuit, the harder it is to train when done globally.\n", + "\n", + "To deal with this, techniques like careful parameter initialization, regularization, and adaptive optimization algorithms are used to make the training process smoother and more stable. We can also trade time to run with better convergence by training a batch of parameters at a time instead of all. This means many more runs of the circuit, but mitigates the orthogonality issue." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

© 2025 Qualition Computing, all rights reserved.

" + ] + } + ], + "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.11.11" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/quick/circuit/__init__.py b/quick/circuit/__init__.py index 094488f..1226eab 100644 --- a/quick/circuit/__init__.py +++ b/quick/circuit/__init__.py @@ -16,6 +16,7 @@ "dag", "gate_matrix", "from_framework", + "Ansatz", "Circuit", "CirqCircuit", "PennylaneCircuit", @@ -32,5 +33,6 @@ from quick.circuit.pennylanecircuit import PennylaneCircuit from quick.circuit.quimbcircuit import QuimbCircuit from quick.circuit.tketcircuit import TKETCircuit +from quick.circuit.ansatz import Ansatz import quick.circuit.from_framework as from_framework import quick.circuit.dag as dag \ No newline at end of file diff --git a/quick/circuit/ansatz.py b/quick/circuit/ansatz.py new file mode 100644 index 0000000..bb5e00f --- /dev/null +++ b/quick/circuit/ansatz.py @@ -0,0 +1,255 @@ +# Copyright 2023-2025 Qualition Computing LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://github.com/Qualition/quick/blob/main/LICENSE +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" Ansatz for variational quantum circuits. +""" + +from __future__ import annotations + +__all__ = ["Ansatz"] + +from quick.circuit import Circuit + +# Type hint for nested lists of floats +Params = list[list[float] | float] | list[float] + + +class Ansatz: + """ `quick.circuit.Ansatz` class for parameterized quantum circuits + which can be used as variational ansatz for quantum machine learning + models. + + Notes + ----- + The `Ansatz` class is a wrapper around the `quick.circuit.Circuit` class + which provides a more user-friendly interface for parameterized quantum + circuits, and means for variationally updating them. + + Use-cases of variational quantum circuits include: + - Supervised QML + - Approximate Quantum Compilation (AQC) + - Quantum Approximate Optimization Algorithm (QAOA) + - Variational Quantum Eigensolver (VQE) + + This class is meant to provide a simple interface for specifically updating + the rotation angles of a parameterized quantum circuit. Users can use the class + in the following manner: + + ```python + from quick.circuit import Ansatz, QiskitCircuit + from quick.circuit.circuit_utils import reshape, flatten + from quick.random import generate_random_state + from scipy.optimize import minimize + + # Create a parameterized quantum circuit with + # random state initialization + circuit = QiskitCircuit(2) + circuit.initialize(generate_random_state(2), [0, 1]) + + # Define a target state + target_state = generate_random_state(2) + + # Create an ansatz object + ansatz = Ansatz(circuit) + + # Define the cost function + def cost_function(thetas, shape): + ansatz.thetas = reshape(thetas, shape) + return 1 - target_state.conj().T @ ansatz.ansatz.state + + initial_thetas, shape = flatten(ansatz.thetas) + + # Optimize the ansatz circuit + result = minimize(cost_function, initial_thetas, args=(shape), method="BFGS") + ``` + + This example demonstrates how one can use the Ansatz class to perform approximate + state preparation by variationally updating the circuit where the cost function is + simply the fidelity between the target state and the state prepared by the ansatz. + + For simplicity, we do not bother with defining specific optimization interfaces so + users can use whatever means of optimization they prefer as long as they update the + parameters of the ansatz circuit per iteration. Our recommendation is to use the + `scipy.optimize.minimize` function which provides a minimalistic and elegant interface + to access a variety of optimization algorithms. It should however be noted that if + the use-case requires more sophisticated optimization techniques depending on the + cost landscape, users are urged to implement their own optimization routines and/or + use more advanced optimization libraries. + + Lastly, `flatten` and `reshape` functions are used to convert the parameters of the + ansatz circuit to a 1D array and vice versa. This is necessary because the optimization + routine expects a 1D array of parameters to optimize over, while the ansatz circuit + requires the original shape to update its definition. Feel free to use these functions + or implement your own as needed. + + You may also make a PR to exclude the need for `flatten` and `reshape` by providing a + more elegant way of handling the parameter updates. + + Parameters + ---------- + `ansatz` : quick.circuit.Circuit + The parameterized quantum circuit to be used as the ansatz. + `ignore_global_phase` : bool, optional, default=True + Whether to ignore the global phase when setting the parameters + of the ansatz. + + Attributes + ---------- + `ansatz` : quick.circuit.Circuit + The parameterized quantum circuit to be used as the ansatz. + `thetas` : numpy.ndarray + The parameters of the ansatz circuit. + `num_params` : int + The number of parameters in the ansatz circuit. + `num_parameterized_gates` : int + The number of parameterized gates in the ansatz circuit. + + Raises + ------ + TypeError + - If the `ansatz` is not an instance of the `quick.circuit.Circuit`. + + Usage + ----- + >>> from quick.circuit import QiskitCircuit + >>> circuit = QiskitCircuit(2) + >>> circuit.H(0) + >>> circuit.CX(0, 1) + >>> ansatz = Ansatz(circuit) + """ + def __init__( + self, + ansatz: Circuit, + ignore_global_phase: bool = True + ) -> None: + """ Initialize a `quick.circuit.Ansatz` instance. + """ + if not isinstance(ansatz, Circuit): + raise TypeError( + "The `ansatz` must be an instance of the `quick.circuit.Circuit`. " + f"Received {type(ansatz)} instead." + ) + + self.ansatz = ansatz + self.ignore_global_phase = ignore_global_phase + + if not self.is_parameterized: + raise ValueError("The `ansatz` must contain parameterized gates.") + + @property + def thetas(self) -> Params: + """ The parameters of the ansatz circuit. + + Returns + ------- + Params + The parameters of the ansatz circuit. + + Usage + ----- + >>> ansatz.thetas + """ + thetas: Params = [] + + for gate in self.ansatz.circuit_log: + if "angles" in gate: + thetas.append(gate["angles"]) + elif "angle" in gate: + if gate["gate"] == "GlobalPhase" and self.ignore_global_phase: + continue + thetas.append(gate["angle"]) + + return thetas + + @thetas.setter + def thetas( + self, + thetas: Params + ) -> None: + """ Set the parameters of the ansatz circuit. + + Parameters + ---------- + `thetas` : Params + The parameters to set for the ansatz circuit. + + Usage + ----- + >>> # Set the parameters of the ansatz circuit + ... # with one U3 gate + >>> ansatz.thetas = np.array([0.1, 0.2, 0.3]) + """ + for gate in self.ansatz.circuit_log: + if "angles" in gate: + gate["angles"] = thetas.pop(0) + elif "angle" in gate: + if gate["gate"] == "GlobalPhase" and self.ignore_global_phase: + continue + gate["angle"] = thetas.pop(0) + + self.ansatz.update() + + @property + def num_params(self) -> int: + """ The number of parameters in the ansatz circuit. + + Returns + ------- + int + The number of parameters in the ansatz circuit. + + Usage + ----- + >>> ansatz.num_params + """ + flattened_thetas: list[float] = [] + + for theta in self.thetas: + if isinstance(theta, list): + flattened_thetas.extend(theta) + else: + flattened_thetas.append(theta) + + return len(flattened_thetas) + + @property + def num_parameterized_gates(self) -> int: + """ The number of parameterized gates in the ansatz circuit. + + Returns + ------- + int + The number of parameterized gates in the ansatz circuit. + + Usage + ----- + >>> ansatz.num_parameterized_gates + """ + return len(self.thetas) + + @property + def is_parameterized(self) -> bool: + """ Check if the ansatz circuit contains parameterized gates. + + Returns + ------- + bool + True if the ansatz circuit contains parameterized gates, + False otherwise. + + Usage + ----- + >>> ansatz.is_parameterized + """ + return len(self.thetas) > 0 \ No newline at end of file diff --git a/quick/circuit/circuit_utils.py b/quick/circuit/circuit_utils.py index 1c9595c..46070ce 100644 --- a/quick/circuit/circuit_utils.py +++ b/quick/circuit/circuit_utils.py @@ -25,7 +25,9 @@ "multiplexor_diagonal_matrix", "simplify", "repetition_search", - "repetition_verify" + "repetition_verify", + "flatten", + "reshape" ] import numpy as np @@ -44,6 +46,9 @@ SQRT2, -SQRT2 ) +# Type hint for nested lists of floats +Params = list[list[float] | float] | list[float] + def decompose_multiplexor_rotations( angles: NDArray[np.float64], @@ -517,4 +522,80 @@ def repetition_verify( mux_copy[next_base] = None base, next_base, i = base + 1, next_base + 1, i + 1 - return True, mux_copy \ No newline at end of file + return True, mux_copy + +def flatten(array: Params) -> tuple[list[float], Params]: # pragma: no cover + """ Flatten a Tree into a list of floats and + the original shape. + + Parameters + ---------- + `array` : Tree + The nested list of floats. + + Returns + ------- + `flattened` : list[float] + The flattened list of parameters. + `shape` : Tree + The shape of the original array. + """ + flattened: list[float] = [] + shape: Params = [] + consecutive_ints = 0 + + for item in array: + if isinstance(item, float): + flattened.append(item) + consecutive_ints += 1 + + # Explicit type check for pylance + elif isinstance(item, list): + flattened.extend(item) + if consecutive_ints: + shape.append(consecutive_ints) + consecutive_ints = 0 + shape.append([len(item)]) # type: ignore + + if consecutive_ints: + shape.append(consecutive_ints) + + return flattened, shape + + +def reshape( + flattened: list[float], + shape: Params + ) -> Params: # pragma: no cover + """ Reshape a flattened list given a shape instruction. + + Parameters + ---------- + `flattened` : list[float] + The flat list of floats. + `shape` : Tree + The shape instruction. + + Returns + ------- + `reshaped` : Tree + The reshaped list of floats. + """ + reshaped: Params = [] + result_index = 0 + + for dim in shape: + if isinstance(dim, int): + subtree = reshaped + + elif isinstance(dim, list): + subtree = [] + reshaped.append(subtree) # type: ignore + dim = dim[0] + + for i in range(result_index, result_index + dim): # type: ignore + subtree.append(flattened[i]) + + result_index += dim # type: ignore + + return reshaped \ No newline at end of file diff --git a/stubs/quick/circuit/ansatz.pyi b/stubs/quick/circuit/ansatz.pyi new file mode 100644 index 0000000..044585f --- /dev/null +++ b/stubs/quick/circuit/ansatz.pyi @@ -0,0 +1,36 @@ +# Copyright 2023-2025 Qualition Computing LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://github.com/Qualition/quick/blob/main/LICENSE +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np +from numpy.typing import NDArray +from quick.circuit import Circuit + +__all__ = ["Ansatz"] + +Params = list[list[float] | float] | list[float] + +class Ansatz: + ansatz: Circuit + ignore_global_phase: bool = True + def __init__(self, ansatz: Circuit, ignore_global_phase: bool = True) -> None: ... + @property + def thetas(self) -> Params: ... + @thetas.setter + def thetas(self, theta_values: Params) -> None: ... + @property + def num_params(self) -> int: ... + @property + def num_parameterized_gates(self) -> int: ... + @property + def is_parameterized(self) -> bool: ... diff --git a/stubs/quick/circuit/circuit_utils.pyi b/stubs/quick/circuit/circuit_utils.pyi index e2f018a..d7a1ba0 100644 --- a/stubs/quick/circuit/circuit_utils.pyi +++ b/stubs/quick/circuit/circuit_utils.pyi @@ -23,9 +23,14 @@ __all__ = [ "multiplexor_diagonal_matrix", "simplify", "repetition_search", - "repetition_verify" + "repetition_verify", + "flatten", + "reshape" ] +Params = list[list[float] | float] | list[float] + + def decompose_multiplexor_rotations( angles: NDArray[np.float64], start_index: int, @@ -55,3 +60,5 @@ def repetition_search( level: int ) -> tuple[set[int], list[NDArray[np.complex128]]]: ... def repetition_verify(base, d, mux, mux_copy) -> tuple[bool, list[NDArray[np.complex128]]]: ... +def flatten(array: Params) -> tuple[list[float], Params]: ... +def reshape(flattened: list[float], shape: Params) -> Params: ... diff --git a/tests/circuit/test_ansatz.py b/tests/circuit/test_ansatz.py new file mode 100644 index 0000000..0a4b46b --- /dev/null +++ b/tests/circuit/test_ansatz.py @@ -0,0 +1,256 @@ +# Copyright 2023-2025 Qualition Computing LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://github.com/Qualition/quick/blob/main/LICENSE +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +__all__ = ["TestAnsatz"] + +import pytest +from quick.circuit import Ansatz, Circuit +from typing import Type + +from tests.circuit import CIRCUIT_FRAMEWORKS + + +class TestAnsatz: + def test_init_type_error(self) -> None: + """ Test TypeError raised when invalid type is passed to Ansatz. + """ + with pytest.raises(TypeError): + Ansatz(2) # type: ignore + + @pytest.mark.parametrize("circuit_framework", CIRCUIT_FRAMEWORKS) + def test_init_value_error( + self, + circuit_framework: Type[Circuit] + ) -> None: + """ Test ValueError raised when the circuit used is not + parameterized. + + Parameters + ---------- + `circuit_framework`: Type[quick.circuit.Circuit] + The circuit framework to use for testing. + """ + circuit = circuit_framework(2) + + circuit.CX(0, 1) + + with pytest.raises(ValueError): + Ansatz(circuit) + + @pytest.mark.parametrize("circuit_framework", CIRCUIT_FRAMEWORKS) + def test_thetas( + self, + circuit_framework: Type[Circuit] + ) -> None: + """ Test thetas property of Ansatz. + + Parameters + ---------- + `circuit_framework`: Type[quick.circuit.Circuit] + The circuit framework to use for testing. + """ + circuit = circuit_framework(1) + + circuit.RY(0.3, 0) + + ansatz = Ansatz(circuit) + + assert ansatz.thetas == [0.3] + + circuit = circuit_framework(2) + circuit.RY(0.3, 0) + circuit.RY(0.2, 1) + + ansatz = Ansatz(circuit) + + assert ansatz.thetas == [0.3, 0.2] + + @pytest.mark.parametrize("circuit_framework", CIRCUIT_FRAMEWORKS) + def test_set_thetas( + self, + circuit_framework: Type[Circuit] + ) -> None: + """ Test thetas setter of Ansatz. + + Parameters + ---------- + `circuit_framework`: Type[quick.circuit.Circuit] + The circuit framework to use for testing. + """ + circuit = circuit_framework(1) + + circuit.RY(0.3, 0) + + ansatz = Ansatz(circuit) + + ansatz.thetas = [0.3] + + assert ansatz.thetas == [0.3] + assert circuit.circuit_log[0]["angle"] == 0.3 + + circuit = circuit_framework(2) + circuit.RY(0.3, 0) + circuit.RY(0.2, 1) + + ansatz = Ansatz(circuit) + + ansatz.thetas = [0.3, 0.2] + + assert ansatz.thetas == [0.3, 0.2] + assert circuit.circuit_log[0]["angle"] == 0.3 + assert circuit.circuit_log[1]["angle"] == 0.2 + + circuit = circuit_framework(1) + + circuit.RY(0.3, 0) + circuit.U3([0.1, 0.2, 0.3], 0) + circuit.RX(0.2, 0) + + ansatz = Ansatz(circuit) + + ansatz.thetas = [0.1, [0.2, 0.3, 0.4], 0.5] + + assert circuit.circuit_log[0]["angle"] == 0.1 + assert circuit.circuit_log[1]["angles"] == [0.2, 0.3, 0.4] + assert circuit.circuit_log[2]["angle"] == 0.5 + + @pytest.mark.parametrize("circuit_framework", CIRCUIT_FRAMEWORKS) + def test_num_params( + self, + circuit_framework: Type[Circuit] + ) -> None: + """ Test num_params property of Ansatz. + + Parameters + ---------- + `circuit_framework`: Type[quick.circuit.Circuit] + The circuit framework to use for testing. + """ + circuit = circuit_framework(1) + + circuit.RY(0.3, 0) + + ansatz = Ansatz(circuit) + + assert ansatz.num_params == 1 + + circuit = circuit_framework(2) + circuit.RY(0.3, 0) + circuit.RY(0.2, 1) + + ansatz = Ansatz(circuit) + + assert ansatz.num_params == 2 + + @pytest.mark.parametrize("circuit_framework", CIRCUIT_FRAMEWORKS) + def test_num_parameterized_gates( + self, + circuit_framework: Type[Circuit] + ) -> None: + """ Test num_parameterized_gates property of Ansatz. + + Parameters + ---------- + `circuit_framework`: Type[quick.circuit.Circuit] + The circuit framework to use for testing. + """ + circuit = circuit_framework(1) + + circuit.RY(0.3, 0) + + ansatz = Ansatz(circuit) + + assert ansatz.num_parameterized_gates == 1 + + circuit = circuit_framework(2) + circuit.RY(0.3, 0) + circuit.RY(0.2, 1) + + ansatz = Ansatz(circuit) + + assert ansatz.num_parameterized_gates == 2 + + @pytest.mark.parametrize("circuit_framework", CIRCUIT_FRAMEWORKS) + def test_is_parameterized( + self, + circuit_framework: Type[Circuit] + ) -> None: + """ Test is_parameterized property of Ansatz. + + Parameters + ---------- + `circuit_framework`: Type[quick.circuit.Circuit] + The circuit framework to use for testing. + """ + circuit = circuit_framework(1) + + circuit.RY(0.3, 0) + + ansatz = Ansatz(circuit) + + assert ansatz.is_parameterized + + circuit = circuit_framework(2) + circuit.RY(0.3, 0) + circuit.RY(0.2, 1) + + ansatz = Ansatz(circuit) + + assert ansatz.is_parameterized + + @pytest.mark.parametrize("circuit_framework", CIRCUIT_FRAMEWORKS) + def test_is_parameterized_error( + self, + circuit_framework: Type[Circuit] + ) -> None: + """ Test is_parameterized property of Ansatz when circuit is not + parameterized. + + Parameters + ---------- + `circuit_framework`: Type[quick.circuit.Circuit] + The circuit framework to use for testing. + """ + circuit = circuit_framework(2) + + circuit.CX(0, 1) + + with pytest.raises(ValueError): + Ansatz(circuit) + + @pytest.mark.parametrize("circuit_framework", CIRCUIT_FRAMEWORKS) + def test_is_parameterized_with_ignore_global_phase( + self, + circuit_framework: Type[Circuit] + ) -> None: + """ Test is_parameterized property of Ansatz with ignore_global_phase + set to True. + + Parameters + ---------- + `circuit_framework`: Type[quick.circuit.Circuit] + The circuit framework to use for testing. + """ + circuit = circuit_framework(1) + + circuit.GlobalPhase(0.3) + + ansatz = Ansatz(circuit, ignore_global_phase=False) + + assert ansatz.is_parameterized + + with pytest.raises(ValueError): + Ansatz(circuit, ignore_global_phase=True) \ No newline at end of file