def validate_network(graph: Dict) -> Dict[str, Union[bool, List]]: """ Parameters ---------- graph : dict graph in graph-dict format. For example: graph = { "directed": True, "nodes": [{"id": "A"}, {"id": "B"}], "edges": [{"source": "A", "target": "B"}], } """ _graph = thin_graph_dict(graph) G = graph_factory(_graph) isvalid = validate.is_valid(G) result: Dict[str, Union[bool, List]] = {"isvalid": isvalid} if isvalid: return result else: _keys = [ "node_cycles", "edge_cycles", "multiple_out_edges", "duplicate_edges" ] for key, value in zip(_keys, validate.validate_network(G)): result[key] = value return result
def solution_sequence( graph: Dict[str, Any], min_branch_size: int ) -> Dict[str, Dict[str, List[Dict[str, List[Dict[str, Union[str, Dict]]]]]]]: _graph = thin_graph_dict(graph) # strip unneeded metadata G = graph_factory(_graph) _sequence = parallel_sequential_subgraph_nodes(G, min_branch_size) sequence = { "parallel": [{ "series": [{ "nodes": [{ "id": n } for n in nodes] } for nodes in series] } for series in _sequence] } result: Dict[str, Any] = {"graph": _graph} result["min_branch_size"] = min_branch_size result["solution_sequence"] = sequence return result
def test_post_solve_watershed_stable(client, watershed_requests, watershed_responses, watershed_test_case): size, pct_tmnt, dirty_nodes = watershed_test_case watershed_request = watershed_requests[size, pct_tmnt] post_response = watershed_responses[size, pct_tmnt] results = post_response.json()["data"]["results"] reqd_min_attrs = attrs_to_resubmit(results) previous_results = { "previous_results": [{k: dct[k] for k in dct.keys() if k in reqd_min_attrs + ["node_id"]} for dct in results] } g = graph_factory(watershed_request["graph"]) subg = nx.DiGraph(g.subgraph(get_subset(g, nodes=dirty_nodes)).edges) subgraph = {"graph": nxGraph_to_dict(subg)} new_request = deepcopy(watershed_request) new_request.update(subgraph) new_request.update(previous_results) payload = json.dumps(new_request) route = config.API_LATEST + "/watershed/solve" response = client.post(route, data=payload) subgraph_results = response.json()["data"]["results"] check_subgraph_response_equal(subgraph_results, results)
def test_watershed_solve_sequence(contexts, watershed_requests, n_nodes, pct_tmnt): watershed_request = deepcopy(watershed_requests[n_nodes, pct_tmnt]) context = contexts["default"] g = graph_factory(watershed_request["graph"]) initial_results = land_surface_loading(watershed_request, False, context=context)["summary"] db = pandas.DataFrame(initial_results).set_index("node_id") _node_list = solution_sequence( watershed_request["graph"], 16)["solution_sequence"]["parallel"][0]["series"] node_list = [[n["id"] for n in nl["nodes"]] for nl in _node_list] presults = [] # no initial results, obvs for branch_nodes in node_list: # this subgraph is empty, has no data. subg = nx.DiGraph(g.subgraph(branch_nodes).edges) subgraph = {"graph": nxGraph_to_dict(subg)} reqd_min_attrs = attrs_to_resubmit(presults) previous_results = { "previous_results": [{ k: dct[k] for k in dct.keys() if k in reqd_min_attrs + ["node_id"] } for dct in presults if dct["node_id"] in subg.nodes()] } subg_request = deepcopy(watershed_request) subg_request.update(subgraph) subg_request.update(previous_results) subgraph_response_dict = solve_watershed( subg_request, False, context=context, ) subgraph_results = subgraph_response_dict["results"] presults.extend(subgraph_results) db = db.combine_first( pandas.DataFrame(subgraph_results).set_index("node_id")) response_dict = solve_watershed( watershed=watershed_request, treatment_pre_validated=False, context=context, ) results = response_dict["results"] + response_dict["leaf_results"] check_db = pandas.DataFrame(results).set_index("node_id").sort_index(0) check_results_dataframes(db.sort_index(0), check_db)
def test_round_trip_dict_to_dict(graph_dict_isvalid): graph_dict, isvalid = graph_dict_isvalid # round trips back to dictionaries are only possible if graph is directed. # this is because if the graph is undirected, then the 'source' and 'target' # keys _might_ be swapped. # if graph_dict['directed']: _g = utils.graph_factory(graph_dict) rt_graph_dict = utils.nxGraph_to_dict(_g) assert is_equal_subset(graph_dict, rt_graph_dict)
def test_thin_graph_dict_roundtrip_graph_to_graph(graph_obj_isvalid): graph_obj, isvalid = graph_obj_isvalid _g = utils.thin_graph_dict(utils.nxGraph_to_dict(graph_obj)) rt_Graph = utils.graph_factory(_g) assert rt_Graph.is_directed() == graph_obj.is_directed() assert rt_Graph.is_multigraph() == graph_obj.is_multigraph() assert nx.is_isomorphic(rt_Graph, graph_obj)
def test_thin_graph_dict_roundtrip_dict_to_dict(graph_dict_isvalid, check): graph_dict, isvalid = graph_dict_isvalid _graph_dict = utils.thin_graph_dict(graph_dict) _g = utils.graph_factory(_graph_dict) rt_graph_dict = utils.nxGraph_to_dict(_g) # if it's in the input, check that it's in the output too. if graph_dict.get(check, None) is not None: assert is_equal_subset(rt_graph_dict[check], graph_dict[check])
def test_clean_graph_object(graph_obj_isvalid): graph_obj, isvalid = graph_obj_isvalid clean_graph_dict = utils.clean_graph_dict(graph_obj) assert all([isinstance(n["id"], str) for n in clean_graph_dict["nodes"]]) assert all([isinstance(e["source"], str) for e in clean_graph_dict["edges"]]) assert all([isinstance(e["target"], str) for e in clean_graph_dict["edges"]]) clean_graph_obj = nx.relabel_nodes(graph_obj, lambda x: str(x)) _g = utils.graph_factory(clean_graph_dict) assert nx.is_isomorphic(_g, clean_graph_obj)
def render_subgraph_svg(task_result: dict, npi: Optional[float] = None) -> IO: g = graph_factory(task_result["graph"]) fig = render_subgraphs( g, request_nodes=task_result["requested_nodes"], subgraph_nodes=task_result["subgraph_nodes"], npi=npi, ) svg_bin = fig_to_image(fig) svg: IO = svg_bin.read() return svg
def test_round_trip_graph_to_graph(graph_obj_isvalid): graph_obj, isvalid = graph_obj_isvalid _g = utils.nxGraph_to_dict(graph_obj) G = utils.graph_factory(_g) assert nx.is_isomorphic(G, graph_obj) # same structure nodes and edges for node, data in G.nodes(data=True): # same node attrs assert data == graph_obj.nodes[node] if G.is_multigraph(): for s, t, k, data in G.edges(data=True, keys=True): # same edge attrs assert is_equal_subset(graph_obj.edges[(s, t, k)], data) else: for s, t, data in G.edges(data=True): # same edge attrs assert is_equal_subset(graph_obj.edges[(s, t)], data)
def initialize_graph( watershed: Dict[str, Any], treatment_pre_validated: bool, context: Dict[str, Any], ) -> Tuple[nx.DiGraph, List[str]]: errors: List[str] = [] g = graph_factory(watershed["graph"]) if not is_valid(g): err_msg = "NetworkValidationError: " _keys = ["node_cycles", "edge_cycles", "multiple_out_edges", "duplicate_edges"] for key, value in zip(_keys, validate_network(g)): if len(value) > 0: err_msg += ", " + ": ".join([key, str(value)]) errors.append(err_msg) land_surface = land_surface_loading(watershed, details=False, context=context) errors.extend(land_surface["errors"]) treatment_facilities = initialize_treatment_facilities( watershed, pre_validated=treatment_pre_validated, context=context ) errors.extend(treatment_facilities["errors"]) treatment_sites = initialize_treatment_sites(watershed, context=context,) errors.extend(treatment_sites["errors"]) data: Dict[str, Any] = {} for dictlist in [ watershed.get("previous_results", []), land_surface.get("summary", []), treatment_facilities.get("treatment_facilities", []), treatment_sites.get("treatment_sites", []), ]: node_data = dictlist_to_dict(dictlist, "node_id") for n, _data in node_data.items(): if n not in data: data[n] = {} data[n].update(_data) nx.set_node_attributes(g, data) return g, errors
def render_solution_sequence_svg(task_result: dict, npi: Optional[float] = None) -> IO: _graph = thin_graph_dict(task_result["graph"]) # strip unneeded metadata g = graph_factory(_graph) _sequence = task_result["solution_sequence"] solution_sequence = [[[n["id"] for n in ser["nodes"]] for ser in p["series"]] for p in _sequence["parallel"]] fig = render_solution_sequence(g, solution_sequence=solution_sequence, npi=npi) svg_bin = fig_to_image(fig) svg: IO = svg_bin.read() return svg
def test_stable_watershed_stable_subgraph_solutions(contexts, watershed_requests, watershed_test_case): n_nodes, pct_tmnt, dirty_nodes = watershed_test_case watershed_request = deepcopy(watershed_requests[(n_nodes, pct_tmnt)]) context = contexts["default"] response_dict = solve_watershed( watershed=watershed_request, treatment_pre_validated=False, context=context, ) results = response_dict["results"] reqd_min_attrs = attrs_to_resubmit(results) previous_results = { "previous_results": [{k: dct[k] for k in dct.keys() if k in reqd_min_attrs + ["node_id"]} for dct in results] } g = graph_factory(watershed_request["graph"]) # this subgraph is empty, has no data. subg = nx.DiGraph(g.subgraph(get_subset(g, nodes=dirty_nodes)).edges) subgraph = {"graph": nxGraph_to_dict(subg)} new_request = deepcopy(watershed_request) new_request.update(subgraph) new_request.update(previous_results) subgraph_response_dict = solve_watershed( watershed=new_request, treatment_pre_validated=False, context=context, ) subgraph_results = subgraph_response_dict["results"] check_subgraph_response_equal(subgraph_results, results)
def network_subgraphs(graph: Dict[str, Any], nodes: List[Dict[str, Any]]) -> Dict[str, Any]: _graph = thin_graph_dict(graph) node_ids = [node["id"] for node in nodes] G = graph_factory(_graph) subset = get_subset(G, node_ids) sub_g = G.subgraph(subset) subgraph_nodes = [{ "nodes": [{ "id": n } for n in nodes] } for nodes in nx.weakly_connected_components(sub_g)] result: Dict[str, Any] = {"graph": _graph} result.update({"requested_nodes": nodes}) result.update({"subgraph_nodes": subgraph_nodes}) return result
def test_validate_network(edgelist, isvalid, exp): expected_result = [isvalid] + list(exp) g = {"edges": [{"source": s, "target": t} for s, t in edgelist], "directed": True} G = graph_factory(g) result = tasks.validate_network(g) keys = [ "isvalid", "node_cycles", "edge_cycles", "multiple_out_edges", "duplicate_edges", ] if isvalid: assert result["isvalid"] == isvalid else: for key, value in zip(keys, expected_result): assert result[key] == value
def test_graph_factory_no_edges(): g1 = {"nodes": [{"id": "A"}, {"id": "C"}, {"id": "B"}]} G = utils.graph_factory(g1) assert len(G.nodes) == 3