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
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)
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
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
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
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)