Source code for qcp.algorithms.grovers_algorithm

# Copyright 2022 Tiernan8r
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Constructs the quantum register, circuits of composite gates, and runs the
simulation of Grover's Algorithm
"""
import math
from typing import List

import qcp.gates as g
from qcp.algorithms import GeneralAlgorithm
from qcp.matrices import Matrix


[docs]def pull_set_bits(n: int) -> List[int]: """ Creates a list of bits that would be set to 1 to make the number n :param int n: number returns: List[int]: list of bits """ bits = [] count = 0 while n: cond = n & 1 if cond: bits.append(count) n >>= 1 count += 1 return bits
[docs]class Grovers(GeneralAlgorithm):
[docs] def __init__(self, size: int, target_state: int): """ This is an implementation of Grover's algorithm which efficiently finds a specific item in a list of items. In this implementation we use Grover's algorithm to increase the amplitude of the "target_state" and reduce all others in a "size"-qubit system :param int size: number of qubits in our circuit :param int target_state: specific state we want to target/select """ assert target_state < (2 ** size), \ "target index must be within number of qbit indices" self.target = target_state # can only reflect size-1 times to get maximum probability self.max_reflections = math.floor((math.pi/4)*(math.sqrt(2**size))) super().__init__(size)
[docs] def single_target_oracle(self) -> Matrix: """ Creates an oracle gate - a gate which 'selects' our target state by phase shifting it by pi (turning 1 into -1 in the matrix representation) returns: Matrix: Matrix representation of our Oracle """ not_placement = (2 ** self.size) - 1 - self.target t = pull_set_bits(not_placement) cz = g.control_z(self.size, [i for i in range(0, self.size - 1)], self.size - 1) selector = g.multi_gate(self.size, t, g.Gate.X) oracle = selector * cz oracle *= selector return oracle
[docs] def diffusion(self) -> Matrix: """ Creates a diffusion gate - a gate which amplifies the probability of selecting our target state returns: Matrix: Matrix representing diffusion gate """ h = g.multi_gate(self.size, [i for i in range(0, self.size)], g.Gate.H) cz = g.control_z(self.size, [i for i in range(0, self.size - 1)], self.size - 1) x = g.multi_gate(self.size, [i for i in range(0, self.size)], g.Gate.X) diff = h * (x * (cz * (x * h))) return diff
[docs] def construct_circuit(self) -> Matrix: """ Constructs the circuit for Grover's algorithm by applying an initial set of Hadamards and repeating the oracle and diffusion gates until our target state is close to 1 in terms of probability returns: Matrix: Matrix representing our completed Grover's algorithm """ self.oracle = self.single_target_oracle() self.diffuser = self.diffusion() circuit = g.multi_gate(self.size, [i for i in range(0, self.size)], g.Gate.H) while self.max_reflections > 0: circuit = self.oracle * circuit circuit = self.diffuser * circuit self.max_reflections -= 1 return circuit
[docs] def measure_probabilities(self): p = self.probabilities() n_bits = int(math.log2(2**self.size)) for i in range(2**self.size): binary = bin(i)[2:].zfill(n_bits) print(f"|{binary}> : {p[i]:.4g}")