def assert_phase_by_is_consistent_with_unitary(val: Any): """Uses `val._unitary_` to check `val._phase_by_`'s behavior.""" original = protocols.unitary(val, None) if original is None: # If there's no unitary, it's vacuously consistent. return qubit_count = len(original).bit_length() - 1 original = original.reshape((2, 2) * qubit_count) for t in [0.125, -0.25, 1]: p = 1j**(t * 4) for i in range(qubit_count): phased = protocols.phase_by(val, t, i, default=None) if phased is None: # If not phaseable, then phase_by is vacuously consistent. continue actual = protocols.unitary(phased).reshape((2, 2) * qubit_count) expected = np.array(original) s = linalg.slice_for_qubits_equal_to([i], 1) expected[s] *= p s = linalg.slice_for_qubits_equal_to([qubit_count + i], 1) expected[s] *= np.conj(p) lin_alg_utils.assert_allclose_up_to_global_phase( actual, expected, atol=1e-8, err_msg='Phased unitary was incorrect for index #{}'.format(i))
def assert_phase_by_is_consistent_with_unitary(val: Any): """Uses `val._unitary_` to check `val._phase_by_`'s behavior.""" original = protocols.unitary(val, None) if original is None: # If there's no unitary, it's vacuously consistent. return qid_shape = protocols.qid_shape(val, default=(2,) * (len(original).bit_length() - 1)) original = original.reshape(qid_shape * 2) for t in [0.125, -0.25, 1, sympy.Symbol('a'), sympy.Symbol('a') + 1]: p = 1j**(t*4) p = protocols.resolve_parameters(p, {'a': -0.125}) for i in range(len(qid_shape)): phased = protocols.phase_by(val, t, i, default=None) if phased is None: # If not phaseable, then phase_by is vacuously consistent. continue phased = protocols.resolve_parameters(phased, {'a': -0.125}) actual = protocols.unitary(phased).reshape(qid_shape * 2) expected = np.array(original) s = linalg.slice_for_qubits_equal_to([i], 1) expected[s] *= p s = linalg.slice_for_qubits_equal_to([len(qid_shape) + i], 1) expected[s] *= np.conj(p) lin_alg_utils.assert_allclose_up_to_global_phase( actual, expected, atol=1e-8, err_msg='Phased unitary was incorrect for index #{}'.format(i))
def assert_phase_by_is_consistent_with_unitary(val: Any): """Uses `val._unitary_` to check `val._phase_by_`'s behavior.""" original = protocols.unitary(val) qubit_count = len(original).bit_length() - 1 original.shape = (2, 2) * qubit_count at_least_one_qubit_is_phaseable = False for t in [0.125, -0.25, 1]: p = 1j**(t * 4) for i in range(qubit_count): phased = protocols.phase_by(val, t, i, default=None) if phased is None: continue at_least_one_qubit_is_phaseable = True actual = protocols.unitary(phased) actual.shape = (2, 2) * qubit_count expected = np.array(original) s = linalg.slice_for_qubits_equal_to([i], 1) expected[s] *= p s = linalg.slice_for_qubits_equal_to([qubit_count + i], 1) expected[s] *= np.conj(p) lin_alg_utils.assert_allclose_up_to_global_phase( actual, expected, atol=1e-8, err_msg='Phased unitary was incorrect for index #{}'.format(i)) assert at_least_one_qubit_is_phaseable, ( '_phase_by_ is consistent with _unitary_, but only because the given ' 'value was not phaseable.')
def _phase_by_(self, phase_turns: float, qubit_index: int) -> 'GateOperation': phased_gate = protocols.phase_by(self._gate, phase_turns, qubit_index, default=None) if phased_gate is None: return NotImplemented return GateOperation(phased_gate, self._qubits)
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 _phase_by_(self, phase_turns: float, qubit_index: int): if qubit_index == 0: return self phased_gate = protocols.phase_by(self.sub_gate, phase_turns, qubit_index - 1, None) if phased_gate is None: return NotImplemented return ControlledGate(phased_gate)
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 map_func(op: 'cirq.Operation', moment_index: int) -> 'cirq.OP_TREE': last_phased_xz_op.update({q: None for q in op.qubits}) if tags_to_ignore & set(op.tags): # Op marked with no-compile, dump phases and do not cross. return [dump_tracked_phase(op.qubits), op] gate = op.gate # Return if circuit operation. if gate is None: return [dump_tracked_phase(op.qubits), op] # Swap phases if `op` is a swap operation. if _is_swaplike(gate): a, b = op.qubits qubit_phase[a], qubit_phase[b] = qubit_phase[b], qubit_phase[a] return op # Z gate before measurement is a no-op. Drop tracked phase. if isinstance(gate, ops.MeasurementGate): for q in op.qubits: qubit_phase[q] = 0 return op # Move Z gates into tracked qubit phases. if isinstance(gate, ops.ZPowGate) and ( eject_parameterized or not protocols.is_parameterized(gate)): qubit_phase[op.qubits[0]] += gate.exponent / 2 return [] # Try to move the tracked phases over the operation via protocols.phase_by(op) phased_op = op for i, p in enumerate([qubit_phase[q] for q in op.qubits]): if not single_qubit_decompositions.is_negligible_turn(p, atol): phased_op = protocols.phase_by(phased_op, -p, i, default=None) if phased_op is None: return [dump_tracked_phase(op.qubits), op] gate = phased_op.gate if isinstance(gate, ops.PhasedXZGate) and ( eject_parameterized or not protocols.is_parameterized(gate.z_exponent)): qubit = phased_op.qubits[0] qubit_phase[qubit] += gate.z_exponent / 2 gate = gate.with_z_exponent(0) phased_op = gate.on(qubit) phased_xz_replacements[moment_index, phased_op] = gate last_phased_xz_op[qubit] = (moment_index, phased_op) return phased_op
def _phase_by_(self, phase_turns: float, qubit_index: int) -> 'cirq.Operation': return protocols.phase_by(self.sub_operation, phase_turns, qubit_index)
def optimize_circuit(self, circuit: circuits.Circuit): # Tracks qubit phases (in half turns; multiply by pi to get radians). qubit_phase: Dict[ops.Qid, float] = defaultdict(lambda: 0) deletions: List[Tuple[int, ops.Operation]] = [] replacements: List[Tuple[int, ops.Operation, ops.Operation]] = [] insertions: List[Tuple[int, ops.Operation]] = [] phased_xz_replacements: Dict[Tuple[int, ops.Qid], int] = {} def dump_tracked_phase(qubits: Iterable[ops.Qid], index: int) -> None: """Zeroes qubit_phase entries by emitting Z gates.""" for q in qubits: p = qubit_phase[q] qubit_phase[q] = 0 if decompositions.is_negligible_turn(p, self.tolerance): continue dumped = False moment_index = circuit.prev_moment_operating_on([q], index) if moment_index is not None: op = circuit.moments[moment_index][q] if op and isinstance(op.gate, ops.PhasedXZGate): # Attach z-rotation to replacing PhasedXZ gate. idx = phased_xz_replacements[moment_index, q] _, _, repl_op = replacements[idx] gate = cast(ops.PhasedXZGate, repl_op.gate) repl_op = gate.with_z_exponent(p * 2).on(q) replacements[idx] = (moment_index, op, repl_op) dumped = True if not dumped: # Add a new Z gate dump_op = ops.Z(q)**(p * 2) insertions.append((index, dump_op)) 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, self.eject_parameterized) 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 isinstance(op.gate, ops.MeasurementGate): 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 not isinstance(op.gate, ops.PhasedXZGate) and all( decompositions.is_negligible_turn(p, self.tolerance) for p in phases): continue if _is_swaplike(op): a, b = op.qubits qubit_phase[a], qubit_phase[b] = qubit_phase[ b], qubit_phase[a] 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: gate = phased_op.gate if isinstance(gate, ops.PhasedXZGate) and ( self.eject_parameterized or not protocols.is_parameterized(gate.z_exponent)): qubit = phased_op.qubits[0] qubit_phase[qubit] += gate.z_exponent / 2 phased_op = gate.with_z_exponent(0).on(qubit) repl_idx = len(replacements) phased_xz_replacements[moment_index, qubit] = repl_idx replacements.append((moment_index, op, phased_op)) else: dump_tracked_phase(op.qubits, moment_index) dump_tracked_phase(qubit_phase.keys(), len(circuit)) circuit.batch_remove(deletions) circuit.batch_replace(replacements) circuit.batch_insert(insertions)