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 _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)
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)
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)
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)