Requirement already satisfied: torch in /opt/conda/lib/python3.10/site-packages (2.1.2)
Requirement already satisfied: filelock in /opt/conda/lib/python3.10/site-packages (from torch) (3.13.1)
Requirement already satisfied: typing-extensions in /opt/conda/lib/python3.10/site-packages (from torch) (4.5.0)
Requirement already satisfied: sympy in /opt/conda/lib/python3.10/site-packages (from torch) (1.12)
Requirement already satisfied: networkx in /opt/conda/lib/python3.10/site-packages (from torch) (3.2.1)
Requirement already satisfied: jinja2 in /opt/conda/lib/python3.10/site-packages (from torch) (3.1.2)
Requirement already satisfied: fsspec in /opt/conda/lib/python3.10/site-packages (from torch) (2023.5.0)
Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.1.105 in /opt/conda/lib/python3.10/site-packages (from torch) (12.1.105)
Requirement already satisfied: nvidia-cuda-runtime-cu12==12.1.105 in /opt/conda/lib/python3.10/site-packages (from torch) (12.1.105)
Requirement already satisfied: nvidia-cuda-cupti-cu12==12.1.105 in /opt/conda/lib/python3.10/site-packages (from torch) (12.1.105)
Display all output >>>
I'll be using PyTorch to advance quantum computing and machine learning. To start, I'll be writing a simple quantum circuit, primarily involving:
Quantum Bits (Qubits)
Quantum Gates
Measurement
Hamiltonian for calculating expectations
The representation for these elements may differ somewhat from traditional methods. For explanatory purposes, I'll be using a simpler tool like NetworkX, rather than conventional quantum circuit visualization tools."
q0 and q1 correspond to quantum bits, and traditionally, the state vector is given separately as |0> = [1, 0] for each.
The H gate is a quantum gate, so it corresponds to a unitary matrix. Regarding the CX gate, in NetworkX, it appears as separate nodes for C and X, but typically, it should be represented as a single matrix with two arms and one node. However, in this case, there are two nodes, each with three arms. The measurements are also labeled as M0 and M1, each assigned to a specific node.
I have a vague memory of having written an article about this before, but I'll start from scratch again.
Copy
import torch.optim as optim
import torch
import numpy as np
q0 is set to |0>,
and similarly,
q1 is also configured.
Copy
q0 = torch.tensor([1,0])
q1 = torch.tensor([1,0])
Next is the H gate. Since it involves matrices, we'll implement it straightforwardly using matrices.
Copy
H = torch.tensor([[1.,1],[1,-1]])/np.sqrt(2)
To calculate q0 and H as a test, you simply need to multiply q0 by H.
Copy
M0= torch.matmul(q0,H)
Copy
print(M0)
tensor([0.7071, 0.7071])
Great, you've successfully obtained the state vector.
Next, I'd like to try the calculation using "einsum." Fundamentally, it's the same approach, but it worked well.
Copy
M0 = torch.einsum("a,ab->b", (q0, H))
print(M0)
tensor([0.7071, 0.7071])
It seems that using "einsum" for calculations might be more straightforward.
Later on, I'd like to explore calculations with variational circuits, so I'll investigate whether it's possible to perform quantum computations with gradient information.
To incorporate gradients, it was necessary to rewrite the state vector and quantum gates as floats.
Copy
q0 = torch.tensor([1.,0.])
H = torch.tensor([[1.,1],[1,-1]], requires_grad=True)/np.sqrt(2)
Let's try
Copy
M0 = torch.einsum("a,ab->b", (q0, H))
print(M0)
tensor([0.7071, 0.7071], grad_fn=<ViewBackward0>)
This time, let's create an entangled state.
Prepare two quantum bits, an H gate, and a CX gate.
Next, we'll put this quantum state and q1 into the CX gate. However, instead of using CX as is, we'll transform it into a tensor. We'll convert it from a matrix to a tensor.
Since we're at it, I'd like to try quantum-classical variational computation. This time, I'll replace H with RY (to minimize the use of complex numbers) and use a variational algorithm to create |11>.
As a criterion for detecting |11>, I'd like to use the expectation value of the Hamiltonian. I aim to train in a way that makes the value of Z0 + Z1 equal to -2. Since we want to compute the expectation value this time, we'll prepare a tensor with Z operators. When visualizing the expectation value of Z0, it looks like...
#initial random parameter
a = torch.tensor([np.random.rand()],requires_grad=True)
#optimize the parameter with adam
op = optim.Adam([a],lr=0.05)
#qubits
q0 = torch.tensor([1.,0])
q1 = torch.tensor([1.,0])
#Z operator for expectation value
Z = torch.tensor([[1.,0],[0,-1]])
#list for result
arr = []
#make a loop for optimization
for _ in range(100):
#making RY gate using pytorch function to keep the grad
RY = torch.tensor([[1,0],[0,1]])*torch.cos(a/2) + torch.tensor([[0,-1],[-1,0]])*torch.sin(a/2)
#let's prepare the tensor
net1 = torch.einsum("a,ab,c,bcde->de", (q0, RY, q1, CX_four))
net2 = torch.einsum("a,ab,c,bcde->de", (q0, RY, q1, CX_four))
#add Z0 and Z1 expectation
expt = torch.einsum("ab,cb,ac->", (net1, net2, Z)) + torch.einsum("ab,ad,bd->", (net1, net2, Z))
#append expectation value to the list
arr.append(expt.detach().numpy())
#optimization step
op.zero_grad()
expt.backward()
op.step()
#draw
plt.plot(arr)
plt.show()
<Figure size 640x480 with 1 Axes>
You've successfully implemented VQE with PyTorch. Now, let's check whether the parameter 'a' has been optimized.
Copy
print(a)
tensor([3.1298], requires_grad=True)
It's almost 3.14, and the quantum bits are set to 1 with RY, followed by CX making both 1. We've successfully achieved |11>.
Quantum computations have been carried out using PyTorch's functionalities. Not only can we leverage GPUs, but we can also seamlessly integrate classical neural networks. That's all for now!