{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This tutorial shows how to apply the `hamiltonian_evolution` operator to evolve the quantum state using an approximated but efficient matrix exponentiation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/mdagrada/.cache/pypoetry/virtualenvs/qucint-YinaOvwL-py3.8/lib/python3.8/site-packages/tqdm/auto.py:22: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    }
   ],
   "source": [
    "from pyqtorch.matrices import single_Z, ZZ\n",
    "from pyqtorch.ansatz import AlternateLayerAnsatz, OneLayerXRotation, OneLayerZRotation, OneLayerEntanglingAnsatz\n",
    "from pyqtorch.core.circuit import QuantumCircuit"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch as th\n",
    "import numpy as np\n",
    "import copy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We start initialising the `QuantumCircuit` instance in order to observe the typical shape of an input/output state in the PyQ format"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([2, 2, 2, 2, 1])"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "N = 4\n",
    "qc = QuantumCircuit(N)\n",
    "psi = qc.uniform_state(1)\n",
    "psi_0 = copy.deepcopy(psi)\n",
    "psi_0.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We perform a deepcopy of `psi` as some operations below (e.g. `hamiltonian evolution`) will overwrite it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def overlap(state1, state2):\n",
    "    N = len(state1.shape)-1\n",
    "    state1_T = th.transpose(state1, N, 0)\n",
    "    overlap = th.tensordot(state1.T.conj(), state2, dims=N)\n",
    "    return float(th.abs(overlap**2).flatten())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Initial overlap:  1.0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_601/4268046128.py:4: UserWarning: The use of `x.T` on tensors of dimension other than 2 to reverse their shape is deprecated and it will throw an error in a future release. Consider `x.mT` to transpose batches of matrices or `x.permute(*torch.arange(x.ndim - 1, -1, -1))` to reverse the dimensions of a tensor. (Triggered internally at ../aten/src/ATen/native/TensorShape.cpp:3277.)\n",
      "  overlap = th.tensordot(state1.T.conj(), state2, dims=N)\n"
     ]
    }
   ],
   "source": [
    "print(\"Initial overlap: \", overlap(psi_0, psi_0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Hamiltonian Evolution"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pyqtorch.core.operation import hamiltonian_evolution"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let us define a simple Hamiltonian for the 4-qubits system, like a $\\sigma_Z \\otimes \\sigma_Z$, in dense format as a $(N^2, N^2)$ tensor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([16, 16])"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sigmaz = th.diag(th.tensor([1.0, -1.0], dtype=th.cdouble))\n",
    "Hbase = th.kron(sigmaz, sigmaz)\n",
    "H = th.kron(Hbase, Hbase)\n",
    "H.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The overlap with itself should stay 1 after evolving for $t=0$, let's check this"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "t_evo = th.tensor([0], dtype=th.cdouble)\n",
    "psi = hamiltonian_evolution(H,\n",
    "                    psi, t_evo,\n",
    "                    range(N), N)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overlap after tensor([0.+0.j], dtype=torch.complex128) :  1.0\n"
     ]
    }
   ],
   "source": [
    "print(f\"Overlap after {t_evo} : \", overlap(psi, psi_0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's now evolve the state for a time $t = \\pi/4$ and check that the overlap matches the expected value of 0.5."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "t_evo = th.tensor([th.pi/4], dtype=th.cdouble)\n",
    "psi = hamiltonian_evolution(H,\n",
    "                    psi, t_evo,\n",
    "                    range(N), N\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overlap after tensor([0.7854+0.j], dtype=torch.complex128) :  0.50000000002474\n"
     ]
    }
   ],
   "source": [
    "print(f\"Overlap after {t_evo} : \", overlap(psi, psi_0))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.5"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "50935ae23c6784d55197a912635e8caab0c12202c38f5b44c7779b3e5667ccc0"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
