Пример #1
0
def output_dw(outfile, problem):
    "Output weights and strengths in dw format."
    if not problem.qubo:
        qprob = problem.convert_to_qubo()
        output_weights, output_strengths = qprob.weights, qprob.strengths
    else:
        output_weights = problem.weights
        output_strengths = problem.strengths
    try:
        L, M, N = qmasm.chimera_topology(qmasm.solver)
    except qmasm.NonChimera:
        qmasm.abend("dw output is supported only for Chimera-graph topologies")
    wdata = []
    for q in range(len(output_weights)):
        if output_weights[q] != 0.0:
            wdata.append("Q%0d <== %.25g" % (q, output_weights[q]))
    wdata.sort()
    sdata = []
    for sp, str in output_strengths.items():
        if str != 0.0:
            try:
                coupler = coupler_number(M, N, L, sp[0], sp[1])
            except IndexError:
                qmasm.abend("dw output is supported only for Chimera-graph topologies")
            sdata.append("C%04d <== %.25g" % (coupler, str))
    sdata.sort()
    outfile.write("\n".join(wdata + sdata) + "\n")
Пример #2
0
def output_dw(outfile, problem):
    "Output weights and strengths in dw format."
    if not problem.qubo:
        qprob = problem.convert_to_qubo()
        output_weights, output_strengths = qprob.weights, qprob.strengths
    else:
        output_weights = problem.weights
        output_strengths = problem.strengths
    try:
        L, M, N = qmasm.chimera_topology(qmasm.solver)
    except qmasm.NonChimera:
        qmasm.abend("dw output is supported only for Chimera-graph topologies")
    wdata = []
    for q in range(len(output_weights)):
        if output_weights[q] != 0.0:
            wdata.append("Q%0d <== %.25g" % (q, output_weights[q]))
    wdata.sort()
    sdata = []
    for sp, str in output_strengths.items():
        if str != 0.0:
            try:
                coupler = coupler_number(M, N, L, sp[0], sp[1])
            except IndexError:
                qmasm.abend("dw output is supported only for Chimera-graph topologies")
            sdata.append("C%04d <== %.25g" % (coupler, str))
    sdata.sort()
    outfile.write("\n".join(wdata + sdata) + "\n")
Пример #3
0
def output_dw(outfile, problem):
    "Output weights and strengths in dw format."
    if not problem.qubo:
        qprob = problem.convert_to_qubo()
        output_weights, output_strengths = qprob.weights, qprob.strengths
    else:
        output_weights = problem.weights
        output_strengths = problem.strengths
    try:
        L, M, N = qmasm.chimera_topology(qmasm.solver)
    except KeyError:
        qmasm.abend("Failed to query the chimera topology")
    wdata = []
    for q in range(len(output_weights)):
        if output_weights[q] != 0.0:
            wdata.append("Q%0d <== %.25g" % (q, output_weights[q]))
    wdata.sort()
    sdata = []
    for sp, str in list(output_strengths.items()):
        if str != 0.0:
            coupler = coupler_number(M, N, L, sp[0], sp[1])
            sdata.append("C%04d <== %.25g" % (coupler, str))
    sdata.sort()
    outfile.write("\n".join(wdata + sdata) + "\n")
Пример #4
0
def output_bqpjson(outfile, as_qubo, problem):
    "Output weights and strengths in bqpjson format, either Ising or QUBO."
    # Prepare the "easy" fields.
    bqp = {}
    bqp["version"] = "1.0.0"
    bqp["id"] = random.randint(2**20, 2**60)
    bqp["scale"] = 1.0
    bqp["offset"] = 0.0
    if as_qubo:
        bqp["variable_domain"] = "boolean"
    else:
        bqp["variable_domain"] = "spin"

    # Prepare the list of all variables.
    var_ids = set(problem.weights.keys())
    for q1, q2 in problem.strengths.keys():
        var_ids.add(q1)
        var_ids.add(q2)
    bqp["variable_ids"] = sorted(var_ids)

    # Prepare the linear terms.
    lin_terms = []
    for q, wt in sorted(problem.weights.items()):
        lin_terms.append({
            "id": q,
            "coeff": wt})
    bqp["linear_terms"] = lin_terms

    # Prepare the quadratic terms.
    quad_terms = []
    strengths = qmasm.canonicalize_strengths(problem.strengths)
    for (q1, q2), wt in sorted(strengths.items()):
        quad_terms.append({
            "id_tail": q1,
            "id_head": q2,
            "coeff": wt})
    bqp["quadratic_terms"] = quad_terms

    # Prepare some metadata.
    metadata = {}
    if as_qubo:
        metadata["description"] = "QUBO problem compiled by QMASM (https://github.com/lanl/qmasm)"
    else:
        metadata["description"] = "Ising problem compiled by QMASM (https://github.com/lanl/qmasm)"
    metadata["command_line"] = qmasm.get_command_line()
    metadata["generated"] = datetime.datetime.utcnow().isoformat()
    if hasattr(problem, "embedding"):
        # Physical problem
        def attempt_assign(key, func):
            "Try assigning a key, but don't complain if we can't."
            try:
                metadata[key] = func()
            except KeyError:
                pass
        attempt_assign("dw_url", lambda: os.environ["DW_INTERNAL__HTTPLINK"])
        attempt_assign("dw_solver_name", lambda: qmasm.solver_name)
        props = qmasm.solver.properties
        attempt_assign("dw_chip_id", lambda: props["chip_id"])
        L, M, N = qmasm.chimera_topology(qmasm.solver)
        metadata["chimera_cell_size"] = L*2
        metadata["chimera_degree"] = max(M, N)
        metadata["equivalent_ids"] = sorted(problem.chains)
        metadata["variable_names"] = {s: problem.embedding[n]
                                      for s, n in qmasm.sym_map.symbol_number_items()}
    else:
        metadata["variable_names"] = {s: [n]
                                      for s, n in qmasm.sym_map.symbol_number_items()}
    bqp["metadata"] = metadata

    # Output the problem in JSON format.
    outfile.write(json.dumps(bqp, indent=2, sort_keys=True) + "\n")
Пример #5
0
def find_dwave_embedding(logical, optimization, verbosity, hw_adj_file):
    """Find an embedding of a logical problem in the D-Wave's physical topology.
    Store the embedding within the Problem object."""
    # SAPI tends to choke when embed_problem is told to embed a problem
    # containing a zero-weight node whose adjacent couplers all have zero
    # strength.  (Tested with SAPI 2.4.)  To help out SAPI, we simply remove
    # all zero-strength couplers.
    edges = [
        e for e in logical.strengths.keys() if logical.strengths[e] != 0.0
    ]
    edges.sort()
    logical.edges = edges
    if hw_adj_file == None:
        try:
            hw_adj = get_hardware_adjacency(qmasm.solver)
        except KeyError:
            # The Ising heuristic solver is an example of a solver that lacks a
            # fixed hardware representation.  We therefore assert that the
            # hardware is an all-to-all network that connects every node to
            # every other node.
            endpoints = set([a for a, b in edges] + [b for a, b in edges])
            hw_adj = [(a, b) for a in endpoints for b in endpoints if a != b]
    else:
        hw_adj = read_hardware_adjacency(hw_adj_file, verbosity)

    # Tell the user if we have any hope at all of embedding the problem.
    if verbosity >= 2:
        report_embeddability(edges, hw_adj)

    # Determine the edges of a rectangle of cells we want to use.  If we read
    # the topology from a file or otherwise can't prove that we have a Chimera
    # graph, we call this rectangle 0x0 and force the main embedding loop to
    # exit after a single iteration because we don't know the topology is even
    # rectangular.
    edgex = 0
    edgey = 0
    M = 0
    N = 0
    try:
        if hw_adj_file == None:
            L, M, N = qmasm.chimera_topology(qmasm.solver)
            L2 = 2 * L
            ncells = (qmasm.next_sym_num +
                      L2) // L2  # Round up the number of cells.
            if optimization >= 2:
                edgey = max(int(math.sqrt(ncells)), 1)
                edgex = max((ncells + edgey - 1) // edgey, 1)
            else:
                edgey = N
                edgex = M
    except qmasm.NonChimera:
        pass

    # Announce what we're about to do.
    if verbosity >= 2:
        sys.stderr.write(
            "Embedding the logical adjacency within the physical topology.\n\n"
        )

    # Repeatedly expand edgex and edgey until the embedding works.
    while edgex <= M and edgey <= N:
        if edgex == M and edgey == N:
            alt_hw_adj = hw_adj
        else:
            # Retain adjacencies only within the rectangle.
            alt_hw_adj = []
            for q1, q2 in hw_adj:
                c1 = q1 // L2
                if c1 % M >= edgex:
                    continue
                if c1 // M >= edgey:
                    continue
                c2 = q2 // L2
                if c2 % M >= edgex:
                    continue
                if c2 // M >= edgey:
                    continue
                alt_hw_adj.append((q1, q2))
            alt_hw_adj = set(alt_hw_adj)
        logical.hw_adj = alt_hw_adj

        # See if we already have an embedding in the embedding cache.
        ec = EmbeddingCache(edges, alt_hw_adj)
        if verbosity >= 2:
            if ec.cachedir == None:
                sys.stderr.write(
                    "  No embedding cache directory was specified ($QMASMCACHE).\n"
                )
            else:
                sys.stderr.write(
                    "  Using %s as the embedding cache directory ...\n" %
                    ec.cachedir)
        embedding = ec.read()
        if embedding == []:
            # Cache hit, but embedding had failed
            if verbosity >= 2:
                sys.stderr.write(
                    "  Found failed embedding %s in the embedding cache.\n\n" %
                    ec.hash)
        elif embedding != None:
            # Successful cache hit!
            if verbosity >= 2:
                sys.stderr.write(
                    "  Found successful embedding %s in the embedding cache.\n\n"
                    % ec.hash)
            logical.embedding = embedding
            return
        if verbosity >= 2 and ec.cachedir != None:
            sys.stderr.write(
                "  No existing embedding found in the embedding cache.\n")

        # Try to find an embedding, unless we previously determined that it had
        # failed.
        if embedding != []:
            if verbosity >= 2:
                # SAPI's find_embedding is hard-wired to write to stdout.
                # Trick it into writing into a pipe instead.
                if edgex == 0 and edgey == 0:
                    sys.stderr.write("  Trying to embed ... ")
                else:
                    sys.stderr.write(
                        "  Trying a %dx%d unit-cell embedding ...\n\n" %
                        (edgex, edgey))
                sepLine = "=== EMBEDDING ===\n"
                r, w = os.pipe()
                pid = os.fork()
                if pid == 0:
                    # Child -- perform the embedding.
                    os.close(r)
                    os.dup2(w, sys.stdout.fileno())
                    embedding = find_embedding(edges, alt_hw_adj, verbose=1)
                    sys.stdout.flush()
                    os.write(w, sepLine)
                    os.write(w, json.dumps(embedding) + "\n")
                    os.close(w)
                    os._exit(0)
                else:
                    # Parent -- report the embedding's progress.
                    os.close(w)
                    pipe = os.fdopen(r, "r", 10000)
                    while True:
                        try:
                            rstr = pipe.readline()
                            if rstr == sepLine:
                                break
                            if rstr == "":
                                qmasm.abend(
                                    "Embedder failed to terminate properly")
                            sys.stderr.write("      %s" % rstr)
                        except:
                            pass

                    # Receive the embedding from the child.
                    embedding = json.loads(pipe.readline())
                    sys.stderr.write("\n")
            else:
                embedding = find_embedding(edges, alt_hw_adj, verbose=0)
            ec.write(embedding)
            if len(embedding) > 0:
                # Success!
                break

        # Continued failure -- increase edgex or edgey and try again.
        if edgex < edgey:
            edgex += 1
        else:
            edgey += 1
    if not (edgex <= M and edgey <= N):
        qmasm.abend("Failed to embed the problem")
    logical.embedding = embedding
Пример #6
0
            continue
        name_list = string.join(sorted(canon2syms[i]))
        sys.stderr.write("    q%-8d  %s\n" % (i + 1, name_list))
    sys.stderr.write("\n")

# Establish a connection to the D-Wave, and use this to talk to a solver.  We
# rely on the qOp infrastructure to set the environment variables properly.
qmasm.connect_to_dwave()

# Output most or all solver properties.
if cl_args.verbose >= 1:
    # Determine the width of the widest key.
    max_key_len = len("Parameter")
    ext_solver_properties = {}
    try:
        L, M, N = qmasm.chimera_topology(qmasm.solver)
        ext_solver_properties["chimera_toplogy_M_N_L"] = [M, N, L]
    except KeyError:
        pass
    ext_solver_properties.update(qmasm.solver.properties)
    solver_props = ext_solver_properties.keys()
    solver_props.sort()
    for k in solver_props:
        max_key_len = max(max_key_len, len(k))

    # Output either "short" values (if verbose = 1) or all values (if
    # verbose > 1).
    short_value_len = 70 - max_key_len
    sys.stderr.write("Encountered the following solver properties:\n\n")
    sys.stderr.write("    %-*s  Value\n" % (max_key_len, "Parameter"))
    sys.stderr.write("    %s  -----\n" % ("-" * max_key_len))
Пример #7
0
def find_dwave_embedding(logical, optimize, verbosity):

    """Find an embedding of a logical problem in the D-Wave's physical topology.
    Store the embedding within the Problem object."""
    # SAPI tends to choke when embed_problem is told to embed a problem
    # containing a zero-weight node whose adjacent couplers all have zero
    # strength.  (Tested with SAPI 2.4.)  To help out SAPI, we simply remove
    # all zero-strength couplers.
    edges = [e for e in list(logical.strengths.keys()) if logical.strengths[e] != 0.0]
    edges.sort()
    logical.edges = edges
    try:
        hw_adj = get_hardware_adjacency(qmasm.solver)
    except KeyError:
        # The Ising heuristic solver is an example of a solver that lacks a
        # fixed hardware representation.  We therefore assert that the hardware
        # is an all-to-all network that connects every node to every other node.
        endpoints = set([a for a, b in edges] + [b for a, b in edges])
        hw_adj = [(a, b) for a in endpoints for b in endpoints if a != b]

    # Tell the user if we have any hope at all of embedding the problem.
    if verbosity >= 2:
        report_embeddability(edges, hw_adj)

    # Determine the edges of a rectangle of cells we want to use.
    L, M, N = qmasm.chimera_topology(qmasm.solver)
    L2 = 2*L
    ncells = (qmasm.next_sym_num + L2) // L2   # Round up the number of cells.
    if optimize:
        edgey = max(int(math.sqrt(ncells)), 1)
        edgex = max((ncells + edgey - 1) // edgey, 1)
    else:
        edgey = N
        edgex = M

    # Announce what we're about to do.
    if verbosity >= 2:
        sys.stderr.write("Embedding the logical adjacency within the physical topology.\n\n")

    # Repeatedly expand edgex and edgey until the embedding works.
    while edgex <= M and edgey <= N:
        # Retain adjacencies only within the rectangle.
        alt_hw_adj = []
        for q1, q2 in hw_adj:
            c1 = q1//L2
            if c1 % M >= edgex:
                continue
            if c1 // M >= edgey:
                continue
            c2 = q2//L2
            if c2 % M >= edgex:
                continue
            if c2 // M >= edgey:
                continue
            alt_hw_adj.append((q1, q2))
        alt_hw_adj = set(alt_hw_adj)
        logical.hw_adj = alt_hw_adj

        # See if we already have an embedding in the embedding cache.
        ec = EmbeddingCache(edges, alt_hw_adj)
        if verbosity >= 2:
            if ec.cachedir == None:
                sys.stderr.write("  No embedding cache directory was specified ($QMASMCACHE).\n")
            else:
                sys.stderr.write("  Using %s as the embedding cache directory ...\n" % ec.cachedir)
        embedding = ec.read()
        if embedding == []:
            # Cache hit, but embedding had failed
            if verbosity >= 2:
                sys.stderr.write("  Found failed embedding %s in the embedding cache.\n\n" % ec.hash)
        elif embedding != None:
            # Successful cache hit!
            if verbosity >= 2:
                sys.stderr.write("  Found successful embedding %s in the embedding cache.\n\n" % ec.hash)
            logical.embedding = embedding
            return
        if verbosity >= 2 and ec.cachedir != None:
            sys.stderr.write("  No existing embedding found in the embedding cache.\n")

        # Try to find an embedding, unless we previously determined that it had
        # failed.
        if embedding != []:
            if verbosity >= 2:
                sys.stderr.write("  Trying a %dx%d unit-cell embedding ... " % (edgex, edgey))
                status_file = tempfile.NamedTemporaryFile(mode="w", delete=False)
                stdout_fd = os.dup(sys.stdout.fileno())
                os.dup2(status_file.fileno(), sys.stdout.fileno())
                embedding = find_embedding(edges, alt_hw_adj, verbose=1)
                sys.stdout.flush()
                os.dup2(stdout_fd, sys.stdout.fileno())
                status_file.close()
                if len(embedding) > 0:
                    sys.stderr.write("succeeded\n\n")
                else:
                    sys.stderr.write("failed\n\n")
                with open(status_file.name, "r") as status:
                    for line in status:
                        sys.stderr.write("    %s" % line)
                sys.stderr.write("\n")
                os.remove(status_file.name)
            else:
                embedding = find_embedding(edges, alt_hw_adj, verbose=0)
            ec.write(embedding)
            if len(embedding) > 0:
                # Success!
                break

        # Continued failure -- increase edgex or edgey and try again.
        if edgex < edgey:
            edgex += 1
        else:
            edgey += 1
    if not(edgex <= M and edgey <= N):
        qmasm.abend("Failed to embed the problem")
    logical.embedding = embedding
Пример #8
0
def output_bqpjson(outfile, as_qubo, problem):
    "Output weights and strengths in bqpjson format, either Ising or QUBO."
    # Prepare the "easy" fields.
    bqp = {}
    bqp["version"] = "1.0.0"
    bqp["id"] = random.randint(2**20, 2**60)
    bqp["scale"] = 1.0
    bqp["offset"] = 0.0
    if as_qubo:
        bqp["variable_domain"] = "boolean"
    else:
        bqp["variable_domain"] = "spin"

    # Prepare the list of all variables.
    var_ids = set(problem.weights.keys())
    for q1, q2 in problem.strengths.keys():
        var_ids.add(q1)
        var_ids.add(q2)
    bqp["variable_ids"] = sorted(var_ids)

    # Prepare the linear terms.
    lin_terms = []
    for q, wt in sorted(problem.weights.items()):
        lin_terms.append({
            "id": q,
            "coeff": wt})
    bqp["linear_terms"] = lin_terms

    # Prepare the quadratic terms.
    quad_terms = []
    strengths = qmasm.canonicalize_strengths(problem.strengths)
    for (q1, q2), wt in sorted(strengths.items()):
        quad_terms.append({
            "id_tail": q1,
            "id_head": q2,
            "coeff": wt})
    bqp["quadratic_terms"] = quad_terms

    # Prepare some metadata.
    metadata = {}
    if as_qubo:
        metadata["description"] = "QUBO problem compiled by QMASM (https://github.com/lanl/qmasm)"
    else:
        metadata["description"] = "Ising problem compiled by QMASM (https://github.com/lanl/qmasm)"
    metadata["command_line"] = qmasm.get_command_line()
    metadata["generated"] = datetime.datetime.utcnow().isoformat()
    if hasattr(problem, "embedding"):
        # Physical problem
        def attempt_assign(key, func):
            "Try assigning a key, but don't complain if we can't."
            try:
                metadata[key] = func()
            except KeyError:
                pass
        attempt_assign("dw_url", lambda: os.environ["DW_INTERNAL__HTTPLINK"])
        attempt_assign("dw_solver_name", lambda: qmasm.solver_name)
        props = qmasm.solver.properties
        attempt_assign("dw_chip_id", lambda: props["chip_id"])
        L, M, N = qmasm.chimera_topology(qmasm.solver)
        metadata["chimera_cell_size"] = L*2
        metadata["chimera_degree"] = max(M, N)
        metadata["equivalent_ids"] = sorted(problem.chains)
        metadata["variable_names"] = {s: problem.embedding[n]
                                      for s, n in qmasm.sym_map.symbol_number_items()}
    else:
        metadata["variable_names"] = {s: [n]
                                      for s, n in qmasm.sym_map.symbol_number_items()}
    bqp["metadata"] = metadata

    # Output the problem in JSON format.
    outfile.write(json.dumps(bqp, indent=2, sort_keys=True) + "\n")
Пример #9
0
if len(discon_syms) > 0:
    qmasm.abend("Disconnected variables encountered: %s" % " ".join(sorted(discon_syms)))

# Convert user-specified chains, anti-chains, and pins to assertions.
logical_ising.append_assertions_from_statements()

# Establish a connection to the D-Wave, and use this to talk to a solver.  We
# rely on the qOp infrastructure to set the environment variables properly.
qmasm.connect_to_dwave()

# Output either short or all solver properties.
if cl_args.verbose >= 1:
    # Introduce a few extra solver properties.
    ext_solver_properties = {}
    try:
        L, M, N = qmasm.chimera_topology(qmasm.solver)
        ext_solver_properties["chimera_toplogy_M_N_L"] = [M, N, L]
    except KeyError:
        pass
    except qmasm.NonChimera:
        pass
    ext_solver_properties["solver_name"] = qmasm.solver_name
    try:
        ext_solver_properties["connection_name"] = os.environ["DW_INTERNAL__CONNECTION"]
    except KeyError:
        pass
    for what in ["couplers", "qubits"]:
        try:
            ext_solver_properties["num_active_" + what] = len(qmasm.solver.properties[what])
        except KeyError:
            pass
Пример #10
0
def find_dwave_embedding(logical, optimization, verbosity, hw_adj_file):
    """Find an embedding of a logical problem in the D-Wave's physical topology.
    Store the embedding within the Problem object."""
    # SAPI tends to choke when embed_problem is told to embed a problem
    # containing a zero-weight node whose adjacent couplers all have zero
    # strength.  (Tested with SAPI 2.4.)  To help out SAPI, we simply remove
    # all zero-strength couplers.
    edges = [e for e in logical.strengths.keys() if logical.strengths[e] != 0.0]
    edges.sort()
    logical.edges = edges
    if hw_adj_file == None:
        try:
            hw_adj = get_hardware_adjacency(qmasm.solver)
        except KeyError:
            # The Ising heuristic solver is an example of a solver that lacks a
            # fixed hardware representation.  We therefore assert that the
            # hardware is an all-to-all network that connects every node to
            # every other node.
            endpoints = set([a for a, b in edges] + [b for a, b in edges])
            hw_adj = [(a, b) for a in endpoints for b in endpoints if a != b]
    else:
        hw_adj = read_hardware_adjacency(hw_adj_file, verbosity)

    # Tell the user if we have any hope at all of embedding the problem.
    if verbosity >= 2:
        report_embeddability(edges, hw_adj)

    # Determine the edges of a rectangle of cells we want to use.  If we read
    # the topology from a file or otherwise can't prove that we have a Chimera
    # graph, we call this rectangle 0x0 and force the main embedding loop to
    # exit after a single iteration because we don't know the topology is even
    # rectangular.
    edgex = 0
    edgey = 0
    M = 0
    N = 0
    num_vars = len(qmasm.sym_map.all_numbers())
    try:
        if hw_adj_file == None:
            L, M, N = qmasm.chimera_topology(qmasm.solver)
            L2 = 2*L
            ncells = (num_vars + L2) // L2   # Round up the number of cells.
            if optimization >= 2:
                edgey = max(int(math.sqrt(ncells)), 1)
                edgex = max((ncells + edgey - 1) // edgey, 1)
            else:
                edgey = N
                edgex = M
    except qmasm.NonChimera:
        pass

    # Announce what we're about to do.
    if verbosity >= 2:
        sys.stderr.write("Embedding the logical adjacency within the physical topology.\n\n")

    # Repeatedly expand edgex and edgey until the embedding works.
    while edgex <= M and edgey <= N:
        if edgex == M and edgey == N:
            alt_hw_adj = hw_adj
        else:
            # Retain adjacencies only within the rectangle.
            alt_hw_adj = []
            for q1, q2 in hw_adj:
                c1 = q1//L2
                if c1 % M >= edgex:
                    continue
                if c1 // M >= edgey:
                    continue
                c2 = q2//L2
                if c2 % M >= edgex:
                    continue
                if c2 // M >= edgey:
                    continue
                alt_hw_adj.append((q1, q2))
            alt_hw_adj = set(alt_hw_adj)
        logical.hw_adj = alt_hw_adj

        # See if we already have an embedding in the embedding cache.
        ec = EmbeddingCache(edges, alt_hw_adj)
        if verbosity >= 2:
            if ec.cachedir == None:
                sys.stderr.write("  No embedding cache directory was specified ($QMASMCACHE).\n")
            else:
                sys.stderr.write("  Using %s as the embedding cache directory ...\n" % ec.cachedir)
        embedding = ec.read()
        if embedding == []:
            # Cache hit, but embedding had failed
            if verbosity >= 2:
                sys.stderr.write("  Found failed embedding %s in the embedding cache.\n\n" % ec.hash)
        elif embedding != None:
            # Successful cache hit!
            if verbosity >= 2:
                sys.stderr.write("  Found successful embedding %s in the embedding cache.\n\n" % ec.hash)
            logical.embedding = embedding
            return
        if verbosity >= 2 and ec.cachedir != None:
            sys.stderr.write("  No existing embedding found in the embedding cache.\n")

        # Try to find an embedding, unless we previously determined that it had
        # failed.
        if embedding != []:
            if verbosity >= 2:
                # SAPI's find_embedding is hard-wired to write to stdout.
                # Trick it into writing into a pipe instead.
                if edgex == 0 and edgey == 0:
                    sys.stderr.write("  Trying to embed ... ")
                else:
                    sys.stderr.write("  Trying a %dx%d unit-cell embedding ...\n\n" % (edgex, edgey))
                sepLine = "=== EMBEDDING ===\n"
                r, w = os.pipe()
                pid = os.fork()
                if pid == 0:
                    # Child -- perform the embedding.
                    os.close(r)
                    os.dup2(w, sys.stdout.fileno())
                    embedding = find_embedding(edges, alt_hw_adj, verbose=1)
                    sys.stdout.flush()
                    os.write(w, sepLine)
                    os.write(w, json.dumps(embedding) + "\n")
                    os.close(w)
                    os._exit(0)
                else:
                    # Parent -- report the embedding's progress.
                    os.close(w)
                    pipe = os.fdopen(r, "r", 10000)
                    while True:
                        try:
                            rstr = pipe.readline()
                            if rstr == sepLine:
                                break
                            if rstr == "":
                                qmasm.abend("Embedder failed to terminate properly")
                            sys.stderr.write("      %s" % rstr)
                        except:
                            pass

                    # Receive the embedding from the child.
                    embedding = json.loads(pipe.readline())
                    sys.stderr.write("\n")
            else:
                embedding = find_embedding(edges, alt_hw_adj, verbose=0)
            ec.write(embedding)
            if len(embedding) > 0:
                # Success!
                break

        # Continued failure -- increase edgex or edgey and try again.
        if edgex < edgey:
            edgex += 1
        else:
            edgey += 1
    if not(edgex <= M and edgey <= N):
        qmasm.abend("Failed to embed the problem")
    logical.embedding = embedding