Example #1
0
    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
Example #4
0
    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())
Example #5
0
    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
Example #6
0
    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
Example #8
0
    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
Example #9
0
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"])