Beispiel #1
0
def test_infer_isolated():
    gr_size = 1000
    model = PairWiseFiniteModel(gr_size, 2)
    model.set_field(np.array([[0, 1]] * gr_size))
    res = model.infer(algorithm='bruteforce')
    assert np.allclose(res.log_pf, gr_size * np.log(1 + np.exp(1)))
    assert np.allclose(res.marg_prob - softmax([0, 1]), 0)
Beispiel #2
0
def test_infer_ising_2_variables():
    model = PairWiseFiniteModel(2, 2)
    j = 5 * np.random.random()
    model.add_interaction(0, 1, np.array([[j, -j], [-j, j]]))

    result = model.infer(algorithm='bruteforce')

    assert np.allclose(result.log_pf, np.log(4 * np.cosh(j)))
    assert np.allclose(result.marg_prob, 0.5 * np.ones((2, 2)))
Beispiel #3
0
def test_infer_1_variable():
    al_size = 10
    probs = _stochastic_vector(al_size)
    model = PairWiseFiniteModel(1, al_size)
    model.set_field(np.log(probs).reshape(1, al_size))

    result = infer_bruteforce(model)

    assert np.allclose(result.log_pf, 0)
    assert np.allclose(result.marg_prob, probs)
Beispiel #4
0
def ising_model_on_graph(graph: networkx.Graph,
                         field_range=0.1,
                         interaction_range=0.1,
                         seed=0) -> PairWiseFiniteModel:
    """Builds random Ising model on given graph.

    :param graph: Graph for the model. Vertices are variables,
      nodes are interactions.
    :param field_range: Fields will be sampled uniformly from
      ``[-field_range, field_range]``.
    :param interaction_range: Interactions will be sampled uniformly from
      ``[-interaction_range, interaction_range]``.
    :param seed: Random seed.
    :return: Generated model.
    """
    # Remap arbitrary variable labels to integers.
    nodes = list(graph.nodes)
    var_index = {nodes[i]: i for i in range(len(nodes))}
    edges = [(var_index[u], var_index[v]) for u, v in graph.edges()]

    np.random.seed(seed)
    field = np.random.uniform(low=-field_range,
                              high=field_range,
                              size=(len(nodes), ))
    field = np.einsum('a,b->ab', field, [-1, 1])
    inter = np.random.uniform(low=-interaction_range,
                              high=interaction_range,
                              size=(len(edges), ))
    inter = np.einsum('a,bc->abc', inter, [[1, -1], [-1, 1]])

    return PairWiseFiniteModel.create(field, edges, inter)
Beispiel #5
0
def grid_potts_model(height,
                     width,
                     al_size=3,
                     seed=111,
                     zero_field=False) -> PairWiseFiniteModel:
    """Generates random PairWiseFinteModel on a grid.

    :param height: Heigth of the grid.
    :param width: Wwidth of the grid.
    :param al_size: Alphabet size.
    :param seed: Random seed.
    :param zero_field: Whether model should be zero-field.
    :return: Generated Potts Model.
    """
    np.random.seed(seed)
    gr_size = width * height
    edges_num = 2 * width * height - width - height
    edges = []
    for x in range(height):
        for y in range(width):
            v = x * width + y
            if x != height - 1:
                edges.append((v, v + width))  # down
            if y != width - 1:
                edges.append((v, v + 1))  # right
    field = 0.1 * np.random.random(size=(gr_size, al_size))
    if zero_field:
        field *= 0
    inter = np.random.random(size=(edges_num, al_size, al_size)) * 5.0
    return PairWiseFiniteModel.create(field, edges, inter)
Beispiel #6
0
def test_max_likelihood():
    model = PairWiseFiniteModel(3, 2)
    model.set_field(np.array([[0.4, 0.6], [0.4, 0.6], [0.4, 0.6]]))
    model.add_interaction(0, 1, np.array([[0, 10], [10, 0]]))
    model.add_interaction(1, 2, np.array([[0, 10], [10, 0]]))

    state = model.max_likelihood(algorithm='bruteforce')

    assert np.allclose(state, np.array([1, 0, 1]))
Beispiel #7
0
def tree_potts_model(gr_size=5,
                     al_size=2,
                     seed=111,
                     same_j=None,
                     zero_field=False) -> PairWiseFiniteModel:
    """Generates random PairWiseFinteModel on a random tree.

    :param gr_size: Size of the graph (number of variables).
    :param al_size: Alphabet size.
    :param seed: Random set.
    :param same_j: If set, interaction matrix for all edges.
    :param zero_field: Whether model should be zero-field.
    :return: Generated Potts Model.
    """
    np.random.seed(seed)
    tree = networkx.generators.trees.random_tree(gr_size, seed=seed)
    model = PairWiseFiniteModel(gr_size, al_size)
    if not zero_field:
        model.set_field(-3.0 + 6.0 * np.random.random((gr_size, al_size)))
    for v1, v2 in tree.edges:
        J = np.random.random((al_size, al_size)) * 5.0
        if same_j is not None:
            J = same_j
        model.add_interaction(v1, v2, J)
    return model
Beispiel #8
0
def test_cycle3():
    np.random.seed(0)
    model = PairWiseFiniteModel(3, 3)
    for i, j in [(0, 1), (1, 2), (0, 2)]:
        model.add_interaction(i, j, np.random.random(size=(3, 3)))
    gt = model.infer(algorithm='bruteforce')

    result = model.infer(algorithm='mean_field')

    assert_results_close(result, gt, log_pf_tol=0.1, mp_mse_tol=1e-4)
Beispiel #9
0
def clique_potts_model(gr_size=5, al_size=2, seed=0) -> PairWiseFiniteModel:
    """Generates random PairWiseFinteModel on a clique."""
    np.random.seed(seed)
    model = PairWiseFiniteModel(gr_size, al_size)
    model.set_field(-3.0 + 6.0 * np.random.random((gr_size, al_size)))
    for i in range(gr_size):
        for j in range(i + 1, gr_size):
            inter = np.random.random((al_size, al_size)) * 5.0
            model.add_interaction(i, j, inter)
    return model
Beispiel #10
0
def test_isolated_exact():
    np.random.seed(0)
    gr_size = 1000
    al_size = 5
    model = PairWiseFiniteModel(gr_size, al_size)
    model.set_field(np.random.random((gr_size, al_size)))
    gt = model.infer(algorithm='bruteforce')

    result = model.infer(algorithm='mean_field')

    assert_results_close(result, gt)
Beispiel #11
0
def pairwise_model_on_graph(graph, al_size=2, zero_field=False, seed=0):
    """Builds random pairwise model with given interaction graph.

    :param graph: Interaction graph. Nodes must be labeled with consecutive
      integers, starting with 0.
    :param al_size: Alphabet size.
    :param zero_field: Whether model should be zero-field.
    :param seed: Random seed.
    :return: Generated model.
    """
    np.random.seed(seed)
    gr_size = len(graph.nodes())
    field = np.random.random(size=(gr_size, al_size))
    if zero_field:
        field *= 0
    edges = np.array(list(graph.edges()))
    interactions = np.random.random(size=(len(edges), al_size, al_size))
    return PairWiseFiniteModel.create(field, edges, interactions)
Beispiel #12
0
def line_potts_model(gr_size=5,
                     al_size=2,
                     seed=111,
                     same_j=None,
                     zero_field=False) -> PairWiseFiniteModel:
    """Generates random PairWiseFinteModel on a line graph.

    :param gr_size: Size of the graph (number of variables).
    :param al_size: Alphabet size.
    :param seed: Random seed.
    :param same_j: If set, interaction matrix for all edges.
    :param zero_field: Whether model should be zero-field.
    :return: Generated model.
    """
    np.random.seed(seed)
    field = np.zeros((gr_size, al_size))
    if not zero_field:
        field = -3.0 + 6.0 * np.random.random(field.shape)
    edges = [[i, i + 1] for i in range(gr_size - 1)]
    inter = np.random.random(size=(gr_size - 1, al_size, al_size)) * 5.0
    if same_j is not None:
        inter = np.tile(same_j, (gr_size - 1, 1, 1))
    return PairWiseFiniteModel.create(field, edges, inter)
Beispiel #13
0
def test_sample_bruteforce():
    gr_size, num_samples = 3, 50
    model = PairWiseFiniteModel(gr_size, 2)
    model.set_field(np.array([[0, 20]] * gr_size))
    samples = sample_bruteforce(model, num_samples=num_samples)
    assert np.allclose(samples, np.ones((num_samples, gr_size)))
Beispiel #14
0
def test_max_likelihood_isolated():
    gr_size = 1000
    model = PairWiseFiniteModel(gr_size, 2)
    model.set_field(np.array([[0, 1]] * gr_size))
    result = model.max_likelihood(algorithm='bruteforce')
    assert np.allclose(result, np.ones(gr_size))
Beispiel #15
0
def to_junction_tree_model(model, algorithm) -> JunctionizedModel:
    """Builds equivalent model on a junction tree.

    First, builds a junction tree using algorithm from NetworkX which uses
    Minimum Fill-in heuristic.

    Then, builds a new model in which variables correspond to nodes in junction
    tree - we will call them "supervariables". Values of new supervariables are
    encoded values of original variables. New alphabet size is original
    alphabet size to the power of maximaljunction size. If some supervariables
    have less variables than others, we just don't use all available for
    encoding "address space". We mark those impossible values as having
    probability 0 (i.e log probability -inf).

    Fields in new model are calculated by multiplying all field and
    interaction factors on variables in the same supervariable. While doing
    this, we make sure that every factor is counted only once. If some factor
    was accounted for in one supervariable field, it won't be accounted for
    again in other supervariables.

    Interaction factors in new model contain consistency requirement. If
    a variable of original model appears in multiple supervariables, we allow
    only those states where it takes the same value in all supervariables. We
    achieve that by using interaction factors which are equal to 1 if values
    of the same original variable in different supervariables are equal, and
    0 if they are not equal. We actually use values 0 and -inf, because we
    work with logarithms.

    See https://en.wikipedia.org/wiki/Tree_decomposition.

    :param model: original model.
    :param algorithm: decomposition algorithm.
    :return: JunctionizedModel object, which contains junction tree and the
      new model, which is equivalent to original model, but whose graph is a
      tree.
    """
    # Build junction tree.
    graph = model.get_graph()
    if algorithm == 'min_fill_in':
        tree_width, junc_tree = treewidth_min_fill_in(graph)
    elif algorithm == 'min_degree':
        tree_width, junc_tree = treewidth_min_degree(graph)
    elif algorithm == 'auto':
        tree_width_1, junc_tree_1 = treewidth_min_fill_in(graph)
        tree_width_2, junc_tree_2 = treewidth_min_degree(graph)
        if tree_width_1 < tree_width_2:
            tree_width, junc_tree = tree_width_1, junc_tree_1
        else:
            tree_width, junc_tree = tree_width_2, junc_tree_2
    else:
        raise ValueError('Unknown treewidth decomposition algorithm %s' %
                         algorithm)

    jt_nodes = list(junc_tree.nodes())
    sv_size = tree_width + 1  # Supervariable size.

    new_gr_size = len(jt_nodes)  # New graph size.
    new_al_size = model.al_size**sv_size  # New alphabet size.
    if new_al_size > 1e6:
        raise TooMuchStatesError("New domain size is too large: %d." %
                                 new_al_size)

    # Build edge list in terms of indices in new graph.
    nodes_lookup = {jt_nodes[i]: i for i in range(len(jt_nodes))}
    new_edges = np.array([[nodes_lookup[u], nodes_lookup[v]]
                          for u, v in junc_tree.edges()])

    # Convert node lists to numpy arrays.
    jt_nodes = [np.fromiter(node, dtype=np.int32) for node in jt_nodes]

    # Calculate fields which describe interaction beteen supervariables.
    # If supervariable has less than ``sv_size`` variables, pad with -inf.
    # Then, when decoding, we will just throw away values from the left.
    # We should account for each factor of the old graph in exactly one factor
    # in the new graph. So, for field and interaction factors of the old graph
    # we keep track of whether we already took them, and don't take them for
    # the second time.
    new_field = np.ones((new_gr_size, new_al_size), dtype=np.float64) * -np.inf
    used_node_fields = set()
    for new_node_id in range(new_gr_size):
        old_nodes = jt_nodes[new_node_id]
        node_field = model.get_subgraph_factor_values(
            old_nodes, vars_skip=used_node_fields)
        new_field[new_node_id, 0:len(node_field)] = node_field
        used_node_fields.update(old_nodes)

    # Now, for every edge in new graph - add interaction factor requiring that
    # the same variable appearing in two supervariables always has the same
    # values.
    # We achieve this by using Kroenker delta function.
    # As we working with logarithms, we populate -inf for impossible states,
    # and 0 for possible states.
    new_interactions = np.zeros((len(new_edges), new_al_size, new_al_size))
    for edge_id in range(len(new_edges)):
        u, v = new_edges[edge_id]
        allowed = build_multi_delta(sv_size, model.al_size, jt_nodes[u],
                                    jt_nodes[v])
        new_interactions[edge_id, np.logical_not(allowed)] = -np.inf

    from inferlo.pairwise.pwf_model import PairWiseFiniteModel
    new_model = PairWiseFiniteModel.create(new_field, new_edges,
                                           new_interactions)
    return JunctionizedModel(new_model, jt_nodes, model.gr_size, model.al_size)