Example #1
0
    def _compute_sholl_data(self, branches):
        '''Compute the number of termination, bifurcation and continuation section for each
        neurite type, sholl layer and shell order

        data[neurite_type][layer][order][action_type] = counts

        Args:
            branches: a collection of Neurite or Section that will be traversed

        Note: This is based on
        https://bbpcode.epfl.ch/browse/code/platform/BlueRepairSDK/tree/BlueRepairSDK/src/morphstats.cpp#n93
        '''
        data = defaultdict(lambda: defaultdict(dict))

        for branch in branches:
            origin = self._get_origin(branch)
            order_offset = self._get_order_offset(branch)

            for section in branch.ipreorder():
                repair_type = self.repair_type_map[section]
                assert repair_type == self.repair_type_map[branch], \
                    'RepairType should not change along the branch way'
                order = branch_order(section) - order_offset
                first_layer, last_layer = (
                    np.linalg.norm(section.points[[0, -1], COLS.XYZ] - origin,
                                   axis=1) //
                    self.params['sholl_layer_size']).astype(int)
                per_type = data[repair_type]

                starting_layer = min(first_layer, last_layer)

                # TODO: why starting_layer + 1 and not starting_layer ?
                # bcoste The continuation from the starting layer should be taken into account
                # But that's how it is done in:
                # https://bbpcode.epfl.ch/source/xref/platform/BlueRepairSDK/BlueRepairSDK/src/morphstats.cpp#88
                for layer in range(starting_layer + 1,
                                   max(first_layer, last_layer)):
                    if order not in per_type[layer]:
                        per_type[layer][order] = {
                            Action.TERMINATION: 0,
                            Action.CONTINUATION: 0,
                            Action.BIFURCATION: 0
                        }
                    per_type[layer][order][Action.CONTINUATION] += 1

                if order not in per_type[last_layer]:
                    per_type[last_layer][order] = {
                        Action.TERMINATION: 0,
                        Action.CONTINUATION: 0,
                        Action.BIFURCATION: 0
                    }

                per_type[last_layer][order][Action.BIFURCATION if section.
                                            children else Action.
                                            TERMINATION] += 1
        return data
Example #2
0
def _tree_distance(sec1, sec2):
    '''
    Returns the number of sections between the 2 sections

    Reimplementation of:
    https://bbpcode.epfl.ch/browse/code/platform/BlueRepairSDK/tree/BlueRepairSDK/src/helper_axon.cpp#n35

    raises: if both sections are not part of the same neurite

    Note:
    I think the implementation of tree distance is true to the original
    but I would expect the tree distance of 2 children with the same parent to be 2 and not 1
    Because in the current case, (root, child1) and (child1, child2) have the
    same tree distance and it should probably not be the case
    '''
    original_sections = (sec1, sec2)
    dist = 0
    while True:
        diff = branch_order(sec1) - branch_order(sec2)
        if diff == 0:
            break

        if diff > 0:
            sec1 = sec1.parent
            dist += 1
        else:
            sec2 = sec2.parent
            dist += 1

    if sec1.id == sec2.id:
        return dist

    dist -= 1
    while sec1.id != sec2.id:
        sec1 = sec1.parent
        sec2 = sec2.parent
        dist += 2
        if None in {sec1, sec2}:
            raise Exception(
                'Sections {} and {} are not part of the same neurite'.format(
                    original_sections[0], original_sections[1]))

    return dist
Example #3
0
    def _get_order_offset(self, branch):
        r'''
        Return what should be considered as the branch order offset for this branch

        For obliques, the branch order is computed with respect to
        the branch order of the first oblique section so we have to remove the offset.

                    3
                 2  |
     oblique ---->\ |
                   \| 1
                    |
                    |
                    | 0

        oblique order is 2 - 1 = 1
        '''
        if self.repair_type_map[branch] == RepairType.oblique:
            return branch_order(branch)
        if self.repair_type_map[branch] == RepairType.tuft:
            return branch_order(self.apical_section)
        return 0
Example #4
0
    def _grow(self, section, order_offset, origin):
        '''grow main method

        Will either:
            - continue growing the section
            - create a bifurcation
            - terminate the growth

        Note: based on https://bbpcode.epfl.ch/browse/code/platform/BlueRepairSDK/tree/BlueRepairSDK/src/helper_dendrite.cpp#n387  # noqa, pylint: disable=line-too-long
        '''
        if (self.repair_type_map[section] == RepairType.tuft
                and _y_cylindrical_extent(section) >
                self.max_y_cylindrical_extent):
            return

        sholl_layer = _get_sholl_layer(section, origin,
                                       self.params['sholl_layer_size'])
        pseudo_order = branch_order(section) - order_offset
        L.debug('In _grow. Layer: %s, order: %s', sholl_layer, pseudo_order)

        proba = _get_sholl_proba(self.info['sholl'],
                                 self.repair_type_map[section], sholl_layer,
                                 pseudo_order)

        L.debug('action proba[%s][%s][%s]: %s', section.type, sholl_layer,
                pseudo_order, proba)
        action = np.random.choice(list(proba.keys()), p=list(proba.values()))

        if action == Action.CONTINUATION:
            L.debug('Continuing')
            backwards_sections = _grow_until_sholl_sphere(
                section, origin, sholl_layer, self.params, self.taper,
                self.tip_radius, self.current_trunk_radius)

            if backwards_sections == 0:
                self._grow(section, order_offset, origin)
            L.debug(section.points[-1])

        elif action == Action.BIFURCATION:
            L.debug('Bifurcating')
            backwards_sections = _grow_until_sholl_sphere(
                section, origin, sholl_layer, self.params, self.taper,
                self.tip_radius, self.current_trunk_radius)
            if backwards_sections == 0:
                self._bifurcation(section, order_offset)
                for child in section.children:
                    self.repair_type_map[child] = self.repair_type_map[section]
                    self._grow(child, order_offset, origin)
Example #5
0
def _branching_angles(section, order_offset=0):
    '''Return a list of 2-tuples. The first element is the branching order and the second one is
    the angles between the direction of the section and its children's ones

    Note: based on https://bbpcode.epfl.ch/browse/code/platform/BlueRepairSDK/tree/BlueRepairSDK/src/morphstats.cpp#n194  # noqa, pylint: disable=line-too-long
    '''
    if section_length(section) < EPSILON:
        return []
    res = []

    branching_order = branch_order(section) - order_offset
    for child in section.children:
        if section_length(child) < EPSILON:
            continue

        theta = np.math.acos(
            np.dot(direction(section), direction(child)) /
            (section_length(section) * section_length(child)))
        res.append((branching_order, theta))
    return res
Example #6
0
    def _bifurcation(self, section, order_offset):
        '''Create 2 children at the end of the current section

        Note: based on https://bbpcode.epfl.ch/browse/code/platform/BlueRepairSDK/tree/BlueRepairSDK/src/helper_dendrite.cpp#n287  # noqa, pylint: disable=line-too-long
        '''
        current_diameter = section.points[-1, COLS.R] * 2

        child_diameters = 2 * [
            self.params["children_diameter_ratio"] * current_diameter
        ]

        median_angle = np.median(
            self._best_case_angle_data(self.repair_type_map[section],
                                       branch_order(section) - order_offset))

        last_segment_vec = _last_segment_vector(section)
        orthogonal = np.cross(last_segment_vec, section.points[-1, COLS.XYZ])

        def shuffle_direction(direction_):
            '''Return the direction to which to grow a child section'''
            theta = median_angle + np.radians(
                np.random.random() * self.params['bifurcation_angle'])
            first_rotation = np.dot(rotation_matrix(orthogonal, theta),
                                    direction_)
            new_dir = np.dot(
                rotation_matrix(direction_,
                                np.random.random() * np.pi * 2),
                first_rotation)
            return new_dir / np.linalg.norm(new_dir)

        for child_diameter in child_diameters:
            child_start = section.points[-1, COLS.XYZ]
            points = [
                child_start.tolist(),
                (child_start + shuffle_direction(last_segment_vec) *
                 self.params['seg_length']).tolist()
            ]
            prop = PointLevel(points, [current_diameter, child_diameter])

            child = section.append_section(prop)
            L.debug('section appended: %s', child.id)
Example #7
0
              np.std(seg_taper_rate),
              ', min=',
              np.min(seg_taper_rate),
              ', max=',
              np.max(seg_taper_rate),
              sep='')

    # Number of bifurcation points.
    print(
        'Number of bifurcation points:',
        sum(1
            for _ in nm.iter_sections(nrn,
                                      iterator_type=Tree.ibifurcation_point)))

    # Number of bifurcation points for apical dendrites
    print(
        'Number of bifurcation points (apical dendrites):',
        sum(1 for _ in nm.iter_sections(nrn,
                                        iterator_type=Tree.ibifurcation_point,
                                        neurite_filter=tree_type_checker(
                                            nm.APICAL_DENDRITE))))

    # Maximum branch order
    print('Maximum branch order:',
          max(sectionfunc.branch_order(s) for s in nm.iter_sections(nrn)))

    # Neuron's bounding box
    # Note: does not account for soma radius
    print('Bounding box ((min x, y, z), (max x, y, z))',
          geom.bounding_box(nrn))