General Embedding#
General embedding refers to embedding that may be useful for any type of graph.
The primary utility function, find_embedding()
, is an implementation of
the heuristic algorithm described in [1]. It accepts various optional parameters
used to tune the algorithm’s execution or constrain the given problem.
This implementation performs on par with tuned, non-configurable implementations while providing users with hooks to easily use the code as a basic building block in research.
[1] https://arxiv.org/abs/1406.2741
Another function, find_clique_embedding()
, can be used to find clique embeddings
for Chimera, Pegasus, and Zephyr graphs in polynomial time. It is an implementation
of the algorithm described in [2]. There are additional utilities for finding
biclique embeddings as well.
[2] https://arxiv.org/abs/1507.04774
- find_embedding(S, T, **params)[source]#
Heuristically attempt to find a minor-embedding of source graph S into a target graph T.
- Parameters:
S (iterable/NetworkX Graph) – The source graph as an iterable of label pairs representing the edges, or a NetworkX Graph.
T (iterable/NetworkX Graph) – The target graph as an iterable of label pairs representing the edges, or a NetworkX Graph.
**params (optional) – See below.
- Returns:
When the optional parameter
return_overlap
is False (the default), the function returns a dict that maps labels in S to lists of labels in T. If the heuristic fails to find an embedding, an empty dictionary is returned.When
return_overlap
is True, the function returns a tuple consisting of a dict that maps labels in S to lists of labels in T and a bool indicating whether or not a valid embedding was found.When interrupted by Ctrl-C, the function returns the best embedding found so far.
Note that failure to return an embedding does not prove that no embedding exists.
- Optional Parameters:
- max_no_improvement (int, optional, default=10):
Maximum number of failed iterations to improve the current solution, where each iteration attempts to find an embedding for each variable of S such that it is adjacent to all its neighbours.
- random_seed (int, optional, default=None):
Seed for the random number generator. If None, seed is set by
os.urandom()
.- timeout (int, optional, default=1000):
Algorithm gives up after timeout seconds.
- max_beta (double, optional, max_beta=None):
Qubits are assigned weight according to a formula (beta^n) where n is the number of chains containing that qubit. This value should never be less than or equal to 1. If None,
max_beta
is effectively infinite.- tries (int, optional, default=10):
Number of restart attempts before the algorithm stops. On D-WAVE 2000Q, a typical restart takes between 1 and 60 seconds.
- inner_rounds (int, optional, default=None):
The algorithm takes at most this many iterations between restart attempts; restart attempts are typically terminated due to
max_no_improvement
. If None,inner_rounds
is effectively infinite.- chainlength_patience (int, optional, default=10):
Maximum number of failed iterations to improve chain lengths in the current solution, where each iteration attempts to find an embedding for each variable of S such that it is adjacent to all its neighbours.
- max_fill (int, optional, default=None):
Restricts the number of chains that can simultaneously incorporate the same qubit during the search. Values above 63 are treated as 63. If None,
max_fill
is effectively infinite.- threads (int, optional, default=1):
Maximum number of threads to use. Note that the parallelization is only advantageous where the expected degree of variables is significantly greater than the number of threads. Value must be greater than 1.
- return_overlap (bool, optional, default=False):
This function returns an embedding, regardless of whether or not qubits are used by multiple variables.
return_overlap
determines the function’s return value. If True, a 2-tuple is returned, in which the first element is the embedding and the second element is a bool representing the embedding validity. If False, only an embedding is returned.- skip_initialization (bool, optional, default=False):
Skip the initialization pass. Note that this only works if the chains passed in through
initial_chains
andfixed_chains
are semi-valid. A semi-valid embedding is a collection of chains such that every adjacent pair of variables (u,v) has a coupler (p,q) in the hardware graph where p is in chain(u) and q is in chain(v). This can be used on a valid embedding to immediately skip to the chain length improvement phase. Another good source of semi-valid embeddings is the output of this function with thereturn_overlap
parameter enabled.- verbose (int, optional, default=0):
Level of output verbosity.
- When set to 0:
Output is quiet until the final result.
- When set to 1:
Output looks like this:
initialized max qubit fill 3; num maxfull qubits=3 embedding trial 1 max qubit fill 2; num maxfull qubits=21 embedding trial 2 embedding trial 3 embedding trial 4 embedding trial 5 embedding found. max chain length 4; num max chains=1 reducing chain lengths max chain length 3; num max chains=5
- When set to 2:
Output the information for lower levels and also report progress on minor statistics (when searching for an embedding, this is when the number of maxfull qubits decreases; when improving, this is when the number of max chains decreases).
- When set to 3:
Report before each pass. Look here when tweaking
tries
,inner_rounds
, andchainlength_patience
.- When set to 4:
Report additional debugging information. By default, this package is built without this functionality. In the C++ headers, this is controlled by the
CPPDEBUG
flag.- Detailed explanation of the output information:
- max qubit fill:
Largest number of variables represented in a qubit.
- num maxfull:
Number of qubits that have max overfill.
- max chain length:
Largest number of qubits representing a single variable.
- num max chains:
Number of variables that have max chain size.
- interactive (bool, optional, default=False):
If logging is None or False, the verbose output will be printed to stdout/stderr as appropriate, and keyboard interrupts will stop the embedding process and the current state will be returned to the user. Otherwise, output will be directed to the logger
logging.getLogger(minorminer.__name__)
and keyboard interrupts will be propagated back to the user. Errors will uselogger.error()
, verbosity levels 1 through 3 will uselogger.info()
and level 4 will uselogger.debug()
.- initial_chains (dict, optional):
Initial chains inserted into an embedding before
fixed_chains
are placed, which occurs before the initialization pass. These can be used to restart the algorithm in a similar state to a previous embedding; for example, to improve chain length of a valid embedding or to reduce overlap in a semi-valid embedding (seeskip_initialization
) previously returned by the algorithm. Missing or empty entries are ignored. Each value in the dictionary is a list of qubit labels.- fixed_chains (dict, optional):
Fixed chains inserted into an embedding before the initialization pass. As the algorithm proceeds, these chains are not allowed to change, and the qubits used by these chains are not used by other chains. Missing or empty entries are ignored. Each value in the dictionary is a list of qubit labels.
- restrict_chains (dict, optional):
Throughout the algorithm, it is guaranteed that chain[i] is a subset of
restrict_chains[i]
for each i, except those with missing or empty entries. Each value in the dictionary is a list of qubit labels.- suspend_chains (dict, optional):
This is a metafeature that is only implemented in the Python interface.
suspend_chains[i]
is an iterable of iterables; for example,suspend_chains[i] = [blob_1, blob_2]
, with each blob_j an iterable of target node labels.This enforces the following:
for each suspended variable i, for each blob_j in the suspension of i, at least one qubit from blob_j will be contained in the chain for i
We accomplish this through the following problem transformation for each iterable blob_j in
suspend_chains[i]
,Add an auxiliary node Zij to both source and target graphs
Set fixed_chains[Zij] = [Zij]
Add the edge (i,Zij) to the source graph
Add the edges (q,Zij) to the target graph for each q in blob_j
Examples#
This example minor embeds a triangular source K3 graph onto a square target graph.
from minorminer import find_embedding
# A triangle is a minor of a square.
triangle = [(0, 1), (1, 2), (2, 0)]
square = [(0, 1), (1, 2), (2, 3), (3, 0)]
# Find an assignment of sets of square variables to the triangle variables
embedding = find_embedding(triangle, square, random_seed=10)
print(len(embedding)) # 3, one set for each variable in the triangle
print(embedding)
# We don't know which variables will be assigned where, here are a
# couple possible outputs:
# [[0, 1], [2], [3]]
# [[3], [1, 0], [2]]
This minorminer execution of the example requires that source variable 0 always be assigned to target node 2.
embedding = find_embedding(triangle, square, fixed_chains={0: [2]})
print(embedding)
# [[2], [3, 0], [1]]
# [[2], [1], [0, 3]]
# And more, but all of them start with [2]
This minorminer execution of the example suggests that source variable 0 be assigned to target node 2 as a starting point for finding an embedding.
embedding = find_embedding(triangle, square, initial_chains={0: [2]})
print(embedding)
# [[2], [0, 3], [1]]
# [[0], [3], [1, 2]]
# Output where source variable 0 has switched to a different target node is possible.
This example minor embeds a fully connected K6 graph into a 30-node random regular graph of degree 3.
import networkx as nx
clique = nx.complete_graph(6).edges()
target_graph = nx.random_regular_graph(d=3, n=30).edges()
embedding = find_clique_embedding(clique, target_graph)
print(embedding)
# There are many possible outputs, and sometimes it might fail
# and return an empty list
# One run returned the following embedding:
{0: [10, 9, 19, 8],
1: [18, 7, 0, 12, 27],
2: [1, 17, 22],
3: [16, 28, 4, 21, 15, 23, 25],
4: [11, 24, 13],
5: [2, 14, 26, 5, 3]}