def verify_ops(*op_tree, max_test_value=2**100, trinary_input=False, depth=1): if trinary_input: raise NotImplementedError # TODO qubits = set() op_list = tuple(ops.flatten_op_tree(op_tree)) for op in op_list: qubits.update(op.qubits) qubits = sorted(qubits) num_qubits = len(qubits) sub_ops = tuple(ops.flatten_op_tree(decompose_depth(op_list, d=depth))) for i in range(min(2**num_qubits, max_test_value)): bits = tuple(map(int, reversed('{:0{}b}'.format(i, num_qubits)))) state = trit_list_to_state(qubits, bits) state2 = state.copy() # Apply gate directly for op in op_list: op.apply_to_ternary_state(state) # Apply gate decomposition for sub_op in sub_ops: sub_op.apply_to_ternary_state(state2) # Verify that the two agree if state != state2: input_state_str = ''.join(map(str, bits)) output_state_str = ''.join(map(str, (state[q] for q in qubits))) output_state2_str = ''.join(map(str, (state2[q] for q in qubits))) raise RuntimeError( 'Gate {!r} decomposition is invalid for input {}: {} != {}'.format( op_list, input_state_str, output_state_str, output_state2_str))
def test_inverse_of_invertible_op_tree(): def rev_freeze(root): return ops.freeze_op_tree(ops.inverse_of_invertible_op_tree(root)) operations = [ ops.Operation(_FlipGate(i), [ops.NamedQubit(str(i))]) for i in range(10) ] expected = [ ops.Operation(_FlipGate(~i), [ops.NamedQubit(str(i))]) for i in range(10) ] # Just an item. assert rev_freeze(operations[0]) == expected[0] # Flat list. assert rev_freeze(operations) == tuple(expected[::-1]) # Tree. assert (rev_freeze( (operations[1:5], operations[0], operations[5:])) == (tuple(expected[5:][::-1]), expected[0], tuple(expected[1:5][::-1]))) # Flattening after reversing is equivalent to reversing then flattening. t = (operations[1:5], operations[0], operations[5:]) assert (tuple(ops.flatten_op_tree(rev_freeze(t))) == tuple( rev_freeze(ops.flatten_op_tree(t))))
def assert_equivalent_op_tree(x: ops.OP_TREE, y: ops.OP_TREE): """Ensures that the two OP_TREEs are equivalent. Args: x: OP_TREE one y: OP_TREE two Returns: None Raises: AssertionError if x != y """ a = list(ops.flatten_op_tree(x)) b = list(ops.flatten_op_tree(y)) assert a == b
def _kak_decomposition_to_operations( q0: 'cirq.Qid', q1: 'cirq.Qid', kak: linalg.KakDecomposition, allow_partial_czs: bool, atol: float = 1e-8, ) -> List[ops.Operation]: """Assumes that the decomposition is canonical.""" b0, b1 = kak.single_qubit_operations_before pre = [_do_single_on(b0, q0, atol=atol), _do_single_on(b1, q1, atol=atol)] a0, a1 = kak.single_qubit_operations_after post = [_do_single_on(a0, q0, atol=atol), _do_single_on(a1, q1, atol=atol)] return list( cast( Iterable[ops.Operation], ops.flatten_op_tree([ pre, _non_local_part(q0, q1, kak.interaction_coefficients, allow_partial_czs, atol=atol), post, ]), ))
def __init__( self, clear_span: int, clear_qubits: Iterable['cirq.Qid'], new_operations: 'cirq.OP_TREE', preserve_moments: bool = False, ) -> None: """Inits PointOptimizationSummary. Args: clear_span: Defines the range of moments to affect. Specifically, refers to the indices in range(start, start+clear_span) where start is an index known from surrounding context. clear_qubits: Defines the set of qubits that should be cleared with each affected moment. new_operations: The operations to replace the cleared out operations with. preserve_moments: If set, `cirq.Moment` instances within `new_operations` will be preserved exactly. Normally the operations would be repacked to fit better into the target space, which may move them between moments. Please be advised that a PointOptimizer consuming this summary will flatten operations no matter what, see https://github.com/quantumlib/Cirq/issues/2406. """ self.new_operations = tuple( ops.flatten_op_tree(new_operations, preserve_moments=preserve_moments)) self.clear_span = clear_span self.clear_qubits = tuple(clear_qubits)
def controlled_op_to_operations(control: ops.QubitId, target: ops.QubitId, operation: np.ndarray, tolerance: float = 0.0) -> List[ops.Operation]: """Decomposes a controlled single-qubit operation into Z/XY/CZ gates. Args: control: The control qubit. target: The qubit to apply an operation to, when the control is on. operation: The single-qubit operation being controlled. tolerance: A limit on the amount of error introduced by the construction. Returns: A list of Operations that apply the controlled operation. """ u, z_phase, global_phase = single_qubit_op_to_framed_phase_form(operation) if abs(z_phase - 1) <= tolerance: return [] u_gates = single_qubit_matrix_to_gates(u, tolerance) if u_gates and isinstance(u_gates[-1], ops.ZPowGate): # Don't keep border operations that commute with CZ. del u_gates[-1] ops_before = [gate(target) for gate in u_gates] ops_after = protocols.inverse(ops_before) effect = ops.CZ(control, target)**(cmath.phase(z_phase) / math.pi) kickback = ops.Z(control)**(cmath.phase(global_phase) / math.pi) return list( ops.flatten_op_tree( (ops_before, effect, kickback if abs(global_phase - 1) > tolerance else [], ops_after)))
def _flatten_to_known_matrix_ops( iter_ops: Iterable[ops.Operation], ext: Extensions) -> Generator[ops.Operation, None, None]: for op in iter_ops: # Check if the operation has a known matrix known_matrix_gate = ext.try_cast(op.gate, ops.KnownMatrixGate) if known_matrix_gate is not None: yield op continue # If not, check if it has a decomposition composite_gate = ext.try_cast(op.gate, ops.CompositeGate) if composite_gate is not None: # Recurse decomposition to get known matrix gates. op_tree = composite_gate.default_decompose(op.qubits) op_list = ops.flatten_op_tree(op_tree) for op in _flatten_to_known_matrix_ops(op_list, ext): yield op continue # Pass measurement gates through meas_gate = ext.try_cast(op.gate, ops.MeasurementGate) if meas_gate is not None: yield op continue # Otherwise, fail raise TypeError( 'Operation without a known matrix or decomposition: {!r}'.format( op))
def _compute_samples_display_value(display: ops.SamplesDisplay, state: np.ndarray, qubit_order: ops.QubitOrder, qubit_map: Dict[ops.Qid, int]): n = len(qubit_map) state = np.reshape(state, (2,) * n * 2) basis_change = ops.flatten_op_tree(display.measurement_basis_change()) for op in basis_change: # TODO: Use apply_channel similar to apply_unitary. indices = [qubit_map[qubit] for qubit in op.qubits] gate = cast(ops.GateOperation, op).gate unitary = protocols.unitary(gate) krauss_tensor = np.reshape(unitary, (2,) * gate.num_qubits() * 2) state = linalg.targeted_left_multiply(krauss_tensor, state, indices) # TODO add a test that fails if the below is not performed state = linalg.targeted_left_multiply( np.conjugate(krauss_tensor), state, [x + n for x in indices]) state = state.reshape((2**n, 2**n)) indices = [qubit_map[qubit] for qubit in display.qubits] samples = density_matrix_utils.sample_density_matrix( state, indices, display.num_samples) return display.value_derived_from_samples(samples)
def insert( self, index: int, operation_tree: ops.OP_TREE, strategy: InsertStrategy = InsertStrategy.NEW_THEN_INLINE) -> int: """Inserts operations into the middle of the circuit. Args: index: The index to insert all of the operations at. operation_tree: An operation or tree of operations. strategy: How to pick/create the moment to put operations into. Returns: The insertion index that will place operations just after the operations that were inserted by this method. Raises: IndexError: Bad insertion index. ValueError: Bad insertion strategy. """ if not 0 <= index <= len(self.moments): raise IndexError('Insert index out of range: {}'.format(index)) k = index for op in ops.flatten_op_tree(operation_tree): p = self._pick_or_create_inserted_op_moment_index(k, op, strategy) while p >= len(self.moments): self.moments.append(Moment()) self.moments[p] = self.moments[p].with_operation(op) k = max(k, p + 1) if strategy is InsertStrategy.NEW_THEN_INLINE: strategy = InsertStrategy.INLINE return k
def get_logical_operations( operations: 'cirq.OP_TREE', initial_mapping: Dict[ops.Qid, ops.Qid]) -> Iterable['cirq.Operation']: """Gets the logical operations specified by the physical operations and initial mapping. Args: operations: The physical operations. initial_mapping: The initial mapping of physical to logical qubits. Raises: ValueError: A non-permutation physical operation acts on an unmapped qubit. """ mapping = initial_mapping.copy() for op in cast(Iterable['cirq.Operation'], ops.flatten_op_tree(operations)): if isinstance(op, ops.GateOperation) and isinstance( op.gate, PermutationGate): op.gate.update_mapping(mapping, op.qubits) else: for q in op.qubits: if mapping.get(q) is None: raise ValueError( f'Operation {op} acts on unmapped qubit {q}.') yield op.transform_qubits(mapping.__getitem__)
def from_ops( *operations: 'cirq.OP_TREE', can_reorder: Callable[['cirq.Operation', 'cirq.Operation'], bool] = _disjoint_qubits, ) -> 'CircuitDag': dag = CircuitDag(can_reorder=can_reorder) for op in ops.flatten_op_tree(operations): dag.append(cast(ops.Operation, op)) return dag
def convert(self, op: ops.Operation) -> ops.OP_TREE: converted = self._convert_one(op) if converted is op: return converted return [ self.convert(cast(ops.Operation, e)) for e in ops.flatten_op_tree(converted) ]
def _verify_unique_measurement_keys(circuit: circuits.Circuit): result = collections.Counter( key for op in ops.flatten_op_tree(iter(circuit)) for key in protocols.measurement_keys(op) ) if result: duplicates = [k for k, v in result.most_common() if v > 1] if duplicates: raise ValueError('Measurement key {} repeated'.format(",".join(duplicates)))
def _evaluate_ternary_circuit(op_tree, state): for op in ops.flatten_op_tree(op_tree): if hasattr(op, 'apply_to_ternary_state'): op.apply_to_ternary_state(state) else: _evaluate_ternary_circuit( op.default_decompose(), state)
def _write_operations(self, op_tree: ops.OP_TREE, output: Callable[[str], None], output_line_gap: Callable[[int], None], top=True) -> None: for op in ops.flatten_op_tree(op_tree): qasm_op = extension.try_cast( # type: ignore ops.QasmConvertibleOperation, op) if qasm_op is not None: out = qasm_op.known_qasm_output(self.args) if out is not None: output(out) continue if isinstance(op, ops.GateOperation): comment = 'Gate: {!s}'.format(op.gate) else: comment = 'Operation: {!s}'.format(op) comp_op = extension.try_cast( # type: ignore ops.CompositeOperation, op) if comp_op is not None: if top: output_line_gap(1) output('// {}\n'.format(comment)) self._write_operations(comp_op.default_decompose(), output, output_line_gap, top=False) if top: output_line_gap(1) continue mat = protocols.unitary(op, None) if len(op.qubits) <= 2 else None if mat is not None and len(op.qubits) == 1: u_op = QasmUGate.from_matrix(mat).on(*op.qubits) if top: output_line_gap(1) output('// {}\n'.format(comment)) output(cast(str, u_op.known_qasm_output(self.args))) if top: output_line_gap(1) continue if mat is not None and len(op.qubits) == 2: u_op = QasmTwoQubitGate.from_matrix(mat).on(*op.qubits) if top: output_line_gap(1) output('// {}\n'.format(comment)) self._write_operations((u_op, ), output, output_line_gap, top=False) if top: output_line_gap(1) continue raise ValueError( 'Cannot output operation as QASM: {!r}'.format(op))
def from_ops(*operations: ops.OP_TREE, can_reorder: Callable[[ops.Operation, ops.Operation], bool] = _disjoint_qubits, device: devices.Device = devices.UnconstrainedDevice ) -> 'CircuitDag': dag = CircuitDag(can_reorder=can_reorder, device=device) for op in ops.flatten_op_tree(operations): dag.append(op) return dag
def from_ops( *operations: 'cirq.OP_TREE', can_reorder: Callable[['cirq.Operation', 'cirq.Operation'], bool] = _disjoint_qubits, device: devices.Device = devices.UNCONSTRAINED_DEVICE, ) -> 'CircuitDag': dag = CircuitDag(can_reorder=can_reorder, device=device) for op in ops.flatten_op_tree(operations): dag.append(cast(ops.Operation, op)) return dag
def _verify_unique_measurement_keys(circuit: 'cirq.AbstractCircuit'): result = collections.Counter( key for op in ops.flatten_op_tree(iter(circuit)) for key in protocols.measurement_key_names(op)) if result: duplicates = [k for k, v in result.most_common() if v > 1] if duplicates: raise ValueError( f"Measurement key {','.join(duplicates)} repeated")
def _write_operations(self, op_tree: ops.OP_TREE, output: Callable[[str], None], output_line_gap: Callable[[int], None], top=True) -> None: for op in ops.flatten_op_tree(op_tree): qasm_op = self.ext.try_cast(ops.QasmConvertableOperation, op) if qasm_op is not None: out = qasm_op.known_qasm_output(self.args) if out is not None: output(out) continue if isinstance(op, ops.GateOperation): comment = 'Gate: {!s}'.format(op.gate) else: comment = 'Operation: {!s}'.format(op) comp_op = self.ext.try_cast(ops.CompositeOperation, op) if comp_op is not None: if top: output_line_gap(1) output('// {}\n'.format(comment)) self._write_operations(comp_op.default_decompose(), output, output_line_gap, top=False) if top: output_line_gap(1) continue matrix_op = self.ext.try_cast(ops.KnownMatrix, op) if matrix_op is not None and len(op.qubits) == 1: u_op = QasmUGate.from_matrix(matrix_op.matrix())(*op.qubits) if top: output_line_gap(1) output('// {}\n'.format(comment)) output(u_op.known_qasm_output(self.args)) if top: output_line_gap(1) continue if matrix_op is not None and len(op.qubits) == 2: u_op = QasmTwoQubitGate.from_matrix(matrix_op.matrix() )(*op.qubits) if top: output_line_gap(1) output('// {}\n'.format(comment)) self._write_operations((u_op,), output, output_line_gap, top=False) if top: output_line_gap(1) continue raise ValueError('Cannot output operation as QASM: {!r}'.format(op))
def get_logical_operations( operations: ops.OP_TREE, initial_mapping: Dict[ops.Qid, ops.Qid]) -> Iterable[ops.Operation]: mapping = initial_mapping.copy() for op in cast(Iterable[ops.Operation], ops.flatten_op_tree(operations)): if (isinstance(op, ops.GateOperation) and isinstance(op.gate, PermutationGate)): op.gate.update_mapping(mapping, op.qubits) else: yield op.transform_qubits(mapping.__getitem__)
def _decompose(self, op: ops.Operation) -> ops.OP_TREE: """Recursively decompose composite gates into an OP_TREE of gates.""" skip = self.no_decomp(op) if skip and (skip is not NotImplemented): return op composite_op = self.extension.try_cast(ops.CompositeOperation, op) if composite_op is None: return op op_iter = ops.flatten_op_tree(composite_op.default_decompose()) return (self._decompose(op) for op in op_iter)
def decompose_depth(*operations, d=1): if d <= 0: yield from operations return for op in ops.flatten_op_tree(operations): composite_op = extension.try_cast(ops.CompositeOperation, op) if composite_op is not None: yield from decompose_depth(composite_op.default_decompose(), d=d-1) else: yield op
def _write_operations(self, op_tree: ops.OP_TREE, output: Callable[[str], None], output_line_gap: Callable[[int], None], top=True) -> None: for op in ops.flatten_op_tree(op_tree): out = protocols.qasm(op, args=self.args, default=None) if out is not None: output(out) continue if isinstance(op, ops.GateOperation): comment = 'Gate: {!s}'.format(op.gate) else: comment = 'Operation: {!s}'.format(op) decomp = protocols.decompose_once(op, None) if decomp is not None: if top: output_line_gap(1) output('// {}\n'.format(comment)) self._write_operations(decomp, output, output_line_gap, top=False) if top: output_line_gap(1) continue mat = protocols.unitary(op, None) if len(op.qubits) <= 2 else None if mat is not None and len(op.qubits) == 1: u_op = QasmUGate.from_matrix(mat).on(*op.qubits) if top: output_line_gap(1) output('// {}\n'.format(comment)) output(cast(str, protocols.qasm(u_op, args=self.args))) if top: output_line_gap(1) continue if mat is not None and len(op.qubits) == 2: u_op = QasmTwoQubitGate.from_matrix(mat).on(*op.qubits) if top: output_line_gap(1) output('// {}\n'.format(comment)) self._write_operations((u_op,), output, output_line_gap, top=False) if top: output_line_gap(1) continue raise ValueError('Cannot output operation as QASM: {!r}'.format(op))
def update_mapping(mapping: Dict[ops.Qid, LogicalIndex], operations: 'cirq.OP_TREE') -> None: """Updates a mapping (in place) from qubits to logical indices according to a set of permutation gates. Any gates other than permutation gates are ignored. Args: mapping: The mapping to update. operations: The operations to update according to. """ for op in ops.flatten_op_tree(operations): if isinstance(op, ops.GateOperation) and isinstance(op.gate, PermutationGate): op.gate.update_mapping(mapping, op.qubits)
def verify_decomposition_inverse(*op_tree, max_test_value=2**100, depth=1): qubits = set() op_list = tuple(ops.flatten_op_tree(op_tree)) for op in op_list: qubits.update(op.qubits) qubits = sorted(qubits) num_qubits = len(qubits) sub_ops = tuple(ops.flatten_op_tree(decompose_depth(op_list, d=depth))) sub_ops_inv = tuple(ops.flatten_op_tree(decompose_depth(ops.inverse(op_list), d=depth))) for trits in itertools.product((0,1,2), repeat=num_qubits): state = trit_list_to_state(qubits, trits) state_orig = state.copy() for op in sub_ops: op.apply_to_ternary_state(state) for op in sub_ops_inv: op.apply_to_ternary_state(state) if state != state_orig: input_state_str = ''.join(map(str, trits)) output_state_str = ''.join(map(str, (state[q] for q in qubits))) raise RuntimeError( 'Gate {!r} inverse is invalid for input {} != {}'.format( op_list, input_state_str, output_state_str))
def from_ops( *operations: 'cirq.OP_TREE', can_reorder: Callable[['cirq.Operation', 'cirq.Operation'], bool] = _disjoint_qubits, device: 'cirq.Device' = devices.UNCONSTRAINED_DEVICE, ) -> 'CircuitDag': if device == devices.UNCONSTRAINED_DEVICE: dag = CircuitDag(can_reorder=can_reorder) else: with _compat.block_overlapping_deprecation(re.escape(circuit._DEVICE_DEP_MESSAGE)): dag = CircuitDag(can_reorder=can_reorder, device=device) for op in ops.flatten_op_tree(operations): dag.append(cast(ops.Operation, op)) return dag
def _write_operations( self, op_tree: 'cirq.OP_TREE', output: Callable[[str], None], output_line_gap: Callable[[int], None], ) -> None: def keep(op: 'cirq.Operation') -> bool: return protocols.qasm(op, args=self.args, default=None) is not None def fallback(op): if len(op.qubits) not in [1, 2]: return NotImplemented mat = protocols.unitary(op, None) if mat is None: return NotImplemented if len(op.qubits) == 1: return QasmUGate.from_matrix(mat).on(*op.qubits) return QasmTwoQubitGate.from_matrix(mat).on(*op.qubits) def on_stuck(bad_op): return ValueError( 'Cannot output operation as QASM: {!r}'.format(bad_op)) for main_op in ops.flatten_op_tree(op_tree): decomposed = protocols.decompose(main_op, keep=keep, fallback_decomposer=fallback, on_stuck_raise=on_stuck) qasms = [protocols.qasm(op, args=self.args) for op in decomposed] should_annotate = decomposed != [main_op ] or qasms[0].count('\n') > 1 if should_annotate: output_line_gap(1) if isinstance(main_op, ops.GateOperation): x = str(main_op.gate).replace('\n', '\n //') output('// Gate: {!s}\n'.format(x)) else: x = str(main_op).replace('\n', '\n //') output('// Operation: {!s}\n'.format(x)) for qasm in qasms: output(qasm) if should_annotate: output_line_gap(1)
def __init__(self, clear_span: int, clear_qubits: Iterable[ops.QubitId], new_operations: ops.OP_TREE) -> None: """ Args: clear_span: Defines the range of moments to affect. Specifically, refers to the indices in range(start, start+clear_span) where start is an index known from surrounding context. clear_qubits: Defines the set of qubits that should be cleared with each affected moment. new_operations: The operations to replace the cleared out operations with. """ self.new_operations = tuple(ops.flatten_op_tree(new_operations)) self.clear_span = clear_span self.clear_qubits = tuple(clear_qubits)
def is_topologically_sorted( dag: 'cirq.CircuitDag', operations: 'cirq.OP_TREE', equals: Callable[[ops.Operation, ops.Operation], bool] = operator.eq, ) -> bool: """Whether a given order of operations is consistent with the DAG. For example, suppose the (transitive reduction of the) circuit DAG is ╭─> Op2 ─╮ Op1 ─┤ ├─> Op4 ╰─> Op3 ─╯ Then [Op1, Op2, Op3, Op4] and [Op1, Op3, Op2, Op4] (and any operations tree that flattens to one of them) are topologically sorted according to the DAG, and any other ordering of the four operations is not. Evaluates to False when the set of operations is different from those in the nodes of the DAG, regardless of the ordering. Args: dag: The circuit DAG. operations: The ordered operations. equals: The function to determine equality of operations. Defaults to `operator.eq`. Returns: Whether or not the operations given are topologically sorted according to the DAG. """ remaining_dag = dag.copy() frontier = [ node for node in remaining_dag.nodes() if not remaining_dag.pred[node] ] for operation in cast(Iterable[ops.Operation], ops.flatten_op_tree(operations)): for i, node in enumerate(frontier): if equals(node.val, operation): frontier.pop(i) succ = remaining_dag.succ[node] remaining_dag.remove_node(node) frontier.extend(new_node for new_node in succ if not remaining_dag.pred[new_node]) break else: return False return not bool(frontier)
def operations_to_part_lens( qubit_order: Sequence['cirq.Qid'], op_tree: 'cirq.OP_TREE', ) -> Tuple[int, ...]: qubit_sort_key = functools.partial(operator.indexOf, qubit_order) op_parts = [tuple(sorted(op.qubits, key=qubit_sort_key)) for op in ops.flatten_op_tree(op_tree)] singletons = [ (q,) for q in set(qubit_order).difference(*op_parts) ] # type: List[Tuple['cirq.Qid', ...]] part_sort_key = lambda p: min(qubit_sort_key(q) for q in p) parts = tuple(tuple(part) for part in sorted(singletons + op_parts, key=part_sort_key)) if sum(parts, ()) != tuple(qubit_order): raise ValueError('sum(parts, ()) != tuple(qubit_order)') return tuple(len(part) for part in parts)
def decompose_once(val: Any, default=RaiseTypeErrorIfNotProvided, *args, **kwargs): """Decomposes a value into operations, if possible. This method decomposes the value exactly once, instead of decomposing it and then continuing to decomposing the decomposed operations recursively until some criteria is met (which is what `cirq.decompose` does). Args: val: The value to call `_decompose_` on, if possible. default: A default result to use if the value doesn't have a `_decompose_` method or that method returns `NotImplemented` or `None`. If not specified, non-decomposable values cause a `TypeError`. args: Positional arguments to forward into the `_decompose_` method of `val`. For example, this is used to tell gates what qubits they are being applied to. kwargs: Keyword arguments to forward into the `_decompose_` method of `val`. Returns: The result of `val._decompose_(*args, **kwargs)`, if `val` has a `_decompose_` method and it didn't return `NotImplemented` or `None`. Otherwise `default` is returned, if it was specified. Otherwise an error is raised. Raises: TypeError: `val` didn't have a `_decompose_` method (or that method returned `NotImplemented` or `None`) and `default` wasn't set. """ method = getattr(val, '_decompose_', None) decomposed = NotImplemented if method is None else method(*args, **kwargs) if decomposed is not NotImplemented and decomposed is not None: return list(ops.flatten_op_tree(decomposed)) if default is not RaiseTypeErrorIfNotProvided: return default if method is None: raise TypeError( f"object of type '{type(val)}' has no _decompose_ method.") raise TypeError("object of type '{}' does have a _decompose_ method, " "but it returned NotImplemented or None.".format( type(val)))
def _kak_decomposition_to_operations(q0: ops.QubitId, q1: ops.QubitId, a0: np.ndarray, a1: np.ndarray, x: float, y: float, z: float, b0: np.ndarray, b1: np.ndarray, allow_partial_czs: bool, tolerance: float = 1e-8 ) -> List[ops.Operation]: """Assumes that the decomposition is canonical.""" pre = [_do_single_on(b0, q0, tolerance), _do_single_on(b1, q1, tolerance)] post = [_do_single_on(a0, q0, tolerance), _do_single_on(a1, q1, tolerance)] return list(ops.flatten_op_tree([ pre, _non_local_part(q0, q1, x, y, z, allow_partial_czs, tolerance), post, ]))
def controlled_op_to_operations( control: ops.QubitId, target: ops.QubitId, operation: np.ndarray, tolerance: float = 0.0) -> List[ops.Operation]: """Decomposes a controlled single-qubit operation into Z/XY/CZ gates. Args: control: The control qubit. target: The qubit to apply an operation to, when the control is on. operation: The single-qubit operation being controlled. tolerance: A limit on the amount of error introduced by the construction. Returns: A list of Operations that apply the controlled operation. """ u, z_phase, global_phase = single_qubit_op_to_framed_phase_form(operation) if abs(z_phase - 1) <= tolerance: return [] u_gates = single_qubit_matrix_to_gates(u, tolerance) if u_gates and isinstance(u_gates[-1], ops.RotZGate): # Don't keep border operations that commute with CZ. del u_gates[-1] ops_before = [gate(target) for gate in u_gates] ops_after = ops.inverse(ops_before) effect = ops.CZ(control, target) ** (cmath.phase(z_phase) / math.pi) kickback = ops.Z(control) ** (cmath.phase(global_phase) / math.pi) return list(ops.flatten_op_tree(( ops_before, effect, kickback if abs(global_phase - 1) > tolerance else [], ops_after)))
def __init__(self, operations: ops.OP_TREE, qubits: Tuple[ops.QubitId, ...], header: str = '', precision: int = 10, version: str = '2.0', ext: extension.Extensions = None) -> None: self.operations = tuple(ops.flatten_op_tree(operations)) self.qubits = qubits self.header = header if ext is None: ext = extension.Extensions() self.ext = ext self.measurements = tuple(cast(ops.GateOperation, op) for op in self.operations if ops.MeasurementGate.is_measurement(op)) meas_key_id_map, meas_comments = self._generate_measurement_ids() self.meas_comments = meas_comments qubit_id_map = self._generate_qubit_ids() self.args = ops.QasmOutputArgs(precision=precision, version=version, qubit_id_map=qubit_id_map, meas_key_id_map=meas_key_id_map)
def convert(self, op: ops.Operation) -> ops.OP_TREE: converted = self._convert_one(op) if converted is op: return converted return [self.convert(e) for e in ops.flatten_op_tree(converted)]