def test_qc_expectation_larger_lattice( client_configuration: QCSClientConfiguration, dummy_compiler: DummyCompiler): qc = QuantumComputer(name="testy!", qam=QVM(client_configuration=client_configuration), compiler=dummy_compiler) q0 = 2 q1 = 3 # bell state program p = Program() p += RESET() p += H(q0) p += CNOT(q0, q1) p.wrap_in_numshots_loop(10) # XX, YY, ZZ experiment sx = ExperimentSetting(in_state=_pauli_to_product_state(sZ(q0) * sZ(q1)), out_operator=sX(q0) * sX(q1)) sy = ExperimentSetting(in_state=_pauli_to_product_state(sZ(q0) * sZ(q1)), out_operator=sY(q0) * sY(q1)) sz = ExperimentSetting(in_state=_pauli_to_product_state(sZ(q0) * sZ(q1)), out_operator=sZ(q0) * sZ(q1)) e = Experiment(settings=[sx, sy, sz], program=p) results = qc.run_experiment(e) # XX expectation value for bell state |00> + |11> is 1 assert np.isclose(results[0].expectation, 1) assert np.isclose(results[0].std_err, 0) assert results[0].total_counts == 40 # YY expectation value for bell state |00> + |11> is -1 assert np.isclose(results[1].expectation, -1) assert np.isclose(results[1].std_err, 0) assert results[1].total_counts == 40 # ZZ expectation value for bell state |00> + |11> is 1 assert np.isclose(results[2].expectation, 1) assert np.isclose(results[2].std_err, 0) assert results[2].total_counts == 40
def test_reset(client_configuration: QCSClientConfiguration): quantum_processor = NxQuantumProcessor(nx.complete_graph(3)) qc = QuantumComputer( name="testy!", qam=QVM(client_configuration=client_configuration), compiler=DummyCompiler(quantum_processor=quantum_processor, client_configuration=client_configuration), ) p = Program( Declare(name="theta", memory_type="REAL"), Declare(name="ro", memory_type="BIT"), RX(MemoryReference("theta"), 0), MEASURE(0, MemoryReference("ro")), ).wrap_in_numshots_loop(10) p.write_memory(region_name="theta", value=np.pi) result = qc.qam.run(p) aref = ParameterAref(name="theta", index=0) assert p._memory.values[aref] == np.pi assert result.readout_data["ro"].shape == (10, 1) assert all([bit == 1 for bit in result.readout_data["ro"]])
def test_run_with_parameters(client_configuration: QCSClientConfiguration, param): quantum_processor = NxQuantumProcessor(nx.complete_graph(3)) qc = QuantumComputer( name="testy!", qam=QVM(client_configuration=client_configuration), compiler=DummyCompiler(quantum_processor=quantum_processor, client_configuration=client_configuration), ) executable = Program( Declare(name="theta", memory_type="REAL"), Declare(name="ro", memory_type="BIT"), RX(MemoryReference("theta"), 0), MEASURE(0, MemoryReference("ro")), ).wrap_in_numshots_loop(1000) executable.write_memory(region_name="theta", value=param) bitstrings = qc.run(executable).readout_data.get("ro") assert bitstrings.shape == (1000, 1) assert all([bit == 1 for bit in bitstrings])
def test_qc_expectation_on_qvm(client_configuration: QCSClientConfiguration, dummy_compiler: DummyCompiler): # regression test for https://github.com/rigetti/forest-tutorials/issues/2 qc = QuantumComputer(name="testy!", qam=QVM(client_configuration=client_configuration), compiler=dummy_compiler) p = Program() theta = p.declare("theta", "REAL") p += RESET() p += RY(theta, 0) p.wrap_in_numshots_loop(10000) sx = ExperimentSetting(in_state=_pauli_to_product_state(sZ(0)), out_operator=sX(0)) e = Experiment(settings=[sx], program=p) thetas = [-np.pi / 2, 0.0, np.pi / 2] results = [] # Verify that multiple calls to qc.experiment with the same experiment backed by a QVM that # requires_exectutable does not raise an exception. for theta in thetas: results.append(qc.run_experiment(e, memory_map={"theta": [theta]})) assert np.isclose(results[0][0].expectation, -1.0, atol=0.01) assert np.isclose(results[0][0].std_err, 0) assert results[0][0].total_counts == 20000 # bounds on atol and std_err here are a little loose to try and avoid test flakiness. assert np.isclose(results[1][0].expectation, 0.0, atol=0.1) assert results[1][0].std_err < 0.01 assert results[1][0].total_counts == 20000 assert np.isclose(results[2][0].expectation, 1.0, atol=0.01) assert np.isclose(results[2][0].std_err, 0) assert results[2][0].total_counts == 20000
def test_readout_symmetrization(client_configuration: QCSClientConfiguration): quantum_processor = NxQuantumProcessor(nx.complete_graph(3)) noise_model = decoherence_noise_with_asymmetric_ro( quantum_processor.to_compiler_isa()) qc = QuantumComputer( name="testy!", qam=QVM(client_configuration=client_configuration, noise_model=noise_model), compiler=DummyCompiler(quantum_processor=quantum_processor, client_configuration=client_configuration), ) prog = Program( Declare("ro", "BIT", 2), I(0), X(1), MEASURE(0, MemoryReference("ro", 0)), MEASURE(1, MemoryReference("ro", 1)), ) prog.wrap_in_numshots_loop(1000) result_1 = qc.run(prog) bitstrings_1 = result_1.readout_data.get("ro") avg0_us = np.mean(bitstrings_1[:, 0]) avg1_us = 1 - np.mean(bitstrings_1[:, 1]) diff_us = avg1_us - avg0_us assert diff_us > 0.03 prog = Program( I(0), X(1), ) bitstrings_2 = qc.run_symmetrized_readout(prog, 1000) avg0_s = np.mean(bitstrings_2[:, 0]) avg1_s = 1 - np.mean(bitstrings_2[:, 1]) diff_s = avg1_s - avg0_s assert diff_s < 0.05
def test_run(client_configuration: QCSClientConfiguration): quantum_processor = NxQuantumProcessor(nx.complete_graph(3)) qc = QuantumComputer( name="testy!", qam=QVM(client_configuration=client_configuration, gate_noise=(0.01, 0.01, 0.01)), compiler=DummyCompiler(quantum_processor=quantum_processor, client_configuration=client_configuration), ) result = qc.run( Program( Declare("ro", "BIT", 3), H(0), CNOT(0, 1), CNOT(1, 2), MEASURE(0, MemoryReference("ro", 0)), MEASURE(1, MemoryReference("ro", 1)), MEASURE(2, MemoryReference("ro", 2)), ).wrap_in_numshots_loop(1000)) bitstrings = result.readout_data.get("ro") assert bitstrings.shape == (1000, 3) parity = np.sum(bitstrings, axis=1) % 3 assert 0 < np.mean(parity) < 0.15
def get_qc(name: str, *, as_qvm: bool = None, noisy: bool = None, connection: ForestConnection = None) -> QuantumComputer: """ Get a quantum computer. A quantum computer is an object of type :py:class:`QuantumComputer` and can be backed either by a QVM simulator ("Quantum/Quil Virtual Machine") or a physical Rigetti QPU ("Quantum Processing Unit") made of superconducting qubits. You can choose the quantum computer to target through a combination of its name and optional flags. There are multiple ways to get the same quantum computer. The following are equivalent:: >>> qc = get_qc("Aspen-0-12Q-A-noisy-qvm") >>> qc = get_qc("Aspen-0-12Q-A", as_qvm=True, noisy=True) and will construct a simulator of the 8q-agave chip with a noise model based on device characteristics. We also provide a means for constructing generic quantum simulators that are not related to a given piece of Rigetti hardware:: >>> qc = get_qc("9q-square-qvm") >>> qc = get_qc("9q-square", as_qvm=True) Finally, you can get request a QVM with "no" topology of a given number of qubits (technically, it's a fully connected graph among the given number of qubits) with:: >>> qc = get_qc("5q-qvm") # or "6q-qvm", or "34q-qvm", ... Redundant flags are acceptable, but conflicting flags will raise an exception:: >>> qc = get_qc("9q-square-qvm") # qc is fully specified by its name >>> qc = get_qc("9q-square-qvm", as_qvm=True) # redundant, but ok >>> qc = get_qc("9q-square-qvm", as_qvm=False) # Error! Use :py:func:`list_quantum_computers` to retrieve a list of known qc names. This method is provided as a convenience to quickly construct and use QVM's and QPU's. Power users may wish to have more control over the specification of a quantum computer (e.g. custom noise models, bespoke topologies, etc.). This is possible by constructing a :py:class:`QuantumComputer` object by hand. Please refer to the documentation on :py:class:`QuantumComputer` for more information. :param name: The name of the desired quantum computer. This should correspond to a name returned by :py:func:`list_quantum_computers`. Names ending in "-qvm" will return a QVM. Names ending in "-noisy-qvm" will return a QVM with a noise model. Otherwise, we will return a QPU with the given name. :param as_qvm: An optional flag to force construction of a QVM (instead of a QPU). If specified and set to ``True``, a QVM-backed quantum computer will be returned regardless of the name's suffix :param noisy: An optional flag to force inclusion of a noise model. If specified and set to ``True``, a quantum computer with a noise model will be returned regardless of the name's suffix. The noise model for QVM's based on a real QPU is an empirically parameterized model based on real device noise characteristics. The generic QVM noise model is simple T1 and T2 noise plus readout error. See :py:func:`decoherance_noise_with_asymmetric_ro`. :param connection: An optional :py:class:ForestConnection` object. If not specified, the default values for URL endpoints, ping time, and status time will be used. Your user id and API key will be read from ~/.pyquil_config. If you deign to change any of these parameters, pass your own :py:class:`ForestConnection` object. :return: """ if connection is None: connection = ForestConnection() full_name = name name, as_qvm, noisy = _parse_name(name, as_qvm, noisy) ma = re.fullmatch(r'(\d+)q', name) if ma is not None: n_qubits = int(ma.group(1)) if not as_qvm: raise ValueError("Please name a valid device or run as a QVM") return _get_unrestricted_qvm(connection=connection, noisy=noisy, n_qubits=n_qubits) if name == '9q-generic' or name == '9q-square': if name == '9q-generic': warnings.warn("Please prefer '9q-square' instead of '9q-generic'", DeprecationWarning) if not as_qvm: raise ValueError( "The device '9q-square' is only available as a QVM") return _get_9q_square_qvm(connection=connection, noisy=noisy) device = get_lattice(name) if not as_qvm: if noisy is not None and noisy: warnings.warn( "You have specified `noisy=True`, but you're getting a QPU. This flag " "is meant for controlling noise models on QVMs.") return QuantumComputer(name=full_name, qam=QPU(endpoint=pyquil_config.qpu_url, user=pyquil_config.user_id), device=device, compiler=QPUCompiler( endpoint=pyquil_config.compiler_url, device=device)) if noisy: noise_model = device.noise_model else: noise_model = None return QuantumComputer(name=full_name, qam=QVM(connection=connection, noise_model=noise_model), device=device, compiler=_get_qvm_compiler_based_on_endpoint( device=device, endpoint=connection.compiler_endpoint))
def __init__(self, clauses, m=None, steps=1, grid_size=None, tol=1e-5, gate_noise=None, verbose=False, visualize=False): self.clauses = clauses self.m = m self.verbose = verbose self.visualize = visualize self.step_by_step_results = None self.optimization_history = None self.gate_noise = gate_noise if grid_size is None: self.grid_size = len(clauses) + len(qubits) else: self.grid_size = grid_size cost_operators, mapping = self.create_operators_from_clauses() self.mapping = mapping driver_operators = self.create_driver_operators() # minimizer_kwargs = {'method': 'BFGS', # 'options': {'gtol': tol, 'disp': False}} # bounds = [(0, np.pi)]*steps + [(0, 2*np.pi)]*steps # minimizer_kwargs = {'method': 'L-BFGS-B', # 'options': {'gtol': tol, 'disp': False}, # 'bounds': bounds} minimizer_kwargs = { 'method': 'Nelder-Mead', 'options': { 'ftol': tol, 'tol': tol, 'disp': False } } if self.verbose: print_fun = print else: print_fun = pass_fun qubits = list(range(len(mapping))) if gate_noise: self.samples = int(1e3) pauli_channel = [gate_noise] * 3 else: self.samples = None pauli_channel = None connection = ForestConnection() qvm = QVM(connection=connection, gate_noise=pauli_channel) topology = nx.complete_graph(len(qubits)) device = NxDevice(topology=topology) qc = QuantumComputer(name="my_qvm", qam=qvm, device=device, compiler=QVMCompiler( device=device, endpoint=connection.compiler_endpoint)) vqe_option = { 'disp': print_fun, 'return_all': True, 'samples': self.samples } self.qaoa_inst = QAOA(qc, qubits, steps=steps, init_betas=None, init_gammas=None, cost_ham=cost_operators, ref_ham=driver_operators, minimizer=scipy.optimize.minimize, minimizer_kwargs=minimizer_kwargs, rand_seed=None, vqe_options=vqe_option, store_basis=True) self.ax = None
def vqe_run(self, variational_state_evolve, hamiltonian, initial_params, gate_noise=None, measurement_noise=None, jacobian=None, qc=None, disp=None, samples=None, return_all=False): """ functional minimization loop. :param variational_state_evolve: function that takes a set of parameters and returns a pyQuil program. :param hamiltonian: (PauliSum) object representing the hamiltonian of which to take the expectation value. :param initial_params: (ndarray) vector of initial parameters for the optimization :param gate_noise: list of Px, Py, Pz probabilities of gate being applied to every gate after each get application :param measurement_noise: list of Px', Py', Pz' probabilities of a X, Y or Z being applied before a measurement. :param jacobian: (optional) method of generating jacobian for parameters (Default=None). :param qc: (optional) QuantumComputer object. :param disp: (optional, bool) display level. If True then each iteration expectation and parameters are printed at each optimization iteration. :param samples: (int) Number of samples for calculating the expectation value of the operators. If `None` then faster method ,dotting the wave function with the operator, is used. Default=None. :param return_all: (optional, bool) request to return all intermediate parameters determined during the optimization. :return: (vqe.OptResult()) object :func:`OptResult <vqe.OptResult>`. The following fields are initialized in OptResult: -x: set of w.f. ansatz parameters -fun: scalar value of the objective function -iteration_params: a list of all intermediate parameter vectors. Only returned if 'return_all=True' is set as a vqe_run() option. -expectation_vals: a list of all intermediate expectation values. Only returned if 'return_all=True' is set as a vqe_run() option. """ self._disp_fun = disp if disp is not None else lambda x: None iteration_params = [] expectation_vals = [] self._current_expectation = None if samples is None: print("""WARNING: Fast method for expectation will be used. Noise models will be ineffective""") if qc is None: qubits = hamiltonian.get_qubits() qc = QuantumComputer(name=f"{len(qubits)}q-noisy-qvm", qam=QVM(gate_noise=gate_noise, measurement_noise=measurement_noise)) else: self.qc = qc def objective_function(params): """ closure representing the functional :param params: (ndarray) vector of parameters for generating the the function of the functional. :return: (float) expectation value """ pyquil_prog = variational_state_evolve(params) mean_value = self.expectation(pyquil_prog, hamiltonian, samples, qc) self._current_expectation = mean_value # store for printing return mean_value def print_current_iter(iter_vars): self._disp_fun("\tParameters: {} ".format(iter_vars)) if jacobian is not None: grad = jacobian(iter_vars) self._disp_fun("\tGrad-L1-Norm: {}".format(np.max( np.abs(grad)))) self._disp_fun("\tGrad-L2-Norm: {} ".format( np.linalg.norm(grad))) self._disp_fun("\tE => {}".format(self._current_expectation)) if return_all: iteration_params.append(iter_vars) expectation_vals.append(self._current_expectation) # using self.minimizer arguments = funcsigs.signature(self.minimizer).parameters.keys() if disp is not None and 'callback' in arguments: self.minimizer_kwargs['callback'] = print_current_iter args = [objective_function, initial_params] args.extend(self.minimizer_args) if 'jac' in arguments: self.minimizer_kwargs['jac'] = jacobian result = self.minimizer(*args, **self.minimizer_kwargs) if hasattr(result, 'status'): if result.status != 0: self._disp_fun( "Classical optimization exited with an error index: %i" % result.status) results = OptResults() if hasattr(result, 'x'): results.x = result.x results.fun = result.fun else: results.x = result if return_all: results.iteration_params = iteration_params results.expectation_vals = expectation_vals return results
def vqe_run(self, variational_state_evolve, hamiltonian, initial_params, gate_noise=None, measurement_noise=None, jacobian=None, qc=None, disp=False, samples=None, return_all=False, callback=None, max_meas=np.inf): """ functional minimization loop. :param variational_state_evolve: function that takes a set of parameters and returns a pyQuil program. :param hamiltonian: (PauliSum) object representing the hamiltonian of which to take the expectation value. :param initial_params: (ndarray) vector of initial parameters for the optimization :param gate_noise: list of Px, Py, Pz probabilities of gate being applied to every gate after each get application :param measurement_noise: list of Px', Py', Pz' probabilities of a X, Y or Z being applied before a measurement. :param jacobian: (optional) method of generating jacobian for parameters (Default=None). :param qc: (optional) QuantumComputer object. :param disp: (optional, bool/callable) display level. If True then each iteration expectation and parameters are printed at each optimization iteration. If callable, called after each iteration. The signature: ``disp(xk, state)`` where ``xk`` is the current parameter vector. and ``state`` is the current expectation value. :param samples: (int) Number of samples for calculating the expectation value of the operators. If `None` then faster method ,dotting the wave function with the operator, is used. Default=None. :param return_all: (optional, bool) request to return all intermediate parameters determined during the optimization. :return: (vqe.OptResult()) object :func:`OptResult <vqe.OptResult>`. The following fields are initialized in OptResult: -x: set of w.f. ansatz parameters -fun: scalar value of the objective function -iteration_params: a list of all intermediate parameter vectors. Only returned if 'return_all=True' is set as a vqe_run() option. -expectation_vals: a list of all intermediate expectation values. Only returned if 'return_all=True' is set as a vqe_run() option. """ """ if (disp is not None and disp is not True and disp is not False): self._disp_fun = disp else: pass """ if samples is not None and max_meas <= samples: raise ValueError('Need more than one fun eval.') self._disp_fun = print iteration_params = [] expectation_vals = [] expectation_vars = [] fun_evals = 0 meas = 0 restarts = 0 callback_idx = [] # Problem: expectation_vals did not (for Nelder-Mead in # scipy.optimize) correspond to objective_function( self._current_expectation = None self._current_variance = None if qc is None: qubits = hamiltonian.get_qubits() qc = QuantumComputer(name=f"{len(qubits)}q-noisy-qvm", qam=QVM(gate_noise=gate_noise, measurement_noise=measurement_noise)) else: self.qc = qc coeffs = np.array([term.coefficient for term in hamiltonian.terms]) sample_list = calc_samples(samples, coeffs) def objective_function(params): """ closure representing the functional :param params: (ndarray) vector of parameters for generating the the function of the functional. :return: (float) expectation value """ pyquil_prog = variational_state_evolve(params) mean_value, tmp_vars = self.expectation(pyquil_prog, hamiltonian, sample_list, qc) self._current_variance = tmp_vars self._current_expectation = mean_value # store for printing # Save params, exp_val and exp_var iteration_params.append(params) expectation_vals.append(mean_value) expectation_vars.append(tmp_vars) nonlocal fun_evals, meas fun_evals += 1 if samples is not None: meas += samples if meas >= max_meas: raise RestartError # attempt restart and break while below return mean_value def print_current_iter(iter_vars): self._disp_fun('\nFunction evaluations: {}'.format(fun_evals)) self._disp_fun("Parameters: {} ".format(iter_vars)) if jacobian is not None: grad = jacobian(iter_vars) self._disp_fun( "\tGrad-L1-Norm: {}".format(np.max(np.abs(grad)))) self._disp_fun( "\tGrad-L2-Norm: {} ".format(np.linalg.norm(grad))) self._disp_fun("E => {}".format(self._current_expectation)) # using self.minimizer arguments = funcsigs.signature(self.minimizer).parameters.keys() if callback is None: def callback(*args, **kwargs): pass def wrap_callbacks(iter_vars, *args, **kwargs): # save values callback_idx.append(fun_evals-1) # display if disp is True: print_current_iter(iter_vars) # call VQE's callback callback(iteration_params, expectation_vals, expectation_vars) if 'callback' in arguments: self.minimizer_kwargs['callback'] = wrap_callbacks args = [objective_function, initial_params] args.extend(self.minimizer_args) if 'jac' in arguments: self.minimizer_kwargs['jac'] = jacobian results = OptResults() result = None while meas < max_meas: break_ = True try: result = self.minimizer(*args, **self.minimizer_kwargs) except BreakError: results.status = -1 results.message = 'Stopped by BreakError.' except RestartError as e: restarts += 1 break_ = False args[1] = iteration_params[int(np.argmin(expectation_vals))] if e.samples is not None: samples = e.samples sample_list = calc_samples(samples, coeffs) else: results.status = 0 results.message = 'Minimizer stopped naturally.' if hasattr(result, 'status'): if result.status != 0: self._disp_fun( "Classical optimization exited with an error index: %i" % result.status) if hasattr(result, 'x'): results.x = result.x results.fun = result.fun else: results.x = result results.fun = expectation_vals[-1] if break_: break else: results.status = 1 results.message = 'Exceeded maximum number of measurements.' if disp: print(f"Exceeded maximum of {max_meas} measurements" f"and terminated.") # Save results in case of Break- or RestartError if not hasattr(results, 'x'): idx = int(np.argmin(expectation_vals)) results.x = iteration_params[idx] results.fun = expectation_vals[idx] if return_all: # Convert to ndarray for better indexing options (se bellow) iteration_params = np.array(iteration_params) expectation_vals = np.array(expectation_vals) expectation_vars = np.array(expectation_vars) # From each time callback is called results.iteration_params = iteration_params[callback_idx] results.expectation_vals = expectation_vals[callback_idx] results.expectation_vars = expectation_vars[callback_idx] # From every function evaluation results.iteration_params_all = iteration_params results.expectation_vals_all = expectation_vals results.expectation_vars_all = expectation_vars results.fun_evals = fun_evals results.meas = meas results.restarts = restarts # Return last model from bayes if hasattr(result, 'models') and hasattr(result, 'space'): results.bayes_special = [result.models[-1], result.space] # x_gp = results.x # if isinstance(x_gp, np.ndarray): # x_gp = [x_gp.tolist()] # x_gp = result.space.transform(x_gp) # try: # Ugly code (seems like we get an index error here) # gp = result.models[-1] # except: # print('Failed on gp = result.models[-1] in vqe_override') # fun, var = gp.predict(x_gp, return_std=True) # results.var = var return results