From 76497433010c9fe17e4abee4afa0b509d2071c0b Mon Sep 17 00:00:00 2001 From: Arie Matsliah Date: Sat, 15 Jul 2023 17:21:41 -0400 Subject: [PATCH] - Finish up motifs backend - show spinner - explainer - sample query - fix test --- src/blueprints/app.py | 20 ++++------------- src/service/motif_search.py | 40 ++++++--------------------------- src/templates/motif_search.html | 2 +- static/js/motif_search.js | 11 +++++++-- tests/unit/test_motif_search.py | 1 + 5 files changed, 22 insertions(+), 52 deletions(-) diff --git a/src/blueprints/app.py b/src/blueprints/app.py index 364d91e0..bb76c34c 100644 --- a/src/blueprints/app.py +++ b/src/blueprints/app.py @@ -1429,22 +1429,10 @@ def motifs(): motifs_query = MotifSearchQuery.from_form_query( request.args, NeuronDataFactory.instance() ) - try: - search_results = motifs_query.search() - log_activity( - f"Motif search with {motifs_query} found {len(search_results)} matches" - ) - except Exception as e: - error = f"Error searching for motif: {e=}" - log_error(error) - return render_template( - "motif_search.html", - regions=list(REGIONS.keys()), - results=search_results, - error=error, - NEURO_TRANSMITTER_NAMES=NEURO_TRANSMITTER_NAMES, - query=request.args, - ) + search_results = motifs_query.search() + log_activity( + f"Motif search with {motifs_query} found {len(search_results)} matches" + ) return render_template( "motif_search.html", diff --git a/src/service/motif_search.py b/src/service/motif_search.py index e2cc9675..39e77ec6 100644 --- a/src/service/motif_search.py +++ b/src/service/motif_search.py @@ -442,10 +442,9 @@ def row_satisfies_constraints(row, edge_constraints): return True @staticmethod - def _feasible_pairs( + def feasible_pairs( neuron_db, x_candidates, y_candidates, xy_constraints, yx_constraints ): - print("+++ here!") ins, outs = neuron_db.input_output_partners_with_synapse_counts() if not xy_constraints and not yx_constraints: @@ -503,7 +502,7 @@ def _feasible_pairs( else: # both edges are specified for x_rid in x_candidates: y_candidates_filtered = y_candidates.intersection( - set(ins[x_rid].keys()).intersection(set(outs[x_rid]).keys()) + set(ins[x_rid].keys()).intersection(set(outs[x_rid].keys())) ) rows = neuron_db.connections_.rows_for_cell(x_rid) from_x_tuples = defaultdict(list) @@ -575,16 +574,6 @@ def _search_triplets( assert all( [isinstance(c, set) for c in [x_candidates, y_candidates, z_candidates]] ) - print( - f"+++ x, y, z: {len(x_candidates)}, {len(y_candidates)}, {len(z_candidates)}" - ) - - print(f"+++ xy_ec: {xy_edge_constraints}") - print(f"+++ xz_ec: {xz_edge_constraints}") - print(f"+++ yx_ec: {yx_edge_constraints}") - print(f"+++ yz_ec: {yz_edge_constraints}") - print(f"+++ zx_ec: {zx_edge_constraints}") - print(f"+++ zy_ec: {zy_edge_constraints}") x_query, y_query, z_query = [], [], [] MotifSearchQuery.append_edge_queries(xy_edge_constraints, x_query, y_query) @@ -604,37 +593,27 @@ def _search_triplets( neuron_db, z_candidates, z_query ) - print( - f"+++ x, y, z after edge queries: {len(x_candidates)}, {len(y_candidates)}, {len(z_candidates)}" - ) - ins, outs = neuron_db.input_output_partner_sets() def z_satisfies_constraints(x_rid, y_rid, z_rid): constraints_satisfied = set() - - print("+++ checking if z satisfies constraints") if not xz_edge_constraints: if z_rid in outs[x_rid]: - print("+++ xz should not be an edge") return False else: constraints_satisfied.add("xz") if not zx_edge_constraints: if x_rid in outs[z_rid]: - print("+++ zx should not be an edge") return False else: constraints_satisfied.add("zx") if not yz_edge_constraints: if z_rid in outs[y_rid]: - print("+++ yz should not be an edge") return False else: constraints_satisfied.add("yz") if not zy_edge_constraints: if y_rid in outs[z_rid]: - print("+++ zy should not be an edge") return False else: constraints_satisfied.add("zy") @@ -643,7 +622,7 @@ def z_satisfies_constraints(x_rid, y_rid, z_rid): return True z_rows = neuron_db.connections_.rows_for_cell(z_rid) - print("+++ checking if z satisfies rows") + for r in z_rows: if ( r[0] == x_rid @@ -659,7 +638,7 @@ def z_satisfies_constraints(x_rid, y_rid, z_rid): if ( r[0] == y_rid and r[1] == z_rid - and not MotifSearchQuery.row_satisfies_constraints( + and MotifSearchQuery.row_satisfies_constraints( r, yz_edge_constraints ) ): @@ -670,7 +649,7 @@ def z_satisfies_constraints(x_rid, y_rid, z_rid): if ( r[0] == z_rid and r[1] == x_rid - and not MotifSearchQuery.row_satisfies_constraints( + and MotifSearchQuery.row_satisfies_constraints( r, zx_edge_constraints ) ): @@ -681,7 +660,7 @@ def z_satisfies_constraints(x_rid, y_rid, z_rid): if ( r[0] == z_rid and r[1] == y_rid - and not MotifSearchQuery.row_satisfies_constraints( + and MotifSearchQuery.row_satisfies_constraints( r, zy_edge_constraints ) ): @@ -705,24 +684,19 @@ def filtered_z_candidates(x_rid, y_rid): return res matches = [] - print("+++ starting the loop") - for _x_rid, _y_rid in MotifSearchQuery._feasible_pairs( + for _x_rid, _y_rid in MotifSearchQuery.feasible_pairs( neuron_db=neuron_db, x_candidates=x_candidates, y_candidates=y_candidates, xy_constraints=xy_edge_constraints, yx_constraints=yx_edge_constraints, ): - print(f"+++ examining {_x_rid}, {_y_rid}") - z_candidates_filtered = filtered_z_candidates(_x_rid, _y_rid) for _z_rid in z_candidates_filtered: if _z_rid == _x_rid or _z_rid == _y_rid: - print("+++ z is equal to y or x") continue if z_satisfies_constraints(x_rid=_x_rid, y_rid=_y_rid, z_rid=_z_rid): - print(f"+++ appending result, have {len(matches)} need {limit}") matches.append( MotifSearchQuery.make_match_dict( neuron_db=neuron_db, diff --git a/src/templates/motif_search.html b/src/templates/motif_search.html index dbe67c90..df1278dd 100644 --- a/src/templates/motif_search.html +++ b/src/templates/motif_search.html @@ -82,7 +82,7 @@ {% endfor %}
- +
diff --git a/static/js/motif_search.js b/static/js/motif_search.js index 7027df3e..d2f1e422 100644 --- a/static/js/motif_search.js +++ b/static/js/motif_search.js @@ -28,9 +28,16 @@ function ExplainerCard() {
What is this?
- [TODO: explanatory text and demo query] + With this tool you can search for specific motifs (sub-graphs) of size 3 in the connectome network. With A, B, C + denoting the motif node names, you can specify a filter for each node (same query language as in search) as well + as connectivity of each pair of nodes (not connected, connected one way, + connected both ways). Additionally you can apply neurotransmitter / brain region / min synapse count constraints + for every pair of connected nodes. Matching + motifs (if found) will be presented as a network along with 3D rendering of the corresponding cells.

- + + Try Example Query +
`; diff --git a/tests/unit/test_motif_search.py b/tests/unit/test_motif_search.py index 3732b173..4aba23cf 100644 --- a/tests/unit/test_motif_search.py +++ b/tests/unit/test_motif_search.py @@ -306,6 +306,7 @@ def test_search_size_3(self): msq.add_node("b", "id == 720575940620181077") msq.add_node("c", "id == 720575940626634789") msq.add_edge("a", "b", regions=None, min_synapse_count=0, nt_type=None) + msq.add_edge("b", "a", regions=None, min_synapse_count=0, nt_type=None) msq.add_edge("b", "c", regions=None, min_synapse_count=0, nt_type=None) msq.add_edge("a", "c", regions=None, min_synapse_count=0, nt_type=None) res = msq.search(limit=limit)