コード例 #1
0
    def test_missing_attributesquantum_instance(self):
        """Test appropriate error is raised if the quantum instance is missing."""
        pvqd = PVQD(
            self.ansatz,
            self.initial_parameters,
            optimizer=L_BFGS_B(maxiter=1),
            expectation=self.expectation,
        )
        problem = EvolutionProblem(self.hamiltonian, time=0.01)

        attrs_to_test = [
            ("optimizer", L_BFGS_B(maxiter=1)),
            ("quantum_instance", self.qasm_backend),
        ]

        for attr, value in attrs_to_test:
            with self.subTest(msg=f"missing: {attr}"):
                # set attribute to None to invalidate the setup
                setattr(pvqd, attr, None)

                with self.assertRaises(ValueError):
                    _ = pvqd.evolve(problem)

                # set the correct value again
                setattr(pvqd, attr, value)

        with self.subTest(msg="all set again"):
            result = pvqd.evolve(problem)
            self.assertIsNotNone(result.evolved_state)
コード例 #2
0
    def test_zero_parameters(self):
        """Test passing an ansatz with zero parameters raises an error."""
        problem = EvolutionProblem(self.hamiltonian, time=0.02)

        pvqd = PVQD(
            QuantumCircuit(2),
            np.array([]),
            optimizer=SPSA(maxiter=10, learning_rate=0.1, perturbation=0.01),
            quantum_instance=self.sv_backend,
            expectation=self.expectation,
        )

        with self.assertRaises(QiskitError):
            _ = pvqd.evolve(problem)
コード例 #3
0
    def test_gradient_supported(self):
        """Test the gradient support is correctly determined."""
        # gradient supported here
        wrapped = EfficientSU2(2)  # a circuit wrapped into a big instruction
        plain = wrapped.decompose(
        )  # a plain circuit with already supported instructions

        # gradients not supported on the following circuits
        x = Parameter("x")
        duplicated = QuantumCircuit(2)
        duplicated.rx(x, 0)
        duplicated.rx(x, 1)

        needs_chainrule = QuantumCircuit(2)
        needs_chainrule.rx(2 * x, 0)

        custom_gate = WhatAmI(x)
        unsupported = QuantumCircuit(2)
        unsupported.append(custom_gate, [0, 1])

        tests = [
            (wrapped, True),  # tuple: (circuit, gradient support)
            (plain, True),
            (duplicated, False),
            (needs_chainrule, False),
            (unsupported, False),
        ]

        # used to store the info if a gradient callable is passed into the
        # optimizer of not
        info = {"has_gradient": None}
        optimizer = partial(gradient_supplied, info=info)

        pvqd = PVQD(
            ansatz=None,
            initial_parameters=np.array([]),
            optimizer=optimizer,
            quantum_instance=self.sv_backend,
            expectation=self.expectation,
        )
        problem = EvolutionProblem(self.hamiltonian, time=0.01)
        for circuit, expected_support in tests:
            with self.subTest(circuit=circuit,
                              expected_support=expected_support):
                pvqd.ansatz = circuit
                pvqd.initial_parameters = np.zeros(circuit.num_parameters)
                _ = pvqd.evolve(problem)
                self.assertEqual(info["has_gradient"], expected_support)
コード例 #4
0
    def test_invalid_num_timestep(self):
        """Test raises if the num_timestep is not positive."""
        pvqd = PVQD(
            self.ansatz,
            self.initial_parameters,
            num_timesteps=0,
            optimizer=L_BFGS_B(),
            quantum_instance=self.sv_backend,
            expectation=self.expectation,
        )
        problem = EvolutionProblem(
            self.hamiltonian,
            time=0.01,
            aux_operators=[self.hamiltonian, self.observable])

        with self.assertRaises(ValueError):
            _ = pvqd.evolve(problem)
コード例 #5
0
    def test_pvqd(self, hamiltonian_type, expectation_cls, gradient,
                  backend_type, num_timesteps):
        """Test a simple evolution."""
        time = 0.02

        if hamiltonian_type == "ising":
            hamiltonian = self.hamiltonian
        elif hamiltonian_type == "ising_matrix":
            hamiltonian = self.hamiltonian.to_matrix_op()
        else:  # hamiltonian_type == "pauli":
            hamiltonian = X ^ X

        # parse input arguments
        if gradient:
            optimizer = GradientDescent(maxiter=1)
        else:
            optimizer = L_BFGS_B(maxiter=1)

        backend = self.sv_backend if backend_type == "sv" else self.qasm_backend
        expectation = expectation_cls()

        # run pVQD keeping track of the energy and the magnetization
        pvqd = PVQD(
            self.ansatz,
            self.initial_parameters,
            num_timesteps=num_timesteps,
            optimizer=optimizer,
            quantum_instance=backend,
            expectation=expectation,
        )
        problem = EvolutionProblem(
            hamiltonian, time, aux_operators=[hamiltonian, self.observable])
        result = pvqd.evolve(problem)

        self.assertTrue(len(result.fidelities) == 3)
        self.assertTrue(np.all(result.times == [0.0, 0.01, 0.02]))
        self.assertTrue(np.asarray(result.observables).shape == (3, 2))
        num_parameters = self.ansatz.num_parameters
        self.assertTrue(
            len(result.parameters) == 3 and np.all([
                len(params) == num_parameters for params in result.parameters
            ]))
コード例 #6
0
    def test_initial_state_raises(self):
        """Test passing an initial state raises an error for now."""
        initial_state = QuantumCircuit(2)
        initial_state.x(0)

        problem = EvolutionProblem(
            self.hamiltonian,
            time=0.02,
            initial_state=initial_state,
        )

        pvqd = PVQD(
            self.ansatz,
            self.initial_parameters,
            optimizer=SPSA(maxiter=0, learning_rate=0.1, perturbation=0.01),
            quantum_instance=self.sv_backend,
            expectation=self.expectation,
        )

        with self.assertRaises(NotImplementedError):
            _ = pvqd.evolve(problem)
コード例 #7
0
    def test_initial_guess_and_observables(self):
        """Test doing no optimizations stays at initial guess."""
        initial_guess = np.zeros(self.ansatz.num_parameters)

        pvqd = PVQD(
            self.ansatz,
            self.initial_parameters,
            num_timesteps=10,
            optimizer=SPSA(maxiter=0, learning_rate=0.1, perturbation=0.01),
            initial_guess=initial_guess,
            quantum_instance=self.sv_backend,
            expectation=self.expectation,
        )
        problem = EvolutionProblem(
            self.hamiltonian,
            time=0.1,
            aux_operators=[self.hamiltonian, self.observable])

        result = pvqd.evolve(problem)

        observables = result.aux_ops_evaluated
        self.assertEqual(observables[0], 0.1)  # expected energy
        self.assertEqual(observables[1], 1)  # expected magnetization
コード例 #8
0
    def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult:
        """
        Evolves a quantum state for a given time using the Trotterization method
        based on a product formula provided. The result is provided in the form of a quantum
        circuit. If auxiliary operators are included in the ``evolution_problem``, they are
        evaluated on an evolved state using a backend provided.

        .. note::
            Time-dependent Hamiltonians are not yet supported.

        Args:
            evolution_problem: Instance defining evolution problem. For the included Hamiltonian,
                ``PauliOp``, ``SummedOp`` or ``PauliSumOp`` are supported by TrotterQRTE.

        Returns:
            Evolution result that includes an evolved state as a quantum circuit and, optionally,
            auxiliary operators evaluated for a resulting state on a backend.

        Raises:
            ValueError: If ``t_param`` is not set to None in the EvolutionProblem (feature not
                currently supported).
            ValueError: If the ``initial_state`` is not provided in the EvolutionProblem.
        """
        evolution_problem.validate_params()
        if evolution_problem.t_param is not None:
            raise ValueError(
                "TrotterQRTE does not accept a time dependent hamiltonian,"
                "``t_param`` from the EvolutionProblem should be set to None.")

        if evolution_problem.aux_operators is not None and (
                self._quantum_instance is None or self._expectation is None):
            raise ValueError(
                "aux_operators were provided for evaluations but no ``expectation`` or "
                "``quantum_instance`` was provided.")
        hamiltonian = evolution_problem.hamiltonian
        if not isinstance(hamiltonian, (PauliOp, PauliSumOp, SummedOp)):
            raise ValueError(
                "TrotterQRTE only accepts PauliOp | "
                f"PauliSumOp | SummedOp, {type(hamiltonian)} provided.")
        if isinstance(hamiltonian, OperatorBase):
            hamiltonian = hamiltonian.bind_parameters(
                evolution_problem.hamiltonian_value_dict)
        if isinstance(hamiltonian, SummedOp):
            hamiltonian = self._summed_op_to_pauli_sum_op(hamiltonian)
        # the evolution gate
        evolution_gate = CircuitOp(
            PauliEvolutionGate(hamiltonian,
                               evolution_problem.time,
                               synthesis=self._product_formula))

        if evolution_problem.initial_state is not None:
            initial_state = evolution_problem.initial_state
            if isinstance(initial_state, QuantumCircuit):
                initial_state = StateFn(initial_state)
            evolved_state = evolution_gate @ initial_state

        else:
            raise ValueError(
                "``initial_state`` must be provided in the EvolutionProblem.")

        evaluated_aux_ops = None
        if evolution_problem.aux_operators is not None:
            evaluated_aux_ops = eval_observables(
                self._quantum_instance,
                evolved_state.primitive,
                evolution_problem.aux_operators,
                self._expectation,
                evolution_problem.truncation_threshold,
            )

        return EvolutionResult(evolved_state, evaluated_aux_ops)