Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
    def _decompose_(self, qubits: Sequence['cirq.Qid']) -> 'cirq.OP_TREE':
        qubit_to_position = {q: i for i, q in enumerate(qubits)}
        mapping = dict(qubit_to_position)
        parts = []
        n_qubits = 0
        for part_len in self.part_lens:
            parts.append(list(qubits[n_qubits:n_qubits + part_len]))
            n_qubits += part_len
        n_parts = len(parts)
        op_sort_key = (None if self.acquaintance_size is None else
                       (lambda op: qubit_to_position[min(
                           op.qubits, key=qubit_to_position.get)] % self.
                        acquaintance_size))
        layers = new_layers()
        for layer_num in range(n_parts):
            layers = new_layers(
                prior_interstitial=layers.posterior_interstitial)
            for i in range(layer_num % 2, n_parts - 1, 2):
                left_part, right_part = parts[i:i + 2]
                acquaint_and_shift(
                    parts=(left_part, right_part),
                    layers=layers,
                    acquaintance_size=self.acquaintance_size,
                    swap_gate=self.swap_gate,
                    mapping=mapping,
                )

                parts_qubits = list(left_part + right_part)
                parts[i] = parts_qubits[:len(right_part)]
                parts[i + 1] = parts_qubits[len(right_part):]
            layers.prior_interstitial.sort(key=op_sort_key)
            for l in ('prior_interstitial', 'pre', 'intra', 'post'):
                yield getattr(layers, l)
        layers.posterior_interstitial.sort(key=op_sort_key)
        yield layers.posterior_interstitial

        assert list(
            itertools.chain(*(sorted(mapping[q] for q in part)
                              for part in reversed(parts)))) == list(
                                  range(n_qubits))

        # finish reversal
        final_permutation = {
            i: n_qubits - 1 - mapping[q]
            for i, q in enumerate(qubits)
        }
        final_gate = LinearPermutationGate(n_qubits, final_permutation,
                                           self.swap_gate)
        if final_gate:
            yield final_gate(*qubits)
Ejemplo n.º 3
0
def cubic_acquaintance_strategy(
        qubits: Iterable['cirq.Qid'],
        swap_gate: 'cirq.Gate' = ops.SWAP) -> 'cirq.Circuit':
    """Acquaints every triple of qubits.

    Exploits the fact that in a simple linear swap network every pair of
    logical qubits that starts at distance two remains so (except temporarily
    near the edge), and that every third one `goes through` the pair at some
    point in the network. The strategy then iterates through a series of
    mappings in which qubits i and i + k are placed at distance two, for k = 1
    through n / 2. Linear swap networks are used in between to effect the
    permutation.
    """

    qubits = tuple(qubits)
    n_qubits = len(qubits)

    swap_gate = SwapPermutationGate(swap_gate)

    moments = []
    index_order = tuple(range(n_qubits))
    max_separation = max(((n_qubits - 1) // 2) + 1, 2)
    for separation in range(1, max_separation):
        stepped_indices_concatenated = tuple(
            itertools.chain(*(range(offset, n_qubits, separation)
                              for offset in range(separation))))
        new_index_order = skip_and_wrap_around(stepped_indices_concatenated)
        permutation = {
            i: new_index_order.index(j)
            for i, j in enumerate(index_order)
        }
        permutation_gate = LinearPermutationGate(n_qubits, permutation,
                                                 swap_gate)
        moments.append(ops.Moment([permutation_gate(*qubits)]))
        for i in range(n_qubits + 1):
            for offset in range(3):
                moment = ops.Moment(
                    acquaint(*qubits[j:j + 3])
                    for j in range(offset, n_qubits - 2, 3))
                moments.append(moment)
            if i < n_qubits:
                moment = ops.Moment(
                    swap_gate(*qubits[j:j + 2])
                    for j in range(i % 2, n_qubits - 1, 2))
                moments.append(moment)
        index_order = new_index_order[::-1]
    return circuits.Circuit(moments, device=UnconstrainedAcquaintanceDevice)
Ejemplo n.º 4
0
    def _decompose_(self, qubits: Sequence[ops.QubitId]) -> ops.OP_TREE:
        qubit_to_position = {q: i for i, q in enumerate(qubits)}
        mapping = dict(qubit_to_position)
        parts = []
        q = 0
        for part_len in self.part_lens:
            parts.append(list(qubits[q:q + part_len]))
            q += part_len
        n_parts = len(parts)
        op_sort_key = (None if self.acquaintance_size is None else
                       (lambda op: qubit_to_position[min(
                           op.qubits, key=qubit_to_position.get)] % self.
                        acquaintance_size))
        layers = new_layers()
        for layer_num in range(n_parts):
            layers = new_layers(
                prior_interstitial=layers.posterior_interstitial)
            for i in range(layer_num % 2, n_parts - 1, 2):
                left_part, right_part = parts[i:i + 2]
                acquaint_and_shift(parts=(left_part, right_part),
                                   layers=layers,
                                   acquaintance_size=self.acquaintance_size,
                                   swap_gate=self.swap_gate,
                                   mapping=mapping)

                parts_qubits = list(left_part + right_part)
                parts[i] = parts_qubits[:len(right_part)]
                parts[i + 1] = parts_qubits[len(right_part):]
            layers.prior_interstitial.sort(key=op_sort_key)
            for l in ('prior_interstitial', 'pre', 'intra', 'post'):
                yield getattr(layers, l)
        layers.posterior_interstitial.sort(key=op_sort_key)
        yield layers.posterior_interstitial

        # finish reversal
        for part in reversed(parts):
            part_len = len(part)
            if part_len > 1:
                positions = [mapping[q] for q in part]
                offset = min(positions)
                reverse_permutation = {
                    i: (offset - mapping[q] - 1) % part_len
                    for i, q in enumerate(part)
                }
                yield LinearPermutationGate(reverse_permutation,
                                            self.swap_gate)(*part)
Ejemplo n.º 5
0
def test_executor_random(n_qubits: int, acquaintance_size: int,
                         gates: Dict[Tuple[cirq.QubitId, ...], cirq.Gate]):
    qubits = cirq.LineQubit.range(n_qubits)
    circuit = complete_acquaintance_strategy(qubits, acquaintance_size)

    logical_circuit = cirq.Circuit.from_ops([g(*Q) for Q, g in gates.items()])
    expected_unitary = logical_circuit.to_unitary_matrix()

    initial_mapping = {q: q for q in qubits}
    execution_strategy = GreedyExecutionStrategy(gates, initial_mapping)
    executor = StrategyExecutor(execution_strategy)
    final_mapping = executor(circuit)
    permutation = {q.x: qq.x for q, qq in final_mapping.items()}
    circuit.append(LinearPermutationGate(permutation)(*qubits))
    actual_unitary = circuit.to_unitary_matrix()

    np.testing.assert_allclose(actual=actual_unitary,
                               desired=expected_unitary,
                               verbose=True)