def test_notarborescence1():
    # Not an arborescence due to not spanning.
    G = nx.MultiDiGraph()
    G.add_nodes_from(range(10))
    G.add_edges_from([(0,1),(0,2),(1,3),(5,6)])
    assert_true(nx.is_branching(G))
    assert_false(nx.is_arborescence(G))
def test_notbranching1():
    # Acyclic violation.
    G = nx.MultiDiGraph()
    G.add_nodes_from(range(10))
    G.add_edges_from([(0,1),(1,0)])
    assert_false(nx.is_branching(G))
    assert_false(nx.is_arborescence(G))
def test_notbranching2():
    # In-degree violation.
    G = nx.MultiDiGraph()
    G.add_nodes_from(range(10))
    G.add_edges_from([(0,1),(0,2),(3,2)])
    assert_false(nx.is_branching(G))
    assert_false(nx.is_arborescence(G))
def test_notbranching1():
    # Acyclic violation.
    G = nx.MultiDiGraph()
    G.add_nodes_from(range(10))
    G.add_edges_from([(0, 1), (1, 0)])
    assert not nx.is_branching(G)
    assert not nx.is_arborescence(G)
def test_notbranching2():
    # In-degree violation.
    G = nx.MultiDiGraph()
    G.add_nodes_from(range(10))
    G.add_edges_from([(0, 1), (0, 2), (3, 2)])
    assert not nx.is_branching(G)
    assert not nx.is_arborescence(G)
def test_notarborescence1():
    # Not an arborescence due to not spanning.
    G = nx.MultiDiGraph()
    G.add_nodes_from(range(10))
    G.add_edges_from([(0, 1), (0, 2), (1, 3), (5, 6)])
    assert nx.is_branching(G)
    assert not nx.is_arborescence(G)
def test_notarborescence2():
    # Not an arborescence due to in-degree violation.
    G = nx.MultiDiGraph()
    nx.add_path(G, range(5))
    G.add_edge(6, 4)
    assert not nx.is_branching(G)
    assert not nx.is_arborescence(G)
def test_notarborescence2():
    # Not an arborescence due to in-degree violation.
    G = nx.MultiDiGraph()
    nx.add_path(G, range(5))
    G.add_edge(6, 4)
    assert_false(nx.is_branching(G))
    assert_false(nx.is_arborescence(G))
Exemple #9
0
    def test_multiple_roots(self):
        """Tests that a directed acyclic graph with multiple degree zero
        nodes creates an arborescence with multiple (weakly) connected
        components.

        """
        G = nx.DiGraph([(0, 1), (0, 2), (1, 3), (2, 3), (5, 2)])
        B = nx.dag_to_branching(G)
        expected = nx.DiGraph([(0, 1), (1, 3), (0, 2), (2, 4), (5, 6), (6, 7)])
        assert_true(nx.is_branching(B))
        assert_false(nx.is_arborescence(B))
        assert_true(nx.is_isomorphic(B, expected))
Exemple #10
0
    def test_multiple_roots(self):
        """Tests that a directed acyclic graph with multiple degree zero
        nodes creates an arborescence with multiple (weakly) connected
        components.

        """
        G = nx.DiGraph([(0, 1), (0, 2), (1, 3), (2, 3), (5, 2)])
        B = nx.dag_to_branching(G)
        expected = nx.DiGraph([(0, 1), (1, 3), (0, 2), (2, 4), (5, 6), (6, 7)])
        assert nx.is_branching(B)
        assert not nx.is_arborescence(B)
        assert nx.is_isomorphic(B, expected)
Exemple #11
0
    def tree(self):
        rslt = {}
        rslt['is_tree'] = nx.is_tree(self.graph)
        rslt['is_forest'] = nx.is_forest(self.graph)

        if self.directed == 'directed':
            rslt['is_arborescence'] = nx.is_arborescence(self.graph)
            rslt['is_branching'] = nx.is_branching(self.graph)

        fname_tree = self.DIR + '/tree.json'
        with open(fname_tree, "w") as f:
            json.dump(rslt, f, cls=SetEncoder, indent=2)
        print(fname_tree)
Exemple #12
0
def analysis_lsde(file_path,address,flag):

    #read gpickle

    G = nx.DiGraph(directed=True)
    G = nx.read_gpickle(file_path)
    important_nodes=[address]# import important nodes
    
    ##########################for nodes(addresses)

    take_value_nodes=fl.fun_nodes(G)
    #nodes_info,clu_coe_filter,avg_coe,cen_in_filter,cen_out_filter,degree_sort
    nodes_info=take_value_nodes[0]
    #print(take_value_nodes[1])

    ########################for edges(transaction records)

    take_value_edges=fl.fun_edges(G,important_nodes)
    #edgesinfo,(important nodes info, important nodes first time&last time&time difference),t-value(total)dataframe,polynomial parameters 
    edges_info=take_value_edges[0]
    #print(take_value_edges[1][1])
    #print(take_value_edges[1][0])
    #print(edges_info)

    ########################for graph characteristics

    graph_charac=fl.graph_characteristic(G,nodes_info,edges_info)
    #addr_total_number,trans_total_number,value_max,value_min,value_average,value_median,value_onefourth,value_threefourth


    ########################for specific nodes

    specific_node_charac = fl.analy_specific_node(important_nodes[0],nodes_info,edges_info)
    #somenode,somenode_as_from,somenode_as_to,connected addr info(as from),connected addr info2(as to)

    ########################for subgraph

    #G2=fl.subgraph_total(G)#subgraph

    ########################summary&analyze
    feature_dic = {}

    addr_total_number=graph_charac[0]
    trans_total_number=graph_charac[1]

    #show in dic
    feature_dic['Total addresses'] = addr_total_number
    feature_dic['Total Transaction'] = trans_total_number
    feature_dic['Normality check(p-value)'] = stats.shapiro(edges_info.Value)[1]#if p > 0.001, transaction value meet the normality requirements
    feature_dic['Significance test(p-value)'] = stats.pearsonr(edges_info.Time,edges_info.Value)[1] #if p>0.01, time and value has the high significance relationship, i.e. time and value have high relativity
    print('24/35 successful!')
    feature_dic['Percentage of cluster coefficient>0 addresses'] = 100*(take_value_nodes[1].shape[0])/addr_total_number
    feature_dic['Average cluster coefficient of the whole graph'] = take_value_nodes[2]
    feature_dic['Percentage of in centrality>0.01 addresses']= 100*(take_value_nodes[3].shape[0])/addr_total_number
    feature_dic['Percentage of out centrality>0.01 addresses']= 100*(take_value_nodes[4].shape[0])/addr_total_number   
    feature_dic['Max value']=graph_charac[2]
    feature_dic['Min value']=graph_charac[3]
    feature_dic['Average value']=graph_charac[4]
    feature_dic['Median value']=graph_charac[5]
    feature_dic['One fourth value']=graph_charac[6]
    feature_dic['Three fourth value']=graph_charac[7]
    feature_dic['The max degree of all addresses'] = len(take_value_nodes[5])-1
#    feature_dic['Polynomial parameters']=take_value_edges[3]
    ttt1 = len(take_value_edges[1][0][0])
    feature_dic['Important nodes information'] = ttt1
    feature_dic['Important nodes start-time']=take_value_edges[1][1][0][0]
    feature_dic['Important nodes end-time']=take_value_edges[1][1][0][1]
    feature_dic['Important nodes time difference']=take_value_edges[1][1][0][2]
    ttt2=len(specific_node_charac[1])
    ttt3=len(specific_node_charac[2])
    if ttt1 ==0:
        feature_dic['This node as from']=0
        feature_dic['This node as to']=0
    else:
        feature_dic['This node as from']=ttt2/ttt1
        feature_dic['This node as to']=ttt3/ttt1
    if specific_node_charac[4]==0:
        feature_dic['This node connected with(as from)']=0
    else:
        feature_dic['This node connected with(as from)']=ttt2/specific_node_charac[4]

    if specific_node_charac[3]==0:
        feature_dic['This node connected with(as to)']=0
    else:
        feature_dic['This node connected with(as to)']=ttt3/specific_node_charac[3]
    
    #print(take_value_edges[1][1][0])
    #print(len(take_value_edges[1][0][0]))
    #print(specific_node_charac[3])

    
    if flag == True:
        add_feature1=nx.pagerank(G)
        iadd1=0
        for v in add_feature1.values():
            if v>0.01:
                iadd1=iadd1+1
        #    print(iadd1)

        add_feature2=nx.transitivity(G)#Compute graph transitivity, the fraction of all possible triangles present in G.
         #Possible triangles are identified by the number of “triads” (two edges with a shared vertex).
        #print(add_feature2)
        print('26/35 successful!')

        add_feature3=nx.degree_assortativity_coefficient(G)#Assortativity measures the similarity of connections in the graph with respect to the node degree.
        add_feature4=nx.is_chordal(G.to_undirected())#A graph is chordal if every cycle of length at least 4 has a chord (an edge joining two nodes not adjacent in the cycle).
        print('28/35 successful!')
        #print(add_feature4)
        add_feature5=nx.is_weakly_connected(G)
        add_feature6=nx.is_strongly_connected(G)
        print('30/35 successful!')
        add_feature7=nx.global_efficiency(G.to_undirected())#The efficiency of a pair of nodes in a graph is the multiplicative inverse of the shortest path distance between the nodes.
        # The average global efficiency of a graph is the average efficiency of all pairs of nodes
        add_feature8=nx.is_branching(G)
        print('32/35 successful!')
        add_feature9=sorted(nx.immediate_dominators(G, address).items())
        add_domin=len(add_feature9)
        add_feature10=nx.is_simple_path(G, address)#A simple path in a graph is a nonempty sequence of nodes in which no node appears more than once in the sequence,
        # and each adjacent pair of nodes in the sequence is adjacent in the graph.
        print('34/35 successful!')
        add_feature11=nx.overall_reciprocity(G)# reciprocity for the whole graph
        print('35/35 successful!')
        #print(add_feature11)
        feature_dic['Percentage of page Rank >0.01 address'] = iadd1/addr_total_number
        feature_dic['Transitivity']=add_feature2
        feature_dic['Assortativity']=add_feature3
        feature_dic['Chordal']=add_feature4
        feature_dic['Strongly connected']=add_feature5
        feature_dic['Weakly connected']=add_feature6
        feature_dic['Global efficiency']=add_feature7
        feature_dic['Is branching'] = add_feature8
        feature_dic['Immediate dominator(numbers)']=add_domin
        feature_dic['Is simple path']=add_feature10
        feature_dic['Reciprocity']=add_feature11
        Header=['Total addresses','Total Transaction','Normality check(p-value)','Significance test(p-value)','Percentage of cluster coefficient>0 addresses',
        'Average cluster coefficient of the whole graph','Percentage of in centrality>0.01 addresses','Percentage of out centrality>0.01 addresses',
        'Max value','Min value','Average value','Median value','One fourth value','Three fourth value','The max degree of all addresses',
        'Important nodes information','Important nodes start-time','Important nodes end-time','Important nodes time difference','This node as from','This node as to',
        'This node connected with(as from)','This node connected with(as to)','Percentage of page Rank >0.01 address','Transitivity','Assortativity','Chordal','Strongly connected',
        'Weakly connected','Global efficiency','Is branching','Immediate dominator(numbers)','Is simple path','Reciprocity']
    else:
        print('Finished')
        Header=['Total addresses','Total Transaction','Normality check(p-value)','Significance test(p-value)','Percentage of cluster coefficient>0 addresses',
        'Average cluster coefficient of the whole graph','Percentage of in centrality>0.01 addresses','Percentage of out centrality>0.01 addresses',
        'Max value','Min value','Average value','Median value','One fourth value','Three fourth value','The max degree of all addresses',
        'Important nodes information','Important nodes start-time','Important nodes end-time','Important nodes time difference','This node as from','This node as to',
        'This node connected with(as from)','This node connected with(as to)']


    #print(somenodesinfo_dic)
    #res=stats.pearsonr(edges_info.Time,edges_info.Value)
    #print(res)
   



    end=time.time()
    print('running time =',end-start)
    
    return feature_dic,Header
print(nx.algorithms.tree.is_forest(G=circuit))

residential = nx.algorithms.cut_size(G=circuit,
                                     S={i
                                        for i in range(1, 6)},
                                     T={j
                                        for j in range(14, 16)})

print(residential)

print(nx.algorithms.is_directed_acyclic_graph(G=circuit))

branching = nx.algorithms.dag_to_branching(G=circuit)

nx.draw_planar(G=branching,
               with_labels=True,
               node_color='g',
               node_size=800,
               font_size=14,
               width=0.8)

plt.show()

print('\n', nx.algorithms.maximum_branching(G=circuit))
print('\n')
print(nx.is_tree(circuit), '\n')
print(nx.is_forest(circuit), '\n')
print(nx.is_directed_acyclic_graph(circuit), '\n')
print(nx.is_arborescence(circuit), '\n')
print(nx.is_branching(circuit))
Exemple #14
0
def ScenarioTreeModelFromNetworkX(
        tree,
        node_name_attribute=None,
        edge_probability_attribute='probability',
        stage_names=None,
        scenario_name_attribute=None):
    """
    Create a scenario tree model from a networkx tree.  The
    height of the tree must be at least 1 (meaning at least
    2 stages).

    Optional Arguments:
      - node_name_attribute:
           By default, node names are the same as the node
           hash in the networkx tree. This keyword can be
           set to the name of some property of nodes in the
           graph that will be used for their name in the
           PySP scenario tree.
      - edge_probability_attribute:
           Can be set to the name of some property of edges
           in the graph that defines the conditional
           probability of that branch (default: 'probability').
           If this keyword is set to None, then all branches
           leaving a node are assigned equal conditional
           probabilities.
      - stage_names:
           Can define a list of stage names to use (assumed
           in time order). The length of this list much
           match the number of stages in the tree.
      - scenario_name_attribute:
           By default, scenario names are the same as the
           leaf-node hash in the networkx tree. This keyword
           can be set to the name of some property of
           leaf-nodes in the graph that will be used for
           their corresponding scenario in the PySP scenario
           tree.

    Examples:

      - A 2-stage scenario tree with 10 scenarios:
           G = networkx.DiGraph()
           G.add_node("Root")
           N = 10
           for i in range(N):
               node_name = "Leaf"+str(i)
               G.add_node(node_name)
               G.add_edge("Root",node_name,probability=1.0/N)
           model = ScenarioTreeModelFromNetworkX(G)

       - A 4-stage scenario tree with 125 scenarios:
           branching_factor = 5
           height = 3
           G = networkx.balanced_tree(
                   branching_factory,
                   height,
                   networkx.DiGraph())
           model = ScenarioTreeModelFromNetworkX(
                       G,
                       edge_probability_attribute=None)
    """

    if not has_networkx:
        raise ValueError("networkx module is not available")

    if not networkx.is_tree(tree):
        raise TypeError(
            "object is not a tree (see networkx.is_tree)")

    if not networkx.is_directed(tree):
        raise TypeError(
            "object is not directed (see networkx.is_directed)")

    if not networkx.is_branching(tree):
        raise TypeError(
            "object is not a branching (see networkx.is_branching")

    if not networkx.is_arborescence(tree):
            raise TypeError("Object must be a directed, rooted tree "
                            "in which all edges point away from the "
                            "root (see networkx.is_arborescence)")

    in_degree_items = tree.in_degree()
    # Prior to networkx ~2.0, in_degree() returned a dictionary.
    # Now it is a view on items, so only call .items() for the old case
    if hasattr(in_degree_items, 'items'):
        in_degree_items = in_degree_items.items()
    root = [u for u,d in in_degree_items if d == 0]
    assert len(root) == 1
    root = root[0]
    num_stages = networkx.eccentricity(tree, v=root) + 1
    if num_stages < 2:
        raise ValueError(
            "The number of stages must be at least 2")
    m = CreateAbstractScenarioTreeModel()
    if stage_names is not None:
        unique_stage_names = set()
        for cnt, stage_name in enumerate(stage_names,1):
            m.Stages.add(stage_name)
            unique_stage_names.add(stage_name)
        if cnt != num_stages:
            raise ValueError(
                "incorrect number of stages names (%s), should be %s"
                % (cnt, num_stages))
        if len(unique_stage_names) != cnt:
            raise ValueError("all stage names were not unique")
    else:
        for i in range(num_stages):
            m.Stages.add('Stage'+str(i+1))
    node_to_name = {}
    node_to_scenario = {}
    def _setup(u, succ):
        if node_name_attribute is not None:
            if node_name_attribute not in tree.node[u]:
                raise KeyError(
                    "node '%s' missing name attribute: '%s'"
                    % (u, node_name_attribute))
            node_name = tree.node[u][node_name_attribute]
        else:
            node_name = u
        node_to_name[u] = node_name
        m.Nodes.add(node_name)
        if u in succ:
            for v in succ[u]:
                _setup(v, succ)
        else:
            # a leaf node
            if scenario_name_attribute is not None:
                if scenario_name_attribute not in tree.node[u]:
                    raise KeyError(
                        "node '%s' missing attribute: '%s'"
                        % (u, scenario_name_attribute))
                scenario_name = tree.node[u][scenario_name_attribute]
            else:
                scenario_name = u
            node_to_scenario[u] = scenario_name
            m.Scenarios.add(scenario_name)

    _setup(root,
           networkx.dfs_successors(tree, root))
    m = m.create_instance()
    def _add_node(u, stage, succ, pred):
        if node_name_attribute is not None:
            if node_name_attribute not in tree.node[u]:
                raise KeyError(
                    "node '%s' missing name attribute: '%s'"
                    % (u, node_name_attribute))
            node_name = tree.node[u][node_name_attribute]
        else:
            node_name = u
        m.NodeStage[node_name] = m.Stages[stage]
        if u == root:
            m.ConditionalProbability[node_name] = 1.0
        else:
            assert u in pred
            # prior to networkx ~2.0, we used a .edge attribute on DiGraph,
            # which no longer exists.
            if hasattr(tree, 'edge'):
                edge = tree.edge[pred[u]][u]
            else:
                edge = tree.edges[pred[u],u]
            probability = None
            if edge_probability_attribute is not None:
                if edge_probability_attribute not in edge:
                    raise KeyError(
                        "edge '(%s, %s)' missing probability attribute: '%s'"
                        % (pred[u], u, edge_probability_attribute))
                probability = edge[edge_probability_attribute]
            else:
                probability = 1.0/len(succ[pred[u]])
            m.ConditionalProbability[node_name] = probability
        if u in succ:
            child_names = []
            for v in succ[u]:
                child_names.append(
                    _add_node(v, stage+1, succ, pred))
            total_probability = 0.0
            for child_name in child_names:
                m.Children[node_name].add(child_name)
                total_probability += \
                    value(m.ConditionalProbability[child_name])
            if abs(total_probability - 1.0) > 1e-5:
                raise ValueError(
                    "edge probabilities leaving node '%s' "
                    "do not sum to 1 (total=%r)"
                    % (u, total_probability))
        else:
            # a leaf node
            scenario_name = node_to_scenario[u]
            m.ScenarioLeafNode[scenario_name] = node_name
            m.Children[node_name].clear()

        return node_name

    _add_node(root,
              1,
              networkx.dfs_successors(tree, root),
              networkx.dfs_predecessors(tree, root))

    return m
Exemple #15
0
def ScenarioTreeModelFromNetworkX(
        tree,
        node_name_attribute=None,
        edge_probability_attribute='weight',
        stage_names=None,
        scenario_name_attribute=None):
    """
    Create a scenario tree model from a networkx tree.  The
    height of the tree must be at least 1 (meaning at least
    2 stages).

    Required node attributes:
        - cost (str): A string identifying a component on
              the model whose value indicates the cost at
              the time stage of the node for any scenario
              traveling through it.

    Optional node attributes:
        - variables (list): A list of variable identifiers
              that will be tracked by the node. If the node
              is not a leaf node, these indicate variables
              with non-anticipativity constraints.
        - derived_variables (list): A list of variable or
              expression identifiers that will be tracked by
              the node (but will never have
              non-anticipativity constraints enforced).
        - bundle: A bundle identifier for the scenario
              defined by a leaf-stage node. This attribute
              is ignored on non-terminal tree nodes. This
              attribute appears on at least one leaf-stage
              node (and is not set to :const:`None`), then
              it must be set on all leaf-stage nodes (to
              something other than :const:`None`);
              otherwise, an exception will be raised.

    Optional edge attributes:
        - weight (float): Indicates the conditional
              probability of moving from the parent node to
              the child node in the directed edge. If not
              present, it will be assumed that all edges
              leaving the parent node have equal probability
              (normalized to sum to one).

    Args:
        stage_names: Can define a list of stage names to use
           (assumed in time order). The length of this list
           much match the number of stages in the tree. The
           default value of :const:`None` indicates that
           stage names should be automatically generated in
           with the form ['Stage1','Stage2',...].
        node_name_attribute: By default, node names are the
           same as the node hash in the networkx tree. This
           keyword can be set to the name of some property
           of nodes in the graph that will be used for their
           name in the PySP scenario tree.
        scenario_name_attribute: By default, scenario names
           are the same as the leaf-node hash in the
           networkx tree. This keyword can be set to the
           name of some property of leaf-nodes in the graph
           that will be used for their corresponding
           scenario name in the PySP scenario tree.
        edge_probability_attribute: Can be set to the name
           of some property of edges in the graph that
           defines the conditional probability of that
           branch (default: 'weight'). If this keyword is
           set to :const:`None`, then all branches leaving a
           node are assigned equal conditional
           probabilities.

    Examples:

        A 2-stage scenario tree with 10 scenarios grouped
        into 2 bundles:

        >>> G = networkx.DiGraph()
        >>> G.add_node("root", variables=["x"])
        >>> N = 10
        >>> for i in range(N):
        >>>     node_name = "s"+str(i)
        >>>     bundle_name = "b"+str(i%2)
        >>>     G.add_node(node_name, bundle=bundle)
        >>>     G.add_edge("root", node_name, weight=1.0/N)
        >>> model = ScenarioTreeModelFromNetworkX(G)

        A 4-stage scenario tree with 125 scenarios:

        >>> branching_factor = 5
        >>> height = 3
        >>> G = networkx.balanced_tree(
                   branching_factor,
                   height,
                   networkx.DiGraph())
        >>> model = ScenarioTreeModelFromNetworkX(G)
    """

    if not has_networkx:                          #pragma:nocover
        raise ValueError(
            "networkx module is not available")

    if not networkx.is_tree(tree):
        raise TypeError(
            "Graph object is not a tree "
            "(see networkx.is_tree)")

    if not networkx.is_directed(tree):
        raise TypeError(
            "Graph object is not directed "
            "(see networkx.is_directed)")

    if not networkx.is_branching(tree):
        raise TypeError(
            "Grapn object is not a branching "
            "(see networkx.is_branching")

    in_degree_items = tree.in_degree()
    # Prior to networkx ~2.0, in_degree() returned a dictionary.
    # Now it is a view on items, so only call .items() for the old case
    if hasattr(in_degree_items, 'items'):
        in_degree_items = in_degree_items.items()
    root = [u for u,d in in_degree_items if d == 0]
    assert len(root) == 1
    root = root[0]
    num_stages = networkx.eccentricity(tree, v=root) + 1
    if num_stages < 2:
        raise ValueError(
            "The number of stages must be at least 2")
    m = CreateAbstractScenarioTreeModel()
    if stage_names is not None:
        unique_stage_names = set()
        for cnt, stage_name in enumerate(stage_names,1):
            m.Stages.add(stage_name)
            unique_stage_names.add(stage_name)
        if cnt != num_stages:
            raise ValueError(
                "incorrect number of stages names (%s), should be %s"
                % (cnt, num_stages))
        if len(unique_stage_names) != cnt:
            raise ValueError("all stage names were not unique")
    else:
        for i in range(num_stages):
            m.Stages.add('Stage'+str(i+1))
    node_to_name = {}
    node_to_scenario = {}
    scenario_bundle = {}
    def _setup(u, succ):
        if node_name_attribute is not None:
            if node_name_attribute not in tree.node[u]:
                raise KeyError(
                    "node '%s' missing node name "
                    "attribute: '%s'"
                    % (u, node_name_attribute))
            node_name = tree.node[u][node_name_attribute]
        else:
            node_name = u
        node_to_name[u] = node_name
        m.Nodes.add(node_name)
        if u in succ:
            for v in succ[u]:
                _setup(v, succ)
        else:
            # a leaf node
            if scenario_name_attribute is not None:
                if scenario_name_attribute not in tree.node[u]:
                    raise KeyError(
                        "node '%s' missing scenario name "
                        "attribute: '%s'"
                        % (u, scenario_name_attribute))
                scenario_name = tree.node[u][scenario_name_attribute]
            else:
                scenario_name = u
            node_to_scenario[u] = scenario_name
            m.Scenarios.add(scenario_name)
            scenario_bundle[scenario_name] = \
                tree.node[u].get('bundle', None)
    _setup(root,
           networkx.dfs_successors(tree, root))
    m = m.create_instance()
    def _add_node(u, stage, succ, pred):
        node_name = node_to_name[u]
        m.NodeStage[node_name] = m.Stages[stage]
        if u == root:
            m.ConditionalProbability[node_name] = 1.0
        else:
            assert u in pred
            # prior to networkx ~2.0, we used a .edge attribute on DiGraph,
            # which no longer exists.
            if hasattr(tree, 'edge'):
                edge = tree.edge[pred[u]][u]
            else:
                edge = tree.edges[pred[u],u]
            probability = None
            if edge_probability_attribute is not None:
                if edge_probability_attribute not in edge:
                    raise KeyError(
                        "edge '(%s, %s)' missing probability attribute: '%s'"
                        % (pred[u], u, edge_probability_attribute))
                probability = edge[edge_probability_attribute]
            else:
                probability = 1.0/len(succ[pred[u]])
            m.ConditionalProbability[node_name] = probability
        # get node variables
        if "variables" in tree.node[u]:
            node_variables = tree.node[u]["variables"]
            assert type(node_variables) in [tuple, list]
            for varstring in node_variables:
                m.NodeVariables[node_name].add(varstring)
        if "derived_variables" in tree.node[u]:
            node_derived_variables = tree.node[u]["derived_variables"]
            assert type(node_derived_variables) in [tuple, list]
            for varstring in node_derived_variables:
                m.NodeDerivedVariables[node_name].add(varstring)
        if "cost" in tree.node[u]:
            assert isinstance(tree.node[u]["cost"], six.string_types)
            m.NodeCost[node_name].value = tree.node[u]["cost"]
        if u in succ:
            child_names = []
            for v in succ[u]:
                child_names.append(
                    _add_node(v, stage+1, succ, pred))
            total_probability = 0.0
            for child_name in child_names:
                m.Children[node_name].add(child_name)
                total_probability += \
                    pyomo.core.value(m.ConditionalProbability[child_name])
            if abs(total_probability - 1.0) > 1e-5:
                raise ValueError(
                    "edge probabilities leaving node '%s' "
                    "do not sum to 1 (total=%r)"
                    % (u, total_probability))
        else:
            # a leaf node
            scenario_name = node_to_scenario[u]
            m.ScenarioLeafNode[scenario_name] = node_name
            m.Children[node_name].clear()

        return node_name

    _add_node(root,
              1,
              networkx.dfs_successors(tree, root),
              networkx.dfs_predecessors(tree, root))

    if any(_b is not None for _b in scenario_bundle.values()):
        if any(_b is None for _b in scenario_bundle.values()):
            raise ValueError("Incomplete bundle specification. "
                             "All scenarios require a bundle "
                             "identifier.")
        m.Bundling.value = True
        bundle_scenarios = {}
        for bundle_name in sorted(set(scenario_bundle.values())):
            m.Bundles.add(bundle_name)
            bundle_scenarios[bundle_name] = []
        for scenario_name in m.Scenarios:
            bundle_scenarios[scenario_bundle[scenario_name]].\
                append(scenario_name)
        for bundle_name in m.Bundles:
            for scenario_name in sorted(bundle_scenarios[bundle_name]):
                m.BundleScenarios[bundle_name].add(scenario_name)

    return m
def test_path():
    G = nx.DiGraph()
    nx.add_path(G, range(5))
    assert nx.is_branching(G)
    assert nx.is_arborescence(G)
Exemple #17
0
def test_emptybranch():
    G = nx.DiGraph()
    G.add_nodes_from(range(10))
    assert_true(nx.is_branching(G))
    assert_false(nx.is_arborescence(G))
def test_emptybranch():
    G = nx.DiGraph()
    G.add_nodes_from(range(10))
    assert nx.is_branching(G)
    assert not nx.is_arborescence(G)
Exemple #19
0
def test_path():
    G = nx.DiGraph()
    nx.add_path(G, range(5))
    assert_true(nx.is_branching(G))
    assert_true(nx.is_arborescence(G))
def test_path():
    G = nx.DiGraph()
    G.add_path(range(5))
    assert_true(nx.is_branching(G))
    assert_true(nx.is_arborescence(G))
Exemple #21
0
def ScenarioTreeModelFromNetworkX(
        tree,
        node_name_attribute=None,
        edge_probability_attribute='weight',
        stage_names=None,
        scenario_name_attribute=None):
    """
    Create a scenario tree model from a networkx tree.  The
    height of the tree must be at least 1 (meaning at least
    2 stages).

    Required node attributes:
        - cost (str): A string identifying a component on
              the model whose value indicates the cost at
              the time stage of the node for any scenario
              traveling through it.

    Optional node attributes:
        - variables (list): A list of variable identifiers
              that will be tracked by the node. If the node
              is not a leaf node, these indicate variables
              with non-anticipativity constraints.
        - derived_variables (list): A list of variable or
              expression identifiers that will be tracked by
              the node (but will never have
              non-anticipativity constraints enforced).
        - bundle: A bundle identifier for the scenario
              defined by a leaf-stage node. This attribute
              is ignored on non-terminal tree nodes. This
              attribute appears on at least one leaf-stage
              node (and is not set to :const:`None`), then
              it must be set on all leaf-stage nodes (to
              something other than :const:`None`);
              otherwise, an exception will be raised.

    Optional edge attributes:
        - weight (float): Indicates the conditional
              probability of moving from the parent node to
              the child node in the directed edge. If not
              present, it will be assumed that all edges
              leaving the parent node have equal probability
              (normalized to sum to one).

    Args:
        stage_names: Can define a list of stage names to use
           (assumed in time order). The length of this list
           much match the number of stages in the tree. The
           default value of :const:`None` indicates that
           stage names should be automatically generated in
           with the form ['Stage1','Stage2',...].
        node_name_attribute: By default, node names are the
           same as the node hash in the networkx tree. This
           keyword can be set to the name of some property
           of nodes in the graph that will be used for their
           name in the PySP scenario tree.
        scenario_name_attribute: By default, scenario names
           are the same as the leaf-node hash in the
           networkx tree. This keyword can be set to the
           name of some property of leaf-nodes in the graph
           that will be used for their corresponding
           scenario name in the PySP scenario tree.
        edge_probability_attribute: Can be set to the name
           of some property of edges in the graph that
           defines the conditional probability of that
           branch (default: 'weight'). If this keyword is
           set to :const:`None`, then all branches leaving a
           node are assigned equal conditional
           probabilities.

    Examples:

        A 2-stage scenario tree with 10 scenarios grouped
        into 2 bundles:

        >>> G = networkx.DiGraph()
        >>> G.add_node("root", variables=["x"])
        >>> N = 10
        >>> for i in range(N):
        >>>     node_name = "s"+str(i)
        >>>     bundle_name = "b"+str(i%2)
        >>>     G.add_node(node_name, bundle=bundle)
        >>>     G.add_edge("root", node_name, weight=1.0/N)
        >>> model = ScenarioTreeModelFromNetworkX(G)

        A 4-stage scenario tree with 125 scenarios:

        >>> branching_factor = 5
        >>> height = 3
        >>> G = networkx.balanced_tree(
                   branching_factor,
                   height,
                   networkx.DiGraph())
        >>> model = ScenarioTreeModelFromNetworkX(G)
    """

    if not has_networkx:                          #pragma:nocover
        raise ValueError(
            "networkx>=2.0 module is not available")

    if not networkx.is_tree(tree):
        raise TypeError(
            "Graph object is not a tree "
            "(see networkx.is_tree)")

    if not networkx.is_directed(tree):
        raise TypeError(
            "Graph object is not directed "
            "(see networkx.is_directed)")

    if not networkx.is_branching(tree):
        raise TypeError(
            "Grapn object is not a branching "
            "(see networkx.is_branching")

    in_degree_items = tree.in_degree()
    # Prior to networkx ~2.0, in_degree() returned a dictionary.
    # Now it is a view on items, so only call .items() for the old case
    if hasattr(in_degree_items, 'items'):
        in_degree_items = in_degree_items.items()
    root = [u for u,d in in_degree_items if d == 0]
    assert len(root) == 1
    root = root[0]
    num_stages = networkx.eccentricity(tree, v=root) + 1
    if num_stages < 2:
        raise ValueError(
            "The number of stages must be at least 2")
    m = CreateAbstractScenarioTreeModel()
    if stage_names is not None:
        unique_stage_names = set()
        for cnt, stage_name in enumerate(stage_names,1):
            m.Stages.add(stage_name)
            unique_stage_names.add(stage_name)
        if cnt != num_stages:
            raise ValueError(
                "incorrect number of stages names (%s), should be %s"
                % (cnt, num_stages))
        if len(unique_stage_names) != cnt:
            raise ValueError("all stage names were not unique")
    else:
        for i in range(num_stages):
            m.Stages.add('Stage'+str(i+1))
    node_to_name = {}
    node_to_scenario = {}
    scenario_bundle = {}
    def _setup(u, succ):
        if node_name_attribute is not None:
            if node_name_attribute not in tree.nodes[u]:
                raise KeyError(
                    "node '%s' missing node name "
                    "attribute: '%s'"
                    % (u, node_name_attribute))
            node_name = tree.nodes[u][node_name_attribute]
        else:
            node_name = u
        node_to_name[u] = node_name
        m.Nodes.add(node_name)
        if u in succ:
            for v in succ[u]:
                _setup(v, succ)
        else:
            # a leaf node
            if scenario_name_attribute is not None:
                if scenario_name_attribute not in tree.nodes[u]:
                    raise KeyError(
                        "node '%s' missing scenario name "
                        "attribute: '%s'"
                        % (u, scenario_name_attribute))
                scenario_name = tree.nodes[u][scenario_name_attribute]
            else:
                scenario_name = u
            node_to_scenario[u] = scenario_name
            m.Scenarios.add(scenario_name)
            scenario_bundle[scenario_name] = \
                tree.nodes[u].get('bundle', None)
    _setup(root,
           networkx.dfs_successors(tree, root))
    m = m.create_instance()
    def _add_node(u, stage, succ, pred):
        node_name = node_to_name[u]
        m.NodeStage[node_name] = m.Stages[stage]
        if u == root:
            m.ConditionalProbability[node_name] = 1.0
        else:
            assert u in pred
            # prior to networkx ~2.0, we used a .edge attribute on DiGraph,
            # which no longer exists.
            if hasattr(tree, 'edge'):
                edge = tree.edge[pred[u]][u]
            else:
                edge = tree.edges[pred[u],u]
            probability = None
            if edge_probability_attribute is not None:
                if edge_probability_attribute not in edge:
                    raise KeyError(
                        "edge '(%s, %s)' missing probability attribute: '%s'"
                        % (pred[u], u, edge_probability_attribute))
                probability = edge[edge_probability_attribute]
            else:
                probability = 1.0/len(succ[pred[u]])
            m.ConditionalProbability[node_name] = probability
        # get node variables
        if "variables" in tree.nodes[u]:
            node_variables = tree.nodes[u]["variables"]
            assert type(node_variables) in [tuple, list]
            for varstring in node_variables:
                m.NodeVariables[node_name].add(varstring)
        if "derived_variables" in tree.nodes[u]:
            node_derived_variables = tree.nodes[u]["derived_variables"]
            assert type(node_derived_variables) in [tuple, list]
            for varstring in node_derived_variables:
                m.NodeDerivedVariables[node_name].add(varstring)
        if "cost" in tree.nodes[u]:
            assert isinstance(tree.nodes[u]["cost"], six.string_types)
            m.NodeCost[node_name].value = tree.nodes[u]["cost"]
        if u in succ:
            child_names = []
            for v in succ[u]:
                child_names.append(
                    _add_node(v, stage+1, succ, pred))
            total_probability = 0.0
            for child_name in child_names:
                m.Children[node_name].add(child_name)
                total_probability += \
                    pyomo.core.value(m.ConditionalProbability[child_name])
            if abs(total_probability - 1.0) > 1e-5:
                raise ValueError(
                    "edge probabilities leaving node '%s' "
                    "do not sum to 1 (total=%r)"
                    % (u, total_probability))
        else:
            # a leaf node
            scenario_name = node_to_scenario[u]
            m.ScenarioLeafNode[scenario_name] = node_name
            m.Children[node_name].clear()

        return node_name

    _add_node(root,
              1,
              networkx.dfs_successors(tree, root),
              networkx.dfs_predecessors(tree, root))

    if any(_b is not None for _b in scenario_bundle.values()):
        if any(_b is None for _b in scenario_bundle.values()):
            raise ValueError("Incomplete bundle specification. "
                             "All scenarios require a bundle "
                             "identifier.")
        m.Bundling.value = True
        bundle_scenarios = {}
        for bundle_name in sorted(set(scenario_bundle.values())):
            m.Bundles.add(bundle_name)
            bundle_scenarios[bundle_name] = []
        for scenario_name in m.Scenarios:
            bundle_scenarios[scenario_bundle[scenario_name]].\
                append(scenario_name)
        for bundle_name in m.Bundles:
            for scenario_name in sorted(bundle_scenarios[bundle_name]):
                m.BundleScenarios[bundle_name].add(scenario_name)

    return m
def ScenarioTreeModelFromNetworkX(
        tree,
        node_name_attribute=None,
        edge_probability_attribute='probability',
        stage_names=None,
        scenario_name_attribute=None):
    """
    Create a scenario tree model from a networkx tree.  The
    height of the tree must be at least 1 (meaning at least
    2 stages).

    Optional Arguments:
      - node_name_attribute:
           By default, node names are the same as the node
           hash in the networkx tree. This keyword can be
           set to the name of some property of nodes in the
           graph that will be used for their name in the
           PySP scenario tree.
      - edge_probability_attribute:
           Can be set to the name of some property of edges
           in the graph that defines the conditional
           probability of that branch (default: 'probability').
           If this keyword is set to None, then all branches
           leaving a node are assigned equal conditional
           probabilities.
      - stage_names:
           Can define a list of stage names to use (assumed
           in time order). The length of this list much
           match the number of stages in the tree.
      - scenario_name_attribute:
           By default, scenario names are the same as the
           leaf-node hash in the networkx tree. This keyword
           can be set to the name of some property of
           leaf-nodes in the graph that will be used for
           their corresponding scenario in the PySP scenario
           tree.

    Examples:

      - A 2-stage scenario tree with 10 scenarios:
           G = networkx.DiGraph()
           G.add_node("Root")
           N = 10
           for i in range(N):
               node_name = "Leaf"+str(i)
               G.add_node(node_name)
               G.add_edge("Root",node_name,probability=1.0/N)
           model = ScenarioTreeModelFromNetworkX(G)

       - A 4-stage scenario tree with 125 scenarios:
           branching_factor = 5
           height = 3
           G = networkx.balanced_tree(
                   branching_factory,
                   height,
                   networkx.DiGraph())
           model = ScenarioTreeModelFromNetworkX(
                       G,
                       edge_probability_attribute=None)
    """

    if not has_networkx:
        raise ValueError("networkx module is not available")

    if not networkx.is_tree(tree):
        raise TypeError(
            "object is not a tree (see networkx.is_tree)")

    if not networkx.is_directed(tree):
        raise TypeError(
            "object is not directed (see networkx.is_directed)")

    if not networkx.is_branching(tree):
        raise TypeError(
            "object is not a branching (see networkx.is_branching")

    if not networkx.is_arborescence(tree):
            raise TypeError("Object must be a directed, rooted tree "
                            "in which all edges point away from the "
                            "root (see networkx.is_arborescence)")

    root = [u for u,d in tree.in_degree().items() if d == 0]
    assert len(root) == 1
    root = root[0]
    num_stages = networkx.eccentricity(tree, v=root) + 1
    if num_stages < 2:
        raise ValueError(
            "The number of stages must be at least 2")
    m = CreateAbstractScenarioTreeModel()
    if stage_names is not None:
        unique_stage_names = set()
        for cnt, stage_name in enumerate(stage_names,1):
            m.Stages.add(stage_name)
            unique_stage_names.add(stage_name)
        if cnt != num_stages:
            raise ValueError(
                "incorrect number of stages names (%s), should be %s"
                % (cnt, num_stages))
        if len(unique_stage_names) != cnt:
            raise ValueError("all stage names were not unique")
    else:
        for i in range(num_stages):
            m.Stages.add('Stage'+str(i+1))
    node_to_name = {}
    node_to_scenario = {}
    def _setup(u, succ):
        if node_name_attribute is not None:
            if node_name_attribute not in tree.node[u]:
                raise KeyError(
                    "node '%s' missing name attribute: '%s'"
                    % (u, node_name_attribute))
            node_name = tree.node[u][node_name_attribute]
        else:
            node_name = u
        node_to_name[u] = node_name
        m.Nodes.add(node_name)
        if u in succ:
            for v in succ[u]:
                _setup(v, succ)
        else:
            # a leaf node
            if scenario_name_attribute is not None:
                if scenario_name_attribute not in tree.node[u]:
                    raise KeyError(
                        "node '%s' missing attribute: '%s'"
                        % (u, scenario_name_attribute))
                scenario_name = tree.node[u][scenario_name_attribute]
            else:
                scenario_name = u
            node_to_scenario[u] = scenario_name
            m.Scenarios.add(scenario_name)

    _setup(root,
           networkx.dfs_successors(tree, root))
    m = m.create_instance()
    def _add_node(u, stage, succ, pred):
        if node_name_attribute is not None:
            if node_name_attribute not in tree.node[u]:
                raise KeyError(
                    "node '%s' missing name attribute: '%s'"
                    % (u, node_name_attribute))
            node_name = tree.node[u][node_name_attribute]
        else:
            node_name = u
        m.NodeStage[node_name] = m.Stages[stage]
        if u == root:
            m.ConditionalProbability[node_name] = 1.0
        else:
            assert u in pred
            edge = tree.edge[pred[u]][u]
            probability = None
            if edge_probability_attribute is not None:
                if edge_probability_attribute not in edge:
                    raise KeyError(
                        "edge '(%s, %s)' missing probability attribute: '%s'"
                        % (pred[u], u, edge_probability_attribute))
                probability = edge[edge_probability_attribute]
            else:
                probability = 1.0/len(succ[pred[u]])
            m.ConditionalProbability[node_name] = probability
        if u in succ:
            child_names = []
            for v in succ[u]:
                child_names.append(
                    _add_node(v, stage+1, succ, pred))
            total_probability = 0.0
            for child_name in child_names:
                m.Children[node_name].add(child_name)
                total_probability += \
                    value(m.ConditionalProbability[child_name])
            if abs(total_probability - 1.0) > 1e-5:
                raise ValueError(
                    "edge probabilities leaving node '%s' "
                    "do not sum to 1 (total=%r)"
                    % (u, total_probability))
        else:
            # a leaf node
            scenario_name = node_to_scenario[u]
            m.ScenarioLeafNode[scenario_name] = node_name
            m.Children[node_name].clear()

        return node_name

    _add_node(root,
              1,
              networkx.dfs_successors(tree, root),
              networkx.dfs_predecessors(tree, root))

    return m