def sample_bitstrings( circuit: cirq.Circuit, noise_model: cirq.NOISE_MODEL_LIKE = cirq.amplitude_damp, # type: ignore noise_level: Tuple[float] = (0.01, ), sampler: cirq.Sampler = cirq.DensityMatrixSimulator(), shots: int = 8192, ) -> MeasurementResult: if sum(noise_level) > 0: circuit = circuit.with_noise(noise_model(*noise_level)) # type: ignore result = sampler.run(circuit, repetitions=shots) return MeasurementResult( result=np.column_stack(list(result.measurements.values())), qubit_indices=tuple( int(q) for k in result.measurements.keys() for q in k.split(",")), )
def sample_heavy_set( circuit: cirq.Circuit, heavy_set: List[int], *, repetitions=10000, sampler: cirq.Sampler = cirq.Simulator(), mapping: Dict[cirq.ops.Qid, cirq.ops.Qid] = None) -> float: """Run a sampler over the given circuit and compute the percentage of its outputs that are in the heavy set. Args: circuit: The circuit to sample. heavy_set: The previously-computed heavy set for the given circuit. repetitions: The number of runs to sample the circuit. sampler: The sampler to run on the given circuit. mapping: An optional mapping from compiled qubits to original qubits, to maintain the ordering between the model and compiled circuits. Returns: A probability percentage, from 0 to 1, representing how many of the output bit-strings were in the heaby set. """ # Add measure gates to the end of (a copy of) the circuit. Ensure that those # gates measure those in the given mapping, preserving this order. qubits = circuit.all_qubits() key = None if mapping: key = lambda q: mapping[q] qubits = frozenset(mapping.keys()) circuit_copy = circuit + cirq.measure(*sorted(qubits, key=key)) # Run the sampler to compare each output against the Heavy Set. measurements = sampler.run(program=circuit_copy, repetitions=repetitions) # Compute the number of outputs that are in the heavy set. num_in_heavy_set = np.sum(np.in1d(measurements.data.iloc[:, 0], heavy_set)) # Return the number of Heavy outputs over the number of runs. return num_in_heavy_set / repetitions
def get_state_tomography_data(sampler: cirq.Sampler, qubits: Sequence[cirq.Qid], circuit: cirq.Circuit, rot_circuit: cirq.Circuit, rot_sweep: cirq.Sweep, repetitions: int = 1000) -> np.ndarray: """Gets the data for each rotation string added to the circuit. For each sequence in prerotation_sequences gets the probability of all 2**n bit strings. Resulting matrix will have dimensions (len(rot_sweep)**n, 2**n). This is a default way to get data that can be replaced by the user if they have a more advanced protocol in mind. Args: sampler: Sampler to collect the data from. qubits: Qubits to do the tomography on. circuit: Circuit to do the tomography on. rot_circuit: Circuit with parametrized rotation gates to do before the final measurements. rot_sweep: The list of rotations on the qubits to perform before measurement. repetitions: Number of times to sample each rotation sequence. Returns: 2D array of probabilities, where first index is which pre-rotation was applied and second index is the qubit state. """ results = sampler.run_sweep(circuit + rot_circuit + [cirq.measure(*qubits, key='z')], params=rot_sweep, repetitions=repetitions) all_probs = [] for result in results: hist = result.histogram(key='z') probs = [hist[i] for i in range(2**len(qubits))] all_probs.append(np.array(probs) / repetitions) return np.array(all_probs)