def test_inverse(): with pytest.raises(TypeError): _ = cirq.inverse(cirq.measure(cirq.NamedQubit('q'))) def rev_freeze(root): return cirq.freeze_op_tree(cirq.inverse(root)) operations = [ cirq.GateOperation(_FlipGate(i), [cirq.NamedQubit(str(i))]) for i in range(10) ] expected = [ cirq.GateOperation(_FlipGate(~i), [cirq.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(cirq.flatten_op_tree(rev_freeze(t))) == tuple( rev_freeze(cirq.flatten_op_tree(t))))
def test_inverse(): with pytest.raises(TypeError): _ = cirq.inverse( cirq.measure(cirq.NamedQubit('q'))) def rev_freeze(root): return cirq.freeze_op_tree(cirq.inverse(root)) operations = [ cirq.GateOperation(_FlipGate(i), [cirq.NamedQubit(str(i))]) for i in range(10) ] expected = [ cirq.GateOperation(_FlipGate(~i), [cirq.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(cirq.flatten_op_tree(rev_freeze(t))) == tuple(rev_freeze(cirq.flatten_op_tree(t))))
def test_flatten_op_tree(): operations = [ cirq.GateOperation(cirq.SingleQubitGate(), [cirq.NamedQubit(str(i))]) for i in range(10) ] # Empty tree. assert list(cirq.flatten_op_tree([[[]]])) == [] # Just an item. assert list(cirq.flatten_op_tree(operations[0])) == operations[:1] # Flat list. assert list(cirq.flatten_op_tree(operations)) == operations # Tree. assert list( cirq.flatten_op_tree( (operations[0], operations[1:5], operations[5:]))) == operations # Flatten moment. assert list( cirq.flatten_op_tree((operations[0], cirq.Moment(operations[1:5]), operations[5:]))) == operations # Bad trees. with pytest.raises(TypeError): _ = list(cirq.flatten_op_tree(None)) with pytest.raises(TypeError): _ = list(cirq.flatten_op_tree(5)) with pytest.raises(TypeError): _ = list(cirq.flatten_op_tree([operations[0], (4, )]))
def test_decomposes_despite_symbol(): q0, q1 = cirq.NamedQubit('q0'), cirq.NamedQubit('q1') gate = cirq.PauliInteractionGate(cirq.Pauli.Z, False, cirq.Pauli.X, False, half_turns=cirq.Symbol('x')) op_tree = gate.default_decompose([q0, q1]) ops = tuple(cirq.flatten_op_tree(op_tree)) assert ops
def test_decomposes_despite_symbol(): q0, q1 = cirq.NamedQubit('q0'), cirq.NamedQubit('q1') gate = cirq.PauliInteractionGate(cirq.Pauli.Z, False, cirq.Pauli.X, False, half_turns=cirq.Symbol('x')) op_tree = gate.default_decompose([q0, q1]) ops = tuple(cirq.flatten_op_tree(op_tree)) assert ops
def test_decomposition_cost(op: cirq.Operation, max_two_cost: int): ops = tuple( cirq.flatten_op_tree(cirq.google.ConvertToXmonGates().convert(op))) two_cost = len([e for e in ops if len(e.qubits) == 2]) over_cost = len([e for e in ops if len(e.qubits) > 2]) assert over_cost == 0 assert two_cost == max_two_cost
def test_flatten_op_tree(): operations = [ cirq.GateOperation(cirq.Gate(), [cirq.NamedQubit(str(i))]) for i in range(10) ] # Empty tree. assert list(cirq.flatten_op_tree([[[]]])) == [] # Just an item. assert list(cirq.flatten_op_tree(operations[0])) == operations[:1] # Flat list. assert list(cirq.flatten_op_tree(operations)) == operations # Tree. assert list(cirq.flatten_op_tree((operations[0], operations[1:5], operations[5:]))) == operations # Bad trees. with pytest.raises(TypeError): _ = list(cirq.flatten_op_tree(None)) with pytest.raises(TypeError): _ = list(cirq.flatten_op_tree(5)) with pytest.raises(TypeError): _ = list(cirq.flatten_op_tree([operations[0], (4,)]))
def _decompose_(self, qubits): a, b, c, d = qubits weights_to_exponents = (self._exponent / 4.) * np.array([ [1, -1, 1], [1, 1, -1], [-1, 1, 1] ]) exponents = weights_to_exponents.dot(self.weights) basis_change = list(cirq.flatten_op_tree([ cirq.CNOT(b, a), cirq.CNOT(c, b), cirq.CNOT(d, c), cirq.CNOT(c, b), cirq.CNOT(b, a), cirq.CNOT(a, b), cirq.CNOT(b, c), cirq.CNOT(a, b), [cirq.X(c), cirq.X(d)], [cirq.CNOT(c, d), cirq.CNOT(d, c)], [cirq.X(c), cirq.X(d)], ])) controlled_Zs = list(cirq.flatten_op_tree([ cirq.CZPowGate(exponent=exponents[0])(b, c), cirq.CNOT(a, b), cirq.CZPowGate(exponent=exponents[1])(b, c), cirq.CNOT(b, a), cirq.CNOT(a, b), cirq.CZPowGate(exponent=exponents[2])(b, c) ])) controlled_swaps = [ [cirq.CNOT(c, d), cirq.H(c)], cirq.CNOT(d, c), controlled_Zs, cirq.CNOT(d, c), [cirq.inverse(op) for op in reversed(controlled_Zs)], [cirq.H(c), cirq.CNOT(c, d)], ] yield basis_change yield controlled_swaps yield basis_change[::-1]
def test_swap_network_gate_permutation(part_lens, acquaintance_size): n_qubits = sum(part_lens) qubits = cirq.LineQubit.range(n_qubits) swap_network_gate = SwapNetworkGate(part_lens, acquaintance_size) operations = cirq.decompose_once_with_qubits(swap_network_gate, qubits) operations = list(cirq.flatten_op_tree(operations)) mapping = {q: i for i, q in enumerate(qubits)} update_mapping(mapping, operations) assert mapping == {q: i for i, q in enumerate(reversed(qubits))}
def test_transform_bad_tree(): with pytest.raises(TypeError): _ = list(cirq.transform_op_tree(None)) with pytest.raises(TypeError): _ = list(cirq.transform_op_tree(5)) with pytest.raises(TypeError): _ = list(cirq.flatten_op_tree(cirq.transform_op_tree([ cirq.GateOperation(cirq.Gate(), [cirq.NamedQubit('q')]), (4,) ])))
def test_transform_bad_tree(): with pytest.raises(TypeError): _ = list(cirq.transform_op_tree(None)) with pytest.raises(TypeError): _ = list(cirq.transform_op_tree(5)) with pytest.raises(TypeError): _ = list(cirq.flatten_op_tree(cirq.transform_op_tree([ cirq.GateOperation(cirq.Gate(), [cirq.QubitId()]), (4,) ])))
def test_flatten_to_ops_or_moments(): operations = [ cirq.GateOperation(cirq.testing.SingleQubitGate(), [cirq.NamedQubit(str(i))]) for i in range(10) ] op_tree = [operations[0], cirq.Moment(operations[1:5]), operations[5:]] output = [operations[0], cirq.Moment(operations[1:5])] + operations[5:] assert list(cirq.flatten_to_ops_or_moments(op_tree)) == output assert list(cirq.flatten_op_tree(op_tree, preserve_moments=True)) == output # Bad trees. with pytest.raises(TypeError): _ = list(cirq.flatten_to_ops_or_moments(None)) with pytest.raises(TypeError): _ = list(cirq.flatten_to_ops_or_moments(5)) with pytest.raises(TypeError): _ = list(cirq.flatten_to_ops_or_moments([operations[0], (4, )]))
def simulate_op(op, temp_state): indices = [qubit_map[q] for q in op.qubits] if isinstance(op.gate, cirq.ResetChannel): self._simulate_reset(op, temp_state, indices) elif cirq.is_measurement(op): if perform_measurements: self._simulate_measurement( op, temp_state, indices, measurements) else: if cirq.num_qubits(op) <= 3: self._simulate_from_matrix(op, temp_state, indices) else: decomp_ops = cirq.decompose_once(op, default=None) if decomp_ops is None: self._simulate_from_matrix(op, temp_state, indices) else: for sub_op in cirq.flatten_op_tree(decomp_ops): simulate_op(sub_op, temp_state)
def test_linear_permutation_gate(n_elements, n_permuted): qubits = cirq.LineQubit.range(n_elements) elements = tuple(range(n_elements)) elements_to_permute = random.sample(elements, n_permuted) permuted_elements = random.sample(elements_to_permute, n_permuted) permutation = {e: p for e, p in zip(elements_to_permute, permuted_elements)} cca.PermutationGate.validate_permutation(permutation, n_elements) gate = cca.LinearPermutationGate(n_elements, permutation) ct.assert_equivalent_repr(gate) assert gate.permutation() == permutation mapping = dict(zip(qubits, elements)) for swap in cirq.flatten_op_tree(cirq.decompose_once_with_qubits( gate, qubits)): assert isinstance(swap, cirq.GateOperation) swap.gate.update_mapping(mapping, swap.qubits) for i in range(n_elements): p = permutation.get(elements[i], i) assert mapping.get(qubits[p], elements[i]) == i
def test_linear_permutation_gate(): for _ in range(20): n_elements = randint(5, 20) n_permuted = randint(0, n_elements) qubits = [cirq.NamedQubit(s) for s in alphabet[:n_elements]] elements = tuple(range(n_elements)) elements_to_permute = sample(elements, n_permuted) permuted_elements = sample(elements_to_permute, n_permuted) permutation = {e: p for e, p in zip(elements_to_permute, permuted_elements)} PermutationGate.validate_permutation(permutation, n_elements) gate = LinearPermutationGate(permutation) assert gate.permutation(n_elements) == permutation mapping = dict(zip(qubits, elements)) for swap in cirq.flatten_op_tree(cirq.decompose_once_with_qubits( gate, qubits)): assert isinstance(swap, cirq.GateOperation) swap.gate.update_mapping(mapping, swap.qubits) for i in range(n_elements): p = permutation.get(elements[i], i) assert mapping.get(qubits[p], elements[i]) == i
def simulate_op(op, temp_state): indices = [qubit_map[q] for q in op.qubits] if isinstance(op.gate, cirq.ResetChannel): self._simulate_reset(op, temp_state, indices) elif cirq.is_measurement(op): if perform_measurements: self._simulate_measurement(op, temp_state, indices, measurements) else: decomp_ops = cirq.decompose_once(op, default=None) if decomp_ops is None: self._simulate_from_matrix(op, temp_state, indices) else: try: temp2_state = temp_state.copy() for sub_op in cirq.flatten_op_tree(decomp_ops): simulate_op(sub_op, temp2_state) temp_state[...] = temp2_state except ValueError: # Non-classical unitary in the decomposition self._simulate_from_matrix(op, temp_state, indices)
def _decompose_(self, qubits): """The goal is to effect a rotation around an axis in the XY plane in each of three orthogonal 2-dimensional subspaces. First, the following basis change is performed: 0000 ↦ 0001 0001 ↦ 1111 1111 ↦ 0010 1110 ↦ 1100 0010 ↦ 0000 0110 ↦ 0101 1101 ↦ 0011 1001 ↦ 0110 0100 ↦ 0100 1010 ↦ 1001 1011 ↦ 0111 0101 ↦ 1010 1000 ↦ 1000 1100 ↦ 1101 0111 ↦ 1011 0011 ↦ 1110 Note that for each 2-dimensional subspace of interest, the first two qubits are the same and the right two qubits are different. The desired rotations thus can be effected by a complex-version of a partial SWAP gate on the latter two qubits, controlled on the first two qubits. This partial SWAP-like gate can be decomposed such that it is parameterized solely by a rotation in the ZY plane on the third qubit. These are the `individual_rotations`; call them U0, U1, U2. To decompose the double controlled rotations, we use four other rotations V0, V1, V2, V3 (the `combined_rotations`) such that U0 = V3 · V1 · V0 U1 = V3 · V2 · V1 U2 = V2 · V0 """ if self._is_parameterized_(): return NotImplemented individual_rotations = [ la.expm(0.5j * self.exponent * np.array([[np.real(w), 1j * s * np.imag(w)], [-1j * s * np.imag(w), -np.real(w)]])) for s, w in zip([1, -1, -1], self.weights) ] combined_rotations = {} combined_rotations[0] = la.sqrtm( np.linalg.multi_dot([ la.inv(individual_rotations[1]), individual_rotations[0], individual_rotations[2] ])) combined_rotations[1] = la.inv(combined_rotations[0]) combined_rotations[2] = np.linalg.multi_dot([ la.inv(individual_rotations[0]), individual_rotations[1], combined_rotations[0] ]) combined_rotations[3] = individual_rotations[0] controlled_rotations = { i: cirq.ControlledGate( cirq.MatrixGate(combined_rotations[i], qid_shape=(2, ))) for i in range(4) } a, b, c, d = qubits basis_change = list( cirq.flatten_op_tree([ cirq.CNOT(b, a), cirq.CNOT(c, b), cirq.CNOT(d, c), cirq.CNOT(c, b), cirq.CNOT(b, a), cirq.CNOT(a, b), cirq.CNOT(b, c), cirq.CNOT(a, b), [cirq.X(c), cirq.X(d)], [cirq.CNOT(c, d), cirq.CNOT(d, c)], [cirq.X(c), cirq.X(d)], ])) controlled_rotations = list( cirq.flatten_op_tree([ controlled_rotations[0](b, c), cirq.CNOT(a, b), controlled_rotations[1](b, c), cirq.CNOT(b, a), cirq.CNOT(a, b), controlled_rotations[2](b, c), cirq.CNOT(a, b), controlled_rotations[3](b, c) ])) controlled_swaps = [ [cirq.CNOT(c, d), cirq.H(c)], cirq.CNOT(d, c), controlled_rotations, cirq.CNOT(d, c), [cirq.inverse(op) for op in reversed(controlled_rotations)], [cirq.H(c), cirq.CNOT(c, d)], ] return [basis_change, controlled_swaps, basis_change[::-1]]
def _assert_equivalent_op_tree(x: cirq.OP_TREE, y: cirq.OP_TREE): a = list(cirq.flatten_op_tree(x)) b = list(cirq.flatten_op_tree(y)) assert a == b
def quick_circuit(*moments: Iterable[cirq.OP_TREE]) -> cirq.Circuit: return cirq.Circuit([ cirq.Moment(cast(Iterable[cirq.Operation], cirq.flatten_op_tree(m))) for m in moments ])
def test_w_circuit_gate_locality(n_qubits): qubits = cirq.LineQubit.range(n_qubits) ops = generate_w_state_circuit(qubits) for op in cirq.flatten_op_tree(ops): assert len(op.qubits) <= 2
def swap_network(qubits: Sequence[cirq.Qid], operation: Callable[ [int, int, cirq.Qid, cirq.Qid], cirq.OP_TREE] = lambda p, q, p_qubit, q_qubit: (), fermionic: bool = False, offset: bool = False) -> List[cirq.Operation]: """Apply operations to pairs of qubits or modes using a swap network. This is used for applying operations between arbitrary pairs of qubits or fermionic modes using only nearest-neighbor interactions on a linear array of qubits. It works by reversing the order of qubits or modes with a sequence of swap gates and applying an operation when the relevant qubits or modes become adjacent. For fermionic modes, this assumes the Jordan-Wigner Transform. Examples -------- Input: .. testcode:: import cirq from openfermioncirq import swap_network qubits = cirq.LineQubit.range(4) circuit = cirq.Circuit(swap_network(qubits)) print(circuit) Output: .. testoutput:: 0: ───×───────×─────── │ │ 1: ───×───×───×───×─── │ │ 2: ───×───×───×───×─── │ │ 3: ───×───────×─────── Input: .. testcode:: circuit = cirq.Circuit(swap_network(qubits, offset=True)) print(circuit) Output: .. testoutput:: 0: ───────×───────×─── │ │ 1: ───×───×───×───×─── │ │ 2: ───×───×───×───×─── │ │ 3: ───────×───────×─── Input: .. testcode:: from openfermioncirq import XXYY circuit = cirq.Circuit( swap_network( qubits, lambda p, q, a, b: XXYY(a, b) if abs(p - q) == 1 else cirq.CZ(a, b), fermionic=True), strategy=cirq.InsertStrategy.EARLIEST) print(circuit) Output: .. testoutput:: 0: ───XXYY───×ᶠ────────────@───×ᶠ─────────────── │ │ │ │ 1: ───XXYY───×ᶠ───@───×ᶠ───@───×ᶠ───XXYY───×ᶠ─── │ │ │ │ 2: ───XXYY───×ᶠ───@───×ᶠ───@───×ᶠ───XXYY───×ᶠ─── │ │ │ │ 3: ───XXYY───×ᶠ────────────@───×ᶠ─────────────── Args: qubits: The qubits sorted so that the j-th qubit in the Sequence represents the j-th qubit or fermionic mode. operation: Returns extra interactions to perform between qubits/modes as they are swapped past each other. A call to this function takes the form ``operation(p, q, p_qubit, q_qubit)`` where p and q are indices representing either qubits or fermionic modes, and p_qubit and q_qubit are the qubits which are currently storing those modes. fermionic: If True, use fermionic swaps under the JWT (that is, swap fermionic modes instead of qubits). If False, use normal qubit swaps. offset: If True, then qubit 0 will participate in odd-numbered layers instead of even-numbered layers. """ n_qubits = len(qubits) order = list(range(n_qubits)) swap_gate = FSWAP if fermionic else cirq.SWAP result = [] # type: List[cirq.Operation] for layer_num in range(n_qubits): lowest_active_qubit = (layer_num + offset) % 2 active_pairs = ((i, i + 1) for i in range(lowest_active_qubit, n_qubits - 1, 2)) for i, j in active_pairs: p, q = order[i], order[j] extra_ops = operation(p, q, qubits[i], qubits[j]) result.extend(cirq.flatten_op_tree(extra_ops)) result.append(swap_gate(qubits[i], qubits[j])) order[i], order[j] = q, p return result
def quick_circuit(*moments: Iterable[cirq.OP_TREE]) -> cirq.Circuit: return cirq.Circuit([cirq.Moment(cirq.flatten_op_tree(m)) for m in moments])
def _op_generator_grad(_qubits): op_list = flatten_op_tree( op_tree_generator(_qubits, **generator_kwargs), preserve_moments=False) # type: typing.Iterable[cirq.Operation] return op_series_grad(list(op_list), parameter)