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