def aux_operators( self, aux_operators: Optional[ Union[OperatorBase, LegacyBaseOperator, List[Optional[Union[OperatorBase, LegacyBaseOperator]]]]] ) -> None: """ Set aux operators """ if aux_operators is None: aux_operators = [] elif not isinstance(aux_operators, list): aux_operators = [aux_operators] # We need to handle the array entries being Optional i.e. having value None self._aux_op_nones = [op is None for op in aux_operators] if aux_operators: zero_op = I.tensorpower(self.operator.num_qubits) * 0.0 converted = [] for op in aux_operators: if op is None: converted.append(zero_op) elif isinstance(op, LegacyBaseOperator): converted.append(op.to_opflow()) else: converted.append(op) # For some reason Chemistry passes aux_ops with 0 qubits and paulis sometimes. aux_operators = [zero_op if op == 0 else op for op in converted] self._aux_operators = aux_operators # type: List
def test_io_consistency(self): """ consistency test """ new_op = X ^ Y ^ I label = 'XYI' # label = new_op.primitive.to_label() self.assertEqual(str(new_op.primitive), label) np.testing.assert_array_almost_equal(new_op.primitive.to_matrix(), Operator.from_label(label).data) self.assertEqual(new_op.primitive, Pauli(label)) x_mat = X.primitive.to_matrix() y_mat = Y.primitive.to_matrix() i_mat = np.eye(2, 2) np.testing.assert_array_almost_equal(new_op.primitive.to_matrix(), np.kron(np.kron(x_mat, y_mat), i_mat)) hi = np.kron(H.to_matrix(), I.to_matrix()) hi2 = Operator.from_label('HI').data hi3 = (H ^ I).to_matrix() np.testing.assert_array_almost_equal(hi, hi2) np.testing.assert_array_almost_equal(hi2, hi3) xy = np.kron(X.to_matrix(), Y.to_matrix()) xy2 = Operator.from_label('XY').data xy3 = (X ^ Y).to_matrix() np.testing.assert_array_almost_equal(xy, xy2) np.testing.assert_array_almost_equal(xy2, xy3) # Check if numpy array instantiation is the same as from Operator matrix_op = Operator.from_label('+r') np.testing.assert_array_almost_equal(PrimitiveOp(matrix_op).to_matrix(), PrimitiveOp(matrix_op.data).to_matrix()) # Ditto list of lists np.testing.assert_array_almost_equal(PrimitiveOp(matrix_op.data.tolist()).to_matrix(), PrimitiveOp(matrix_op.data).to_matrix())
def aux_operators( self, aux_operators: Optional[ Union[OperatorBase, LegacyBaseOperator, List[Optional[Union[OperatorBase, LegacyBaseOperator]]]]] ) -> None: if aux_operators is None: aux_operators = [] elif not isinstance(aux_operators, list): aux_operators = [aux_operators] if aux_operators: zero_op = I.tensorpower(self.operator.num_qubits) * 0.0 converted = [ op.to_opflow() if isinstance(op, LegacyBaseOperator) else op for op in aux_operators ] # For some reason Chemistry passes aux_ops with 0 qubits and paulis sometimes. aux_operators = [zero_op if op == 0 else op for op in converted] self._aux_operators = aux_operators
def test_is_hermitian(self): """Test is_hermitian method.""" with self.subTest("I"): self.assertTrue(I.is_hermitian()) with self.subTest("X"): self.assertTrue(X.is_hermitian()) with self.subTest("Y"): self.assertTrue(Y.is_hermitian()) with self.subTest("Z"): self.assertTrue(Z.is_hermitian()) with self.subTest("XY"): self.assertFalse((X @ Y).is_hermitian()) with self.subTest("CX"): self.assertTrue(CX.is_hermitian()) with self.subTest("T"): self.assertFalse(T.is_hermitian())
def compute_minimum_eigenvalue( self, operator: OperatorBase, aux_operators: Optional[List[Optional[OperatorBase]]] = None ) -> MinimumEigensolverResult: super().compute_minimum_eigenvalue(operator, aux_operators) if self.quantum_instance is None: raise AlgorithmError( "A QuantumInstance or Backend " "must be supplied to run the quantum algorithm.") if operator is None: raise AlgorithmError("The operator was never provided.") operator = self._check_operator(operator) # We need to handle the array entries being Optional i.e. having value None if aux_operators: zero_op = I.tensorpower(operator.num_qubits) * 0.0 converted = [] for op in aux_operators: if op is None: converted.append(zero_op) else: converted.append(op) # For some reason Chemistry passes aux_ops with 0 qubits and paulis sometimes. aux_operators = [zero_op if op == 0 else op for op in converted] else: aux_operators = None self._quantum_instance.circuit_summary = True self._eval_count = 0 # Convert the gradient operator into a callable function that is compatible with the # optimization routine. if self._gradient: if isinstance(self._gradient, GradientBase): self._gradient = self._gradient.gradient_wrapper( ~StateFn(operator) @ StateFn(self._var_form), bind_params=self._var_form_params, backend=self._quantum_instance) if not self._expect_op: self._expect_op = self.construct_expectation( self._var_form_params, operator) vqresult = self.find_minimum(initial_point=self.initial_point, var_form=self.var_form, cost_fn=self._energy_evaluation, gradient_fn=self._gradient, optimizer=self.optimizer) self._ret = VQEResult() self._ret.combine(vqresult) if vqresult.optimizer_evals is not None and \ self._eval_count >= vqresult.optimizer_evals: self._eval_count = vqresult.optimizer_evals self._eval_time = vqresult.optimizer_time logger.info( 'Optimization complete in %s seconds.\nFound opt_params %s in %s evals', self._eval_time, vqresult.optimal_point, self._eval_count) self._ret.eigenvalue = vqresult.optimal_value + 0j self._ret.eigenstate = self.get_optimal_vector() self._ret.eigenvalue = self.get_optimal_cost() if aux_operators: self._eval_aux_ops(aux_operators) self._ret.aux_operator_eigenvalues = self._ret.aux_operator_eigenvalues[ 0] self._ret.cost_function_evals = self._eval_count return self._ret
def compute_minimum_eigenvalue( self, operator: OperatorBase, aux_operators: Optional[ListOrDict[OperatorBase]] = None ) -> MinimumEigensolverResult: super().compute_minimum_eigenvalue(operator, aux_operators) if self.quantum_instance is None: raise AlgorithmError( "A QuantumInstance or Backend must be supplied to run the quantum algorithm." ) self.quantum_instance.circuit_summary = True # this sets the size of the ansatz, so it must be called before the initial point # validation self._check_operator_ansatz(operator) # set an expectation for this algorithm run (will be reset to None at the end) initial_point = _validate_initial_point(self.initial_point, self.ansatz) bounds = _validate_bounds(self.ansatz) # We need to handle the array entries being zero or Optional i.e. having value None if aux_operators: zero_op = I.tensorpower(operator.num_qubits) * 0.0 # Convert the None and zero values when aux_operators is a list. # Drop None and convert zero values when aux_operators is a dict. if isinstance(aux_operators, list): key_op_iterator = enumerate(aux_operators) converted = [zero_op] * len(aux_operators) else: key_op_iterator = aux_operators.items() converted = {} for key, op in key_op_iterator: if op is not None: converted[key] = zero_op if op == 0 else op aux_operators = converted else: aux_operators = None # Convert the gradient operator into a callable function that is compatible with the # optimization routine. if isinstance(self._gradient, GradientBase): gradient = self._gradient.gradient_wrapper( ~StateFn(operator) @ StateFn(self._ansatz), bind_params=self._ansatz_params, backend=self._quantum_instance, ) else: gradient = self._gradient self._eval_count = 0 energy_evaluation, expectation = self.get_energy_evaluation( operator, return_expectation=True) start_time = time() # keep this until Optimizer.optimize is removed try: opt_result = self.optimizer.minimize(fun=energy_evaluation, x0=initial_point, jac=gradient, bounds=bounds) except AttributeError: # self.optimizer is an optimizer with the deprecated interface that uses # ``optimize`` instead of ``minimize``` warnings.warn( "Using an optimizer that is run with the ``optimize`` method is " "deprecated as of Qiskit Terra 0.19.0 and will be unsupported no " "sooner than 3 months after the release date. Instead use an optimizer " "providing ``minimize`` (see qiskit.algorithms.optimizers.Optimizer).", DeprecationWarning, stacklevel=2, ) opt_result = self.optimizer.optimize(len(initial_point), energy_evaluation, gradient, bounds, initial_point) eval_time = time() - start_time result = VQEResult() result.optimal_point = opt_result.x result.optimal_parameters = dict(zip(self._ansatz_params, opt_result.x)) result.optimal_value = opt_result.fun result.cost_function_evals = opt_result.nfev result.optimizer_time = eval_time result.eigenvalue = opt_result.fun + 0j result.eigenstate = self._get_eigenstate(result.optimal_parameters) logger.info( "Optimization complete in %s seconds.\nFound opt_params %s in %s evals", eval_time, result.optimal_point, self._eval_count, ) # TODO delete as soon as get_optimal_vector etc are removed self._ret = result if aux_operators is not None: aux_values = self._eval_aux_ops(opt_result.x, aux_operators, expectation=expectation) result.aux_operator_eigenvalues = aux_values return result
def compute_eigenvalues( self, operator: OperatorBase, aux_operators: Optional[List[Optional[OperatorBase]]] = None ) -> EigensolverResult: super().compute_eigenvalues(operator, aux_operators) if operator is None: raise AlgorithmError("Operator was never provided") self._check_set_k(operator) if aux_operators: zero_op = I.tensorpower(operator.num_qubits) * 0.0 # For some reason Chemistry passes aux_ops with 0 qubits and paulis sometimes. aux_operators = [ zero_op if op == 0 else op for op in aux_operators ] else: aux_operators = None k_orig = self._k if self._filter_criterion: # need to consider all elements if a filter is set self._k = 2**operator.num_qubits self._ret = EigensolverResult() self._solve(operator) # compute energies before filtering, as this also evaluates the aux operators self._get_energies(operator, aux_operators) # if a filter is set, loop over the given values and only keep if self._filter_criterion: eigvecs = [] eigvals = [] aux_ops = [] cnt = 0 for i in range(len(self._ret.eigenvalues)): eigvec = self._ret.eigenstates[i] eigval = self._ret.eigenvalues[i] if self._ret.aux_operator_eigenvalues is not None: aux_op = self._ret.aux_operator_eigenvalues[i] else: aux_op = None if self._filter_criterion(eigvec, eigval, aux_op): cnt += 1 eigvecs += [eigvec] eigvals += [eigval] if self._ret.aux_operator_eigenvalues is not None: aux_ops += [aux_op] if cnt == k_orig: break self._ret.eigenstates = np.array(eigvecs) self._ret.eigenvalues = np.array(eigvals) # conversion to np.array breaks in case of aux_ops self._ret.aux_operator_eigenvalues = aux_ops self._k = k_orig # evaluate ground state after filtering (in case a filter is set) self._get_ground_state_energy(operator) if self._ret.eigenstates is not None: self._ret.eigenstates = ListOp( [StateFn(vec) for vec in self._ret.eigenstates]) logger.debug('EigensolverResult:\n%s', self._ret) return self._ret
def compute_minimum_eigenvalue( self, operator: OperatorBase, aux_operators: Optional[List[Optional[OperatorBase]]] = None ) -> MinimumEigensolverResult: super().compute_minimum_eigenvalue(operator, aux_operators) if self.quantum_instance is None: raise AlgorithmError( "A QuantumInstance or Backend must be supplied to run the quantum algorithm." ) self.quantum_instance.circuit_summary = True # this sets the size of the ansatz, so it must be called before the initial point # validation self._check_operator_ansatz(operator) # set an expectation for this algorithm run (will be reset to None at the end) initial_point = _validate_initial_point(self.initial_point, self.ansatz) bounds = _validate_bounds(self.ansatz) # We need to handle the array entries being Optional i.e. having value None if aux_operators: zero_op = I.tensorpower(operator.num_qubits) * 0.0 converted = [] for op in aux_operators: if op is None: converted.append(zero_op) else: converted.append(op) # For some reason Chemistry passes aux_ops with 0 qubits and paulis sometimes. aux_operators = [zero_op if op == 0 else op for op in converted] else: aux_operators = None # Convert the gradient operator into a callable function that is compatible with the # optimization routine. if isinstance(self._gradient, GradientBase): gradient = self._gradient.gradient_wrapper( ~StateFn(operator) @ StateFn(self._ansatz), bind_params=self._ansatz_params, backend=self._quantum_instance, ) else: gradient = self._gradient self._eval_count = 0 energy_evaluation, expectation = self.get_energy_evaluation( operator, return_expectation=True) start_time = time() opt_params, opt_value, nfev = self.optimizer.optimize( num_vars=len(initial_point), objective_function=energy_evaluation, gradient_function=gradient, variable_bounds=bounds, initial_point=initial_point, ) eval_time = time() - start_time result = VQEResult() result.optimal_point = opt_params result.optimal_parameters = dict(zip(self._ansatz_params, opt_params)) result.optimal_value = opt_value result.cost_function_evals = nfev result.optimizer_time = eval_time result.eigenvalue = opt_value + 0j result.eigenstate = self._get_eigenstate(result.optimal_parameters) logger.info( "Optimization complete in %s seconds.\nFound opt_params %s in %s evals", eval_time, result.optimal_point, self._eval_count, ) # TODO delete as soon as get_optimal_vector etc are removed self._ret = result if aux_operators is not None: aux_values = self._eval_aux_ops(opt_params, aux_operators, expectation=expectation) result.aux_operator_eigenvalues = aux_values[0] return result
class TestPhaseEstimation(QiskitAlgorithmsTestCase): """Evolution tests.""" # pylint: disable=invalid-name def one_phase( self, unitary_circuit, state_preparation=None, backend_type=None, phase_estimator=None, num_iterations=6, ): """Run phase estimation with operator, eigenvalue pair `unitary_circuit`, `state_preparation`. Return the estimated phase as a value in :math:`[0,1)`. """ if backend_type is None: backend_type = "qasm_simulator" backend = qiskit.BasicAer.get_backend(backend_type) qi = qiskit.utils.QuantumInstance(backend=backend, shots=10000) if phase_estimator is None: phase_estimator = IterativePhaseEstimation if phase_estimator == IterativePhaseEstimation: p_est = IterativePhaseEstimation(num_iterations=num_iterations, quantum_instance=qi) elif phase_estimator == PhaseEstimation: p_est = PhaseEstimation(num_evaluation_qubits=6, quantum_instance=qi) else: raise ValueError("Unrecognized phase_estimator") result = p_est.estimate(unitary=unitary_circuit, state_preparation=state_preparation) phase = result.phase return phase @data( (X.to_circuit(), 0.5, "statevector_simulator", IterativePhaseEstimation), (X.to_circuit(), 0.5, "qasm_simulator", IterativePhaseEstimation), (None, 0.0, "qasm_simulator", IterativePhaseEstimation), (X.to_circuit(), 0.5, "qasm_simulator", PhaseEstimation), (None, 0.0, "qasm_simulator", PhaseEstimation), (X.to_circuit(), 0.5, "statevector_simulator", PhaseEstimation), ) @unpack def test_qpe_Z(self, state_preparation, expected_phase, backend_type, phase_estimator): """eigenproblem Z, |0> and |1>""" unitary_circuit = Z.to_circuit() phase = self.one_phase( unitary_circuit, state_preparation, backend_type=backend_type, phase_estimator=phase_estimator, ) self.assertEqual(phase, expected_phase) @data( (H.to_circuit(), 0.0, IterativePhaseEstimation), ((H @ X).to_circuit(), 0.5, IterativePhaseEstimation), (H.to_circuit(), 0.0, PhaseEstimation), ((H @ X).to_circuit(), 0.5, PhaseEstimation), ) @unpack def test_qpe_X_plus_minus(self, state_preparation, expected_phase, phase_estimator): """eigenproblem X, (|+>, |->)""" unitary_circuit = X.to_circuit() phase = self.one_phase(unitary_circuit, state_preparation, phase_estimator=phase_estimator) self.assertEqual(phase, expected_phase) @data( (X.to_circuit(), 0.125, IterativePhaseEstimation), (I.to_circuit(), 0.875, IterativePhaseEstimation), (X.to_circuit(), 0.125, PhaseEstimation), (I.to_circuit(), 0.875, PhaseEstimation), ) @unpack def test_qpe_RZ(self, state_preparation, expected_phase, phase_estimator): """eigenproblem RZ, (|0>, |1>)""" alpha = np.pi / 2 unitary_circuit = QuantumCircuit(1) unitary_circuit.rz(alpha, 0) phase = self.one_phase(unitary_circuit, state_preparation, phase_estimator=phase_estimator) self.assertEqual(phase, expected_phase) def test_check_num_iterations(self): """test check for num_iterations greater than zero""" unitary_circuit = X.to_circuit() state_preparation = None with self.assertRaises(ValueError): self.one_phase(unitary_circuit, state_preparation, num_iterations=-1) def phase_estimation( self, unitary_circuit, state_preparation=None, num_evaluation_qubits=6, backend=None, construct_circuit=False, ): """Run phase estimation with operator, eigenvalue pair `unitary_circuit`, `state_preparation`. Return all results """ if backend is None: backend = qiskit.BasicAer.get_backend("statevector_simulator") qi = qiskit.utils.QuantumInstance(backend=backend, shots=10000) phase_est = PhaseEstimation( num_evaluation_qubits=num_evaluation_qubits, quantum_instance=qi) if construct_circuit: pe_circuit = phase_est.construct_circuit(unitary_circuit, state_preparation) result = phase_est.estimate_from_pe_circuit( pe_circuit, unitary_circuit.num_qubits) else: result = phase_est.estimate(unitary=unitary_circuit, state_preparation=state_preparation) return result @data(True, False) def test_qpe_Zplus(self, construct_circuit): """superposition eigenproblem Z, |+>""" unitary_circuit = Z.to_circuit() state_preparation = H.to_circuit() # prepare |+> result = self.phase_estimation( unitary_circuit, state_preparation, backend=qiskit.BasicAer.get_backend("statevector_simulator"), construct_circuit=construct_circuit, ) phases = result.filter_phases(1e-15, as_float=True) with self.subTest("test phases has correct values"): self.assertEqual(list(phases.keys()), [0.0, 0.5]) with self.subTest("test phases has correct probabilities"): np.testing.assert_allclose(list(phases.values()), [0.5, 0.5]) with self.subTest("test bitstring representation"): phases = result.filter_phases(1e-15, as_float=False) self.assertEqual(list(phases.keys()), ["000000", "100000"])