Skip to content

Routine to find zephyr[m,tp] in possibly-faulty zephyr[m,t] with tp < t#261

Draft
VolodyaCO wants to merge 1 commit intodwavesystems:mainfrom
VolodyaCO:zephyr-quotient-search
Draft

Routine to find zephyr[m,tp] in possibly-faulty zephyr[m,t] with tp < t#261
VolodyaCO wants to merge 1 commit intodwavesystems:mainfrom
VolodyaCO:zephyr-quotient-search

Conversation

@VolodyaCO
Copy link

This PR adds a heuristic routine written by @jackraymond to find an embedding from a source graph zephyr[m, tp] to a target graph which is a subgraph of zephyr[m,t]. The motivation of this routine is to have a tool to generate zephyr subgraphs that are resilient to possible future qubit/coupler defects in actual QPUs.

Tests are added to make sure that the routine indeed improves the yield of the embedded graph.

@thisac thisac requested a review from kevinchern March 13, 2026 16:51
Copy link
Contributor

@kevinchern kevinchern left a comment

Choose a reason for hiding this comment

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

Nice work Jack&Vlad 🤩!! I did a quick first pass with mostly minor suggestions.

@@ -0,0 +1,914 @@
# Copyright 2026 D-Wave Systems Inc.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
# Copyright 2026 D-Wave Systems Inc.
# Copyright 2026 D-Wave

and other occurrences


from dwave_networkx import zephyr_coordinates, zephyr_graph

ZephyrNode = tuple[int, int, int, int, int] # (u, w, k, j, z) coordinate tuple
Copy link
Contributor

Choose a reason for hiding this comment

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

Does something similar exist in dnx? @mahdiehmalekian @thisac

ZephyrNode = tuple[int, int, int, int, int] # (u, w, k, j, z) coordinate tuple
Embedding = dict[ZephyrNode, ZephyrNode]
YieldType = Literal["node", "edge", "rail-edge"]
KSearchType = Literal["by_quotient_rail", "by_quotient_node", "by_rail_then_node"]
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a more descriptive name for "K"? (if not, consider adding a comment explaining what K refers to)



def _validate_graph_inputs(source: nx.Graph, target: nx.Graph) -> tuple[int, int, int]:
"""Validate Zephyr graph compatibility and return ``(m, tp, t)``.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we describe the checklist of requirements? e.g., inputs are required to be networkx graphs.

TypeError: If inputs are not NetworkX graphs.
ValueError: If graph metadata is missing or incompatible.
"""
if not isinstance(source, nx.Graph) or not isinstance(target, nx.Graph):
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we don't require them to be networkx graphs and we'll be moving away from networkx.
That said, this is fine; just a heads-up

``(_source, source_nodes, to_source)`` where ``_source`` is
coordinate-labelled, ``source_nodes`` is the full canonical coordinate
node set implied by ``m`` and ``tp``, and ``to_source`` maps
coordinate nodes back to the original source labelling spcae.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
coordinate nodes back to the original source labelling spcae.
coordinate nodes back to the original source labelling space.

) -> tuple[nx.Graph, Callable[[ZephyrNode], int | ZephyrNode]]:
"""Return a coordinate-labelled target graph and conversion callable.

This is similar to the source version but does not need to return the full canonical node set
Copy link
Contributor

Choose a reason for hiding this comment

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

I would add a standalone description before contrasting with the source version

ksymmetric: bool = False,
yield_type: YieldType = "edge",
) -> tuple[dict, dict | None, ZephyrSearchMetadata]:
r"""Compute a high-yield Zephyr-to-Zephyr embedding.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
r"""Compute a high-yield Zephyr-to-Zephyr embedding.
r"""Compute a high-yield Zephyr-to-Zephyr embedding.

😜

Returns:
tuple[dict, dict | None, ZephyrSearchMetadata]:
``(subgraph_embedding, minor_embedding, metadata)``
``subgraph_embedding`` is a pruned one-to-one node map ``source_node -> target_node``.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we make the returned embeddings consistent in the sense that the one-to-one embeddings are length-1 chains, e.g., emb[1] == (53,) instead of emb[1] == 53?

cc @jackraymond

Comment on lines +668 to +670
``source_node -> tuple[target_nodes, ...]`` or ``None``. If the greedy search did not
achieve full yield and minorminer is disabled, or minorminer was not able to find an
embedding, then ``minor_embedding`` is ``None``. If full yield is achieved, then
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this interface can be naturally split in two functions. Currently, it attempts to A) embed with one method, and tries B) minorminer with a seeded solution when the first fails. Would having the two functions A and B work? This current function can still exist, just as a wrapper that calls A and B in succession.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants