def find_bitstring(self, qc: QuantumComputer, bitstring_map: Dict[str, int]) -> str: """ Runs Grover's Algorithm to find the bitstring that is designated by ``bistring_map``. In particular, this will prepare an initial state in the uniform superposition over all bit- strings, an then use Grover's Algorithm to pick out the desired bitstring. :param qc: the connection to the Rigetti cloud to run pyQuil programs. :param bitstring_map: a mapping from bitstrings to the phases that the oracle should impart on them. If the oracle should "look" for a bitstring, it should have a ``-1``, otherwise it should have a ``1``. :return: Returns the bitstring resulting from measurement after Grover's Algorithm. """ self._init_attr(bitstring_map) ro = self.grover_circuit.declare('ro', 'BIT', len(self.qubits)) self.grover_circuit += [ MEASURE(qubit, ro[idx]) for idx, qubit in enumerate(self.qubits) ] executable = qc.compile(self.grover_circuit) sampled_bitstring = qc.run(executable) return "".join([str(bit) for bit in sampled_bitstring[0]])
def test_qc_joint_expectation(forest): device = NxDevice(nx.complete_graph(2)) qc = QuantumComputer( name="testy!", qam=QVM(connection=forest), device=device, compiler=DummyCompiler() ) # |01> state program p = Program() p += RESET() p += X(0) p.wrap_in_numshots_loop(10) # ZZ experiment sz = ExperimentSetting( in_state=sZ(0) * sZ(1), out_operator=sZ(0) * sZ(1), additional_expectations=[[0], [1]] ) e = Experiment(settings=[sz], program=p) results = qc.experiment(e) # ZZ expectation value for state |01> is -1 assert np.isclose(results[0].expectation, -1) assert np.isclose(results[0].std_err, 0) assert results[0].total_counts == 40 # Z0 expectation value for state |01> is -1 assert np.isclose(results[0].additional_results[0].expectation, -1) assert results[0].additional_results[1].total_counts == 40 # Z1 expectation value for state |01> is 1 assert np.isclose(results[0].additional_results[1].expectation, 1) assert results[0].additional_results[1].total_counts == 40
def test_readout_symmetrization(forest): device = NxDevice(nx.complete_graph(3)) noise_model = decoherence_noise_with_asymmetric_ro(gates=gates_in_isa(device.get_isa())) qc = QuantumComputer( name='testy!', qam=QVM(connection=forest, noise_model=noise_model), device=device, compiler=DummyCompiler() ) prog = Program(I(0), X(1), MEASURE(0, 0), MEASURE(1, 1)) prog.wrap_in_numshots_loop(1000) bs1 = qc.run(prog) avg0_us = np.mean(bs1[:, 0]) avg1_us = 1 - np.mean(bs1[:, 1]) diff_us = avg1_us - avg0_us assert diff_us > 0.03 bs2 = qc.run_symmetrized_readout(prog, 1000) avg0_s = np.mean(bs2[:, 0]) avg1_s = 1 - np.mean(bs2[:, 1]) diff_s = avg1_s - avg0_s assert diff_s < 0.05
def test_qpu_run(): config = PyquilConfig() if config.qpu_url and config.qpu_compiler_url: g = nx.Graph() g.add_node(0) device = NxDevice(g) qc = QuantumComputer( name="pyQuil test QC", qam=QPU(endpoint=config.qpu_url, user="******"), device=device, compiler=QPUCompiler( quilc_endpoint=config.quilc_url, qpu_compiler_endpoint=config.qpu_compiler_url, device=device, ), ) bitstrings = qc.run_and_measure(program=Program(X(0)), trials=1000) assert bitstrings[0].shape == (1000, ) assert np.mean(bitstrings[0]) > 0.8 bitstrings = qc.run(qc.compile(Program(X(0)))) assert bitstrings.shape == (0, 0) else: pytest.skip( "QPU or compiler-server not available; skipping QPU run test.")
def test_measure_bitstrings(forest): device = NxDevice(nx.complete_graph(2)) qc_pyqvm = QuantumComputer(name='testy!', qam=PyQVM(n_qubits=2), device=device, compiler=DummyCompiler()) qc_forest = QuantumComputer(name='testy!', qam=QVM(connection=forest, gate_noise=[0.00] * 3), device=device, compiler=DummyCompiler()) prog = Program(I(0), I(1)) meas_qubits = [0, 1] sym_progs, flip_array = _symmetrization(prog, meas_qubits, symm_type=-1) results = _measure_bitstrings(qc_pyqvm, sym_progs, meas_qubits, num_shots=1) # test with pyQVM answer = [ np.array([[0, 0]]), np.array([[0, 1]]), np.array([[1, 0]]), np.array([[1, 1]]) ] assert all([np.allclose(x, y) for x, y in zip(results, answer)]) # test with regular QVM results = _measure_bitstrings(qc_forest, sym_progs, meas_qubits, num_shots=1) assert all([np.allclose(x, y) for x, y in zip(results, answer)])
def test_qc(): from pyquil.api import ForestConnection, QuantumComputer from pyquil.api._compiler import _extract_attribute_dictionary_from_program from pyquil.api._qac import AbstractCompiler from pyquil.device import NxDevice from pyquil.gates import I class BasicQVMCompiler(AbstractCompiler): def quil_to_native_quil(self, program: Program): return basic_compile(program) def native_quil_to_executable(self, nq_program: Program): return PyQuilExecutableResponse( program=nq_program.out(), attributes=_extract_attribute_dictionary_from_program(nq_program)) try: qc = QuantumComputer( name='testing-qc', qam=QVM(connection=ForestConnection(), random_seed=52), device=NxDevice(nx.complete_graph(2)), compiler=BasicQVMCompiler(), ) qc.run_and_measure(Program(I(0)), trials=1) return qc except (RequestException, TimeoutError) as e: return pytest.skip("This test requires a running local QVM: {}".format(e))
def get_qubit_registers_for_adder(qc: QuantumComputer, num_length: int, qubits: Optional[Sequence[int]] = None)\ -> Tuple[Sequence[int], Sequence[int], int, int]: """ Searches for a layout among the given qubits for the two n-bit registers and two additional ancilla that matches the simple layout given in figure 4 of [CDKM96]_. This method ignores any considerations of physical characteristics of the qc aside from the qubit layout. An error is thrown if the appropriate layout is not found. :param qc: the quantum resource on which an adder program will be executed. :param num_length: the length of the bitstring representation of one summand :param qubits: the available qubits on which to run the adder program. :return: the necessary registers and ancilla labels for implementing an adder program to add the numbers a and b. The output can be passed directly to :func:`adder` """ if qubits is None: unavailable = [] # assume this means all qubits in qc are available else: unavailable = [qubit for qubit in qc.qubits() if qubit not in qubits] graph = qc.qubit_topology().copy() for qubit in unavailable: graph.remove_node(qubit) # network x only provides subgraph isomorphism, but we want a subgraph monomorphism, i.e. we # specifically want to match the edges desired_layout with some subgraph of graph. To # accomplish this, we swap the nodes and edges of graph by making a line graph. line_graph = nx.line_graph(graph) # We want a path of n nodes, which has n-1 edges. Since we are matching edges of graph with # nodes of layout we make a layout of n-1 nodes. num_desired_nodes = 2 * num_length + 2 desired_layout = nx.path_graph(num_desired_nodes - 1) g_matcher = nx.algorithms.isomorphism.GraphMatcher(line_graph, desired_layout) try: # pick out a subgraph isomorphic to the desired_layout if one exists # this is an isomorphic mapping from edges in graph (equivalently nodes of line_graph) to # nodes in desired_layout (equivalently edges of a path graph with one more node) edge_iso = next(g_matcher.subgraph_isomorphisms_iter()) except IndexError: raise Exception( "An appropriate layout for the qubits could not be found among the " "provided qubits.") # pick out the edges of the isomorphism from the original graph subgraph = nx.Graph(graph.edge_subgraph(edge_iso.keys())) # pick out an endpoint of our path to start the assignment start_node = -1 for node in subgraph.nodes: if subgraph.degree(node) == 1: # found an endpoint start_node = node break return assign_registers_to_line_or_cycle(start_node, subgraph, num_length)
def test_device_stuff(): topo = nx.from_edgelist([(0, 4), (0, 99)]) qc = QuantumComputer( name='testy!', qam=None, # not necessary for this test device=NxDevice(topo), compiler=DummyCompiler()) assert nx.is_isomorphic(qc.qubit_topology(), topo) isa = qc.get_isa(twoq_type='CPHASE') assert sorted(isa.edges)[0].type == 'CPHASE' assert sorted(isa.edges)[0].targets == [0, 4]
def test_run(forest): device = NxDevice(nx.complete_graph(3)) qc = QuantumComputer(name='testy!', qam=QVM(connection=forest, gate_noise=[0.01] * 3), device=device, compiler=DummyCompiler()) bitstrings = qc.run( Program(H(0), CNOT(0, 1), CNOT(1, 2), MEASURE(0, 0), MEASURE(1, 1), MEASURE(2, 2)).wrap_in_numshots_loop(1000)) assert bitstrings.shape == (1000, 3) parity = np.sum(bitstrings, axis=1) % 3 assert 0 < np.mean(parity) < 0.15
def test_run_pyqvm_noiseless(): device = NxDevice(nx.complete_graph(3)) qc = QuantumComputer(name='testy!', qam=PyQVM(n_qubits=3), device=device, compiler=DummyCompiler()) bitstrings = qc.run( Program(H(0), CNOT(0, 1), CNOT(1, 2), MEASURE(0, 0), MEASURE(1, 1), MEASURE(2, 2)).wrap_in_numshots_loop(1000)) assert bitstrings.shape == (1000, 3) parity = np.sum(bitstrings, axis=1) % 3 assert np.mean(parity) == 0
def process_characterisation(qc: QuantumComputer) -> dict: """Convert a :py:class:`pyquil.api.QuantumComputer` to a dictionary containing Rigetti device Characteristics :param qc: A quantum computer to be converted :type qc: QuantumComputer :return: A dictionary containing Rigetti device characteristics """ specs = qc.device.get_specs() coupling_map = [[n, ni] for n, neigh_dict in qc.qubit_topology().adjacency() for ni, _ in neigh_dict.items()] node_ers_dict = {} link_ers_dict = {} t1_times_dict = {} t2_times_dict = {} device_node_fidelities = specs.f1QRBs() # type: ignore device_link_fidelities = specs.fCZs() # type: ignore device_fROs = specs.fROs() # type: ignore device_t1s = specs.T1s() # type: ignore device_t2s = specs.T2s() # type: ignore for index in qc.qubits(): error_cont = QubitErrorContainer({OpType.Rx, OpType.Rz}) error_cont.add_readout(1 - device_fROs[index]) # type: ignore t1_times_dict[index] = device_t1s[index] t2_times_dict[index] = device_t2s[index] error_cont.add_error( (OpType.Rx, 1 - device_node_fidelities[index])) # type: ignore # Rigetti use frame changes for Rz, so they effectively have no error. node_ers_dict[Node(index)] = error_cont for (a, b), fid in device_link_fidelities.items(): error_cont = QubitErrorContainer({OpType.CZ}) error_cont.add_error((OpType.CZ, 1 - fid)) # type: ignore link_ers_dict[(Node(a), Node(b))] = error_cont link_ers_dict[(Node(b), Node(a))] = error_cont arc = Architecture(coupling_map) characterisation = dict() characterisation["NodeErrors"] = node_ers_dict characterisation["EdgeErrors"] = link_ers_dict characterisation["Architecture"] = arc characterisation["t1times"] = t1_times_dict characterisation["t2times"] = t2_times_dict return characterisation
def metadata_save(qc: QuantumComputer, repo_path: str = None, filename: str = None) -> pd.DataFrame: ''' This helper function saves metadata related to your run on a Quantum computer. Basic data saved includes the date and time. Additionally information related to the quantum computer is saved: device name, topology, qubit labels, edges that have two qubit gates, device specs If a path is passed to this function information related to the Git Repository is saved, specifically the repository: name, branch and commit. :param qc: The QuantumComputer to run the experiment on. :param repo_path: path to repository e.g. '../' :param filename: The name of the file to write JSON-serialized results to. :return: pandas DataFrame ''' # Git related things if repo_path is not None: repo = Repo(repo_path) branch = repo.active_branch sha = repo.head.object.hexsha short_sha = repo.git.rev_parse(sha, short=7) the_repo = repo.git_dir the_branch = branch.name the_commit = short_sha else: the_repo = None the_branch = None the_commit = None metadata = { # Time and date 'Date': str(date.today()), 'Time': str(datetime.now().time()), # Git stuff 'Repository': the_repo, 'Branch': the_branch, 'Git_commit': the_commit, # QPU data 'Device_name': qc.name, 'Topology': qc.qubit_topology(), 'Qubits': list(qc.qubit_topology().nodes), 'Two_Qubit_Gates': list(qc.qubit_topology().edges), 'Device_Specs': qc.device.get_specs(), } if filename: pd.DataFrame(metadata).to_json(filename) return pd.DataFrame(metadata)
def test_run_pyqvm_noiseless(): device = NxDevice(nx.complete_graph(3)) qc = QuantumComputer( name="testy!", qam=PyQVM(n_qubits=3), device=device, compiler=DummyCompiler() ) prog = Program(H(0), CNOT(0, 1), CNOT(1, 2)) ro = prog.declare("ro", "BIT", 3) for q in range(3): prog += MEASURE(q, ro[q]) bitstrings = qc.run(prog.wrap_in_numshots_loop(1000)) assert bitstrings.shape == (1000, 3) parity = np.sum(bitstrings, axis=1) % 3 assert np.mean(parity) == 0
def test_run_with_parameters(forest): device = NxDevice(nx.complete_graph(3)) qc = QuantumComputer(name='testy!', qam=QVM(connection=forest), device=device, compiler=DummyCompiler()) bitstrings = qc.run(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), memory_map={'theta': [np.pi]}) assert bitstrings.shape == (1000, 1) assert all([bit == 1 for bit in bitstrings])
def test_run_pyqvm_noisy(): device = NxDevice(nx.complete_graph(3)) qc = QuantumComputer( name='testy!', qam=PyQVM(n_qubits=3, post_gate_noise_probabilities={'relaxation': 0.01}), device=device, compiler=DummyCompiler()) bitstrings = qc.run( Program(H(0), CNOT(0, 1), CNOT(1, 2), MEASURE(0, 0), MEASURE(1, 1), MEASURE(2, 2)).wrap_in_numshots_loop(1000)) assert bitstrings.shape == (1000, 3) parity = np.sum(bitstrings, axis=1) % 3 assert 0 < np.mean(parity) < 0.15
def run(self, qc: QuantumComputer, bitstring_map: Dict[str, str]) -> 'BernsteinVazirani': """ Runs the Bernstein-Vazirani algorithm. Given a connection to a QVM or QPU, find the :math:`\\mathbf{a}` and :math:`b` corresponding to the function represented by the oracle function that will be constructed from the bitstring map. :param qc: connection to the QPU or QVM :param bitstring_map: a truth table describing the boolean function, whose dot-product vector and bias is to be found """ # initialize all attributes self.input_bitmap = bitstring_map self.n_qubits = len(list(bitstring_map.keys())[0]) self.computational_qubits = list(range(self.n_qubits)) self.ancilla = self.n_qubits # is the highest index now. # construct BV circuit self.bv_circuit = self._create_bv_circuit(bitstring_map) # find vector by running the full bv circuit full_circuit = Program() full_ro = full_circuit.declare('ro', 'BIT', len(self.computational_qubits) + 1) full_circuit += self.bv_circuit full_circuit += [ MEASURE(qubit, ro) for qubit, ro in zip(self.computational_qubits, full_ro) ] full_executable = qc.compile(full_circuit) full_results = qc.run(full_executable) bv_vector = full_results[0][::-1] # To get the bias term we skip the Walsh-Hadamard transform ancilla_circuit = Program() ancilla_ro = ancilla_circuit.declare( 'ro', 'BIT', len(self.computational_qubits) + 1) ancilla_circuit += self.bv_circuit ancilla_circuit += [MEASURE(self.ancilla, ancilla_ro[self.ancilla])] ancilla_executable = qc.compile(ancilla_circuit) ancilla_results = qc.run(ancilla_executable) bv_bias = ancilla_results[0][0] self.solution = ''.join([str(b) for b in bv_vector]), str(bv_bias) return self
def __init__(self, data, label, shuffle=False, qpu=False): weights = np.load(data) n_graphs = len(weights) # read labels from file, or as single label if os.path.exists(label): labels = np.load(label) else: labels = [label for _ in range(n_graphs)] if shuffle: self._shuffled_order = np.random.permutation(n_graphs) weights = weights[self._shuffled_order] labels = labels[self._shuffled_order] self.pset = AllProblems(weights, labels) self.num_qubits = self.pset.num_variables() qubits = list(range(self.num_qubits)) angles = np.linspace(0, 2 * np.pi, NUM_ANGLES, endpoint=False) self.instrs = [ CNOT(q0, q1) for q0, q1 in product(qubits, qubits) if q0 != q1 ] self.instrs += [ op(theta, q) for q, op, theta in product(qubits, [RX, RY, RZ], angles) ] self.action_space = gym.spaces.Discrete(len(self.instrs)) obs_len = NUM_SHOTS * self.num_qubits + len(self.pset.problem(0)) self.observation_space = gym.spaces.Box(np.full(obs_len, -1.0), np.full(obs_len, 1.0), dtype=np.float32) self.reward_threshold = 0.8 self.qpu = qpu if qpu: self._qc = get_qc(QPU_NAME) else: self._qc = QuantumComputer( name="qvm", qam=PyQVM(n_qubits=self.num_qubits), device=NxDevice(nx.complete_graph(self.num_qubits)), compiler=MinimalPyQVMCompiler(), ) self.reset()
def apply_noise(qc: QuantumComputer, gate_time=50e-9): """ Get new QuantumComputer with noise model applied :param qc: QuantumComputer instance to patch :param gate_time: gate time for using in noise model :return: patched QuantumComputer instance """ if not isinstance(qc, QuantumComputer): raise ValueError( f"Invalid parameter {qc} for apply_em(). You should provide a QuantumComputer instance." ) if hasattr(qc.qam, "noise_model") and qc.qam.noise_model is not None: raise ValueError( f"QuantumComputer {qc} instance should not have noise model.") original_run = qc.run def new_run(self, executable, *args, **kwargs): original_exec = executable.copy() new_exec = get_noisy_executable(qc, get_native_program(qc, original_exec), noise_param=gate_time) original_exec.program = new_exec.program return original_run(executable=original_exec, *args, **kwargs) qc.run = types.MethodType(new_run, qc) return qc
def compile_rigetti(num_qubits, topology, program): if topology.lower() == 'ring': edge_list = [] for i in range(0, num_qubits): edge_list.append((i, (i + 1) % num_qubits)) topology = nx.from_edgelist(edge_list) device = NxDevice(topology) compiler = LocalQVMCompiler("http://localhost:6000", device) my_qc = QuantumComputer(name='my_qc', qam=QVM(connection=ForestConnection()), device=device, compiler=compiler) executable = compiler.quil_to_native_quil( program) #create QC compatible specification depth = compute_depth_rigetti(executable) volume = len(executable) - 3 #subtract extra directives print(executable) q2_count = two_qubit_count(executable) out_str = str(executable) out_str = out_str + ("#DEPTH: %s |VOL.: %s |2Q GATE COUNT: %s\n" % (depth, volume, q2_count)) print("DEPTH: %s |VOL.: %s |2Q GATE COUNT: %s" % (depth, volume, q2_count)) print() print() return out_str
def test_for_negative_probabilities(): # trivial program to do state tomography on prog = Program(I(0)) # make TomographyExperiment expt_settings = [ExperimentSetting(zeros_state([0]), pt) for pt in [sI(0), sX(0), sY(0), sZ(0)]] experiment_1q = TomographyExperiment(settings=expt_settings, program=prog) # make a quantum computer object device = NxDevice(nx.complete_graph(1)) qc_density = QuantumComputer( name="testy!", qam=PyQVM(n_qubits=1, quantum_simulator_type=ReferenceDensitySimulator), device=device, compiler=DummyCompiler(), ) # initialize with a pure state initial_density = np.array([[1.0, 0.0], [0.0, 0.0]]) qc_density.qam.wf_simulator.density = initial_density try: list(measure_observables(qc=qc_density, tomo_experiment=experiment_1q, n_shots=3000)) except ValueError as e: # the error is from np.random.choice by way of self.rs.choice in ReferenceDensitySimulator assert str(e) != "probabilities are not non-negative" # initialize with a mixed state initial_density = np.array([[0.9, 0.0], [0.0, 0.1]]) qc_density.qam.wf_simulator.density = initial_density try: list(measure_observables(qc=qc_density, tomo_experiment=experiment_1q, n_shots=3000)) except ValueError as e: assert str(e) != "probabilities are not non-negative"
def run_t1(qc: QuantumComputer, qubits: Union[int, List[int]], stop_time: float, n_shots: int = 1000, num_points: int = 15, filename: str = None) -> pd.DataFrame: """ Execute experiments to measure the t1 decay time of 1 or more qubits. :param qc: The QuantumComputer to run the experiment on. :param qubits: Which qubits to measure. :param stop_time: The maximum decay time to measure at. :param n_shots: The number of shots to average over for each data point. :param num_points: The number of points for each t1 curve. :param filename: The name of the file to write JSON-serialized results to. """ results = [] for t, program in generate_t1_experiments(qubits, stop_time, n_shots, num_points): executable = qc.compiler.native_quil_to_executable(program) bitstrings = qc.run(executable) for i in range(len(qubits)): avg = np.mean(bitstrings[:, i]) results.append({ 'qubit': qubits[i], 'time': t, 'n_bitstrings': len(bitstrings), 'avg': float(avg), }) df = pd.DataFrame(results) if filename is not None: df.to_json(filename) return df
def run_rabi(qc: QuantumComputer, qubits: Union[int, List[int]], n_shots: int = 1000, num_points: int = 15, filename: str = None) -> pd.DataFrame: """ Execute experiments to measure Rabi flop one or more qubits. :param qc: The QuantumComputer to run the experiment on. :param qubits: Which qubits to measure. :param n_shots: The number of shots to average over for each data point. :param num_points: The number of points for each Rabi curve. :param filename: The name of the file to write JSON-serialized results to. :return: DataFrame with Rabi results. """ results = [] for theta, program in generate_rabi_experiments(qubits, n_shots, num_points): executable = qc.compiler.native_quil_to_executable(program) bitstrings = qc.run(executable) for i in range(len(qubits)): avg = np.mean(bitstrings[:, i]) results.append({ 'qubit': qubits[i], 'angle': theta, 'n_bitstrings': len(bitstrings), 'avg': float(avg), }) if filename: pd.DataFrame(results).to_json(filename) return pd.DataFrame(results)
def acquire_data_t2( qc: QuantumComputer, t2_experiment: pd.DataFrame, ) -> pd.DataFrame: """ Execute experiments to measure the T2 star or T2 echo decay time of 1 or more qubits. :param qc: The QuantumComputer to run the experiment on :param t2_experiment: A pandas DataFrame containing: time, T2 program :param detuning: The additional detuning frequency about the z axis. :return: pandas DataFrame containing T2 results, and detuning used in creating experiments for those results. """ results = [] for index, row in t2_experiment.iterrows(): t = row['Time'] program = row['Program'] detuning = row['Detuning'] executable = qc.compiler.native_quil_to_executable(program) bitstrings = qc.run(executable) qubits = list(program.get_qubits()) for i in range(len(qubits)): avg = np.mean(bitstrings[:, i]) results.append({ 'Qubit': qubits[i], 'Time': t, 'Num_bitstrings': len(bitstrings), 'Average': float(avg), 'Detuning': float(detuning), }) return pd.DataFrame(results)
def get_parametric_program(qc: QuantumComputer, q0: int, q1: int, n_shots: int = 1000): """ Run one (beta, gamma) point for the simplest Maxcut QAOA. :param qc: The QuantumComputer to use :param beta: The beta angle (p = 1) :param gamma: The gamma angle (p = 1) :param q0: The index of the first qubit :param q1: The index of the second qubit :param n_shots: The number of shots to take for this (beta, gamma) point. """ ising = generate_maxcut_ising(nx.from_edgelist([(0, 1)])) driver = sX(q0) + sX(q1) reward = sZ(q0) * sZ(q1) + 0 # add 0 to turn to PauliSum program = Program() beta = program.declare('beta', memory_type='REAL') gamma = program.declare('gamma', memory_type='REAL') program += get_qaoa_program(qubits=[q0, q1], driver=driver, reward=reward, betas=[beta], gammas=[gamma]) ro = program.declare('ro', memory_size=2) program += MEASURE(q0, ro[0]) program += MEASURE(q1, ro[1]) program = program.wrap_in_numshots_loop(shots=n_shots) executable = qc.compile(program) return executable, ising
def acquire_data_t1( qc: QuantumComputer, t1_experiment: pd.DataFrame, ) -> pd.DataFrame: """ Execute experiments to measure the T1 decay time of 1 or more qubits. :param qc: The QuantumComputer to run the experiment on :param t1_experiment: A pandas DataFrame with columns: time, t1 program :return: pandas DataFrame """ results = [] for index, row in t1_experiment.iterrows(): t = row['Time'] program = row['Program'] executable = qc.compiler.native_quil_to_executable(program) bitstrings = qc.run(executable) qubits = list(program.get_qubits()) for i in range(len(qubits)): avg = np.mean(bitstrings[:, i]) results.append({ 'Qubit': qubits[i], 'Time': t, 'Num_bitstrings': len(bitstrings), 'Average': float(avg), 'Program': program, }) df = pd.DataFrame(results) return df
def acquire_data_rabi(qc: QuantumComputer, rabi_experiment: pd.DataFrame, filename: str = None) -> pd.DataFrame: """ Execute experiments to measure Rabi flop one or more qubits. :param qc: The QuantumComputer to run the experiment on :param rabi_experiment: pandas DataFrame: (theta, Rabi program) :return: DataFrame with Rabi results """ results = [] for index, row in rabi_experiment.iterrows(): theta = row['Angle'] program = row['Program'] executable = qc.compiler.native_quil_to_executable(program) bitstrings = qc.run(executable) qubits = list(program.get_qubits()) for i in range(len(qubits)): avg = np.mean(bitstrings[:, i]) results.append({ 'Qubit': qubits[i], 'Angle': theta, 'Num_bitstrings': len(bitstrings), 'Average': float(avg), }) if filename: pd.DataFrame(results).to_json(filename) return pd.DataFrame(results)
def test_run_with_parameters(forest): device = NxDevice(nx.complete_graph(3)) qc = QuantumComputer( name="testy!", qam=QVM(connection=forest), device=device, compiler=DummyCompiler() ) bitstrings = qc.run( 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), memory_map={"theta": [np.pi]}, ) assert bitstrings.shape == (1000, 1) assert all([bit == 1 for bit in bitstrings])
def expectation_from_sampling(pyquil_program: Program, marked_qubits: List[int], qc: QuantumComputer, samples: int, stacked_params: np.ndarray) -> float: """ Calculation of Z_{i} at marked_qubits Given a wavefunctions, this calculates the expectation value of the Zi operator where i ranges over all the qubits given in marked_qubits. :param pyquil_program: pyQuil program generating some state :param marked_qubits: The qubits within the support of the Z pauli operator whose expectation value is being calculated :param qc: A QuantumComputer object. :param samples: Number of bitstrings collected to calculate expectation from sampling. :returns: The expectation value as a float. """ program = Program() ro = program.declare('ro', 'BIT', max(marked_qubits) + 1) program += pyquil_program program += [ MEASURE(qubit, r) for qubit, r in zip(list(range(max(marked_qubits) + 1)), ro) ] program.wrap_in_numshots_loop(samples) try: executable = qc.compile(program) except: import pdb pdb.set_trace() bitstring_samples = qc.run(executable) bitstring_tuples = list(map(tuple, bitstring_samples)) freq = Counter(bitstring_tuples) # perform weighted average expectation = 0 for bitstring, count in freq.items(): bitstring_int = int("".join([str(x) for x in bitstring[::-1]]), 2) if parity_even_p(bitstring_int, marked_qubits): expectation += float(count) / samples else: expectation -= float(count) / samples return expectation
def sample_rand_circuits_for_heavy_out( qc: QuantumComputer, qubits: Sequence[int], depth: int, program_generator: Callable[ [QuantumComputer, Sequence[int], Sequence[np.ndarray], np.ndarray], Program], num_circuits: int = 100, num_shots: int = 1000, show_progress_bar: bool = False) -> int: """ This method performs the bulk of the work in the quantum volume measurement. For the given depth, num_circuits many random model circuits are generated, the heavy outputs are determined from the ideal output distribution of each circuit, and a native quil implementation of the model circuit output by the program generator is run on the qc. The total number of sampled heavy outputs is returned. :param qc: the quantum resource that will implement the PyQuil program for each model circuit :param qubits: the qubits available in the qc for the program_generator to use. :param depth: the depth (and width in num of qubits) of the model circuits :param program_generator: a method which takes an abstract description of a model circuit and returns a native quil program that implements that circuit. See measure_quantum_volume docstring for specifics. :param num_circuits: the number of random model circuits to sample at this depth; should be >100 :param num_shots: the number of shots to sample from each model circuit :param show_progress_bar: displays a progress bar via tqdm if true. :return: the number of heavy outputs sampled among all circuits generated for this depth """ wfn_sim = NumpyWavefunctionSimulator(depth) num_heavy = 0 # display progress bar using tqdm for _ in tqdm(range(num_circuits), disable=not show_progress_bar): permutations, gates = generate_abstract_qv_circuit(depth) # generate a PyQuil program in native quil that implements the model circuit # The program should measure the output qubits in the order that is consistent with the # comparison of the bitstring results to the heavy outputs given by collect_heavy_outputs program = program_generator(qc, qubits, permutations, gates) # run the program num_shots many times program.wrap_in_numshots_loop(num_shots) executable = qc.compiler.native_quil_to_executable(program) results = qc.run(executable) # classically simulate model circuit represented by the perms and gates for heavy outputs heavy_outputs = collect_heavy_outputs(wfn_sim, permutations, gates) # determine if each result bitstring is a heavy output, as determined from simulation for result in results: # convert result to int for comparison with heavy outputs. output = bit_array_to_int(result) if output in heavy_outputs: num_heavy += 1 return num_heavy
def apply_em( qc: QuantumComputer, base_gate_time: float = 50e-9, order: int = 3, noise_param_multiply_coefficients: np.ndarray = None ) -> QuantumComputer: """ Get new QuantumComputer which will run programs with EM technique applied. :param qc: QuantumComputer instance to patch :param base_gate_time: base noise parameter :param order: order of the Richardson extrapolation :param noise_param_multiply_coefficients: coefficients to be used for stretching base noise parameter :return: the new patched QuantumComputer instance """ if not isinstance(qc, QuantumComputer): raise ValueError( f"Invalid parameter {qc} for apply_em(). You should provide a QuantumComputer instance." ) if hasattr(qc.qam, "noise_model") and qc.qam.noise_model is not None: raise ValueError( f"QuantumComputer {qc} instance should not have noise model.") original_run = qc.run if noise_param_multiply_coefficients is None: noise_param_multiply_coefficients = np.arange(1, order + 2) def new_run(self, executable, *args, **kwargs): noise_params = noise_param_multiply_coefficients * base_gate_time original_exec = executable.copy() native_program = get_native_program(qc, original_exec) original_results = [] trials, qubits = None, None for noise_param in noise_params: new_exec = get_noisy_executable(qc, native_program, noise_param) original_exec.program = new_exec.program bitstring = original_run(executable=original_exec, *args, **kwargs) trials, qubits = bitstring.shape original_results.append(get_expectations(bitstring)) original_results = np.array(original_results) mitigated_result = get_extrapolated_with_richardson( noise_param_multiply_coefficients, original_results) result_bitstring = get_bitstrings_from_expectations( mitigated_result, (qubits, trials)) return result_bitstring qc.run = types.MethodType(new_run, qc) return qc