diff --git a/src/hiv_tx_network.js b/src/hiv_tx_network.js index 25ccd6b..432129e 100755 --- a/src/hiv_tx_network.js +++ b/src/hiv_tx_network.js @@ -799,7 +799,8 @@ class HIVTxNetwork { filter, no_clone, given_json, - include_extra_edges + include_extra_edges, + edge_subset ) { var cluster_json = {}; var map_to_id = {}; @@ -818,17 +819,20 @@ class HIVTxNetwork { given_json = given_json || this.json; - cluster_json.Edges = _.filter(given_json.Edges, (e) => { - if (_.isUndefined(e.source) || _.isUndefined(e.target)) { - return false; - } + cluster_json.Edges = _.filter( + edge_subset ? edge_subset : given_json.Edges, + (e) => { + if (_.isUndefined(e.source) || _.isUndefined(e.target)) { + return false; + } - return ( - given_json.Nodes[e.source].id in map_to_id && - given_json.Nodes[e.target].id in map_to_id && - (include_extra_edges || !HIVTxNetwork.is_edge_injected(e)) - ); - }); + return ( + given_json.Nodes[e.source].id in map_to_id && + given_json.Nodes[e.target].id in map_to_id && + (include_extra_edges || !HIVTxNetwork.is_edge_injected(e)) + ); + } + ); if (filter) { cluster_json.Edges = _.filter(cluster_json.Edges, filter); @@ -1269,6 +1273,9 @@ class HIVTxNetwork { this.node_id_to_object[n.id] = n; nodeID2idx[n.id] = i; }); + + let traversal_cache = null; + _.each(groups, (pg) => { if (!pg.validated) { pg.node_objects = []; @@ -1318,17 +1325,35 @@ class HIVTxNetwork { /** all the network nodes connected to the nodes in the CoI at 1.5%; directly or indirectly*/ + if (!traversal_cache) { + traversal_cache = [ + misc.hivtrace_compute_adjacency_with_edges( + this.json["Nodes"], + this.json["Edges"], + (e) => e.length <= 0.015 + ), + misc.hivtrace_compute_adjacency_with_edges( + this.json["Nodes"], + this.json["Edges"], + (e) => e.length <= this.subcluster_threshold + ), + ]; + } + + let saved_traversal_edges = []; const node_set15 = _.flatten( misc.hivtrace_cluster_depthwise_traversal( this.json["Nodes"], this.json["Edges"], (e) => e.length <= 0.015, + saved_traversal_edges, + pg.node_objects, null, - pg.node_objects + traversal_cache[0] ) ); - const saved_traversal_edges = auto_extend ? [] : null; + let saved_traversal_edges_sub = []; /** all the network nodes connected to the nodes in the subcluster threshold (0.5%); also saves all the edges that have been taken if auto_extend is true */ @@ -1338,11 +1363,15 @@ class HIVTxNetwork { this.json["Nodes"], this.json["Edges"], (e) => e.length <= this.subcluster_threshold, - saved_traversal_edges, - pg.node_objects + saved_traversal_edges_sub, + pg.node_objects, + null, + traversal_cache[1] ) ); + //console.log (saved_traversal_edges) + const direct_at_15 = new Set(); /** all the network nodes connected to the nodes in the CoI at 1.5%; only directly */ @@ -1354,7 +1383,8 @@ class HIVTxNetwork { (my_nodeset.has(this.json["Nodes"][e.target].id) || my_nodeset.has(this.json["Nodes"][e.source].id)), //null, - true + true, + saved_traversal_edges ); /** all the network nodes connected to the nodes in the CoI at 1.5%; only directly */ @@ -1376,7 +1406,8 @@ class HIVTxNetwork { e.length <= this.subcluster_threshold && (my_nodeset.has(this.json["Nodes"][e.target].id) || my_nodeset.has(this.json["Nodes"][e.source].id)), - true + true, + saved_traversal_edges_sub ); const direct_subcluster = new Set(); diff --git a/src/misc.js b/src/misc.js index 9746f9f..dd6b5cc 100644 --- a/src/misc.js +++ b/src/misc.js @@ -598,6 +598,44 @@ function hivtrace_compute_adjacency(nodes, edges, edge_filter) { return adjacency; } +/** + * Performs a depth-wise traversal on a cluster of nodes, considering edges and optional filters. + * + * @param {Object[]} nodes - An array of node objects. Each node should have an `id` property. + * @param {Object[]} edges - An array of edge objects. Each edge should have `source` and `target` properties + * referencing node IDs. + * @param {Function} [edge_filter] - An optional filtering function applied to edges before traversal. + * The function should accept an edge object and return a boolean indicating whether to include the edge. + + computes and returns an adjacency list in the form + + node id => set of adjacent (connected) node ids + + +*/ + +function hivtrace_compute_adjacency_with_edges(nodes, edges, edge_filter) { + let adjacency = {}; + + _.each(edges, (e) => { + if (!edge_filter || edge_filter(e)) { + let src = nodes[e.source]; + let tgt = nodes[e.target]; + + if (!(src.id in adjacency)) { + adjacency[src.id] = []; + } + adjacency[src.id].push([tgt, e]); + + if (!(tgt.id in adjacency)) { + adjacency[tgt.id] = []; + } + adjacency[tgt.id].push([src, e]); + } + }); + return adjacency; +} + /** * Performs a depth-wise traversal on a cluster of nodes, considering edges and optional filters. * @@ -623,7 +661,8 @@ function hivtrace_cluster_depthwise_traversal( edge_filter, save_edges, seed_nodes, - white_list + white_list, + given_adjacency // an optional set of node IDs (a subset of 'nodes') that will be considered for traversal // it is further assumed that seed_nodes are a subset of white_list, if the latter is specified ) { @@ -633,33 +672,44 @@ function hivtrace_cluster_depthwise_traversal( seed_nodes = seed_nodes || nodes; - _.each(nodes, (n) => { - n.visited = false; - adjacency[n.id] = []; - }); - - if (edge_filter) { - edges = _.filter(edges, edge_filter); - } + if (given_adjacency) { + _.each(nodes, (n) => { + n.visited = false; + }); + adjacency = given_adjacency; + } else { + _.each(nodes, (n) => { + n.visited = false; + adjacency[n.id] = []; + }); - if (white_list) { - edges = _.filter( - edges, - (e) => - white_list.has(nodes[e.source].id) && white_list.has(nodes[e.target].id) - ); - } + if (edge_filter) { + edges = _.filter(edges, edge_filter); + } - _.each(edges, (e) => { - try { - adjacency[nodes[e.source].id].push([nodes[e.target], e]); - adjacency[nodes[e.target].id].push([nodes[e.source], e]); - } catch { - throw Error( - "Edge does not map to an existing node " + e.source + " to " + e.target + if (white_list) { + edges = _.filter( + edges, + (e) => + white_list.has(nodes[e.source].id) && + white_list.has(nodes[e.target].id) ); } - }); + + _.each(edges, (e) => { + try { + adjacency[nodes[e.source].id].push([nodes[e.target], e]); + adjacency[nodes[e.target].id].push([nodes[e.source], e]); + } catch { + throw Error( + "Edge does not map to an existing node " + + e.source + + " to " + + e.target + ); + } + }); + } var traverse = function (node) { if (!(node.id in by_node)) { @@ -1277,4 +1327,5 @@ module.exports = { get_ui_element_selector_by_role, hivtrace_cluster_complete_clusters, hivtrace_compute_adjacency, + hivtrace_compute_adjacency_with_edges, }; diff --git a/src/tables.js b/src/tables.js index 3e6440b..3b584ec 100644 --- a/src/tables.js +++ b/src/tables.js @@ -37,6 +37,8 @@ function add_a_sortable_table( return; } + container.style("display", "none"); + var thead = container.selectAll("thead"); var tbody = container.selectAll("tbody"); @@ -106,6 +108,8 @@ function add_a_sortable_table( .select(misc.get_ui_element_selector_by_role("table-count-shown")) .text(content.length); } + + container.style("display", null); } /**