Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parallel XEB: Add option to specify pairs #6787

Merged
merged 7 commits into from
Nov 8, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
add option to specify pairs for parallel two-qubit xeb
  • Loading branch information
eliottrosenberg committed Nov 7, 2024
commit 9a48e3cd763c9633dce3cd1032390f7b58d3b658
36 changes: 28 additions & 8 deletions cirq-core/cirq/experiments/two_qubit_xeb.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,20 +351,23 @@ def plot_histogram(
def parallel_xeb_workflow(
sampler: 'cirq.Sampler',
qubits: Optional[Sequence['cirq.GridQubit']] = None,
pairs: Optional[Sequence[tuple['cirq.GridQubit', 'cirq.GridQubit']]] = None,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

usually new arguments go to the end

entangling_gate: 'cirq.Gate' = ops.CZ,
n_repetitions: int = 10**4,
n_combinations: int = 10,
n_circuits: int = 20,
cycle_depths: Sequence[int] = (5, 25, 50, 100, 200, 300),
random_state: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None,
ax: Optional[plt.Axes] = None,
pool: Optional['multiprocessing.pool.Pool'] = None,
**plot_kwargs,
) -> Tuple[pd.DataFrame, Sequence['cirq.Circuit'], pd.DataFrame]:
"""A utility method that runs the full XEB workflow.

Args:
sampler: The quantum engine or simulator to run the circuits.
qubits: Qubits under test. If none, uses all qubits on the sampler's device.
pairs: Pairs to use. If not specified, use all pairs between adjacent qubits.
entangling_gate: The entangling gate to use.
n_repetitions: The number of repetitions to use.
n_combinations: The number of combinations to generate.
Expand All @@ -373,6 +376,7 @@ def parallel_xeb_workflow(
random_state: The random state to use.
ax: the plt.Axes to plot the device layout on. If not given,
no plot is created.
pool: An optional multiprocessing pool.
**plot_kwargs: Arguments to be passed to 'plt.Axes.plot'.

Returns:
Expand All @@ -389,13 +393,23 @@ def parallel_xeb_workflow(
rs = value.parse_random_state(random_state)

if qubits is None:
qubits = _grid_qubits_for_sampler(sampler)
if qubits is None:
raise ValueError("Couldn't determine qubits from sampler. Please specify them.")

graph = nx.Graph(
pair for pair in itertools.combinations(qubits, 2) if _manhattan_distance(*pair) == 1
)
if pairs is None:
Copy link
Collaborator

@NoureldinYosri NoureldinYosri Nov 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extract this logic into a method and use it here and in the other methods

def qubits_and_pairs(
    sampler: 'cirq.Sampler',
    qubits: Optional[Sequence['cirq.GridQubit']] = None,
    pairs: Optional[Sequence[tuple['cirq.GridQubit', 'cirq.GridQubit']]] = None,
) -> Tuple[Sequence['cirq.GridQubit'], Sequence[tuple['cirq.GridQubit', 'cirq.GridQubit']]]:
    """Extract qubits and pairs from sampler.
    

    If qubits are not provided, then they are extracted from the pairs (if given) or the sampler.
    If pairs are not provided then all pairs of adjacent qubits are used.

    Args:
        sampler: The quantum engine or simulator to run the circuits.
        qubits: Optional list of qubits.
        pairs: Optional list of pair to use.
    
    Returns:
        - Qubits to use.
        - Pairs of qubits to use.
    
    Raises:
        ValueError: If qubits are not specified and can't be deduced from other arguments.
    """
    if qubits is None:
        if pairs is None:
            qubits = _grid_qubits_for_sampler(sampler)
            if qubits is None:
                raise ValueError("Couldn't determine qubits from sampler. Please specify them.")
        else:
            qubits_set = set(itertools.chain(*pairs))
            qubits = list(qubits_set)

    if pairs is None:
        pairs = [
            pair for pair in itertools.combinations(qubits, 2) if _manhattan_distance(*pair) == 1
        ]

    return qubits, pairs

qubits = _grid_qubits_for_sampler(sampler)
if qubits is None:
raise ValueError("Couldn't determine qubits from sampler. Please specify them.")
else:
qubits_set = set()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
qubits_set = set()
qubit_set = set(itertools.chain(*pairs))

for pair in pairs:
qubits_set.add(pair[0])
qubits_set.add(pair[1])
qubits = list(qubits_set)

if pairs is None:
pairs = [
pair for pair in itertools.combinations(qubits, 2) if _manhattan_distance(*pair) == 1
]

graph = nx.Graph(pairs)

if ax is not None:
nx.draw_networkx(graph, pos={q: (q.row, q.col) for q in qubits}, ax=ax)
Expand Down Expand Up @@ -426,7 +440,7 @@ def parallel_xeb_workflow(
)

fids = benchmark_2q_xeb_fidelities(
sampled_df=sampled_df, circuits=circuit_library, cycle_depths=cycle_depths
sampled_df=sampled_df, circuits=circuit_library, cycle_depths=cycle_depths, pool=pool
)

return fids, circuit_library, sampled_df
Expand All @@ -435,6 +449,7 @@ def parallel_xeb_workflow(
def parallel_two_qubit_xeb(
sampler: 'cirq.Sampler',
qubits: Optional[Sequence['cirq.GridQubit']] = None,
pairs: Optional[Sequence[tuple['cirq.GridQubit', 'cirq.GridQubit']]] = None,
entangling_gate: 'cirq.Gate' = ops.CZ,
n_repetitions: int = 10**4,
n_combinations: int = 10,
Expand All @@ -449,6 +464,7 @@ def parallel_two_qubit_xeb(
Args:
sampler: The quantum engine or simulator to run the circuits.
qubits: Qubits under test. If none, uses all qubits on the sampler's device.
pairs: Pairs to use. If not specified, use all pairs between adjacent qubits.
entangling_gate: The entangling gate to use.
n_repetitions: The number of repetitions to use.
n_combinations: The number of combinations to generate.
Expand All @@ -466,6 +482,7 @@ def parallel_two_qubit_xeb(
fids, *_ = parallel_xeb_workflow(
sampler=sampler,
qubits=qubits,
pairs=pairs,
entangling_gate=entangling_gate,
n_repetitions=n_repetitions,
n_combinations=n_combinations,
Expand All @@ -481,6 +498,7 @@ def parallel_two_qubit_xeb(
def run_rb_and_xeb(
sampler: 'cirq.Sampler',
qubits: Optional[Sequence['cirq.GridQubit']] = None,
pairs: Optional[Sequence[tuple['cirq.GridQubit', 'cirq.GridQubit']]] = None,
repetitions: int = 10**3,
num_circuits: int = 20,
num_clifford_range: Sequence[int] = tuple(
Expand All @@ -496,6 +514,7 @@ def run_rb_and_xeb(
Args:
sampler: The quantum engine or simulator to run the circuits.
qubits: Qubits under test. If none, uses all qubits on the sampler's device.
pairs: Pairs to use. If not specified, use all pairs between adjacent qubits.
repetitions: The number of repetitions to use for RB and XEB.
num_circuits: The number of circuits to generate for RB and XEB.
num_clifford_range: The different numbers of Cliffords in the RB study.
Expand Down Expand Up @@ -527,6 +546,7 @@ def run_rb_and_xeb(
xeb = parallel_two_qubit_xeb(
sampler=sampler,
qubits=qubits,
pairs=pairs,
entangling_gate=entangling_gate,
n_repetitions=repetitions,
n_circuits=num_circuits,
Expand Down