State evolution
This tutorial shows how to apply the hamiltonian_evolution operator to evolve the quantum state using an approximated but efficient matrix exponentiation.
In [1]:
Copied!
from pyqtorch.matrices import single_Z, ZZ
from pyqtorch.ansatz import AlternateLayerAnsatz, OneLayerXRotation, OneLayerZRotation, OneLayerEntanglingAnsatz
from pyqtorch.core.circuit import QuantumCircuit
from pyqtorch.matrices import single_Z, ZZ
from pyqtorch.ansatz import AlternateLayerAnsatz, OneLayerXRotation, OneLayerZRotation, OneLayerEntanglingAnsatz
from pyqtorch.core.circuit import QuantumCircuit
/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 from .autonotebook import tqdm as notebook_tqdm
In [2]:
Copied!
import torch as th
import numpy as np
import copy
import torch as th
import numpy as np
import copy
We start initialising the QuantumCircuit instance in order to observe the typical shape of an input/output state in the PyQ format
In [3]:
Copied!
N = 4
qc = QuantumCircuit(N)
psi = qc.uniform_state(1)
psi_0 = copy.deepcopy(psi)
psi_0.shape
N = 4
qc = QuantumCircuit(N)
psi = qc.uniform_state(1)
psi_0 = copy.deepcopy(psi)
psi_0.shape
Out[3]:
torch.Size([2, 2, 2, 2, 1])
We perform a deepcopy of psi as some operations below (e.g. hamiltonian evolution) will overwrite it.
In [4]:
Copied!
def overlap(state1, state2):
N = len(state1.shape)-1
state1_T = th.transpose(state1, N, 0)
overlap = th.tensordot(state1.T.conj(), state2, dims=N)
return float(th.abs(overlap**2).flatten())
def overlap(state1, state2):
N = len(state1.shape)-1
state1_T = th.transpose(state1, N, 0)
overlap = th.tensordot(state1.T.conj(), state2, dims=N)
return float(th.abs(overlap**2).flatten())
In [5]:
Copied!
print("Initial overlap: ", overlap(psi_0, psi_0))
print("Initial overlap: ", overlap(psi_0, psi_0))
Initial overlap: 1.0
/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.) overlap = th.tensordot(state1.T.conj(), state2, dims=N)
Hamiltonian Evolution¶
In [6]:
Copied!
from pyqtorch.core.operation import hamiltonian_evolution
from pyqtorch.core.operation import hamiltonian_evolution
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
In [7]:
Copied!
sigmaz = th.diag(th.tensor([1.0, -1.0], dtype=th.cdouble))
Hbase = th.kron(sigmaz, sigmaz)
H = th.kron(Hbase, Hbase)
H.shape
sigmaz = th.diag(th.tensor([1.0, -1.0], dtype=th.cdouble))
Hbase = th.kron(sigmaz, sigmaz)
H = th.kron(Hbase, Hbase)
H.shape
Out[7]:
torch.Size([16, 16])
The overlap with itself should stay 1 after evolving for $t=0$, let's check this
In [8]:
Copied!
t_evo = th.tensor([0], dtype=th.cdouble)
psi = hamiltonian_evolution(H,
psi, t_evo,
range(N), N)
t_evo = th.tensor([0], dtype=th.cdouble)
psi = hamiltonian_evolution(H,
psi, t_evo,
range(N), N)
In [9]:
Copied!
print(f"Overlap after {t_evo} : ", overlap(psi, psi_0))
print(f"Overlap after {t_evo} : ", overlap(psi, psi_0))
Overlap after tensor([0.+0.j], dtype=torch.complex128) : 1.0
Let's now evolve the state for a time $t = \pi/4$ and check that the overlap matches the expected value of 0.5.
In [10]:
Copied!
t_evo = th.tensor([th.pi/4], dtype=th.cdouble)
psi = hamiltonian_evolution(H,
psi, t_evo,
range(N), N
)
t_evo = th.tensor([th.pi/4], dtype=th.cdouble)
psi = hamiltonian_evolution(H,
psi, t_evo,
range(N), N
)
In [11]:
Copied!
print(f"Overlap after {t_evo} : ", overlap(psi, psi_0))
print(f"Overlap after {t_evo} : ", overlap(psi, psi_0))
Overlap after tensor([0.7854+0.j], dtype=torch.complex128) : 0.50000000002474