def test_batch_evaluate_with_qnspsa(self):
        """Test batch evaluating with QNSPSA works."""
        ansatz = TwoLocal(2,
                          rotation_blocks=["ry", "rz"],
                          entanglement_blocks="cz")

        wrapped_backend = BasicAer.get_backend("qasm_simulator")
        inner_backend = BasicAer.get_backend("statevector_simulator")

        callcount = {"count": 0}

        def wrapped_run(circuits, **kwargs):
            kwargs["callcount"]["count"] += 1
            return inner_backend.run(circuits)

        wrapped_backend.run = partial(wrapped_run, callcount=callcount)

        fidelity = QNSPSA.get_fidelity(ansatz, backend=wrapped_backend)
        qnspsa = QNSPSA(fidelity, maxiter=5)

        vqe = VQE(
            ansatz=ansatz,
            optimizer=qnspsa,
            max_evals_grouped=100,
            quantum_instance=wrapped_backend,
        )
        _ = vqe.compute_minimum_eigenvalue(Z ^ Z)

        # 1 calibration + 1 stddev estimation + 1 initial blocking
        # + 5 (1 loss + 1 fidelity + 1 blocking) + 1 return loss + 1 VQE eval
        expected = 1 + 1 + 1 + 5 * 3 + 1 + 1

        self.assertEqual(callcount["count"], expected)
    def test_qnspsa(self):
        """Test QN-SPSA optimizer is serializable."""
        ansatz = RealAmplitudes(1)
        fidelity = QNSPSA.get_fidelity(ansatz)
        options = {
            "fidelity": fidelity,
            "maxiter": 100,
            "blocking": True,
            "allowed_increase": 0.1,
            "learning_rate": 0.02,
            "perturbation": 0.05,
            "regularization": 0.1,
            "resamplings": 2,
            "perturbation_dims": 5,
            "lse_solver": None,
            "initial_hessian": None,
            "callback": None,
            "termination_checker": None,
            "hessian_delay": 0,
        }
        spsa = QNSPSA(**options)

        settings = spsa.settings
        expected = options.copy()

        with self.subTest(msg="check constructed dictionary"):
            self.assertDictEqual(settings, expected)

        reconstructed = QNSPSA(**settings)  # pylint: disable=unexpected-keyword-arg
        with self.subTest(msg="test reconstructed optimizer"):
            self.assertDictEqual(reconstructed.settings, expected)
Exemple #3
0
    def test_pauli_two_design(self, method):
        """Test SPSA on the Pauli two-design example."""
        circuit = PauliTwoDesign(3, reps=1, seed=1)
        parameters = list(circuit.parameters)
        obs = Z ^ Z ^ I
        expr = ~StateFn(obs) @ StateFn(circuit)

        initial_point = np.array([
            0.82311034, 0.02611798, 0.21077064, 0.61842177, 0.09828447,
            0.62013131
        ])

        def objective(x):
            return expr.bind_parameters(dict(zip(parameters, x))).eval().real

        settings = {"maxiter": 100, "blocking": True, "allowed_increase": 0}

        if method == "2spsa":
            settings["second_order"] = True
            settings["regularization"] = 0.01
            expected_nfev = settings["maxiter"] * 5 + 1
        elif method == "qnspsa":
            settings["fidelity"] = QNSPSA.get_fidelity(circuit)
            settings["regularization"] = 0.001
            settings["learning_rate"] = 0.05
            settings["perturbation"] = 0.05

            expected_nfev = settings["maxiter"] * 7 + 1
        else:
            expected_nfev = settings["maxiter"] * 3 + 1

        if method == "qnspsa":
            spsa = QNSPSA(**settings)
        else:
            spsa = SPSA(**settings)

        with self.assertWarns(DeprecationWarning):
            result = spsa.optimize(circuit.num_parameters,
                                   objective,
                                   initial_point=initial_point)

        with self.subTest("check final accuracy"):
            self.assertLess(result[1], -0.95)  # final loss

        with self.subTest("check number of function calls"):
            self.assertEqual(result[2], expected_nfev)  # function evaluations
    def test_qnspsa(self):
        """Test QN-SPSA optimizer is serializable."""
        ansatz = RealAmplitudes(1)
        fidelity = QNSPSA.get_fidelity(ansatz)
        options = {
            "fidelity": fidelity,
            "maxiter": 100,
            "blocking": True,
            "allowed_increase": 0.1,
            "learning_rate": 0.02,
            "perturbation": 0.05,
            "regularization": 0.1,
            "resamplings": 2,
            "perturbation_dims": 5,
            "lse_solver": None,
            "initial_hessian": None,
            "callback": None,
            "hessian_delay": 0,
        }
        spsa = QNSPSA(**options)

        settings = spsa.settings
        expected = options.copy()
        expected.pop("fidelity")  # fidelity cannot be serialized

        with self.subTest(msg="check constructed dictionary"):
            self.assertDictEqual(settings, expected)

        # no idea why pylint complains about unexpected args (like "second_order") which are
        # definitely not in the settings dict
        # pylint: disable=unexpected-keyword-arg
        with self.subTest(msg="fidelity missing"):
            # fidelity cannot be serialized, so it must be added back in
            with self.assertRaises(TypeError):
                _ = QNSPSA(**settings)

        settings["fidelity"] = fidelity
        reconstructed = QNSPSA(**settings)
        with self.subTest(msg="test reconstructed optimizer"):
            self.assertDictEqual(reconstructed.settings, expected)
def main(
    backend,
    user_messenger,
    hamiltonian,
    x0,
    ansatz="EfficientSU2",
    ansatz_config=None,
    optimizer="SPSA",
    optimizer_config=None,
    shots=8192,
    use_measurement_mitigation=False,
):
    """
    The main sample VQE program.

    Args:
        backend (qiskit.providers.ibmq.runtime.ProgramBackend): Qiskit backend instance.
        user_messenger (qiskit.providers.ibmq.runtime.UserMessenger): Used to communicate with the program user.
        hamiltonian (list): Hamiltonian whose ground state we want to find. e.g. [(1, XY),(1, IH)].
        x0 (array_like): Initial vector of parameters.
        ansatz (str): Optional, QuantumCircuit or the name of ansatz quantum circuit to use, default='EfficientSU2'.
        ansatz_config (dict): Optional, configuration parameters for the ansatz circuit.
        optimizer (str): Optional, string specifying classical optimizer, default='SPSA'.
        optimizer_config (dict): Optional, configuration parameters for the optimizer.
        shots (int): Optional, number of shots to take per circuit.
        use_measurement_mitigation (bool): Optional, use measurement mitigation, default=False.

    Returns:
        OptimizeResult: The result in SciPy optimization format.
    """

    if ansatz_config is None:
        ansatz_config = {}

    if optimizer_config is None:
        optimizer_config = {"maxiter": 100}

    coeffs = np.array([item[0] for item in hamiltonian], dtype=complex)
    op_strings = [item[1] for item in hamiltonian]

    num_qubits = len(op_strings[0])

    # Get the Qiskit circuit from the library if a str was given
    if isinstance(ansatz, str):
        ansatz_instance = getattr(lib_local, ansatz)
        ansatz_circuit = ansatz_instance(num_qubits, **ansatz_config)
    else:
        ansatz_circuit = ansatz

    meas_circs = opstr_to_meas_circ(op_strings)

    meas_strings = [
        string.replace("X", "Z").replace("Y", "Z").replace("H", "Z")
        for string in op_strings
    ]

    # Take the ansatz circuits and add measurements
    full_circs = [
        ansatz_circuit.compose(mcirc).measure_all(inplace=False)
        for mcirc in meas_circs
    ]

    num_params = ansatz_circuit.num_parameters

    # Check initial state
    if x0 is not None:
        x0 = np.asarray(x0, dtype=float)
        if x0.shape[0] != num_params:
            shape = x0.shape[0]
            raise ValueError(
                f"Number of params in x0 ({shape}) does not match number \
                              of ansatz parameters ({num_params}).")
    else:
        x0 = 2 * np.pi * np.random.rand(num_params)

    # Transpile the circuits
    trans_dict = {}
    if not backend.configuration().simulator:
        trans_dict = {"layout_method": "sabre", "routing_method": "sabre"}
    trans_circs = transpile(full_circs,
                            backend,
                            optimization_level=3,
                            **trans_dict)

    # Measurement mitigation
    if use_measurement_mitigation:
        maps = mthree.utils.final_measurement_mapping(trans_circs)
        mit = mthree.M3Mitigation(backend)
        mit.cals_from_system(maps)

    def callback(*args):
        user_messenger.publish(args)

    def vqe_func(params):
        # Binds parameters to the transpiled circuits.
        bound_circs = [circ.bind_parameters(params) for circ in trans_circs]

        # Submit the job and get the counts
        counts = backend.run(bound_circs, shots=shots).result().get_counts()

        if use_measurement_mitigation:
            quasi_collection = mit.apply_correction(counts, maps)
            expvals = quasi_collection.expval(meas_strings)
        else:
            expvals = mthree.utils.expval(counts, meas_strings)

        energy = np.sum(coeffs * expvals).real
        return energy

    # SPSA and QNSPSA are taken from Qiskit and not SciPy
    if optimizer == "SPSA":
        spsa = SPSA(**optimizer_config, callback=callback)
        x, loss, nfev = spsa.optimize(num_params, vqe_func, initial_point=x0)
        res = OptimizeResult(
            fun=loss,
            x=x,
            nit=optimizer_config["maxiter"],
            nfev=nfev,
            message="Optimization terminated successfully.",
            success=True,
        )
    elif optimizer == "QNSPSA":
        fidelity = QNSPSA.get_fidelity(ansatz_circuit)
        spsa = QNSPSA(fidelity, **optimizer_config, callback=callback)
        x, loss, nfev = spsa.optimize(num_params, vqe_func, initial_point=x0)
        res = OptimizeResult(
            fun=loss,
            x=x,
            nit=optimizer_config["maxiter"],
            nfev=nfev,
            message="Optimization terminated successfully.",
            success=True,
        )
    # SciPy optimizers
    else:
        res = opt.minimize(vqe_func,
                           x0,
                           method=optimizer,
                           options=optimizer_config,
                           callback=callback)

    return res