def embed_ising(source_linear, source_quadratic, embedding, target_adjacency, chain_strength=1.0):
    """Embeds a logical Ising model onto another graph via an embedding.

    Args:
        source_linear (dict): The linear biases to be embedded. Should be a dict of
            the form {v: bias, ...} where v is a variable in the source model
            and bias is the linear bias associated with v.
        source_quadratic (dict): The quadratic biases to be embedded. Should be a dict
            of the form {(u, v): bias, ...} where u, v are variables in the
            source model and bias is the quadratic bias associated with (u, v).
        embedding (dict): The mapping from the source graph to the target graph.
            Should be of the form {v: {s, ...}, ...} where v is a variable in the
            source model and s is a variable in the target model.
        target_adjacency (dict/:class:`networkx.Graph`): The adjacency dict of the target
            graph. Should be a dict of the form {s: Ns, ...} where s is a variable
            in the target graph and Ns is the set of neighbours of s.
        chain_strength (float, optional): The quadratic bias that should be used
            to create chains.

    Returns:
        (dict, dict, dict): A 3-tuple containing:

            dict: The linear biases of the target problem. In the form {s: bias, ...}
            where s is a node in the target graph and bias is the associated linear bias.

            dict: The quadratic biases of the target problem. A dict of the form
            {(s, t): bias, ...} where (s, t) is an edge in the target graph and bias is
            the associated quadratic bias.

            dict: The quadratic biases that induce the variables in the target problem to
            act as one. A dict of the form {(s, t): -chain_strength, ...} which
            is the quadratic biases associated with the chains.

    Examples:
        >>> source_linear = {'a': 1, 'b': 1}
        >>> source_quadratic = {('a', 'b'): -1}
        >>> embedding = {'a': [0, 1], 'b': [2]}
        >>> target_adjacency = {0: {1, 2}, 1: {0, 2}, 2: {0, 1}}
        >>> target_linear, target_quadratic, chain_quadratic = embed_ising(
        ...     source_linear, source_quadratic, embedding, target_adjacency)
        >>> target_linear
        {0: 0.5, 1: 0.5, 2: 1.0}
        >>> target_quadratic
        {(0, 2): -0.5, (1, 2): -0.5}
        >>> chain_quadratic
        {(0, 1): -1.0}

    """

    # store variables in the target graph that the embedding hasn't used
    unused = {v for v in target_adjacency} - set().union(*embedding.values())

    # ok, let's begin with the linear biases.
    # we spread the value of h evenly over the chain
    target_linear = {v: 0. for v in target_adjacency}
    for v, bias in iteritems(source_linear):
        try:
            chain_variables = embedding[v]
        except KeyError:
            # if our embedding doesn't deal with this variable, assume it's an isolated vertex and embed it to one of
            # the unused variables. if this turns out to not be an isolated vertex, it will be caught below when
            # handling quadratic biases
            try:
                embedding[v] = {unused.pop()}
            except KeyError:
                raise ValueError('no embedding provided for source variable {}'.format(v))
            chain_variables = embedding[v]

        b = bias / len(chain_variables)

        for s in chain_variables:
            try:
                target_linear[s] += b
            except KeyError:
                raise ValueError('chain variable {} not in target_adjacency'.format(s))

    # next up the quadratic biases.
    # We spread the quadratic biases evenly over the edges
    target_quadratic = {}
    for (u, v), bias in iteritems(source_quadratic):
        edges = set()

        if u not in embedding:
            raise ValueError('no embedding provided for source variable {}'.format(u))
        if v not in embedding:
            raise ValueError('no embedding provided for source variable {}'.format(v))

        for s in embedding[u]:
            for t in embedding[v]:
                try:
                    if s in target_adjacency[t] and (t, s) not in edges:
                        edges.add((s, t))
                except KeyError:
                    raise ValueError('chain variable {} not in target_adjacency'.format(s))

        if not edges:
            raise ValueError("no edges in target graph between source variables {}, {}".format(u, v))

        b = bias / len(edges)

        # in some cases the logical J can have (u, v) and (v, u) as inputs, so make
        # sure we are not doubling them up with our choice of ordering
        for s, t in edges:
            if (s, t) in target_quadratic:
                target_quadratic[(s, t)] += b
            elif (t, s) in target_quadratic:
                target_quadratic[(t, s)] += b
            else:
                target_quadratic[(s, t)] = b

    # finally we need to connect the nodes in the chains
    chain_quadratic = {}
    for chain in itervalues(embedding):
        chain_quadratic.update(chain_to_quadratic(chain, target_adjacency, chain_strength))

    return target_linear, target_quadratic, chain_quadratic
Embeds a logical Ising model onto another graph via an embedding.

    Args:
        source_linear (dict): The linear biases to be embedded. Should be a dict of
            the form {v: bias, ...} where v is a variable in the source model
            and bias is the linear bias associated with v.
        source_quadratic (dict): The quadratic biases to be embedded. Should be a dict
            of the form {(u, v): bias, ...} where u, v are variables in the
            source model and bias is the quadratic bias associated with (u, v).
        embedding (dict): The mapping from the source graph to the target graph.
            Should be of the form {v: {s, ...}, ...} where v is a variable in the
            source model and s is a variable in the target model.
        target_adjacency (dict/:class:`networkx.Graph`): The adjacency dict of the target
            graph. Should be a dict of the form {s: Ns, ...} where s is a variable
            in the target graph and Ns is the set of neighbours of s.
        chain_strength (float, optional): The quadratic bias that should be used
            to create chains.

    Returns:
        (dict, dict, dict): A 3-tuple containing:

            dict: The linear biases of the target problem. In the form {s: bias, ...}
            where s is a node in the target graph and bias is the associated linear bias.

            dict: The quadratic biases of the target problem. A dict of the form
            {(s, t): bias, ...} where (s, t) is an edge in the target graph and bias is
            the associated quadratic bias.

            dict: The quadratic biases that induce the variables in the target problem to
            act as one. A dict of the form {(s, t): -chain_strength, ...} which
            is the quadratic biases associated with the chains.

    Examples:
        >>> source_linear = {'a': 1, 'b': 1}
        >>> source_quadratic = {('a', 'b'): -1}
        >>> embedding = {'a': [0, 1], 'b': [2]}
        >>> target_adjacency = {0: {1, 2}, 1: {0, 2}, 2: {0, 1}}
        >>> target_linear, target_quadratic, chain_quadratic = embed_ising(
        ...     source_linear, source_quadratic, embedding, target_adjacency)
        >>> target_linear
        {0: 0.5, 1: 0.5, 2: 1.0}
        >>> target_quadratic
        {(0, 2): -0.5, (1, 2): -0.5}
        >>> chain_quadratic
        {(0, 1): -1.0}