def validate_radius_threshold(morphology): """ This function validates the radius for types 1, 3, and 4 """ result = [] soma_radius_threshold = 35 dendrite_radius_threshold = 20 soma = morphology.get_root() if soma['radius'] < soma_radius_threshold: result.append( ve( "The radius must be above %spx for type 1" % soma_radius_threshold, soma['id'], "Info")) dendrite_nodes = morphology.get_node_by_types( [BASAL_DENDRITE, APICAL_DENDRITE]) for node in dendrite_nodes: if node['radius'] > dendrite_radius_threshold: result.append( ve( "The radius must be below %spx for types 3 and 4" % dendrite_radius_threshold, node['id'], "Info")) return result
def validate_type_thirty_count(marker_file): """ This function checks whether there is exactly one type 30 in the file """ result = [] type_30_markers = [] for marker in marker_file: if marker['name'] is TYPE_30: type_30_markers.append(marker) if len(type_30_markers) > 1: for marker in type_30_markers: result.append( ve( "Total number of type 30s is %s" % len(type_30_markers), { 'x': marker['original_x'], 'y': marker['original_y'], 'z': marker['original_z'], 'name': marker['name'] }, "Warning")) if len(type_30_markers) < 1: result.append( ve("Total number of type 30s is %s" % len(type_30_markers), {}, "Warning")) return result
def validate_node_parent(morphology): """ This function validates the type of parent node for a specific type of child node """ result = [] valid_soma_parents = {None} valid_axon_parents = {SOMA, AXON, BASAL_DENDRITE, None} valid_basal_dendrite_parents = {SOMA, BASAL_DENDRITE} valid_apical_dendrite_parents = {SOMA, APICAL_DENDRITE} soma_node_with_invalid_parents = morphology.filter_nodes( lambda node: node['type'] == SOMA and morphology.parent_of(node)) if soma_node_with_invalid_parents: result.append( ve( "Type 1 can only have a parent of the following types: %s" % valid_soma_parents, soma_node_with_invalid_parents[0]['id'], "Error")) axon_nodes_with_invalid_parents = morphology.filter_nodes( lambda node: node['type'] == AXON and morphology.parent_of(node) and morphology.parent_of(node)['type'] not in valid_axon_parents) for node in axon_nodes_with_invalid_parents: result.append( ve( "Type 2 can only have a parent of the following types: %s" % valid_axon_parents, node['id'], "Error")) basal_dendrite_nodes = morphology.get_node_by_types([BASAL_DENDRITE]) for node in basal_dendrite_nodes: if not valid_dendrite_parent(morphology, node, valid_basal_dendrite_parents): result.append( ve( "Type 3 can only have a parent of the following types: %s" % valid_basal_dendrite_parents, node['id'], "Error")) apical_dendrite_nodes = morphology.get_node_by_types([APICAL_DENDRITE]) for node in apical_dendrite_nodes: if not valid_dendrite_parent(morphology, node, valid_apical_dendrite_parents): result.append( ve( "Type 4 can only have a parent of the following types: %s" % valid_apical_dendrite_parents, node['id'], "Error")) return result
def validate_types_three_four_traceable_back_to_soma(morphology): """ This function checks if types 3,4 are traceable back to soma """ result = [] traceable_types = {BASAL_DENDRITE, APICAL_DENDRITE} traceable_nodes = set() to_visit = [morphology.get_root()] while to_visit: node = to_visit.pop() traceable_nodes.add(node['id']) to_visit.extend(morphology.children_of(node)) must_be_traceable = [] for node in reduce(list.__add__, map(morphology.get_node_by_types, [traceable_types])): must_be_traceable.append(node['id']) for node_id in must_be_traceable: if node_id not in traceable_nodes: result.append( ve( "Nodes of type %s must be traceable back to the soma" % traceable_types, node_id, "Warning")) return result
def validate_extreme_taper(morphology): """ This function checks whether there is an extreme taper. Extreme taper occurs when for each segment, the average radius of the first two nodes is more than two times the average radius of the last two nodes. Note: This tests is limited to segments of at lease 8 nodes. """ result = [] for segment in morphology.get_segment_list(): if len(segment) > 7: if segment[0]['type'] in [BASAL_DENDRITE, APICAL_DENDRITE]: average_radius_beginning = (segment[0]['radius'] + segment[1]['radius']) / 2 average_radius_end = (segment[-1]['radius'] + segment[-2]['radius']) / 2 if average_radius_beginning > 4 * average_radius_end: result.append( ve( "Extreme Taper: For types 3 and 4, the average radius of the first two nodes " "in a segment should not be greater than four times the average radius of the " "last two nodes in a segment (For segments that have more than 8 nodes)", [segment[0]['id'], segment[-2]['id']], "Info")) return result
def validate_constrictions(morphology): """ This function checks if the radius of basal dendrite and apical dendrite nodes is smaller 2.0px """ result = [] # start with root, do depth-first traverse up to depth 10 eligible_nodes = [] to_visit = [(morphology.get_root(), 0)] while to_visit: (node, depth) = to_visit.pop() if depth < 10: eligible_nodes.append(node) to_visit.extend([(child_node, depth + 1) for child_node in morphology.children_of(node)]) for node in eligible_nodes: if node['type'] in [BASAL_DENDRITE, APICAL_DENDRITE]: if node['radius'] < 2.0: result.append( ve( "Constriction: The radius of types 3 and 4 " "should not be less than 2.0px", node['id'], "Warning")) return result
def validate_coordinates_corresponding_to_axon_tip(marker_file, morphology): """ This function checks whether the coordinates for each axon marker corresponds to a tip of a axon type in the related morphology """ result = [] marker_types = [NO_RECONSTRUCTION] morphology_tip_nodes = [] morphology_axon_nodes = morphology.get_node_by_types([AXON]) for node in morphology_axon_nodes: if not morphology.children_of(node): morphology_tip_nodes.append(node) for marker in marker_file: tip_marker = False if marker['name'] in marker_types: for node in morphology_tip_nodes: """ Subtract one from the coordinates because there is a known discrepancy between the coordinates of the marker file and the swc file """ if (marker['original_x'] - 1) == node['x'] and (marker['original_y'] - 1) == node['y'] \ and (marker['original_z'] - 1) == node['z']: tip_marker = True if not tip_marker: result.append( ve( "Coordinates for each axon (type 20) needs to correspond to a tip of an axon " "type (type 2) in the related morphology", { 'x': marker['original_x'], 'y': marker['original_y'], 'z': marker['original_z'], 'name': marker['name'] }, "Info")) return result
def validate_independent_axon_has_more_than_four_nodes(morphology): """ This function checks if an independent (parent is -1) axon has more than three nodes """ result = [] axon_nodes = morphology.get_node_by_types([AXON]) for node in axon_nodes: if morphology.parent_of(node) is None: if len(morphology.children_of(node)) == 0: result.append( ve("There is an independent axon with less than 4 nodes", node['id'], "Info")) elif len(morphology.children_of(node)) == 1: if len(morphology.children_of( morphology.children_of(node)[0])) == 0: result.append( ve( "There is an independent axon with less than 4 nodes", node['id'], "Info")) elif len( morphology.children_of( morphology.children_of(node)[0])) == 1: if len( morphology.children_of( morphology.children_of( morphology.children_of(node)[0])[0])) == 0: result.append( ve( "There is an independent axon with less than 4 nodes", node['id'], "Info")) elif len(morphology.children_of(node)) == 2: if len(morphology.children_of(morphology.children_of(node)[0])) == 0 and \ len(morphology.children_of(morphology.children_of(node)[1])) == 0: result.append( ve( "There is an independent axon with less than 4 nodes", node['id'], "Info")) return result
def validate_expected_types(morphology): """ This function validates the expected types of the nodes """ result = [] nodes_with_invalid_type = morphology.filter_nodes( lambda node: node['type'] not in valid_types) for node in nodes_with_invalid_type: result.append( ve("Node type needs to be one of these values: %s" % valid_types, node['id'], "Warning")) return result
def validate_children_nodes_appear_before_parent_nodes(morphology): result = [] nodes = morphology.nodes() for node in nodes: parent = morphology.parent_of(node) if parent and node['id'] < parent['id']: result.append( ve("Child node needs to come before parent node", node['id'], "Error")) return result
def validate_number_of_soma_nodes(morphology): """ This function validates the number of type 1 nodes """ result = [] soma = morphology.get_node_by_types([SOMA]) if len(soma) != 1: for node in soma: result.append( ve("The morphology needs to have one soma node", node['id'], "Error")) return result
def validate_distance_between_connected_nodes(morphology): result = [] for compartment in morphology.compartments: node1 = compartment[0] node2 = compartment[1] if node1['type'] is not SOMA and node2['type'] is not SOMA: if morphology.get_compartment_length(compartment) > 50.0: result.append( ve( "The distance between two nodes should be less than 50px", [node1['id'], node2['id']], "Error")) return result
def validate_immediate_children_of_soma_cannot_branch(morphology): """ This function validates that immediate children of soma cannot branch """ result = [] soma = morphology.get_root() if soma: soma_children = morphology.children_of(soma) for node in soma_children: if len(morphology.children_of(node)) > 1: result.append( ve("Immediate children of soma cannot branch", node['id'], "Error")) return result
def validate_radius_has_negative_slope_dendrite(morphology, dendrite): """ This function checks whether the radius for dendrite nodes decreases when you are going away from the soma. """ result = [] root = (morphology.get_root()) if root['type'] is not SOMA: return result nodes_by_branch_order = collections.defaultdict(list) root_children = morphology.get_children_of_node_by_types(root, [dendrite]) to_visit = [(child, 1) for child in root_children] while to_visit: (node, parent_order) = to_visit.pop() children = morphology.get_children_of_node_by_types(node, [dendrite]) if len(children) > 1: order = parent_order + 1 else: order = parent_order nodes_by_branch_order[order].append(node) to_visit.extend([(child_node, order) for child_node in children]) orders = sorted(nodes_by_branch_order.keys()) avg_radius = [] for order in orders: nodes = nodes_by_branch_order[order] total_radius = 0 for node in nodes: total_radius += node['radius'] avg_radius.append(total_radius / len(nodes)) if len(orders) > 1: if slope_linear_regression_branch_order_avg_radius(orders, avg_radius) >= 0: result.append( ve( "Radius should have a negative slope for the " "following type: %s" % dendrite, [], "Warning")) return result
def validate_expected_name(marker_file): """ This function checks whether the markers have the expected types """ result = [] valid_names = [CUT_DENDRITE, NO_RECONSTRUCTION, TYPE_30] for marker in marker_file: if marker['name'] not in valid_names: result.append( ve( "Marker name needs to be one of these values: %s" % valid_names, { 'x': marker['original_x'], 'y': marker['original_y'], 'z': marker['original_z'], 'name': marker['name'] }, "Warning")) return result
def validate_multiple_axon_initiation_points(morphology): """ This function validates that the parent of axon (either type 1 or 3) only happens once """ nodes = morphology.nodes() expected_count = 1 matched_node_numbers = [] result = [] for node in nodes: if node['type'] == AXON: parent = morphology.parent_of(node) if parent and parent['type'] in [BASAL_DENDRITE, SOMA]: matched_node_numbers.append(node['id']) if len(matched_node_numbers) > expected_count: for node_number in matched_node_numbers: result.append( ve( "Axon can only have one parent of type basal dendrite or soma", node_number, "Error")) return result
def validate_count_node_parent(morphology, node_type, parent_type, expected_count): """ This function validates the number of nodes that have a specific type of parent """ matched_node_numbers = [] result = [] nodes_by_type = morphology.get_node_by_types([node_type]) for node in nodes_by_type: if morphology.parent_of( node) and morphology.parent_of(node)['type'] is parent_type: matched_node_numbers.append(node['id']) if len(matched_node_numbers) > expected_count: for node_number in matched_node_numbers: result.append( ve( "Nodes of type %s can only have %s parent of type %s" % (node_type, expected_count, parent_type), node_number, "Warning")) return result
def validate_coordinates_corresponding_to_dendrite_tip(marker_file, morphology): """ This function checks whether the coordinates for each dendrite marker corresponds to a tip of a dendrite type in the related morphology """ result = [] marker_types = [CUT_DENDRITE] morphology_tip_nodes = [] morphology_dendrite_nodes = morphology.get_node_by_types( [BASAL_DENDRITE, APICAL_DENDRITE]) for node in morphology_dendrite_nodes: if not morphology.children_of(node): morphology_tip_nodes.append(node) for marker in marker_file: tip_marker = False if marker['name'] in marker_types: for node in morphology_tip_nodes: """ Subtract one from the coordinates because there is a known discrepancy between the coordinates of the marker file and the swc file """ if (marker['original_x'] - 1) == node['x'] and ( marker['original_y'] - 1) == node['y'] and ( marker['original_z'] - 1) == node['z']: tip_marker = True if not tip_marker: result.append( ve( "Coordinates for each dendrite (type 10) needs to correspond to a tip of a dendrite " "type (type 3 or 4) in the related morphology", { 'x': marker['original_x'], 'y': marker['original_y'], 'z': marker['original_z'], 'name': marker['name'] }, "Info")) return result
def validate_no_reconstruction_count(marker_file): """ This function checks whether there is exactly one type 20 in the file """ result = [] no_reconstruction_markers = [] for marker in marker_file: if marker['name'] is NO_RECONSTRUCTION: no_reconstruction_markers.append(marker) if len(no_reconstruction_markers) > 1: for marker in no_reconstruction_markers: result.append( ve( "Total number of type 20s is more than one: %s" % len(no_reconstruction_markers), { 'x': marker['original_x'], 'y': marker['original_y'], 'z': marker['original_z'], 'name': marker['name'] }, "Warning")) return result