© Bhagvan Kommadi 2020
B. KommadiQuantum Computing Solutionshttps://doi.org/10.1007/978-1-4842-6516-1_4

4. Quantum Information Processing Framework

Bhagvan Kommadi1  
(1)
Hyderabad, India
 

Introduction

“A classical computation is like a solo voice—one line of pure tones succeeding each other. A quantum computation is like a symphony—many lines of tones interfering with one another.”

—Seth Lloyd

This chapter gives an overview of a quantum information processing framework. You will see how a quantum information processing framework is applied in real life. Code examples are presented for the quantum algorithms discussed. In addition, this chapter discusses quantum circuits in detail.

Initial Setup

You need to set up Python 3.5 to run the code samples in this chapter. You can download it from https://www.python.org/downloads/release/python-350/.

Quantum Circuits

A quantum circuit is a key part of quantum computing. As we have seen, quantum computing is based on quantum mechanics principles. For example, quantum mechanics principles such as superposition and quantum entanglement are widely used in quantum computing. Quantum bits are the same as the classical bits. The Hilbert state space has the eigenvalues related to the linear superposition. The state values will be |0> and |1>. These eigen states are operated on using unitary operations.

Let’s look at what a Hilbert space is. A Hilbert space is similar to an inner product space of finite-dimensional complex vectors. The inner product of two complex vectors is the same as the inner product of two matrices that represent the vectors. Now let’s see what eigenvalues are. Eigenvalues are complex numbers that satisfy the linear equation B |v > = v|v>. |v> is a nonzero vector, which is the eigenvector with linear operator B.

Let’s look at the superposition principle that was discussed in earlier chapters. The superposition principle in quantum mechanics is related to the states of the quantum system. Let’s say |u> and |v> are the two states of the quantum system. Superposition α|u> + β |v> is a state that is valid if |α|2 + |β|2 = 1. Schrodinger’s equation is another principle from quantum mechanics that is used in quantum computing. The quantum system’s state evolves with time. The time evolution is explained mathematically by Schrodinger’s equation.

Figure 4-1 shows a sample quantum circuit with Hadamard gates. The input and output will be in quantum bits. The Hadamard gate was introduced in earlier chapters. It is a popular quantum gate in terms of usage. It operates on the single quantum bit gates by rotation and reflection on the Bloch sphere. The rotation of 90 degrees is around the y-axis, and the x-axis rotation is 180 degrees.
../images/490826_1_En_4_Chapter/490826_1_En_4_Fig1_HTML.jpg
Figure 4-1

Quantum circuit

The quantum circuit model is based on the quantum entanglement of quantum bits. Quantum entanglement has no classical equivalent. The superposition principle is used in quantum computers to save data in exponential size compared to classical computers. Classical computers using M bits can save one out of the 2M combinations. Quantum computers can save 2M combinations of information. Let’s see what quantum entanglement is. We discussed quantum entanglement in the initial chapters. Entanglement is related to a pair of entangled particles whose state is dependent on each other. This phenomenon is observed when they are separated by huge distances.

Quantum circuits are used to create a quantum computer. The circuit consists of wires and quantum gates. Quantum logic gates are based on either a quantum bit or multiple quantum bit gates. These gates are reversible. Quantum gates are used to transmit and modify the information.

Now let’s look at the controlled-NOT gate circuit shown in Figure 4-2. A controlled-NOT gate is a multiquantum bit logic gate. It is one of the universal quantum bit gates. A CNOT gate has two input quantum bits. These quantum bits are the control quantum bit and the target quantum bit. Similarly, the CNOT operator takes 2 qubits as input. For the pair of input quantum bits, one is called a controlled quantum bit and the other the target quantum bit. When you change the controlled quantum bit to |1>, the target quantum bit’s value is flipped. Otherwise, the target quantum bit remains the same.
../images/490826_1_En_4_Chapter/490826_1_En_4_Fig2_HTML.jpg
Figure 4-2

CNOT gate circuit

Figure 4-3 shows a CNOT gate circuit in matrix form.
../images/490826_1_En_4_Chapter/490826_1_En_4_Fig3_HTML.jpg
Figure 4-3

CNOT gate circuit in matrix form

Let’s look at another type of circuit. A controlled-U gate circuit is based on a controlled-U gate. This gate operates on the input of 2 qubits. The first quantum bit is a control quantum bit. Figure 4-4 shows the circuit. The controlled-U gate has U, which is a unitary matrix. The unitary matrix operates on a number m of quantum bits. The basic example of a controlled-U gate is a controlled-NOT gate.
../images/490826_1_En_4_Chapter/490826_1_En_4_Fig4_HTML.jpg
Figure 4-4

Controlled-U gate circuit

Figure 4-5 shows the controlled-U gate circuit in matrix form.
../images/490826_1_En_4_Chapter/490826_1_En_4_Fig5_HTML.jpg
Figure 4-5

Controlled-U gate circuit matrix

A Toffoli gate circuit is another type of circuit, as shown in Figure 4-6. A Toffoli gate combined with a Hadamard gate is a 3 qubit logic gate.
../images/490826_1_En_4_Chapter/490826_1_En_4_Fig6_HTML.jpg
Figure 4-6

Toffoli gate circuit

A Toffoli gate has an input of 3 bits and an output of 3 bits. Out of the three input bits, two of them are control bits, and the third bit is a target bit. If control bits have a value of 1, the target bit is flipped. If the control bits do not have a value of 1, the control bits are not changed.

Figure 4-7 shows a Toffoli gate circuit in matrix form.
../images/490826_1_En_4_Chapter/490826_1_En_4_Fig7_HTML.jpg
Figure 4-7

Toffoli gate circuit matrix

After looking at the different types of quantum circuits, the last step in the quantum circuit model is the measurement projected on the computational scale. This step is not reversible. There are two types of measurement: deferred and implicit. The deferred measurement method allows changing the quantum operations to the classical equivalents. In this method, a measurement can be sent from the middle stage to the end of the circuit. In the implicit measurement method, quantum bits that are not measured can be measured implicitly. In this method, generality loss does not happen.

Note

A quantum circuit has reversible changes to a mechanical analog, which is a quantum register.

Quantum Communication

Quantum communication is the process of sending the quantum state from one point to another, as shown in Figure 4-8. Superdense coding is a popular communication protocol that was first proposed in 1992 by Bennett and Wiesner. Quantum teleportation is related to the movement of quantum states without quantum communication. This is an inverse method to superdense coding. Quantum teleportation is a method for shifting the quantum states without the quantum communication channel between the receiver and the sender.
../images/490826_1_En_4_Chapter/490826_1_En_4_Fig8_HTML.jpg
Figure 4-8

Quantum communication

In superdense coding, classical information bits are transferred from the source to the destination. In quantum teleportation, quantum bits are transferred by using the communication channel between two classical bits.

The superdense protocol has an instruction set and the output results. The protocol is valid if it generates the expected output. The superdense protocol is based on quantum entanglement. Quantum entanglement occurs when a single common source can produce multiple quantum systems. The state of the quantum systems does not follow Bell inequality rules. The systems that are entangled impact each other even if they are separated by huge distances.

We have looked at the superdense quantum communication protocol; the other protocols are the continuous variable, satellite, and linear optics quantum communication protocols. The electric field of the incident light is measured using a homodyne detector. The measurement results in a continuous value. Continuous variable quantum communication is based on the homodyne detector measurement outcome. The satellite communication protocol is based on ground-to-satellite and satellite-to-satellite communication channels. The linear optical quantum communication protocol is based on optical fiber–based communication channels.

Quantum Noise

Quantum noise occurs because of uncertainty of a quantum origin–based physical entity. Spectral density is used to measure the noise intensity. This is measured at a specific frequency in a time-dependent manner. Quantum devices create noise when the field is amplified. Quantum computers also create noise because of the noncommutation of the modes of the quantum field. Figure 4-9 represents the noise in quantum computing form and the wave form.
../images/490826_1_En_4_Chapter/490826_1_En_4_Fig9_HTML.jpg
Figure 4-9

Quantum noise

H.B.Callen and T.A.Welton represented the spectral density in mathematical equations. Roger H.Koch measured the spectral entities of the quantum noise experimentally first. Spectral density is related to the signal periodograms of time segments. Mathematically, Fourier analysis is a good method to measure spectral density by breaking down the function to a set of periodograms. A periodogram is a time period series in mathematics.

Note

Quantum noise happens because of signal changes in an electric circuit. The signal changes occur because of the electron’s discrete character.

Quantum Error Correction

The quantum error correction method helps to shield the quantum information from quantum noise and decoherence errors. The quantum error correction method was first created in 1995. Figure 4-10 shows error correction in the quantum circuit.
../images/490826_1_En_4_Chapter/490826_1_En_4_Fig10_HTML.jpg
Figure 4-10

Quantum error correction

Let’s look at classical error correction techniques such as coding. Coding is related to data cloning. This technique is not helpful for quantum error correction because of the no-cloning principle. The no-cloning principle states that a quantum state that is not known cannot be copied exactly. Quantum error correction does not allow a huge number of copies. Errors cannot be prevented by using direct measurement because quantum superposition fails. Quantum errors can happen due to quantum bit flip, phase shift, and continuous errors.

Now let’s look at how to fix quantum errors. Coherent techniques help to bring down the control errors. The errors due to decoherence are time dependent. Modeling the errors helps to measure the errors, and positive operator–valued measures are used to measure the errors. Tracing the lost quantum bit is another way to model the quantum bit loss and removal. Correction is done by validating the presence of the quantum bit in the system first. Quantum bits are lost due to controls that are significant. The Shor code protocol designed by Peter Shor helps in identifying and fixing the errors from a quantum bit.

Let’s now look at the Shor code protocol. The Shor code is a group of three quantum bit phase flip and bit flip codes. This quantum code helps to guard against the error impact on a quantum bit. Quantum states are operated by the unitary operation to result in a quantum error correction–based code. This result will be part of the subspace D of a bigger Hilbert space. A Hilbert space is an inner product of finite-dimensional complex vectors.

Limitations of Quantum Computing

Let’s look at the limitations of quantum computing before we look at different quantum algorithms. Quantum computers are prone to errors and problems due to the environment. Decoherence happens because of the collapse of the state function due to the measurement of state for a quantum bit. Decoherence causes the errors in the computation. The quantum bits count impacts the decoherence probability exponentially. Decoherence occurs because of the quantum bit flip, spontaneous symmetric breaking, and phase flip coding protocols. Quantum bit flip code flips the quantum bit based on a probability. The symmetric breaking protocol is based on a symmetry group F if an F transformation on the quantum system can be achieved by operating transformations on the quantum subsystems. Quantum bit phase flip code is based on the flip of the phase based on a probability.

Small errors can be removed by using error correction protocols in classical computation. In quantum computation, small errors cannot be fixed. Quantum systems are entangled to the environment, and hence the error is induced before the computation begins. Initialization of the quantum bit induces the errors due to unitary transformations.

Quantum Algorithms

Algorithms are used in computation and data processing. The real-life situations are modeled and solved using heuristics and methods. An algorithm consists of a set of commands to execute a task. Quantum algorithms help in providing solutions to complex problems that cannot be solved by classical computers.

Quantum programming languages help to create quantum software with quantum algorithms. A quantum algorithm consists of steps such as encoding the quantum data to quantum bits, a unitary quantum gates chain operating on the quantum bits, and terminating after measuring the quantum bits. A unitary quantum gate is similar to a unitary matrix that operates on the quantum bits. A unitary matrix inverse will be equal to its conjugate transpose.

Quantum computing is based on quantum mechanical properties such as superposition and entanglement. Specifically, quantum algorithms use these principles.

Note

The word algorithm word originates from the word algebra. It is named after Arabian mathematician Al Khwarizmi.

Let’s look at some quantum algorithms, starting with the Deutsch–Jozsa algorithm.

Deutsch–Jozsa Algorithm

The Deutsch–Jozsa algorithm was designed by David Deutsch and Richard Jozsa in 1992. This algorithm was modified by Richard Cleve and others in 1998. This method is nondeterministic and created a barrier between the quantum and classical toughness of the problem. This technique is about quantum amplitudes having positive and negative values. The algorithm identifies the black-box oracle function. Figure 4-11 shows an example of an oracle function.
../images/490826_1_En_4_Chapter/490826_1_En_4_Fig11_HTML.jpg
Figure 4-11

Oracle function

This algorithm uses a black-box (Oracle) function that takes as input the binary values and gives either 0 or 1 as output. The method finds whether the function g used is constant or balanced.

In the next sections, we discuss the algorithm implementation. To start with, let’s look at the quantum register. The quantum register has a constructor and GetMeasure and ApplyOperation methods. The GetMeasure method computes the quantum states and probabilities. The ApplyOperation method updates the class instance variable data to the product of the input gate matrix and data instance.

Code Sample

Quantum_Register.py
import numpy as nump
from itertools import product
from Quantum_Gate import Quantum_Gate
class Quantum_Register:
    def __init__(self, n_qbits, init):
        self._n = n_qbits
        assert len(init) == self._n
        self._data = nump.zeros((2 ** self._n), dtype=nump.complex64)
        self._data[int('0b' + init, 2)] = 1
    def Get_Measure(self):
        probs = nump.real(self._data) ** 2 + nump.imag(self._data) ** 2
        states = nump.arange(2 ** self._n)
        mstate = nump.random.choice(states, size=1, p=probs)[0]
        return f'{mstate:>0{self._n}b}'
    def Apply_Gate(self, gate):
        assert isinstance(gate, Quantum_Gate)
        assert self._n == gate._n
        self._data = gate._data @ self._data

Now let’s look at the quantum gate implementation. It has a constructor, matrix multiplication, and power methods. The matrix multiplication method operates on the input data and the instance data to return a Kronecker product. The power method takes the input to return the quantum gate.

The I, H, X, Y, and Z gates are initialized in this class. The U_Quantum_Gate gate method creates a quantum gate based on the input parameters.

Code Sample

Quantum_Gate.py
import numpy as nump
from itertools import product
class Quantum_Gate:
    def __init__(self, matrix):
        self._data = nump.array(matrix, dtype=nump.complex64)
        assert len(self._data.shape) == 2
        assert self._data.shape[0] == self._data.shape[1]
        self._n = nump.log2(self._data.shape[0])
        assert self._n.is_integer()
        self._n = int(self._n)
    def __matmul__(self, other):
        return Quantum_Gate(nump.kron(self._data, other._data))
    def __pow__(self, n, modulo=None):
        x = self._data.copy()
        for _ in range(n - 1):
            x = nump.kron(x, self._data)
        return Quantum_Gate(x)
IGate = Quantum_Gate([[1, 0], [0, 1]])
HGate = Quantum_Gate(nump.array([[1, 1], [1, -1]]) / nump.sqrt(2))
XGate = Quantum_Gate([[0, 1], [1, 0]])
YGate = Quantum_Gate([[0, -1j], [1j, 0]])
ZGate = Quantum_Gate([[1, 0], [0, -1]])
def U_Quantum_Gate(f, n):
    m = n + 1
    U = nump.zeros((2**m, 2**m), dtype=nump.complex64)
    def bin2int(xs):
        r = 0
        for i, x in enumerate(reversed(xs)):
            r += x * 2 ** i
        return r
    for xs in product({0, 1}, repeat=m):
        x = xs[:~0]
        y = xs[~0]
        z = y ^ f(*x)
        instate = bin2int(xs)
        outstate = bin2int(list(x) + [z])
        U[instate, outstate] = 1
    return Quantum_Gate(U)

Functions g1_func, g2_func, g3_func, and g4_func are shown in the code sample. g1_func takes v and returns v. g2_func takes v as an input value and returns value 1. g3_func takes v and w as input values and returns the binary XOR of v and w. g4_func takes v, w, and x as input values and returns a zero value. The CheckIfConstant method takes the input function and an integer to validate whether the function is a constant or balanced function.

Code Sample

Deutsch_Jozsa_Algorithm.py
from Quantum_Register import Quantum_Register
from Quantum_Gate import HGate, IGate, UGate
def Check_If_Constant(g, n):
    qr = Quantum_Register(n + 1, '0' * n + '1')
    qr.Apply_Gate(HGate ** (n + 1))
    qr.Apply_Gate(UGate(g, n))
    qr.Apply_Gate(HGate ** n @ IGate)
    return qr.Get_Measure()[:~0] == '0' * n
def g1_func(v):
    return v
def g2_func(w):
    return 1
def g3_func(v, w):
    return v ^ w
def g4_func(v, w,x):
    return 0
print('g(v) = v is {}'.format('constant function' if Check_If_Constant(g1_func, 1) else 'balanced function'))
print('g(v) = 1 is {}'.format('constant function' if Check_If_Constant(g2_func, 1) else 'balanced function'))
print('g(v, w) = v ^ w is {}'.format('constant function' if Check_If_Constant(g3_func, 2) else 'balanced function'))
print('g(v, w, x) = 0 is {}'.format('constant function' if Check_If_Constant(g4_func, 3) else 'balanced function'))

Command for Execution

pip3 install numpy
python3 Deutsch_Jozsa_Algorithm.py

Output

g(v) = v is balanced function
g(v) = 1 is constant function
g(v, w) = v ^ w is balanced function
g(v, w, x) = 0 is constant function
Note

A balanced Boolean function yields output of Booleans 0s equal to 1s. A constant function has an output value as a constant.

Simon’s Algorithm

Now let’s look at Simon’s quantum algorithm. This algorithm helps in identifying a black-box function h(x), where h(x) = h(y) and x= y ⊕ t. Note that t ∈ {0, 1}n and ⊕ represents bitwise addition based on module 2. The goal of the algorithm is to find that by using h. This quantum algorithm performs exponentially better than the classical equivalent. This algorithm inspired Shor to come up with Shor’s algorithm.

Let’s look at the Python code implementation of this algorithm.

The RetrieveOnetoOneMap method takes the input mask as a parameter and returns the output, which is a bitmap function. The RetrieveValidTwoToOneMap method takes input parameters such as search mask and random seed. It returns output that is a two-to-one mapping function.

Code Sample

Simons_Algorithm.py
from collections import defaultdict
import numpy as nump
from mock import patch
from collections import defaultdict
from operator import xor
from typing import Dict, Tuple, List
import numpy.random as rand
from pyquil import Program
from pyquil.api import QuantumComputer
from pyquil.gates import H, MEASURE
def RetrieveOnetoOneMap(mask: str) -> Dict[str, str]:
         n_bits = len(mask)
         form_string = "{0:0" + str(n_bits) + "b}"
         bit_map_dct = {}
         for idx in range(2**n_bits):
         bit_string = form_string.format(idx)
         bit_map_dct[bit_string] = InvokeBitwiseXorOperation(bit_string, mask)
         return bit_map_dct
def RetrieveValidTwotoOneMap(mask: str, random_seed: int = None) -> Dict[str, str]:
         if random_seed is not None:
         rand.seed(random_seed)
         bit_map = RetrieveOnetoOneMap(mask)
         n_samples = int(len(bit_map.keys()) / 2)
         range_of_2to1_map = list(rand.choice(list(sorted(bit_map.keys())), replace=False, size=n_samples))
         list_of_bitstring_tuples = sorted([(k, v) for k, v in bit_map.items()], key=lambda x: x[0])
         bit_map_dct = {}
         for cnt in range(n_samples):
         bitstring_tup = list_of_bitstring_tuples[cnt]
         val = range_of_2to1_map[cnt]
         bit_map_dct[bitstring_tup[0]] = val
         bit_map_dct[bitstring_tup[1]] = val
       return bit_map_dct

The Simons_Algorithm class is used to find the black-box function to satisfy a set of values. It has a constructor and the following methods: RetrieveSimonCircuit, CalculateUnitaryOracleMatrix, RetrieveBitMaskRetrieveSampleIndependentBits, RetrieveInverseMaskEquation, AppendToDictofIndepBits, RetrieveMissingMsbVector, and CheckValidMaskIfCorrect. The RetrieveSimonCircuit method takes as input the quantum bits and returns the output, which is the quantum circuit.

The CalculateUnitaryOracleMatrix method of the Simons_Algorithm class takes the input map of bit strings. The output will be a dense matrix, which is a permutation of the values from the dictionary. The RetrieveBitMask method of the Simons_Algorithm class takes as input the quantum computer and bit strings. The output is a bit string map.

The RetrieveSampleIndependentBits method takes as input the quantum computer and bit strings. The independent bit vectors are updated with the bit strings. The RetrieveInverse mask equation method creates the bit mask based on the input function. The AppendToDictofIndepBits method takes as input an array. This method adds to the dictionary of the independent vectors. The RetrieveMissingMsbVector method searches the provenance value in the set of independent bit vectors. The method modifies the collection by adding the unit vector. The CheckValidMaskIfCorrect method validates the given mask if it is correct.

Let’s now look at what an independent vector is. If two vectors are not zero and are not parallel, they are linearly independent. A linear independent vector is typically a nonzero vector. The converse is also true. After the independent vector, let’s see what a bit mask is. A bit mask helps in retrieving the bits in a given byte of information.
class Simons_Algorithm(object):
          def __init__(self):
          self.unitary_function_mapping = None
          self.n_qubits = None
          self.n_ancillas = None
          self._qubits = None
          self.computational_qubits = None
          self.ancillas = None
          self.simon_circuit = None
          self._dict_of_linearly_indep_bit_vectors = {}
          self.search_mask = None
          self.bit_map = None
          self.classical_register = None
         def RetrieveSimonCircuit(self) -> Program:
         simon_circuit = Program()
          oracle_name = "SIMON_ORACLE_FUNCTION"
          simon_circuit.defgate(oracle_name, self.unitary_function_mapping)
          simon_circuit.inst([H(i) for i in self.computational_qubits])
          simon_circuit.inst(tuple([oracle_name] + sorted(self._qubits, reverse=True)))
          simon_circuit.inst([H(i) for i in self.computational_qubits])
          return simon_circuit
          def _Initialize_Attributes(self, bitstring_map: Dict[str, str]) -> None:
          self.bit_map = bitstring_map
          self.n_qubits = len(list(bitstring_map.keys())[0])
          self.n_ancillas = self.n_qubits
          self._qubits = list(range(self.n_qubits + self.n_ancillas))
          self.computational_qubits = self._qubits[:self.n_qubits]
          self.ancillas = self._qubits[self.n_qubits:]
          self.unitary_function_mapping, _ = self.CalculateUnitaryOracle(bitstring_map)
          self.simon_circuit = self.RetrieveSimonCircuit()
          self._dict_of_linearly_indep_bit_vectors = {}
          self.search_mask = None
          @staticmethod
           def CalculateUnitaryOracle(bitstring_map: Dict[str, str]) -> Tuple[nump.ndarray,Dict[str, str]]:
          n_bits = len(list(bitstring_map.keys())[0])
          ufunc = nump.zeros(shape=(2 ** (2 * n_bits), 2 ** (2 * n_bits)))
          index_mapping_dct = defaultdict(dict)
          for b in range(2**n_bits):
          pad_str = nump.binary_repr(b, n_bits)
          for k, v in bitstring_map.items():
                 index_mapping_dct[pad_str + k] = InvokeBitwiseXorOperation(pad_str, v) + k
                 i, j = int(pad_str+k, 2), int(InvokeBitwiseXorOperation(pad_str, v) + k, 2)
                 ufunc[i, j] = 1
         return ufunc, index_mapping_dct
         def RetrieveBitMask(self, qc: QuantumComputer, bitstring_map: Dict[str, str]) -> str:
         self._Initialize_Attributes(bitstring_map)
         self.RetrieveSampleIndependentBits(qc)
         self.RetrieveInverseMaskEquation()
          if self.CheckValidMaskIfCorrect():
          return self.search_mask
          else:
          raise Exception("No valid mask found")
         def RetrieveSampleIndependentBits(self, quantum_computer: QuantumComputer) -> None:
        while len(self._dict_of_linearly_indep_bit_vectors) < self.n_qubits - 1:
         prog = Program()
         simon_ro = prog.declare('ro', 'BIT', len(self.computational_qubits))
         prog += self.simon_circuit
         prog += [MEASURE(qubit, ro) for qubit, ro in zip(self.computational_qubits, simon_ro)]
               executable = quantum_computer.compile(prog)
         sampled_bit_string = nump.array(quantum_computer.run(executable)[0], dtype=int)
         self.AppendToDictofIndepBits(sampled_bit_string)
          def RetrieveInverseMaskEquation(self) -> None:
          missing_msb = self.RetrieveMissingMsbVector()
          upper_triangular_matrix = nump.asarray(
          [tup[1] for tup in sorted(zip(self._dict_of_linearly_indep_bit_vectors.keys(),
                                           self._dict_of_linearly_indep_bit_vectors.values()),
                                    key=lambda x: x[0])])
        msb_unit_vec = nump.zeros(shape=(self.n_qubits,), dtype=int)
        msb_unit_vec[missing_msb] = 1
        self.search_mask = RetrieveBinaryBackSubstitute(upper_triangular_matrix, msb_unit_vec).tolist()
        def AppendToDictofIndepBits(self, z: nump.ndarray) -> None:
        if (z == 0).all() or (z == 1).all():
        return None
        msb_z = RetrieveMostSignificantBit(z)
         if msb_z not in self._dict_of_linearly_indep_bit_vectors.keys():
        self._dict_of_linearly_indep_bit_vectors[msb_z] = z
        else:
         conflict_z = self._dict_of_linearly_indep_bit_vectors[msb_z]
         not_z = [xor(conflict_z[idx], z[idx]) for idx in range(len(z))]
         if (nump.asarray(not_z) == 0).all():
             return None
         msb_not_z = most_significant_bit(nump.asarray(not_z))
         if msb_not_z not in self._dict_of_linearly_indep_bit_vectors.keys():
                 self._dict_of_linearly_indep_bit_vectors[msb_not_z] = not_z
         def RetrieveMissingMsbVector(self) -> int:
         missing_msb = None
         for idx in range(self.n_qubits):
         if idx not in self._dict_of_linearly_indep_bit_vectors.keys():
                 missing_msb = idx
         if missing_msb is None:
         raise ValueError("Expected a missing provenance, but didn't find one.")
         augment_vec = nump.zeros(shape=(self.n_qubits,))
         augment_vec[missing_msb] = 1
         self._dict_of_linearly_indep_bit_vectors[missing_msb] = augment_vec.astype(int).tolist()
         return missing_msb
         def CheckValidMaskIfCorrect(self) -> bool:
         mask_str = ''.join([str(b) for b in self.search_mask])
         return all([self.bit_map[k] == self.bit_map[InvokeBitwiseXorOperation(k, mask_str)] for k in self.bit_map.keys()])
PADDED_BINARY_BIT_STRING = "{0:0{1:0d}b}"
def CheckValidIfUnitary(matrix: nump.ndarray) -> bool:
          rows, cols = matrix.shape
          if rows != cols:
          return False
          return nump.allclose(nump.eye(rows), matrix.dot(matrix.T.conj()))
def RetrieveMostSignificantBit(lst: nump.ndarray) -> int:
         return nump.argwhere(nump.asarray(lst) == 1)[0][0]
def InvokeBitwiseXorOperation(bs0: str, bs1: str) -> str:
         if len(bs0) != len(bs1):
         raise ValueError("Bit strings are not of equal length")
         n_bits = len(bs0)
         return PADDED_BINARY_BIT_STRING.format(xor(int(bs0, 2), int(bs1, 2)), n_bits)
def RetrieveBinaryBackSubstitute(W: nump.ndarray, s: nump.ndarray) -> nump.ndarray:
         m = nump.copy(s)
         n = len(s)
         for row_num in range(n - 2, -1, -1):
         row = W[row_num]
         for col_num in range(row_num + 1, n):
         if row[col_num] == 1:
                  m[row_num] = xor(s[row_num], s[col_num])
        return m[::-1]
search_mask = '110'
bm = RetrieveValidTwotoOneMap(search_mask, random_seed=42)
expected_map = {
         '000': '001',
         '001': '101',
         '010': '000',
         '011': '111',
         '100': '000',
         '101': '111',
         '110': '001',
         '111': '101'
}
for k, v in bm.items():
         assert v == expected_map[k]
reverse_bitmap = defaultdict(list)
for k, v in bm.items():
         reverse_bitmap[v].append(k)
expected_reverse_bitmap = {
         '001': ['000', '110'],
         '101': ['001', '111'],
         '000': ['010', '100'],
         '111': ['011', '101']
}
for k, v in reverse_bitmap.items():
         assert sorted(v) == sorted(expected_reverse_bitmap[k])
with patch("pyquil.api.QuantumComputer") as quantum_computer:
        quantum_computer.run.side_effect = [
        (nump.asarray([0, 1, 1], dtype=int), ),
        (nump.asarray([1, 1, 1], dtype=int), ),
        (nump.asarray([1, 1, 1], dtype=int), ),
        (nump.asarray([1, 0, 0], dtype=int), ),
        ]
simon_algo = Simons_Algorithm()
result_mask = simon_algo.RetrieveBitMask(quantum_computer, bm)
print("mask", search_mask," result mask",result_mask)

Command for Execution

pip3 install numpy
pip3 install pyquil
python3 Simons_Algorithm.py

Output

mask 110  result mask [1, 1, 0]

Shor’s Algorithm

Peter Shor was the first to come up with a quantum algorithm for the factorization of integers in 1994. Shor’s algorithm performs in polynomial time to factor an integer M. The order of performance is O(logN). This outperforms the classical equivalent. In addition, this algorithm has the potential to break the RSA cryptographic method.

The algorithm takes a number M and finds an integer q between 1 and M, which divides M. The algorithm has two steps: the reduction of factoring to order the finding method and the order finding technique.

Let’s look at the classical solution. The greatest common divisor is found for b < M . gcd(b,M). This is computed using the Euclidean method. The steps are as follows:
  • If gcd(b,M) != 1, there are nontrivial factors for M, and the problem is solved.

  • If gcd(b,M) == 1 , find the period of g(x) = ax mod M to find r for which g(x+r) = g(x) (this is step 1).
    • If r is odd, go back to step 1.

    • If ar/2 = -1 (mod M), go back to step 1.

    • gcd(ar/2 +- 1, M) is a nontrivial factor of M. The problem is solved.

Now let’s look at the quantum algorithm. The quantum circuit for the algorithm is based on M and a in g(x) = ax mod M: Given M, Q= 2q and Q/r > M. The input and output quantum bits have superpositions of values from 0 to Q-1, and they have q quantum bits each. The g(x) function is used to transform the states of the quantum bits. The final state will be a superposition of multiple Q states, which will be Q^2 states.

Let’s look at the Shor’s algorithm implementation in Python.

Shor’s algorithm is used for prime factorization. The QuantumMap class has the properties of state and amplitude. QuantumEntanglement has the properties of amplitude, register, and entangled. The UpdateEntangled method of the QuantumEntanglement class takes input parameters such as state and amplitude. The RetrieveEntangles method returns the list of entangled states.

Code Sample

Shors_Algorithm.py
class QuantumMap:
     def __init__(self, state, amplitude):
          self.state = state
          self.amplitude = amplitude
class QuantumEntanglement:
      def __init__(self, amplitude, register):
            self.amplitude = amplitude
            self.register = register
            self.entangled = {}
      def UpdateEntangled(self, fromState, amplitude):
           register = fromState.register
           entanglement = QuantumMap(fromState, amplitude)
           try:
                         self.entangled[register].append(entanglement)
                    except KeyError:
                         self.entangled[register] = [entanglement]
       def RetrieveEntangles(self, register = None):
                entangles = 0
                if register is None:
                     for states in self.entangled.values():
                                entangles += len(states)
                     else:
                               entangles = len(self.entangled[register])
                    return entangles

The QuantumRecord class has numBits, numStates, an entangled list, and a states array. The SetPropagate property of the QuantumRecord class takes fromRegister as the parameter and sets propagate on the register. The UpdateMap method takes such inputs as toRegister, mapping, and propagate parameters. This method sets the normalized tensorX and tensorY lists. The FindMeasure method of the QuantumRecord class returns the output, which is the final X state. The RetrieveEntangles method takes as input the register and returns the output, which is the entangled state value. The RetrieveAmplitudes method returns the output, which is the amplitudes array of quantum states.

Code Sample

class QuantumRecord:
     def __init__(self, numBits):
          self.numBits = numBits
          self.numStates = 1 << numBits
          self.entangled = []
          self.states = [QuantumEntanglement(complex(0.0), self) for x in range(self.numStates)]
          self.states[0].amplitude = complex(1.0)
     def UpdatePropagate(self, fromRegister = None):
          if fromRegister is not None:
               for state in self.states:
                    amplitude = complex(0.0)
                    try:
                         entangles = state.entangled[fromRegister]
                         for entangle in entangles:
                              amplitude += entangle.state.amplitude * entangle.amplitude
                         state.amplitude = amplitude
                    except KeyError:
                         state.amplitude = amplitude
          for register in self.entangled:
               if register is fromRegister:
                    continue
               register.UpdatePropagate(self)
     def UpdateMap(self, toRegister, mapping, propagate = True):
          self.entangled.append(toRegister)
          toRegister.entangled.append(self)
          mapTensorX = {}
          mapTensorY = {}
          for x in range(self.numStates):
               mapTensorX[x] = {}
               codomain = mapping(x)
               for element in codomain:
                    y = element.state
                    mapTensorX[x][y] = element
                    try:
                         mapTensorY[y][x] = element
                    except KeyError:
                         mapTensorY[y] = { x: element }
          def UpdateNormalize(tensor, p = False):
               lSqrt = math.sqrt
               for vectors in tensor.values():
                    sumProb = 0.0
                    for element in vectors.values():
                         amplitude = element.amplitude
                         sumProb += (amplitude * amplitude.conjugate()).real
                    normalized = lSqrt(sumProb)
                    for element in vectors.values():
                         element.amplitude = element.amplitude / normalized
          UpdateNormalize(mapTensorX)
          UpdateNormalize(mapTensorY, True)
          for x, yStates in mapTensorX.items():
               for y, element in yStates.items():
                    amplitude = element.amplitude
                    toState = toRegister.states[y]
                    fromState = self.states[x]
                    toState.UpdateEntangled(fromState, amplitude)
                    fromState.UpdateEntangled(toState, amplitude.conjugate())
          if propagate:
               toRegister.UpdatePropagate(self)
     def RetrieveMeasure(self):
          measure = random.random()
          sumProb = 0.0
          # Pick a state
          finalX = None
          finalState = None
          for x, state in enumerate(self.states):
               amplitude = state.amplitude
               sumProb += (amplitude * amplitude.conjugate()).real
               if sumProb > measure:
                    finalState = state
                    finalX = x
                    break
          if finalState is not None:
               for state in self.states:
                    state.amplitude = complex(0.0)
               finalState.amplitude = complex(1.0)
               self.UpdatePropagate()
          return finalX
     def RetrieveEntangles(self, register = None):
          entangles = 0
          for state in self.states:
               entangles += state.entangles(None)
          return entangles
     def RetrieveAmplitudes(self):
          amplitudes = []
          for state in self.states:
               amplitudes.append(state.amplitude)
          return amplitudes

The FindListEntangles method prints the list of entangles. The FindListAmplitudes method prints the values of the amplitudes of the register. The InvokeHadamard method takes input such as lambda x and the quantum bit. The codomain array is returned as the output. The InvokeQModExp method takes the input parameters aval, exponent expval, and the modval operator value. The state is calculated using the method InvokeModExp.

The InvokeQft method takes inputs such as parameters x and the quantum bit Q. The quantum mapping of the state and amplitude is returned by the method InvokeQft. The FindPeriod method takes the inputs a and N. The period r for the function is returned as output from this method.

The ExecuteShors method takes inputs such as N, attempts, neighborhood, and numPeriods as parameters. This method applies Shor’s algorithm to find the prime factors of a given number N.

Code Sample

def FindListEntangles(register):
     print("Entangles: " + str(register.RetrieveEntangles()))
def FindListAmplitudes(register):
     amplitudes = register.amplitudes()
     for x, amplitude in enumerate(amplitudes):
          print('State #' + str(x) + ''s Amplitude value: ' + str(amplitude))
def InvokeHadamard(x, Q):
     codomain = []
     for y in range(Q):
          amplitude = complex(pow(-1.0, RetrieveBitCount(x & y) & 1))
          codomain.append(QuantumMap(y, amplitude))
     return  codomain
def InvokeQModExp(a, exp, mod):
     state = InvokeModExp(a, exp, mod)
     amplitude = complex(1.0)
     return [QuantumMap(state, amplitude)]
def InvokeQft(x, Q):
     fQ = float(Q)
     k = -2.0 * math.pi
     codomain = []
     for y in range(Q):
          theta = (k * float((x * y) % Q)) / fQ
          amplitude = complex(math.cos(theta), math.sin(theta))
          codomain.append(QuantumMap(y, amplitude))
     return codomain
def DeterminePeriod(a, N):
     nNumBits = N.bit_length()
     inputNumBits = (2 * nNumBits) - 1
     inputNumBits += 1 if ((1 << inputNumBits) < (N * N)) else 0
     Q = 1 << inputNumBits
     print("The period is...")
     print("Q = " + str(Q) + " a = " + str(a))
     inputRegister = QuantumRecord(inputNumBits)
     hmdInputRegister = QuantumRecord(inputNumBits)
     qftInputRegister = QuantumRecord(inputNumBits)
     outputRegister = QuantumRecord(inputNumBits)
     print("Registers are instantiated")
     print("Executing Hadamard on the input")
     inputRegister.UpdateMap(hmdInputRegister, lambda x: InvokeHadamard(x, Q), False)
     print("Hadamard operation is invoked")
     print("Mapping input register to the output")
     hmdInputRegister.UpdateMap(outputRegister, lambda x: InvokeQModExp(a, x, N), False)
     print("Modular exponentiation is invoked")
     print("Executing quantum Fourier transform on the output")
     hmdInputRegister.UpdateMap(qftInputRegister, lambda x: InvokeQft(x, Q), False)
     inputRegister.UpdatePropagate()
     print("Quantum Fourier transform is invoked")
     print("Retrieving a measurement on the output")
     y = outputRegister.RetrieveMeasure()
     print("Measuring the Output register y = " + str(y))
     print("Retrieving  a measurement on the periodicity")
     x = qftInputRegister.RetrieveMeasure()
     print("Measuring QFT   x = " + str(x))
     if x is None:
          return None
     print("Retrieving the period via continued fractions")
     r = RetrieveContinuedFraction(x, Q, N)
     print("Determined Candidate period r = " + str(r))
     return r
def RetrieveBitCount(x):
     sumBits = 0
     while x > 0:
          sumBits += x & 1
          x >>= 1
     return sumBits
def RetrieveGcd(a, b):
     while b != 0:
          tA = a % b
          a = b
          b = tA
     return a
def RetrieveExtendedGCD(a, b):
     fractions = []
     while b != 0:
          fractions.append(a // b)
          tA = a % b
          a = b
          b = tA
     return fractions
def RetrieveContinuedFraction(y, Q, N):
     fractions = RetrieveExtendedGCD(y, Q)
     depth = 2
     def RetrievePartial(fractions, depth):
          c = 0
          r = 1
          for i in reversed(range(depth)):
               tR = fractions[i] * r + c
               c = r
               r = tR
          return c
     r = 0
     for d in range(depth, len(fractions) + 1):
          tR = RetrievePartial(fractions, d)
          if tR == r or tR >= N:
               return r
          r = tR
     return r
def InvokeModExp(a, exp, mod):
     fx = 1
     while exp > 0:
          if (exp & 1) == 1:
               fx = fx * a % mod
          a = (a * a) % mod
          exp = exp >> 1
     return fx
def RetrieveRandom(N):
     a = math.floor((random.random() * (N - 1)) + 0.5)
     return a
def RetrieveNeighBorCandidates(a, r, N, neighborhood):
     if r is None:
          return None
     for k in range(1, neighborhood + 2):
          tR = k * r
          if InvokeModExp(a, a, N) == InvokeModExp(a, a + tR, N):
               return tR
     for tR in range(r - neighborhood, r):
          if InvokeModExp(a, a, N) == InvokeModExp(a, a + tR, N):
               return tR
     for tR in range(r + 1, r + neighborhood + 1):
          if InvokeModExp(a, a, N) == InvokeModExp(a, a + tR, N):
               return tR
     return None
def ExecuteShorsAlgorithm(N, attempts = 1, neighborhood = 0.0, numPeriods = 1):
     periods = []
     neighborhood = math.floor(N * neighborhood) + 1
     print("N value is" + str(N))
     print("Neighborhood value is = " + str(neighborhood))
     print("Number of periods is = " + str(numPeriods))
     for attempt in range(attempts):
          print(" Attempt #" + str(attempt))
          a = RetrieveRandom(N)
          while a < 2:
               a = RetrieveRandom(N)
          d = RetrieveGcd(a, N)
          if d > 1:
               print("Determined factors classically, re-attempt")
               continue
          r = DeterminePeriod(a, N)
          print("validating the candidate period, nearby values, and multiples")
          r = RetrieveNeighBorCandidates(a, r, N, neighborhood)
          if r is None:
               print("Period was not determined, re-attempt")
               continue
          if (r % 2) > 0:
               print("Period is odd, re-attempt")
               continue
          d = InvokeModExp(a, (r // 2), N)
          if r == 0 or d == (N - 1):
               print("Period is trivial, re-attempt")
               continue
          print("Period found r = " + str(r))
          periods.append(r)
          if(len(periods) < numPeriods):
               continue
          print(" Determining  least common multiple of all periods")
          r = 1
          for period in periods:
               d = RetrieveGcd(period, r)
               r = (r * period) // d
          b = InvokeModExp(a, (r // 2), N)
          f1 = RetrieveGcd(N, b + 1)
          f2 = RetrieveGcd(N, b - 1)
          return [f1, f2]
     return None
results = ExecuteShorsAlgorithm(35, 20, 0.01, 2)
print("Results are: " + str(results[0]) + ", " + str(results[1]))

Command for Execution

pip3 install numpy
python3 Shors_Algorithm.py

Output

N = 35
Neighborhood = 1
Number of periods = 2
Attempt #0
Printing the period...
Q = 2048     a = 4
Registers created
Performing Hadamard on input register
Hadamard operation applied
Mapping input register to output register
Modular exponentiation applied
Applying quantum Fourier transform on output register
Quantum Fourier transform applied
Getting a measurement on the output register
Measuring Output register      y = 9
Getting a measurement on the periodicity register
Measuring QFT register      x = 1024
Getting the period via continued fractions
Traceback (most recent call last):
  File "Shors_Algorithm.py", line 384, in <module>
    results = ExecuteShorsAlgorithm(35, 20, 0.01, 2)
  File "Shors_Algorithm.py", line 343, in ExecuteShorsAlgorithm
    r = DeterminePeriod(a, N)
  File "Shors_Algorithm.py", line 230, in DeterminePeriod
    r = RetrieveContinuedFraction(x, Q, N)
  File "Shors_Algorithm.py", line 281, in RetrieveContinuedFraction
    tR = RetrievePartial(fractions, d)
NameError: name 'RetrievePartial' is not defined
apples-MacBook-Air:chapter4 bhagvan.kommadi$ python3 Shors_Algorithm.py
N value is35
Neighborhood value is = 1
Number of periods is = 2
Attempt #0
The period is...
Q = 2048     a = 24
Registers are instantiated
Executing Hadamard on the input
Hadamard operation is invoked
Mapping input register to the output
Modular exponentiation is invoked
Executing quantum Fourier transform on the output
Quantum Fourier transform is invoked
Retrieving a measurement on the output
Measuring the Output register      y = 1
Retrieving  a measurement on the periodicity
Measuring QFT       x = 1707
Retrieving the period via continued fractions
Determined Candidate period     r = 6
validating the candidate period, nearby values, and multiples
Period is trivial, re-attempt
Attempt #1
Determined factors classically, re-attempt
Attempt #2
The period is...
Q = 2048     a = 23
Registers are instantiated
Executing Hadamard on the input
Hadamard operation is invoked
Mapping input register to the output
Modular exponentiation is invoked
Executing quantum Fourier transform on the output
Quantum Fourier transform is invoked
Retrieving a measurement on the output
Measuring the Output register      y = 16
Retrieving  a measurement on the periodicity
Measuring QFT       x = 1196
Retrieving the period via continued fractions
Determined Candidate period     r = 12
validating the candidate period, nearby values, and multiples
Period found     r = 12
Attempt #3
The period is...
Q = 2048     a = 2
Registers are instantiated
Executing Hadamard on the input
Hadamard operation is invoked
Mapping input register to the output
Modular exponentiation is invoked
Executing quantum Fourier transform on the output
Quantum Fourier transform is invoked
Retrieving a measurement on the output
Measuring the Output register      y = 4
Retrieving  a measurement on the periodicity
Measuring QFT       x = 1365
Retrieving the period via continued fractions
Determined Candidate period     r = 3
validating the candidate period, nearby values, and multiples
Period was not determined, re-attempt
Attempt #4
The period is...
Q = 2048     a = 19
Registers are instantiated
Executing Hadamard on the input
Hadamard operation is invoked
Mapping input register to the output
Modular exponentiation is invoked
Executing quantum Fourier transform on the output
Quantum Fourier transform is invoked
Retrieving a measurement on the output
Measuring the Output register      y = 1
Retrieving  a measurement on the periodicity
Measuring QFT       x = 683
Retrieving the period via continued fractions
Determined Candidate period     r = 3
validating the candidate period, nearby values, and multiples
Period is trivial, re-attempt
Attempt #5
The period is...
Q = 2048     a = 29
Registers are instantiated
Executing Hadamard on the input
Hadamard operation is invoked
Mapping input register to the output
Modular exponentiation is invoked
Executing quantum Fourier transform on the output
Quantum Fourier transform is invoked
Retrieving a measurement on the output
Measuring the Output register      y = 29
Retrieving  a measurement on the periodicity
Measuring QFT       x = 1024
Retrieving the period via continued fractions
Determined Candidate period     r = 2
validating the candidate period, nearby values, and multiples
Period found     r = 2
 Determining  least common multiple of all periods
Results are:     1, 35

Grover’s Algorithm

Grover’s algorithm is used for searching a database. The search algorithm can find an entry in a database of M entries with O (√N) time and O(logN) space. This method was found by Lov Grover in 1996. He found that his method gave a quadratic speedup compared to other quantum algorithms. In addition, Grover’s method gives exponential speed over the classical equivalents.

Grover’s algorithm inverts a function. Let’s say y = g(x). Grover’s algorithm finds x when y is given as input. Searching for a value of y in the database is similar to finding a match of x. It is popular when solving NP-hard problems by using exhaustive search over a possible set of solutions. You can use Grover’s algorithm to search for a phone number in a phone book of M entries in √ M steps.

Grover’s algorithm consists of the following steps:
  1. 1.

    Initialize the system to the state.

     
  2. 2.

    Execute the Grover iteration r(M) ties.

     
  3. 3.

    Measure the amplitude Ω.

     
  4. 4.

    From Ωw (result), find w.

     

Let’s look at the implementation of Grover’s algorithm in Python.

Grover’s search algorithm searches the target value of a group by computing the mean amplitude and Grover’s amplitude. The plot of the graph is computed from the amplitudes derived from Grover’s algorithm. The plot presents the target value with the highest amplitude. The ApplyOracleFunction method takes input x and returns the output as the hex digest of x. ApplyGrover’s algorithm takes as input the target, objects, nvalue, and rounds. The amplitude is returned as the output.

The target of the algorithm is to search for 9 in the group of string objects {'14', '5', '13', '7','9','11','97'}. The amplitude is searched from the dictionary (group of string objects) based on the value 1/ square root of the length of the set (9).

Code Sample

Grovers_Algorithm.py
import matplotlib.pyplot as plot
import numpy as nump
import string
import hashlib
from math import sqrt, pi
from collections import OrderedDict
from statistics import mean
def RenderGraph(amplitude_value, n):
    y_position = nump.arange(n)
    plot.bar(y_position, amplitude_value.values(), align="center", color="g")
    plot.xticks(y_position, amplitude_value.keys())
    plot.ylabel('Amplitude Value')
    plot.title('Grovers Algorithm')
    plot.show()
def ApplyOracleFunction(xvalue):
    return hashlib.sha256(bytes(xvalue, 'utf-8')).hexdigest()
def ApplyGroverAlgorithm(target, objects, nvalue, rounds):
    y_pos = nump.arange(nvalue)
    amplitude = OrderedDict.fromkeys(objects, 1/sqrt(nvalue))
    for i in range(0, rounds, 2):
        for k, v in amplitude.items():
            if ApplyOracleFunction(k) == target:
                amplitude[k] = v * -1
        average = mean(amplitude.values())
        for k, v in amplitude.items():
            if ApplyOracleFunction(k) == target:
                amplitude[k] = (2 * average) + abs(v)
                continue
            amplitude[k] = v-(2*(v-average))
    return amplitude
target_algorithm = '9'
objects_grover = ('14', '5', '13', '7','9','11','97')
number = len(objects_grover)
amplitude_grover = OrderedDict.fromkeys(objects_grover, 1/sqrt(number))
amplitude_grover[target_algorithm] = amplitude_grover[target_algorithm] * -1
print(amplitude_grover)
average_grover = mean(amplitude_grover.values())
print("Mean is {}".format(average_grover))
for k, v in amplitude_grover.items():
    if k == target_algorithm:
        amplitude_grover[k] = (2 * average_grover) + abs(v)
        continue
    amplitude_grover[k] = v-(2*(v-average_grover))
print(amplitude_grover)
needle_value = "2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881"
haystack_value = string.ascii_lowercase
num = len(haystack_value)
num_rounds = int((pi / 4) * sqrt(num))
print("number of rounds are {}".format(num_rounds))
RenderGraph(ApplyGroverAlgorithm(needle_value, haystack_value, num, num_rounds), num)

Command for Execution

pip3 install numpy
python3 Grovers_Algorithm.py

Output

OrderedDict([('4', 0.3779644730092272), ('5', 0.3779644730092272), ('3', -0.3779644730092272), ('7', 0.3779644730092272), ('9', 0.3779644730092272), ('11', 0.3779644730092272), ('97', 0.3779644730092272)])
Mean is 0.26997462357801943
OrderedDict([('4', 0.16198477414681167), ('5', 0.16198477414681167), ('3', 0.9179137201652661), ('7', 0.16198477414681167), ('9', 0.16198477414681167), ('11', 0.16198477414681167), ('97', 0.16198477414681167)])
number of rounds are 4

Quantum Subroutines

Quantum computer simulators are built to simulate a quantum computer of size n quantum bits. Quantum computer simulation packages are now available to write software related to quantum computing. The packages are like black boxes that abstract the methods and techniques of quantum algorithms. The quantum Fourier transform, QPhase, and matrix inversion subroutines are available in the packages to help solve complex problems in real life.

The software packages are Project Q, IBM Quantum Experience, PennyLane, QuidPro, Liquid, etc. Quantum circuits can be simulated using the packages for analysis and computation.

The applications are developed using the quantum subroutine packages for quantum and classical algorithms. The code is compiled, and the input files are used for quantum algorithms to execute the algorithm. Quantum circuits are modeled using the subroutine API packages. The quantum circuits have a set of control gates, and measurements are performed on the simulated quantum circuits.

Execution of the quantum algorithms takes place using the group of quantum circuits that represent and model the real-life problem. The postprocessing of the results is performed for analysis purposes.

Summary

In this chapter, we looked at quantum circuits, quantum communication, quantum noise, quantum error correction, and the limitations of quantum computing. Quantum algorithms such as Deutsch–Jozsa, Simon, Shor’s, and Grover’s were discussed in detail. Quantum subroutines and algorithms were also presented with examples.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.85.76