def compile_circuit(
    circuit: cirq.Circuit,
    *,
    device_graph: nx.Graph,
    routing_attempts: int,
    compiler: Callable[[cirq.Circuit], cirq.Circuit] | None = None,
    routing_algo_name: str | None = None,
    router: Callable[..., ccr.SwapNetwork] | None = None,
    add_readout_error_correction=False,
) -> CompilationResult:
    """Compile the given model circuit onto the given device graph. This uses a
    different compilation method than described in
    https://arxiv.org/pdf/1811.12926.pdf Appendix A. The latter goes through a
    7-step process involving various decompositions, routing, and optimization
    steps. We route the model circuit and then run a series of optimizers on it
    (which can be passed into this function).

    Args:
        circuit: The model circuit to compile.
        device_graph: The device graph to compile onto.
        routing_attempts: See doc for calculate_quantum_volume.
        compiler: An optional function to deconstruct the model circuit's
            gates down to the target devices gate set and then optimize it.
        routing_algo_name: The name of the routing algorithm, see ROUTING in
            `route_circuit.py`.
        router: The function that actually does the routing.
        add_readout_error_correction: If true, add some parity bits that will
            later be used to detect readout error.

    Returns: A tuple where the first value is the compiled circuit and the
        second value is the final mapping from the model circuit to the compiled
        circuit. The latter is necessary in order to preserve the measurement
        order.

    """
    compiled_circuit = circuit.copy()

    # Optionally add some the parity check bits.
    parity_map: dict[cirq.Qid, cirq.Qid] = {}  # original -> parity
    if add_readout_error_correction:
        num_qubits = len(compiled_circuit.all_qubits())
        # Sort just to make it deterministic.
        for idx, qubit in enumerate(sorted(compiled_circuit.all_qubits())):
            # For each qubit, create a new qubit that will serve as its parity
            # check. An inverse-controlled-NOT is performed on the qubit and its
            # parity bit. Later, these two qubits will be checked for parity -
            # if they are equal, there was likely a readout error.
            qubit_num = idx + num_qubits
            parity_qubit = cirq.LineQubit(qubit_num)
            compiled_circuit.append(cirq.X(qubit))
            compiled_circuit.append(cirq.CNOT(qubit, parity_qubit))
            compiled_circuit.append(cirq.X(qubit))
            parity_map[qubit] = parity_qubit

    # Swap Mapping (Routing). Ensure the gates can actually operate on the
    # target qubits given our topology.
    if router is None and routing_algo_name is None:
        # TODO: The routing algorithm sometimes does a poor job with the parity
        # qubits, adding SWAP gates that are unnecessary. This should be fixed,
        # or we can add the parity qubits manually after routing.
        # Github issue: https://github.com/quantumlib/Cirq/issues/2967
        routing_algo_name = 'greedy'

    swap_networks: list[ccr.SwapNetwork] = []
    for _ in range(routing_attempts):
        swap_network = ccr.route_circuit(
            compiled_circuit, device_graph, router=router, algo_name=routing_algo_name
        )
        swap_networks.append(swap_network)
    assert len(swap_networks) > 0, 'Unable to get routing for circuit'

    # Sort by the least number of qubits first (as routing sometimes adds extra
    # ancilla qubits), and then the length of the circuit second.
    swap_networks.sort(
        key=lambda swap_network: (len(swap_network.circuit.all_qubits()), len(swap_network.circuit))
    )

    routed_circuit = swap_networks[0].circuit
    mapping = swap_networks[0].final_mapping()
    # Replace the PermutationGates with regular gates, so we don't proliferate
    # the routing implementation details to the compiler and the device itself.

    def replace_swap_permutation_gate(op: cirq.Operation, _):
        if isinstance(op.gate, cirq.contrib.acquaintance.SwapPermutationGate):
            return [op.gate.swap_gate.on(*op.qubits)]
        return op

    routed_circuit = cirq.map_operations_and_unroll(
        routed_circuit, map_func=replace_swap_permutation_gate
    )

    if not compiler:
        return CompilationResult(circuit=routed_circuit, mapping=mapping, parity_map=parity_map)

    # Compile. This should decompose the routed circuit down to a gate set that
    # our device supports, and then optimize. The paper uses various
    # compiling techniques - because Quantum Volume is intended to test those
    # as well, we allow this to be passed in. This compiler is not allowed to
    # change the order of the qubits.
    return CompilationResult(
        circuit=compiler(routed_circuit), mapping=mapping, parity_map=parity_map
    )
Compile the given model circuit onto the given device graph. This uses a
    different compilation method than described in
    https://arxiv.org/pdf/1811.12926.pdf Appendix A. The latter goes through a
    7-step process involving various decompositions, routing, and optimization
    steps. We route the model circuit and then run a series of optimizers on it
    (which can be passed into this function).

    Args:
        circuit: The model circuit to compile.
        device_graph: The device graph to compile onto.
        routing_attempts: See doc for calculate_quantum_volume.
        compiler: An optional function to deconstruct the model circuit's
            gates down to the target devices gate set and then optimize it.
        routing_algo_name: The name of the routing algorithm, see ROUTING in
            `route_circuit.py`.
        router: The function that actually does the routing.
        add_readout_error_correction: If true, add some parity bits that will
            later be used to detect readout error.

    Returns: A tuple where the first value is the compiled circuit and the
        second value is the final mapping from the model circuit to the compiled
        circuit. The latter is necessary in order to preserve the measurement
        order.

    
cirq-core/cirq/contrib/quantum_volume/quantum_volume.py