def _scan_two_qubit_ops_into_matrix( self, circuit: circuits.Circuit, index: Optional[int], qubits: Tuple[ops.QubitId, ...] ) -> Tuple[List[ops.Operation], List[int], np.ndarray]: """Accumulates operations affecting the given pair of qubits. The scan terminates when it hits the end of the circuit, finds an operation without a known matrix, or finds an operation that interacts the given qubits with other qubits. Args: circuit: The circuit to scan for operations. index: The index to start scanning forward from. qubits: The pair of qubits we care about. Returns: A tuple containing: 0. The operations. 1. The moment indices those operations were on. 2. A matrix equivalent to the effect of the scanned operations. """ product = np.eye(4, dtype=np.complex128) all_operations = [] touched_indices = [] while index is not None: operations = list({circuit.operation_at(q, index) for q in qubits}) op_data = [ self._op_to_matrix(op, qubits) for op in operations if op is not None ] # Stop at any non-constant or non-local interaction. if any(e is None for e in op_data): break present_ops = [op for op in operations if op] present_op_data = cast(List[np.ndarray], op_data) for op_mat in present_op_data: product = np.dot(op_mat, product) all_operations.extend(present_ops) touched_indices.append(index) index = circuit.next_moment_operating_on(qubits, index + 1) return all_operations, touched_indices, product
def _sample_measurements(circuit: Circuit, step_result: 'XmonStepResult', repetitions: int) -> Dict[str, List]: """Sample from measurements in the given circuit. This should only be called if the circuit has only terminal measurements. Args: circuit: The circuit to sample from. step_result: The XmonStepResult from which to sample. This should be the step at the end of the circuit. Can be None if no steps were taken. repetitions: The number of time to sample. Returns: A dictionary from the measurement keys to the measurement results. These results are lists of lists, with the outer list corresponding to the repetition, and the inner list corresponding to the qubits as ordered in the measurement gate. """ if step_result is None: return {} bounds = {} all_qubits = [] # type: List[raw_types.QubitId] current_index = 0 for _, op, gate in circuit.findall_operations_with_gate_type( xmon_gates.XmonMeasurementGate): key = gate.key bounds[key] = (current_index, current_index + len(op.qubits)) all_qubits.extend(op.qubits) current_index += len(op.qubits) sample = step_result.sample(all_qubits, repetitions) return {k: [x[s:e] for x in sample] for k,(s, e) in bounds.items()}
def optimized_for_xmon( circuit: circuits.Circuit, new_device: Optional[xmon_device.XmonDevice] = None, qubit_map: Callable[[ops.QubitId], devices.GridQubit] = lambda e: cast(devices.GridQubit, e), allow_partial_czs: bool = False, ) -> circuits.Circuit: """Optimizes a circuit with XmonDevice in mind. Starts by converting the circuit's operations to the xmon gate set, then begins merging interactions and rotations, ejecting pi-rotations and phasing operations, dropping unnecessary operations, and pushing operations earlier. Args: circuit: The circuit to optimize. new_device: The device the optimized circuit should be targeted at. If set to None, the circuit's current device is used. qubit_map: Transforms the qubits (e.g. so that they are GridQubits). allow_partial_czs: If true, the optimized circuit may contain partial CZ gates. Otherwise all partial CZ gates will be converted to full CZ gates. At worst, two CZ gates will be put in place of each partial CZ from the input. Returns: The optimized circuit. """ copy = circuit.copy() opts = _OPTIMIZERS_PART_CZ if allow_partial_czs else _OPTIMIZERS for optimizer in opts: optimizer.optimize_circuit(copy) return circuits.Circuit.from_ops( (op.transform_qubits(qubit_map) for op in copy.all_operations()), strategy=circuits.InsertStrategy.EARLIEST, device=new_device or copy.device)
def move_non_clifford_into_clifford(circuit_left: Union[circuits.Circuit, circuits.CircuitDag], circuit_right: circuits.Circuit ) -> circuits.Circuit: if isinstance(circuit_left, circuits.CircuitDag): string_dag = cast(circuits.CircuitDag, circuit_left) else: string_dag = pauli_string_dag_from_circuit( cast(circuits.Circuit, circuit_left)) output_ops = list(circuit_right.all_operations()) rightmost_nodes = (set(string_dag.nodes) - set(before for before, _ in string_dag.edges)) def possible_string_placements( ) -> Iterator[Tuple[PauliStringGateOperation, int, circuits.Unique[PauliStringGateOperation]]]: for right_node in rightmost_nodes: string_op = right_node.val # Try moving the Pauli string through, stop at measurements yield string_op, 0, right_node for i, out_op in enumerate(output_ops): if not set(out_op.qubits) & set(string_op.qubits): # Skip if operations don't share qubits continue if not (isinstance(out_op, ops.GateOperation) and isinstance(out_op.gate, (ops.CliffordGate, ops.PauliInteractionGate))): # This is as far through as this Pauli string can move break string_op = string_op.pass_operations_over([out_op], after_to_before=True) yield string_op, i+1, right_node while rightmost_nodes: # Pick the Pauli string that can be moved furthest through the Clifford # circuit best_string_op, best_index, best_node = max( possible_string_placements(), key=lambda placement: (-len(placement[0].pauli_string), placement[1])) # Place the best one into the output circuit output_ops.insert(best_index, best_string_op) # Remove the best one from the dag and update rightmost_nodes rightmost_nodes.remove(best_node) rightmost_nodes.update( pred_node for pred_node in string_dag.predecessors(best_node) if len(string_dag.succ[pred_node]) <= 1) string_dag.remove_node(best_node) assert not string_dag.nodes, 'There was a cycle in the CircuitDag' return circuits.Circuit.from_ops( output_ops, strategy=circuits.InsertStrategy.EARLIEST, device=circuit_right.device)
def to_circuit(self) -> Circuit: """Convert the schedule to a circuit. This discards most timing information from the schedule, but does place operations that are scheduled at the same time in the same Moment. """ circuit = Circuit() ops = [] # type: List[Operation] time = None # type: Optional[Timestamp] for so in self.scheduled_operations: if so.time != time: circuit.append(ops) ops = [so.operation] time = so.time else: ops.append(so.operation) circuit.append(ops) return circuit
def linearize_circuit_qubits( circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT ) -> None: qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for( circuit.all_qubits()) qubit_map = {q: line.LineQubit(i) for i, q in enumerate(qubits)} QubitMapper(qubit_map.__getitem__).optimize_circuit(circuit)
def find_measurement_keys(circuit: Circuit) -> Set[str]: keys = set() # type: Set[str] for _, _, gate in circuit.findall_operations_with_gate_type( xmon_gates.XmonMeasurementGate): key = gate.key if key in keys: raise ValueError('Repeated Measurement key {}'.format(key)) keys.add(key) return keys
def circuit_to_quirk_url(circuit: circuits.Circuit, prefer_unknown_gate_to_failure: bool=False, escape_url=True) -> str: """Returns a Quirk URL for the given circuit. Args: circuit: The circuit to open in Quirk. prefer_unknown_gate_to_failure: If not set, gates that fail to convert will cause this function to raise an error. If set, a URL containing bad gates will be generated. (Quirk will open the URL, and replace the bad gates with parse errors, but still get the rest of the circuit.) escape_url: If set, the generated URL will have special characters such as quotes escaped using %. This makes it possible to paste the URL into forums and the command line and etc and have it properly parse. If not set, the generated URL will be more compact and human readable (and can still be pasted directly into a browser's address bar). Returns: """ circuit = circuit.copy() linearize_circuit_qubits(circuit) cols = [] # Type: List[List[Any]] for moment in circuit: can_merges = [] for op in moment.operations: for col, can_merge in _to_quirk_cols( op, prefer_unknown_gate_to_failure): if can_merge: can_merges.append(col) else: cols.append(col) if can_merges: merged_col = [1] * max(len(e) for e in can_merges) for col in can_merges: for i in range(len(col)): if col[i] != 1: merged_col[i] = col[i] cols.append(merged_col) circuit_json = json.JSONEncoder(ensure_ascii=False, separators=(',', ':'), sort_keys=True).encode({'cols': cols}) if escape_url: suffix = urllib.parse.quote(circuit_json) else: suffix = circuit_json return 'http://algassert.com/quirk#circuit={}'.format(suffix)
def _to_xmon_circuit(self, circuit: Circuit, param_resolver: ParamResolver, extensions: Extensions = None ) -> Tuple[Circuit, Set[str]]: converter = ConvertToXmonGates(extensions) extensions = converter.extensions # TODO: Use one optimization pass. xmon_circuit = circuit.with_parameters_resolved_by( param_resolver, extensions) converter.optimize_circuit(xmon_circuit) DropEmptyMoments().optimize_circuit(xmon_circuit) keys = find_measurement_keys(xmon_circuit) return xmon_circuit, keys
def _to_xmon_circuit( self, circuit: Circuit, param_resolver: ParamResolver, extensions: Extensions = None) -> Tuple[Circuit, Set[str]]: converter = ConvertToXmonGates(extensions) extensions = converter.extensions # TODO: Use one optimization pass. xmon_circuit = circuit.with_parameters_resolved_by( param_resolver, extensions) converter.optimize_circuit(xmon_circuit) DropEmptyMoments().optimize_circuit(xmon_circuit) keys = find_measurement_keys(xmon_circuit) return xmon_circuit, keys
def _run_sweep_sample(self, circuit: circuits.Circuit, repetitions: int) -> Dict[str, List[np.ndarray]]: for step_result in self._base_iterator( circuit=circuit, qubit_order=ops.QubitOrder.DEFAULT, initial_state=0, perform_measurements=False): pass # We can ignore the mixtures since this is a run method which # does not return the state. measurement_ops = [ op for _, op, _ in circuit.findall_operations_with_gate_type( ops.MeasurementGate) ] return step_result.sample_measurement_ops(measurement_ops, repetitions)
def merge_single_qubit_gates_into_phxz(circuit: circuits.Circuit, atol: float = 1e-8) -> None: """Canonicalizes runs of single-qubit rotations in a circuit. Specifically, any run of non-parameterized single-qubit gates will be replaced by an optional PhasedXZ operation. Args: circuit: The circuit to rewrite. This value is mutated in-place. atol: Absolute tolerance to angle error. Larger values allow more negligible gates to be dropped, smaller values increase accuracy. """ circuit._moments = [ *transformers.merge_single_qubit_gates_to_phxz(circuit, atol=atol) ]
def _run(self, circuit: circuits.Circuit, repetitions: int) -> Dict[str, np.ndarray]: measurements: Dict[str, List[int]] = { key: [] for key in protocols.measurement_keys(circuit) } axes_map = {q: i for i, q in enumerate(circuit.all_qubits())} for _ in range(repetitions): state = ActOnCliffordTableauArgs( CliffordTableau(num_qubits=len(axes_map)), axes=(), prng=self._prng, log_of_measurement_results={}, ) for op in circuit.all_operations(): state.axes = tuple(axes_map[q] for q in op.qubits) protocols.act_on(op, state) for k, v in state.log_of_measurement_results.items(): measurements[k].append(v) return {k: np.array(v) for k, v in measurements.items()}
def test_u3_decomposition(self): q_0 = cirq.NamedQubit("q_0") before = cirq.Circuit([QasmUGate(3 / 2, 1 / 2, 1).on(q_0)]) after = cirq.Circuit([ cirq.rz(np.pi).on(q_0), cirq.H.on(q_0), cirq.rz((3 * np.pi) / 2).on(q_0), cirq.H.on(q_0), cirq.rz(np.pi / 2).on(q_0) ]) circuit = Circuit( decompose(before, intercepting_decomposer=decompose_library, keep=need_to_keep)) self.assertEqual(circuit, after)
def to_calibration_layer(self) -> CalibrationLayer: circuit = Circuit([self.gate.on(*pair) for pair in self.pairs]) return CalibrationLayer( calibration_type=_FLOQUET_PHASED_FSIM_HANDLER_NAME, program=circuit, args={ 'est_theta': self.options.characterize_theta, 'est_zeta': self.options.characterize_zeta, 'est_chi': self.options.characterize_chi, 'est_gamma': self.options.characterize_gamma, 'est_phi': self.options.characterize_phi, # Experimental option that should always be set to True. 'readout_corrections': True, }, )
def package_results(self, circuit: CirqCircuit, q_bits: Sequence[Qubit]) -> List[BackendResult]: moments = self._simulator.simulate_moment_steps( circuit, qubit_order=ops.QubitOrder.as_qubit_order( ops.QubitOrder.DEFAULT).order_for(circuit.all_qubits()), ) all_backres = [ BackendResult( state=cast(CliffordSimulatorStepResult, run).state.state_vector(), q_bits=q_bits, ) for run in moments ] return all_backres
def _run_sweep_sample(self, circuit: circuits.Circuit, repetitions: int) -> Dict[str, np.ndarray]: for step_result in self._base_iterator( circuit=circuit, qubit_order=ops.QubitOrder.DEFAULT, initial_state=0, all_measurements_are_terminal=True): pass measurement_ops = [ op for _, op, _ in circuit.findall_operations_with_gate_type( ops.MeasurementGate) ] return step_result.sample_measurement_ops(measurement_ops, repetitions, seed=self._prng)
def parameterize_circuit( circuit: 'cirq.Circuit', options: XEBCharacterizationOptions, ) -> 'cirq.Circuit': """Parameterize PhasedFSim-like gates in a given circuit according to `phased_fsim_options`. """ gate = options.get_parameterized_gate() return Circuit( ops.Moment( gate.on(*op.qubits) if options.should_parameterize(op) else op for op in moment.operations ) for moment in circuit.moments )
def _run(self, circuit: circuits.Circuit, param_resolver: study.ParamResolver, repetitions: int) -> Dict[str, List[np.ndarray]]: """See definition in `cirq.SimulatesSamples`.""" param_resolver = param_resolver or study.ParamResolver({}) resolved_circuit = protocols.resolve_parameters( circuit, param_resolver) self._check_all_resolved(resolved_circuit) def measure_or_mixture(op): return protocols.is_measurement(op) or protocols.has_mixture(op) if circuit.are_all_matches_terminal(measure_or_mixture): return self._run_sweep_sample(resolved_circuit, repetitions) return self._run_sweep_repeat(resolved_circuit, repetitions)
def _base_iterator( self, circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, initial_state: int ) -> Iterator['cirq.CliffordSimulatorStepResult']: """Iterator over CliffordSimulatorStepResult from Moments of a Circuit Args: circuit: The circuit to simulate. qubit_order: Determines the canonical ordering of the qubits. This is often used in specifying the initial state, i.e. the ordering of the computational basis states. initial_state: The initial state for the simulation. Yields: CliffordStepResult from simulating a Moment of the Circuit. """ qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for( circuit.all_qubits()) qubit_map = {q: i for i, q in enumerate(qubits)} if len(circuit) == 0: yield CliffordSimulatorStepResult(measurements={}, state=CliffordState( qubit_map, initial_state=initial_state)) return state = CliffordState(qubit_map, initial_state=initial_state) for moment in circuit: measurements: Dict[ str, List[np.ndarray]] = collections.defaultdict(list) for op in moment: if isinstance(op.gate, ops.MeasurementGate): key = protocols.measurement_key(op) measurements[key].extend( state.perform_measurement(op.qubits, self._prng)) elif protocols.has_unitary(op): state.apply_unitary(op) else: raise NotImplementedError( f"Unrecognized operation: {op!r}") yield CliffordSimulatorStepResult(measurements=measurements, state=state)
def _run( self, circuit: circuits.Circuit, param_resolver: study.ParamResolver, repetitions: int, ) -> Dict[str, List[np.ndarray]]: """See definition in `cirq.SimulatesSamples`.""" circuit = protocols.resolve_parameters(circuit, param_resolver) _verify_xmon_circuit(circuit) # Delegate to appropriate method based on contents. if circuit.are_all_measurements_terminal(): return self._run_sweep_sample(circuit, repetitions) return self._run_sweep_repeat(circuit, repetitions)
def _run_sweep_sample(self, circuit: circuits.Circuit, repetitions: int) -> Dict[str, List[np.ndarray]]: step_result = None for step_result in self._base_iterator( circuit=circuit, qubit_order=ops.QubitOrder.DEFAULT, initial_state=0, perform_measurements=False): pass if step_result is None: return {} measurement_ops = [ op for _, op, _ in circuit.findall_operations_with_gate_type( ops.MeasurementGate) ] return step_result.sample_measurement_ops(measurement_ops, repetitions)
def test_moment_by_moment_schedule_empty_moment_ignored(): device = _TestDevice() qubits = device.qubits circuit = Circuit( [Moment([ops.H(qubits[0])]), Moment([]), Moment([ops.H(qubits[0])])]) schedule = moment_by_moment_schedule(device, circuit) zero_ns = cirq.Timestamp() twenty_ns = cirq.Timestamp(nanos=20) assert set(schedule.scheduled_operations) == { ScheduledOperation.op_at_on(ops.H(qubits[0]), zero_ns, device), ScheduledOperation.op_at_on(ops.H(qubits[0]), twenty_ns, device), }
def optimize_circuit(self, circuit: circuits.Circuit): state = _OptimizerState() for moment_index, moment in enumerate(circuit): for op in moment.operations: affected = [q for q in op.qubits if state.held_w_phases.get(q) is not None] # Collect, phase, and merge Ws. w = _try_get_known_w(op) if w is not None: if decompositions.is_negligible_turn( cast(float, w.half_turns) - 1, self.tolerance): _potential_cross_whole_w(moment_index, op, self.tolerance, state) else: _potential_cross_partial_w(moment_index, op, state) continue if not affected: continue # Absorb Z rotations. t = _try_get_known_z_half_turns(op) if t is not None: _absorb_z_into_w(moment_index, op, state) continue # Dump coherent flips into measurement bit flips. if ops.MeasurementGate.is_measurement(op): _dump_into_measurement(moment_index, op, state) # Cross CZs using kickback. if _try_get_known_cz_half_turns(op) is not None: if len(affected) == 1: _single_cross_over_cz(moment_index, op, affected[0], state) else: _double_cross_over_cz(op, state) continue # Don't know how to handle this situation. Dump the gates. _dump_held(op.qubits, moment_index, state) # Put anything that's still held at the end of the circuit. _dump_held(state.held_w_phases.keys(), len(circuit), state) circuit.batch_remove(state.deletions) circuit.batch_insert_into(state.inline_intos) circuit.batch_insert(state.insertions)
def optimize_circuit(self, circuit: circuits.Circuit): state = _OptimizerState() for moment_index, moment in enumerate(circuit): for op in moment.operations: affected = [q for q in op.qubits if state.held_w_phases.get(q) is not None] # Collect, phase, and merge Ws. w = _try_get_known_w(op) if w is not None: if decompositions.is_negligible_turn( cast(float, w.exponent) - 1, self.tolerance): _potential_cross_whole_w(moment_index, op, self.tolerance, state) else: _potential_cross_partial_w(moment_index, op, state) continue if not affected: continue # Absorb Z rotations. t = _try_get_known_z_half_turns(op) if t is not None: _absorb_z_into_w(moment_index, op, state) continue # Dump coherent flips into measurement bit flips. if ops.MeasurementGate.is_measurement(op): _dump_into_measurement(moment_index, op, state) # Cross CZs using kickback. if _try_get_known_cz_half_turns(op) is not None: if len(affected) == 1: _single_cross_over_cz(moment_index, op, affected[0], state) else: _double_cross_over_cz(op, state) continue # Don't know how to handle this situation. Dump the gates. _dump_held(op.qubits, moment_index, state) # Put anything that's still held at the end of the circuit. _dump_held(state.held_w_phases.keys(), len(circuit), state) circuit.batch_remove(state.deletions) circuit.batch_insert_into(state.inline_intos) circuit.batch_insert(state.insertions)
def optimize_circuit(self, circuit: circuits.Circuit): # Tracks qubit phases (in half turns; multiply by pi to get radians). qubit_phase = defaultdict(lambda: 0) # type: Dict[ops.QubitId, float] def dump_tracked_phase(qubits: Iterable[ops.QubitId], index: int) -> None: """Zeroes qubit_phase entries by emitting Z gates.""" for q in qubits: p = qubit_phase[q] if not decompositions.is_negligible_turn(p, self.tolerance): dump_op = ops.Z(q)**(p * 2) insertions.append((index, dump_op)) qubit_phase[q] = 0 deletions = [] # type: List[Tuple[int, ops.Operation]] inline_intos = [] # type: List[Tuple[int, ops.Operation]] insertions = [] # type: List[Tuple[int, ops.Operation]] for moment_index, moment in enumerate(circuit): for op in moment.operations: # Move Z gates into tracked qubit phases. h = _try_get_known_z_half_turns(op) if h is not None: q = op.qubits[0] qubit_phase[q] += h / 2 deletions.append((moment_index, op)) continue # Z gate before measurement is a no-op. Drop tracked phase. if ops.MeasurementGate.is_measurement(op): for q in op.qubits: qubit_phase[q] = 0 # If there's no tracked phase, we can move on. phases = [qubit_phase[q] for q in op.qubits] if all(decompositions.is_negligible_turn(p, self.tolerance) for p in phases): continue # Try to move the tracked phasing over the operation. phased_op = op for i, p in enumerate(phases): if not decompositions.is_negligible_turn(p, self.tolerance): phased_op = protocols.phase_by(phased_op, -p, i, default=None) if phased_op is not None: deletions.append((moment_index, op)) inline_intos.append((moment_index, cast(ops.Operation, phased_op))) else: dump_tracked_phase(op.qubits, moment_index) dump_tracked_phase(qubit_phase.keys(), len(circuit)) circuit.batch_remove(deletions) circuit.batch_insert_into(inline_intos) circuit.batch_insert(insertions)
def _core_iterator( self, circuit: circuits.Circuit, sim_state: TActOnArgs, all_measurements_are_terminal: bool = False, ) -> Iterator[TStepResult]: """Standard iterator over StepResult from Moments of a Circuit. Args: circuit: The circuit to simulate. sim_state: The initial args for the simulation. The form of this state depends on the simulation implementation. See documentation of the implementing class for details. Yields: StepResults from simulating a Moment of the Circuit. """ if len(circuit) == 0: yield self._create_step_result(sim_state, sim_state.qubit_map) return noisy_moments = self.noise.noisy_moments(circuit, sorted(circuit.all_qubits())) measured: Dict[Tuple[cirq.Qid, ...], bool] = collections.defaultdict(bool) for moment in noisy_moments: for op in ops.flatten_to_ops(moment): try: # TODO: support more general measurements. # Github issue: https://github.com/quantumlib/Cirq/issues/3566 if all_measurements_are_terminal and measured[op.qubits]: continue if isinstance(op.gate, ops.MeasurementGate): measured[op.qubits] = True if all_measurements_are_terminal: continue if self._ignore_measurement_results: op = ops.phase_damp(1).on(*op.qubits) sim_state.axes = tuple(sim_state.qubit_map[qubit] for qubit in op.qubits) protocols.act_on(op, sim_state) except TypeError: raise TypeError( f"{self.__class__.__name__} doesn't support {op!r}") yield self._create_step_result(sim_state, sim_state.qubit_map) sim_state.log_of_measurement_results.clear()
def _generate_json( self, circuit: circuits.Circuit, param_resolver: study.ParamResolverOrSimilarType, ) -> str: """Generates the JSON string from a Circuit The json format is defined as follows: [[op_string,gate_exponent,qubits]] which is a list of sequential quantum operations, each operation defined by: op_string: str that specifies the operation type: "X","Y","Z","MS" gate_exponent: float that specifies the gate_exponent of the operation qubits: list of qubits where the operation acts on. Args: circuit: Circuit to be run param_resolver: Param resolver for the Returns: json formatted string of the sequence """ seq_list: List[Union[Tuple[str, float, List[int]], Tuple[str, float, float, List[int]]]] = [] circuit = resolve_parameters(circuit, param_resolver) for op in circuit.all_operations(): line_qubit = cast(Tuple[LineQubit], op.qubits) op = cast(ops.GateOperation, op) qubit_idx = [obj.x for obj in line_qubit] op_str = get_op_string(op) gate: Union[ops.EigenGate, ops.PhasedXPowGate] if op_str == 'R': gate = cast(ops.PhasedXPowGate, op.gate) seq_list.append((op_str, float(gate.exponent), float(gate.phase_exponent), qubit_idx)) else: gate = cast(ops.EigenGate, op.gate) seq_list.append((op_str, float(gate.exponent), qubit_idx)) if len(seq_list) == 0: raise RuntimeError('Cannot send an empty circuit') json_str = json.dumps(seq_list) return json_str
def circuit_to_latex_using_qcircuit( circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT) -> str: """Returns a QCircuit-based latex diagram of the given circuit. Args: circuit: The circuit to represent in latex. qubit_order: Determines the order of qubit wires in the diagram. Returns: Latex code for the diagram. """ diagram = circuit.to_text_diagram_drawer( qubit_namer=qcircuit_qubit_namer, qubit_order=qubit_order, get_circuit_diagram_info=get_qcircuit_diagram_info) return _render(diagram)
def replace_acquaintance_with_swap_network( circuit: circuits.Circuit, qubit_order: Sequence[ops.Qid], acquaintance_size: Optional[int] = 0, swap_gate: ops.Gate = ops.SWAP ) -> bool: """ Replace every moment containing acquaintance gates (after rectification) with a generalized swap network, with the partition given by the acquaintance gates in that moment (and singletons for the free qubits). Accounts for reversing effect of swap networks. Args: circuit: The acquaintance strategy. qubit_order: The qubits, in order, on which the replacing swap network gate acts on. acquaintance_size: The acquaintance size of the new swap network gate. swap_gate: The gate used to swap logical indices. Returns: Whether or not the overall effect of the inserted swap network gates is to reverse the order of the qubits, i.e. the parity of the number of swap network gates inserted. Raises: TypeError: circuit is not an acquaintance strategy. """ if not is_acquaintance_strategy(circuit): raise TypeError('not is_acquaintance_strategy(circuit)') rectify_acquaintance_strategy(circuit) reflected = False reverse_map = {q: r for q, r in zip(qubit_order, reversed(qubit_order))} for moment_index, moment in enumerate(circuit): if reflected: moment = moment.transform_qubits(reverse_map.__getitem__) if all(isinstance(op.gate, AcquaintanceOpportunityGate) for op in moment.operations): swap_network_gate = SwapNetworkGate.from_operations( qubit_order, moment.operations, acquaintance_size, swap_gate) swap_network_op = swap_network_gate(*qubit_order) moment = ops.Moment([swap_network_op]) reflected = not reflected circuit._moments[moment_index] = moment return reflected
def _run_sweep_repeat(self, circuit: circuits.Circuit, repetitions: int) -> Dict[str, np.ndarray]: measurements = {} # type: Dict[str, List[np.ndarray]] if repetitions == 0: for _, op, _ in circuit.findall_operations_with_gate_type( ops.MeasurementGate): measurements[protocols.measurement_key(op)] = np.empty([0, 1]) for _ in range(repetitions): all_step_results = self._base_iterator( circuit, qubit_order=ops.QubitOrder.DEFAULT, initial_state=0) for step_result in all_step_results: for k, v in step_result.measurements.items(): if not k in measurements: measurements[k] = [] measurements[k].append(np.array(v, dtype=np.uint8)) return {k: np.array(v) for k, v in measurements.items()}
def create_corrected_circuit(target_unitary: np.ndarray, program: circuits.Circuit, q0: ops.Qid, q1: ops.Qid): # Get the local equivalents b_0, b_1, a_0, a_1 = find_local_equivalents( target_unitary, program.unitary(qubit_order=ops.QubitOrder.explicit([q0, q1]))) # Apply initial corrections yield (gate(q0) for gate in optimizers.single_qubit_matrix_to_gates(b_0)) yield (gate(q1) for gate in optimizers.single_qubit_matrix_to_gates(b_1)) # Apply interaction part yield program # Apply final corrections yield (gate(q0) for gate in optimizers.single_qubit_matrix_to_gates(a_0)) yield (gate(q1) for gate in optimizers.single_qubit_matrix_to_gates(a_1))
def create_corrected_circuit(target_unitary: np.ndarray, program: circuits.Circuit, q0: ops.Qid, q1: ops.Qid) -> ops.OP_TREE: # Get the local equivalents b_0, b_1, a_0, a_1 = find_local_equivalents( target_unitary, program.unitary(qubit_order=ops.QubitOrder.explicit([q0, q1]))) # Apply initial corrections yield from _phased_x_z_ops(b_0, q0) yield from _phased_x_z_ops(b_1, q1) # Apply interaction part yield program # Apply final corrections yield from _phased_x_z_ops(a_0, q0) yield from _phased_x_z_ops(a_1, q1)
def program_as_schedule(self, program: Union[Circuit, Schedule], device: Device = None) -> Schedule: if isinstance(program, Circuit): device = device or UnconstrainedDevice circuit_copy = Circuit(program.moments) ConvertToXmonGates().optimize_circuit(circuit_copy) DropEmptyMoments().optimize_circuit(circuit_copy) device.validate_circuit(circuit_copy) return moment_by_moment_schedule(device, circuit_copy) elif isinstance(program, Schedule): if device: raise ValueError( 'Device can not be provided when running a schedule.') return program else: raise TypeError('Unexpected program type.')
def move_pauli_strings_into_circuit( circuit_left: Union[circuits.Circuit, circuits.CircuitDag], circuit_right: circuits.Circuit) -> circuits.Circuit: if isinstance(circuit_left, circuits.CircuitDag): string_dag = circuits.CircuitDag(pauli_string_reorder_pred, circuit_left) else: string_dag = pauli_string_dag_from_circuit( cast(circuits.Circuit, circuit_left)) output_ops = list(circuit_right.all_operations()) rightmost_nodes = (set(string_dag.nodes()) - set(before for before, _ in string_dag.edges())) while rightmost_nodes: # Sort the pauli string placements based on paulistring length and # furthest possible distance in circuit_right placements = _sorted_best_string_placements(rightmost_nodes, output_ops) last_index = len(output_ops) # Pick the Pauli string that can be moved furthest through # the Clifford circuit for best_string_op, best_index, best_node in placements: assert best_index <= last_index, ( "Unexpected insertion index order," " {} >= {}, len: {}".format(best_index, last_index, len(output_ops))) last_index = best_index output_ops.insert(best_index, best_string_op) # Remove the best one from the dag and update rightmost_nodes rightmost_nodes.remove(best_node) rightmost_nodes.update( pred_node for pred_node in string_dag.predecessors(best_node) if len(string_dag.succ[pred_node]) <= 1) string_dag.remove_node(best_node) assert not string_dag.nodes(), 'There was a cycle in the CircuitDag' return circuits.Circuit(output_ops, strategy=circuits.InsertStrategy.EARLIEST, device=circuit_right.device)
def stratify_circuit(classifiers: Iterable[Classifier], circuit: circuits.Circuit): """Performs the stratification by iterating through the operations in the circuit and using the given classifiers to align them. Args: classifiers: A list of rules to align the circuit. Must be exhaustive, i.e. all operations will be caught by one of the processors. circuit: The circuit to break out into homogeneous moments. Will not be edited. Returns: The stratified circuit. """ solution = circuits.Circuit() circuit_copy = circuit.copy() while len(circuit_copy.all_qubits()) > 0: for classifier in classifiers: current_moment = ops.Moment() blocked_qubits: Set[ops.Qid] = set() for moment_idx, moment in enumerate(circuit_copy.moments): for op in moment.operations: can_insert = classifier(op) if not can_insert: blocked_qubits.update(op.qubits) else: # Ensure that all the qubits for this operation are # still available. if not any(qubit in blocked_qubits for qubit in op.qubits): # Add the operation to the current moment and # remove it from the circuit. current_moment = current_moment.with_operation(op) blocked_qubits.update(op.qubits) circuit_copy.batch_remove([(moment_idx, op)]) # Short-circuit: If all the qubits are blocked, go on to the # next moment. if blocked_qubits.issuperset(circuit_copy.all_qubits()): break if len(current_moment) > 0: solution.append(current_moment) return solution
def test_moment_by_moment_schedule_max_duration(): device = _TestDevice() qubits = device.qubits circuit = Circuit([ Moment([ops.H(qubits[0]), ops.CZ(qubits[1], qubits[2])]), Moment([ops.H(qubits[0])]) ]) schedule = moment_by_moment_schedule(device, circuit) zero_ns = cirq.Timestamp() fourty_ns = cirq.Timestamp(nanos=40) assert set(schedule.scheduled_operations) == { ScheduledOperation.op_at_on(ops.H(qubits[0]), zero_ns, device), ScheduledOperation.op_at_on(ops.CZ(qubits[1], qubits[2]), zero_ns, device), ScheduledOperation.op_at_on(ops.H(qubits[0]), fourty_ns, device), }
def test_moment_by_moment_schedule_two_moments(): device = _TestDevice() qubits = device.qubits circuit = Circuit([ Moment(ops.H(q) for q in qubits), Moment((ops.CZ(qubits[i], qubits[i + 1]) for i in range(0, 9, 3))) ]) schedule = moment_by_moment_schedule(device, circuit) zero_ns = cirq.Timestamp() twenty_ns = cirq.Timestamp(nanos=20) expected_one_qubit = set( ScheduledOperation.op_at_on(ops.H(q), zero_ns, device) for q in qubits) expected_two_qubit = set( ScheduledOperation.op_at_on(ops.CZ(qubits[i], qubits[i + 1]), twenty_ns, device) for i in range(0, 9, 3)) expected = expected_one_qubit.union(expected_two_qubit) assert set(schedule.scheduled_operations) == expected
def _run_sweep_sample( self, circuit: circuits.Circuit, repetitions: int, act_on_args: act_on_density_matrix_args.ActOnDensityMatrixArgs, ) -> Dict[str, np.ndarray]: for step_result in self._core_iterator( circuit=circuit, sim_state=act_on_args, all_measurements_are_terminal=True, ): pass measurement_ops = [ op for _, op, _ in circuit.findall_operations_with_gate_type( ops.MeasurementGate) ] return step_result.sample_measurement_ops(measurement_ops, repetitions, seed=self._prng)
def optimize_circuit(self, circuit: circuits.Circuit): turns_state = defaultdict(lambda: 0) # type: Dict[ops.QubitId, float] def dump_phases(qubits, index): for q in qubits: p = turns_state[q] if not is_negligible_turn(p, self.tolerance): dump_op = ops.Z(q)**(p * 2) insertions.append((index, dump_op)) turns_state[q] = 0 deletions = [] # type: List[Tuple[int, ops.Operation]] inline_intos = [] # type: List[Tuple[int, ops.Operation]] insertions = [] # type: List[Tuple[int, ops.Operation]] for moment_index, moment in enumerate(circuit): for op in moment.operations: h = _try_get_known_z_half_turns(op) if h is not None: q = op.qubits[0] turns_state[q] += h / 2 deletions.append((moment_index, op)) continue if ops.MeasurementGate.is_measurement(op): for q in op.qubits: turns_state[q] = 0 phases = [turns_state[q] for q in op.qubits] if all(is_negligible_turn(p, self.tolerance) for p in phases): continue phased_op = op for i, p in enumerate(phases): if p and phased_op is not None: phased_op = protocols.phase_by(phased_op, -p, i, default=None) if phased_op is not None: deletions.append((moment_index, op)) inline_intos.append( (moment_index, cast(ops.Operation, phased_op))) else: dump_phases(op.qubits, moment_index) dump_phases(turns_state.keys(), len(circuit)) circuit.batch_remove(deletions) circuit.batch_insert_into(inline_intos) circuit.batch_insert(insertions)
def _canonicalize_up_to_terminal_measurement_phase( circuit1: circuits.Circuit, circuit2: circuits.Circuit) -> Tuple[np.ndarray, np.ndarray]: qubits = circuit1.all_qubits().union(circuit2.all_qubits()) order = ops.QubitOrder.DEFAULT.order_for(qubits) assert circuit1.are_all_measurements_terminal() assert circuit2.are_all_measurements_terminal() measured_1 = {q for op in circuit1.all_operations() if ops.MeasurementGate.is_measurement(op) for q in op.qubits} measured_2 = {q for op in circuit2.all_operations() if ops.MeasurementGate.is_measurement(op) for q in op.qubits} assert measured_1 == measured_2 matrix1 = circuit1.to_unitary_matrix(qubits_that_should_be_present=qubits) matrix2 = circuit2.to_unitary_matrix(qubits_that_should_be_present=qubits) for q in measured_1: _cancel_qubit_phase(matrix1, matrix2, order.index(q)) return matrix1, matrix2
def validate_circuit(self, circuit: circuits.Circuit): super().validate_circuit(circuit) _verify_unique_measurement_keys(circuit.all_operations())
def _simulator_iterator( circuit: Circuit, options: 'XmonOptions' = XmonOptions(), qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, initial_state: Union[int, np.ndarray]=0, perform_measurements: bool=True, ) -> Iterator['XmonStepResult']: """Iterator over XmonStepResult from Moments of a Circuit. This should rarely be instantiated directly, instead prefer to create an XmonSimulator and use methods on that object to get an iterator. Args: circuit: The circuit to simulate. Must contain only xmon gates with no unresolved parameters. options: XmonOptions configuring the simulation. qubit_order: Determines the canonical ordering of the qubits used to define the order of amplitudes in the wave function. initial_state: If this is an int, the state is set to the computational basis state corresponding to the integer. Note that the low bit of the integer corresponds to the value of the first qubit as determined by the basis argument. If this is a np.ndarray it is the full initial state. In this case it must be the correct size, be normalized (an L2 norm of 1), and be safely castable to a np.complex64. perform_measurements: Whether or not to perform the measurements in the circuit. Should only be set to False when optimizing for sampling over the measurements. Yields: XmonStepResults from simulating a Moment of the Circuit. Raises: TypeError: if the circuit contains gates that are not XmonGates or composite gates made of XmonGates. """ qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for( circuit.all_qubits()) qubit_map = {q: i for i, q in enumerate(reversed(qubits))} if isinstance(initial_state, np.ndarray): initial_state = initial_state.astype(dtype=np.complex64, casting='safe') with xmon_stepper.Stepper( num_qubits=len(qubits), num_prefix_qubits=options.num_prefix_qubits, initial_state=initial_state, min_qubits_before_shard=options.min_qubits_before_shard, use_processes=options.use_processes ) as stepper: for moment in circuit: measurements = collections.defaultdict( list) # type: Dict[str, List[bool]] phase_map = {} # type: Dict[Tuple[int, ...], float] for op in moment.operations: gate = cast(ops.GateOperation, op).gate if isinstance(gate, xmon_gates.ExpZGate): index = qubit_map[op.qubits[0]] phase_map[(index,)] = cast(float, gate.half_turns) elif isinstance(gate, xmon_gates.Exp11Gate): index0 = qubit_map[op.qubits[0]] index1 = qubit_map[op.qubits[1]] phase_map[(index0, index1)] = cast(float, gate.half_turns) elif isinstance(gate, xmon_gates.ExpWGate): index = qubit_map[op.qubits[0]] stepper.simulate_w( index=index, half_turns=gate.half_turns, axis_half_turns=gate.axis_half_turns) elif isinstance(gate, xmon_gates.XmonMeasurementGate): if perform_measurements: invert_mask = ( gate.invert_mask or len(op.qubits) * (False,)) for qubit, invert in zip(op.qubits, invert_mask): index = qubit_map[qubit] result = stepper.simulate_measurement(index) if invert: result = not result measurements[cast(str, gate.key)].append(result) else: raise TypeError('{!r} is not supported by the ' 'xmon simulator.'.format(gate)) stepper.simulate_phases(phase_map) yield XmonStepResult(stepper, qubit_map, measurements)
def converted_gate_set(circuit: circuits.Circuit, atol: float = 1e-7 ) -> circuits.Circuit: """Returns a new, equivalent circuit using the gate set {CliffordGate, PauliInteractionGate, PauliStringPhasor}. The circuit structure may differ because it is optimized during conversion. """ xmon_circuit = google.optimized_for_xmon(circuit, allow_partial_czs=False) qubits = circuit.all_qubits() tol = linalg.Tolerance(atol=atol) def is_clifford_rotation(half_turns): return tol.all_near_zero_mod(half_turns, 0.5) def to_quarter_turns(half_turns): return round(2 * half_turns) % 4 def is_quarter_turn(half_turns): return (is_clifford_rotation(half_turns) and to_quarter_turns(half_turns) % 2 == 1) def is_half_turn(half_turns): return (is_clifford_rotation(half_turns) and to_quarter_turns(half_turns) == 2) def is_no_turn(half_turns): return (is_clifford_rotation(half_turns) and to_quarter_turns(half_turns) == 0) def rotation_to_clifford_op(pauli, qubit, half_turns): quarter_turns = to_quarter_turns(half_turns) if quarter_turns == 0: return ops.CliffordGate.I(qubit) elif quarter_turns == 2: return ops.CliffordGate.from_pauli(pauli)(qubit) else: gate = ops.CliffordGate.from_pauli(pauli, True)(qubit) if quarter_turns == 3: gate = gate.inverse() return gate def rotation_to_non_clifford_op(pauli, qubit, half_turns): return PauliStringPhasor(ops.PauliString.from_single(qubit, pauli), half_turns=half_turns) def single_qubit_matrix_to_ops(mat, qubit): # Decompose matrix z_rad_before, y_rad, z_rad_after = ( linalg.deconstruct_single_qubit_matrix_into_angles(mat)) z_ht_before = z_rad_before / np.pi - 0.5 m_ht = y_rad / np.pi m_pauli = ops.Pauli.X z_ht_after = z_rad_after / np.pi + 0.5 # Clean up angles if is_clifford_rotation(z_ht_before): if is_quarter_turn(z_ht_before) or is_quarter_turn(z_ht_after): z_ht_before += 0.5 z_ht_after -= 0.5 m_pauli = ops.Pauli.Y if is_half_turn(z_ht_before) or is_half_turn(z_ht_after): z_ht_before -= 1 z_ht_after += 1 m_ht = -m_ht if is_no_turn(m_ht): z_ht_before += z_ht_after z_ht_after = 0 elif is_half_turn(m_ht): z_ht_after -= z_ht_before z_ht_before = 0 # Generate operations rotation_list = [ (ops.Pauli.Z, z_ht_before), (m_pauli, m_ht), (ops.Pauli.Z, z_ht_after)] is_clifford_list = [is_clifford_rotation(ht) for pauli, ht in rotation_list] op_list = [rotation_to_clifford_op(pauli, qubit, ht) if is_clifford else rotation_to_non_clifford_op(pauli, qubit, ht) for is_clifford, (pauli, ht) in zip(is_clifford_list, rotation_list)] # Merge adjacent Clifford gates for i in reversed(range(len(op_list) - 1)): if is_clifford_list[i] and is_clifford_list[i+1]: op_list[i] = op_list[i].gate.merged_with(op_list[i+1].gate )(qubit) is_clifford_list.pop(i+1) op_list.pop(i+1) # Yield non-identity ops for is_clifford, op in zip(is_clifford_list, op_list): if is_clifford and op.gate == ops.CliffordGate.I: continue yield op def generate_ops(): conv_cz = ops.PauliInteractionGate(ops.Pauli.Z, False, ops.Pauli.Z, False) matrices = {qubit: np.eye(2) for qubit in qubits} def clear_matrices(qubits): for qubit in qubits: yield single_qubit_matrix_to_ops(matrices[qubit], qubit) matrices[qubit] = np.eye(2) for op in xmon_circuit.all_operations(): # Assumes all Xmon operations are GateOperation gate = op.gate if isinstance(gate, google.XmonMeasurementGate): yield clear_matrices(op.qubits) yield ops.MeasurementGate(gate.key, gate.invert_mask )(*op.qubits) elif len(op.qubits) == 1: # Collect all one qubit rotations # Assumes all Xmon gates implement KnownMatrix qubit, = op.qubits matrices[qubit] = op.matrix().dot(matrices[qubit]) elif isinstance(gate, google.Exp11Gate): yield clear_matrices(op.qubits) if gate.half_turns != 1: # coverage: ignore raise ValueError('Unexpected partial CZ: {}'.format(op)) yield conv_cz(*op.qubits) else: # coverage: ignore raise TypeError('Unknown Xmon operation: {}'.format(op)) yield clear_matrices(qubits) return circuits.Circuit.from_ops( generate_ops(), strategy=circuits.InsertStrategy.EARLIEST)