Example #1
0
    def test_phase_estimation(self):
        """Test the standard phase estimation circuit."""
        with self.subTest('U=S, psi=|1>'):
            unitary = QuantumCircuit(1)
            unitary.s(0)

            eigenstate = QuantumCircuit(1)
            eigenstate.x(0)

            # eigenvalue is 1j = exp(2j pi 0.25) thus phi = 0.25 = 0.010 = '010'
            # using three digits as 3 evaluation qubits are used
            phase_as_binary = '0100'

            pec = PhaseEstimation(4, unitary)

            self.assertPhaseEstimationIsCorrect(pec, eigenstate,
                                                phase_as_binary)

        with self.subTest('U=SZ, psi=|11>'):
            unitary = QuantumCircuit(2)
            unitary.z(0)
            unitary.s(1)

            eigenstate = QuantumCircuit(2)
            eigenstate.x([0, 1])

            # eigenvalue is -1j = exp(2j pi 0.75) thus phi = 0.75 = 0.110 = '110'
            # using three digits as 3 evaluation qubits are used
            phase_as_binary = '110'

            pec = PhaseEstimation(3, unitary)

            self.assertPhaseEstimationIsCorrect(pec, eigenstate,
                                                phase_as_binary)

        with self.subTest('a 3-q unitary'):
            unitary = QuantumCircuit(3)
            unitary.x([0, 1, 2])
            unitary.cz(0, 1)
            unitary.h(2)
            unitary.ccx(0, 1, 2)
            unitary.h(2)

            eigenstate = QuantumCircuit(3)
            eigenstate.h(0)
            eigenstate.cx(0, 1)
            eigenstate.cx(0, 2)

            # the unitary acts as identity on the eigenstate, thus the phase is 0
            phase_as_binary = '00'

            pec = PhaseEstimation(2, unitary)

            self.assertPhaseEstimationIsCorrect(pec, eigenstate,
                                                phase_as_binary)
    def phase_estimation(self, ancilla_bits, starting_state):
        """
            Creates the phase estimation circuit given starting state and number of ancilla bits
            
            Parameters:
                ancilla_bits (int): number of ancilla bits
                starting_state (numpy array): starting state as a superposition over edges
                
            Returns:
                QPE (QuantumCircuit): the quantum phase estimation circuit
        """
        # We can just use qiskit.circuit.library.PhaseEstimation on our operator that we embed into an n qubit space
        #Finding number of qubits needed to encode all edge states
        num_operator_qubits = int(
            np.ceil(np.log2(self.quantumWalkOperator.shape[0])))
        # Embedding R_BR_A into the space of the qubits
        unitary_matrix = np.identity(2**num_operator_qubits,
                                     dtype=np.complex128)
        for i in range(self.quantumWalkOperator.shape[0]):
            for j in range(self.quantumWalkOperator.shape[1]):
                unitary_matrix[i, j] = self.quantumWalkOperator[i, j]
        # Setting up the Unitary Operator as a circuit
        reg = QuantumRegister(num_operator_qubits)
        unitary_circuit = QuantumCircuit(reg)
        unitary_circuit.unitary(unitary_matrix, reg)
        print("Set up Unitary Quantum Walk Gate ...")
        # Setting up the Phase Estimation
        QPE = PhaseEstimation(ancilla_bits, unitary_circuit)
        print("Set up Phase Estimation Circuit ...")
        # Initializing the state we are to run Phase Estimation on
        # Getting the ancilla and actual registers used in the QPE circuit
        ancillae = QPE.qubits[0].register
        edge_register = QPE.qubits[-1].register

        # Setting up the initializations we need
        initializer = QuantumCircuit(ancillae, edge_register)
        starting_state_proper = np.zeros(2**num_operator_qubits)
        for i in range(num_operator_qubits):
            starting_state_proper[i] = starting_state[i]

        # Appending the initializer circuit to front of circuit
        initializer.initialize(starting_state_proper, edge_register)
        QPE = initializer.combine(QPE)
        print("Initialized starting state ...")
        # Adding Measurement
        print("Running simulation ...")
        Measurement_Register = ClassicalRegister(ancilla_bits)
        QPE = QPE.combine(QuantumCircuit(Measurement_Register))
        QPE.measure(ancillae, Measurement_Register)
        return QPE
Example #3
0
    def test_phase_estimation_iqft_setting(self):
        """Test default and custom setting of the QFT circuit."""
        unitary = QuantumCircuit(1)
        unitary.s(0)

        with self.subTest('default QFT'):
            pec = PhaseEstimation(3, unitary)
            expected_qft = QFT(3, inverse=True, do_swaps=False).reverse_bits()
            self.assertEqual(pec.data[-1][0].definition, expected_qft)

        with self.subTest('custom QFT'):
            iqft = QFT(3, approximation_degree=2).inverse()
            pec = PhaseEstimation(3, unitary, iqft=iqft)
            self.assertEqual(pec.data[-1][0].definition, iqft)
Example #4
0
    def construct_circuit(self, measurement: bool = False) -> QuantumCircuit:
        """Construct the Amplitude Estimation quantum circuit.

        Args:
            measurement: Boolean flag to indicate if measurements should be included in the circuit.

        Returns:
            The QuantumCircuit object for the constructed circuit.
        """
        if self.state_preparation is None:  # circuit factories
            # ignore deprecation warnings from getter, user has already been warned when
            # the a_factory has been passed
            warnings.filterwarnings('ignore', category=DeprecationWarning)
            q_factory = self.q_factory
            warnings.filterwarnings('always', category=DeprecationWarning)

            iqft = QFT(self._m, do_swaps=False, inverse=True).reverse_bits() \
                if self._iqft is None else self._iqft
            pec = PhaseEstimationCircuit(
                iqft=iqft,
                num_ancillae=self._m,
                state_in_circuit_factory=self._a_factory,
                unitary_circuit_factory=q_factory)
            self._circuit = pec.construct_circuit(measurement=measurement)
        else:
            if self._pec is not None:
                pec = self._pec
            else:
                from qiskit.circuit.library import PhaseEstimation
                pec = PhaseEstimation(self._m,
                                      self.grover_operator,
                                      iqft=self._iqft)

            # mypy thinks self.circuit is None even after explicitly being set to QuantumCircuit
            self._circuit = QuantumCircuit(*pec.qregs)
            self._circuit.compose(
                self.state_preparation,  # type: ignore
                list(
                    range(self._m,
                          self._m + self.state_preparation.num_qubits)),
                inplace=True)
            self._circuit.compose(pec, inplace=True)  # type: ignore

            if measurement:
                cr = ClassicalRegister(self._m)
                self._circuit.add_register(cr)  # type: ignore
                self._circuit.measure(list(range(self._m)),
                                      list(range(self._m)))  # type: ignore

        return self._circuit
Example #5
0
    def construct_circuit(self,
                          estimation_problem: EstimationProblem,
                          measurement: bool = False) -> QuantumCircuit:
        """Construct the Amplitude Estimation quantum circuit.

        Args:
            estimation_problem: The estimation problem for which to construct the QAE circuit.
            measurement: Boolean flag to indicate if measurements should be included in the circuit.

        Returns:
            The QuantumCircuit object for the constructed circuit.
        """
        # use custom Phase Estimation circuit if provided
        if self._pec is not None:
            pec = self._pec

        # otherwise use the circuit library -- note that this does not include the A operator
        else:
            from qiskit.circuit.library import PhaseEstimation

            pec = PhaseEstimation(self._m,
                                  estimation_problem.grover_operator,
                                  iqft=self._iqft)

        # combine the Phase Estimation circuit with the A operator
        circuit = QuantumCircuit(*pec.qregs)
        circuit.compose(
            estimation_problem.state_preparation,
            list(range(self._m, circuit.num_qubits)),
            inplace=True,
        )
        circuit.compose(pec, inplace=True)

        # add measurements if necessary
        if measurement:
            cr = ClassicalRegister(self._m)
            circuit.add_register(cr)
            circuit.measure(list(range(self._m)), list(range(self._m)))

        return circuit
Example #6
0
    def construct_circuit(
            self, matrix: Union[np.ndarray, QuantumCircuit],
            vector: Union[np.ndarray, QuantumCircuit]) -> QuantumCircuit:
        """Construct the HHL circuit.

        Args:
            matrix: The matrix specifying the system, i.e. A in Ax=b.
            vector: The vector specifying the right hand side of the equation in Ax=b.

        Returns:
            The HHL circuit.

        Raises:
            ValueError: If the input is not in the correct format.
            ValueError: If the type of the input matrix is not supported.
        """
        # State preparation circuit - default is qiskit
        if isinstance(vector, QuantumCircuit):
            nb = vector.num_qubits
            vector_circuit = vector
        elif isinstance(vector, np.ndarray):
            nb = int(np.log2(len(vector)))
            vector_circuit = QuantumCircuit(nb)
            vector_circuit.isometry(vector / np.linalg.norm(vector),
                                    list(range(nb)), None)

        # If state preparation is probabilistic the number of qubit flags should increase
        nf = 1

        # Hamiltonian simulation circuit - default is Trotterization
        if isinstance(matrix, QuantumCircuit):
            matrix_circuit = matrix
        elif isinstance(matrix, (list, np.ndarray)):
            if isinstance(matrix, list):
                matrix = np.array(matrix)

            if matrix.shape[0] != matrix.shape[1]:
                raise ValueError("Input matrix must be square!")
            if np.log2(matrix.shape[0]) % 1 != 0:
                raise ValueError("Input matrix dimension must be 2^n!")
            if not np.allclose(matrix, matrix.conj().T):
                raise ValueError("Input matrix must be hermitian!")
            if matrix.shape[0] != 2**vector_circuit.num_qubits:
                raise ValueError("Input vector dimension does not match input "
                                 "matrix dimension! Vector dimension: " +
                                 str(vector_circuit.num_qubits) +
                                 ". Matrix dimension: " + str(matrix.shape[0]))
            matrix_circuit = NumPyMatrix(matrix, evolution_time=2 * np.pi)
        else:
            raise ValueError(f'Invalid type for matrix: {type(matrix)}.')

        # Set the tolerance for the matrix approximation
        if hasattr(matrix_circuit, "tolerance"):
            matrix_circuit.tolerance = self._epsilon_a

        # check if the matrix can calculate the condition number and store the upper bound
        if hasattr(matrix_circuit, "condition_bounds") and matrix_circuit.condition_bounds() is not\
                None:
            kappa = matrix_circuit.condition_bounds()[1]
        else:
            kappa = 1
        # Update the number of qubits required to represent the eigenvalues
        nl = max(nb + 1, int(np.log2(kappa)) + 1)

        # check if the matrix can calculate bounds for the eigenvalues
        if hasattr(matrix_circuit,
                   "eigs_bounds") and matrix_circuit.eigs_bounds() is not None:
            lambda_min, lambda_max = matrix_circuit.eigs_bounds()
            # Constant so that the minimum eigenvalue is represented exactly, since it contributes
            # the most to the solution of the system
            delta = self._get_delta(nl, lambda_min, lambda_max)
            # Update evolution time
            matrix_circuit.evolution_time = 2 * np.pi * delta / lambda_min
            # Update the scaling of the solution
            self.scaling = lambda_min
        else:
            delta = 1 / (2**nl)
            print("The solution will be calculated up to a scaling factor.")

        if self._exact_reciprocal:
            reciprocal_circuit = ExactReciprocal(nl, delta)
            # Update number of ancilla qubits
            na = matrix_circuit.num_ancillas
        else:
            # Calculate breakpoints for the reciprocal approximation
            num_values = 2**nl
            constant = delta
            a = int(round(num_values**(2 / 3)))  # pylint: disable=invalid-name

            # Calculate the degree of the polynomial and the number of intervals
            r = 2 * constant / a + np.sqrt(np.abs(1 - (2 * constant / a)**2))
            degree = min(
                nb,
                int(
                    np.log(1 +
                           (16.23 * np.sqrt(np.log(r)**2 +
                                            (np.pi / 2)**2) * kappa *
                            (2 * kappa - self._epsilon_r)) / self._epsilon_r)))
            num_intervals = int(
                np.ceil(np.log((num_values - 1) / a) / np.log(5)))

            # Calculate breakpoints and polynomials
            breakpoints = []
            for i in range(0, num_intervals):
                # Add the breakpoint to the list
                breakpoints.append(a * (5**i))

                # Define the right breakpoint of the interval
                if i == num_intervals - 1:
                    breakpoints.append(num_values - 1)

            reciprocal_circuit = PiecewiseChebyshev(
                lambda x: np.arcsin(constant / x), degree, breakpoints, nl)
            na = max(matrix_circuit.num_ancillas,
                     reciprocal_circuit.num_ancillas)

        # Initialise the quantum registers
        qb = QuantumRegister(nb)  # right hand side and solution
        ql = QuantumRegister(nl)  # eigenvalue evaluation qubits
        if na > 0:
            qa = AncillaRegister(na)  # ancilla qubits
        qf = QuantumRegister(nf)  # flag qubits

        if na > 0:
            qc = QuantumCircuit(qb, ql, qa, qf)
        else:
            qc = QuantumCircuit(qb, ql, qf)

        # State preparation
        qc.append(vector_circuit, qb[:])
        # QPE
        phase_estimation = PhaseEstimation(nl, matrix_circuit)
        if na > 0:
            qc.append(phase_estimation,
                      ql[:] + qb[:] + qa[:matrix_circuit.num_ancillas])
        else:
            qc.append(phase_estimation, ql[:] + qb[:])
        # Conditioned rotation
        if self._exact_reciprocal:
            qc.append(reciprocal_circuit, ql[::-1] + [qf[0]])
        else:
            qc.append(reciprocal_circuit.to_instruction(),
                      ql[:] + [qf[0]] + qa[:reciprocal_circuit.num_ancillas])
        # QPE inverse
        if na > 0:
            qc.append(phase_estimation.inverse(),
                      ql[:] + qb[:] + qa[:matrix_circuit.num_ancillas])
        else:
            qc.append(phase_estimation.inverse(), ql[:] + qb[:])
        return qc
Example #7
0
    def test_phase_estimation(self):
        """Test the standard phase estimation circuit."""
        with self.subTest("U=S, psi=|1>"):
            unitary = QuantumCircuit(1)
            unitary.s(0)

            eigenstate = QuantumCircuit(1)
            eigenstate.x(0)

            # eigenvalue is 1j = exp(2j pi 0.25) thus phi = 0.25 = 0.010 = '010'
            # using three digits as 3 evaluation qubits are used
            phase_as_binary = "0100"

            pec = PhaseEstimation(4, unitary)

            self.assertPhaseEstimationIsCorrect(pec, eigenstate, phase_as_binary)

        with self.subTest("U=SZ, psi=|11>"):
            unitary = QuantumCircuit(2)
            unitary.z(0)
            unitary.s(1)

            eigenstate = QuantumCircuit(2)
            eigenstate.x([0, 1])

            # eigenvalue is -1j = exp(2j pi 0.75) thus phi = 0.75 = 0.110 = '110'
            # using three digits as 3 evaluation qubits are used
            phase_as_binary = "110"

            pec = PhaseEstimation(3, unitary)

            self.assertPhaseEstimationIsCorrect(pec, eigenstate, phase_as_binary)

        with self.subTest("a 3-q unitary"):
            #      ┌───┐
            # q_0: ┤ X ├──■────■───────
            #      ├───┤  │    │
            # q_1: ┤ X ├──■────■───────
            #      ├───┤┌───┐┌─┴─┐┌───┐
            # q_2: ┤ X ├┤ H ├┤ X ├┤ H ├
            #      └───┘└───┘└───┘└───┘
            unitary = QuantumCircuit(3)
            unitary.x([0, 1, 2])
            unitary.cz(0, 1)
            unitary.h(2)
            unitary.ccx(0, 1, 2)
            unitary.h(2)

            #      ┌───┐
            # q_0: ┤ H ├──■────■──
            #      └───┘┌─┴─┐  │
            # q_1: ─────┤ X ├──┼──
            #           └───┘┌─┴─┐
            # q_2: ──────────┤ X ├
            #                └───┘
            eigenstate = QuantumCircuit(3)
            eigenstate.h(0)
            eigenstate.cx(0, 1)
            eigenstate.cx(0, 2)

            # the unitary acts as identity on the eigenstate, thus the phase is 0
            phase_as_binary = "00"

            pec = PhaseEstimation(2, unitary)

            self.assertPhaseEstimationIsCorrect(pec, eigenstate, phase_as_binary)