This repository has been archived by the owner on Feb 28, 2023. It is now read-only.
forked from XanaduAI/QHack2021
-
Notifications
You must be signed in to change notification settings - Fork 0
/
vqe_500_template_SSVQE.py
165 lines (126 loc) · 4.97 KB
/
vqe_500_template_SSVQE.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#! /usr/bin/python3
import sys
import pennylane as qml
import autograd.numpy as np
def find_excited_states(H):
"""
Fill in the missing parts between the # QHACK # markers below. Implement
a variational method that can find the three lowest energies of the provided
Hamiltonian.
Args:
H (qml.Hamiltonian): The input Hamiltonian
Returns:
The lowest three eigenenergies of the Hamiltonian as a comma-separated string,
sorted from smallest to largest.
"""
energies = np.zeros(3)
# QHACK #
## Subspace-Search VQE
## This uses SS-VQE, and it does output relatively accurate minimum energies
## However, it seems to be too slow/inaccurate for the hackathon
def variational_ansatz(params, wires, basis_state=None):
"""variational ansatz circuit"""
# initialise given basis state
qml.BasisState(basis_state, wires=wires)
# variationally tweak the state
for param in params:
qml.broadcast(qml.Rot, wires, pattern="single", parameters=param)
qml.broadcast(qml.CNOT, wires, pattern="ring")
def cost_ssvqe(num_eigen, *args, **kwargs):
"""cost function for SS-VQE with num_eigen orthogonal states"""
out = []
for i in range(num_eigen):
# get orthogonal state |...00>, |...01>, |...10>, etc
basis_state = format(i, "0%db" % num_qubits)
basis_state = np.array(list(basis_state), dtype=int)
# find cost of orthogonal state
costfn = qml.ExpvalCost(lambda *args, **kwargs: \
variational_ansatz(*args,**kwargs,basis_state=basis_state), H, dev)
out.append(costfn(*args, **kwargs))
return np.array(out)
def cost_fn(*args, **kwargs):
"""sums up SS-VQE to train classifier"""
return np.sum(cost_ssvqe(num_eigen, *args, **kwargs))
# fixed variables
num_layers = 3 # number of layers in variational ansatz
num_iter = 500 # number of VQE iterations
num_eigen = 3 # number of eigen-energies to output
num_qubits = len(H.wires) # number of qubits
threshold = 1e-5 # threshold to stop
# initialise qnode & optimiser
dev = qml.device("default.qubit", wires=num_qubits)
opt = qml.GradientDescentOptimizer(0.1)
# VQE
params = np.random.uniform(size=(num_layers, num_qubits, 3))
for _ in range(num_iter):
params, prev_energy = opt.step_and_cost(cost_fn, params)
energy = cost_fn(params)
if np.abs(energy - prev_energy) < threshold:
break
# # print progress
# if _ % 20 == 0:
# print('Iteration = {:}, Energy = {:.8f} Ha'.format(_, energy))
energies = np.sort(cost_ssvqe(num_eigen, params))
# QHACK #
return ",".join([str(E) for E in energies])
def pauli_token_to_operator(token):
"""
DO NOT MODIFY anything in this function! It is used to judge your solution.
Helper function to turn strings into qml operators.
Args:
token (str): A Pauli operator input in string form.
Returns:
A qml.Operator instance of the Pauli.
"""
qubit_terms = []
for term in token:
# Special case of identity
if term == "I":
qubit_terms.append(qml.Identity(0))
else:
pauli, qubit_idx = term[0], term[1:]
if pauli == "X":
qubit_terms.append(qml.PauliX(int(qubit_idx)))
elif pauli == "Y":
qubit_terms.append(qml.PauliY(int(qubit_idx)))
elif pauli == "Z":
qubit_terms.append(qml.PauliZ(int(qubit_idx)))
else:
print("Invalid input.")
full_term = qubit_terms[0]
for term in qubit_terms[1:]:
full_term = full_term @ term
return full_term
def parse_hamiltonian_input(input_data):
"""
DO NOT MODIFY anything in this function! It is used to judge your solution.
Turns the contents of the input file into a Hamiltonian.
Args:
filename(str): Name of the input file that contains the Hamiltonian.
Returns:
qml.Hamiltonian object of the Hamiltonian specified in the file.
"""
# Get the input
coeffs = []
pauli_terms = []
# Go through line by line and build up the Hamiltonian
for line in input_data.split("S"):
line = line.strip()
tokens = line.split(" ")
# Parse coefficients
sign, value = tokens[0], tokens[1]
coeff = float(value)
if sign == "-":
coeff *= -1
coeffs.append(coeff)
# Parse Pauli component
pauli = tokens[2:]
pauli_terms.append(pauli_token_to_operator(pauli))
return qml.Hamiltonian(coeffs, pauli_terms)
if __name__ == "__main__":
# DO NOT MODIFY anything in this code block
# Turn input to Hamiltonian
H = parse_hamiltonian_input(sys.stdin.read())
# Send Hamiltonian through VQE routine and output the solution
lowest_three_energies = find_excited_states(H)
print(lowest_three_energies)