from typing import Any, Dict
import numpy as np
from braket.circuits import Circuit
from braket.devices import Device
[docs]
def qft(num_qubits: int, inverse: bool = False) -> Circuit:
"""Creates the quantum Fourier transform circuit and its inverse.
Args:
num_qubits (int): Number of qubits in the circuit
inverse (bool): If true return the inverse of the circuit. Default is False
Returns:
Circuit: Circuit object that implements the quantum Fourier transform or its inverse
"""
qc = Circuit()
N = num_qubits - 1
if not inverse:
qc.h(N)
for n in range(1, N + 1):
qc.cphaseshift(N - n, N, 2 * np.pi / 2 ** (n + 1))
for i in range(1, N):
qc.h(N - i)
for n in range(1, N - i + 1):
qc.cphaseshift(N - (n + i), N - i, 2 * np.pi / 2 ** (n + 1))
qc.h(0)
else: # The inverse of the quantum Fourier transform
qc.h(0)
for i in range(N - 1, 0, -1):
for n in range(N - i, 0, -1):
qc.cphaseshift(N - (n + i), N - i, -2 * np.pi / 2 ** (n + 1))
qc.h(N - i)
for n in range(N, 0, -1):
qc.cphaseshift(N - n, N, -2 * np.pi / 2 ** (n + 1))
qc.h(N)
return qc
[docs]
def qft_conditional_add_1(num_qubits: int) -> Circuit:
"""Creates the quantum circuit that conditionally add +1 or -1 using:
1) The first qubit to control if add 1 or subtract 1: when the first qubit is 0, we add 1 from
the number, and when the first qubit is 1, we subtract 1 from the number.
2) The second register with `num_qubits` qubits to save the result.
Args:
num_qubits (int): Number of qubits that saves the result.
Returns:
Circuit: Circuit object that implements the circuit that conditionally add +1 or -1.
"""
qc = Circuit()
qc.add(qft(num_qubits), target=range(1, num_qubits + 1))
# add \pm 1 with control phase gates
for i in range(num_qubits):
qc.cphaseshift01(control=0, target=num_qubits - i, angle=2 * np.pi / 2 ** (num_qubits - i))
qc.cphaseshift(control=0, target=num_qubits - i, angle=-2 * np.pi / 2 ** (num_qubits - i))
qc.add(qft(num_qubits, inverse=True), target=range(1, num_qubits + 1))
return qc
[docs]
def quantum_walk(n_nodes: int, num_steps: int = 1) -> Circuit:
"""Creates the quantum random walk circuit.
Args:
n_nodes (int): The number of nodes in the graph
num_steps (int): The number of steps for the quantum walk. Default is 1
Returns:
Circuit: Circuit object that implements the quantum random walk algorithm
Raises:
If `np.log2(n_nodes)` is not an integer, a value error will be raised.
"""
n = np.log2(n_nodes) # number of qubits for the graph
if float(n).is_integer():
n = int(n)
else:
raise ValueError("The number of nodes has to be 2^n for integer n.")
qc = Circuit()
for _ in range(num_steps):
qc.h(0)
qc.add_circuit(qft_conditional_add_1(n))
qc.x(0) # flip the coin after the shift
return qc
[docs]
def run_quantum_walk(
circ: Circuit,
device: Device,
shots: int = 1000,
) -> Dict[str, Any]:
"""Function to run quantum random walk algorithm and return measurement counts.
Args:
circ (Circuit): Quantum random walk circuit
device (Device): Braket device backend
shots (int): Number of measurement shots. Default is 1000.
Returns:
Dict[str, Any]: measurements and results from running Quantum Phase Estimation
"""
# Add results_types
circ.probability()
# get total number of qubits
num_qubits = circ.qubit_count
# Run the circuit with all zeros input.
# The query_circuit subcircuit generates the desired input from all zeros.
task = device.run(circ, shots=shots)
result = task.result()
# get output probabilities (see result_types above)
probs_values = result.values[0]
# get measurement results
measurement_counts = result.measurement_counts
format_bitstring = "{0:0" + str(num_qubits) + "b}"
bitstring_keys = [format_bitstring.format(ii) for ii in range(2**num_qubits)]
quantum_walk_measurement_counts = {}
for key, val in measurement_counts.items():
node = int(key[1:][::-1], 2)
if node in quantum_walk_measurement_counts:
quantum_walk_measurement_counts[node] += val / shots
else:
quantum_walk_measurement_counts[node] = val / shots
output = {
"circuit": circ,
"task_metadata": result.task_metadata,
"measurements": result.measurements,
"measured_qubits": result.measured_qubits,
"measurement_counts": measurement_counts,
"measurement_probabilities": result.measurement_probabilities,
"probs_values": probs_values,
"bitstring_keys": bitstring_keys,
"quantum_walk_measurement_counts": quantum_walk_measurement_counts,
}
return output