From 0ace7a6b222307a62dcdaa55826083fc2285a918 Mon Sep 17 00:00:00 2001 From: kanari Date: Wed, 21 Nov 2018 11:05:49 +0100 Subject: [PATCH] Cleaning-up basic functionality. Add path distance and gradient based growth. Change-Id: I4ec1a5ca421e82a8b694203a76ef94f17be3ac90 --- tns/extract_input/__init__.py | 5 +- tns/extract_input/from_TMD.py | 4 +- tns/extract_input/from_diameter.py | 10 ++ tns/generate/algorithms/tmdgrower.py | 149 ++++++++++++++--- tns/generate/algorithms/tmdgrower_path.py | 184 +++++++++++++++++++++ tns/generate/grower.py | 2 +- tns/generate/section.py | 192 +++++++++++++++------- tns/generate/tree.py | 75 +++++++-- 8 files changed, 526 insertions(+), 95 deletions(-) create mode 100644 tns/generate/algorithms/tmdgrower_path.py diff --git a/tns/extract_input/__init__.py b/tns/extract_input/__init__.py index 09c4ebbc..96c4eda7 100644 --- a/tns/extract_input/__init__.py +++ b/tns/extract_input/__init__.py @@ -14,7 +14,7 @@ def default_keys(): 'axon': {}} -def distributions(filepath, neurite_types=None, threshold_sec=2, diameter_model=False): +def distributions(filepath, neurite_types=None, threshold_sec=2, diameter_model=False, feature='radial_distances'): '''Extracts the input distributions from an input population defined by a directory of swc or h5 files threshold_sec: defines the minimum accepted number of terminations @@ -45,7 +45,8 @@ def fill_input_distributions(input_distr, neurite_type): input_distr[neurite_type].update( from_TMD.persistent_homology_angles(pop_tmd, threshold=threshold_sec, - neurite_type=neurite_type)) + neurite_type=neurite_type, + feature=feature)) for ntype in neurite_types: fill_input_distributions(input_distributions, ntype) diff --git a/tns/extract_input/from_TMD.py b/tns/extract_input/from_TMD.py index 4c4b9c4c..31261a9e 100644 --- a/tns/extract_input/from_TMD.py +++ b/tns/extract_input/from_TMD.py @@ -2,13 +2,13 @@ import numpy as np -def persistent_homology_angles(pop, threshold=2, neurite_type='basals'): +def persistent_homology_angles(pop, threshold=2, neurite_type='basals', feature='radial_distances'): """Adds the persistent homology extracted from a population of apicals to the distr dictionary Each tree in the population is associated with a persistence barcode (diagram) and a set of angles that will be used as input for synthesis. """ - ph_ang = [tmd.methods.get_ph_angles(tr) for tr in getattr(pop, neurite_type)] + ph_ang = [tmd.methods.get_ph_angles(tr, feature=feature) for tr in getattr(pop, neurite_type)] # Keep only the trees whose number of terminations is above the threshold # Saves the list of persistence diagrams for the selected neurite_type diff --git a/tns/extract_input/from_diameter.py b/tns/extract_input/from_diameter.py index a539069d..0f64e07c 100644 --- a/tns/extract_input/from_diameter.py +++ b/tns/extract_input/from_diameter.py @@ -3,6 +3,10 @@ from neurom.core import Tree from neurom import morphmath +default_model = {'Rall_ratio': 3./2., + 'siblings_ratio': 1./3.} + + def section_mean_taper(s): initial_diam = min(s.points[:,3]) @@ -123,6 +127,9 @@ def model(neuron): "term_max_diam": [c for c in chain(*term_max_diam)], "trunk": max_diam, "trunk_taper": tr_taper} + + values[typee-1].update(default_model) + return values @@ -161,4 +168,7 @@ def population_model(neurons): "term_max_diam": [c for c in chain(*term_max_diam)], "trunk": max_diam, "trunk_taper": tr_taper} + + values[typee-1].update(default_model) + return values diff --git a/tns/generate/algorithms/tmdgrower.py b/tns/generate/algorithms/tmdgrower.py index fa300dd5..0779914d 100644 --- a/tns/generate/algorithms/tmdgrower.py +++ b/tns/generate/algorithms/tmdgrower.py @@ -30,6 +30,53 @@ def __init__(self, self.angles = None self.bt_all = None + def curate_bif(self, stop, currentBF, currentTR): + '''Checks if selected bar is smaller than current bar length. + Returns the smallest bifurcation for which the length of the bar + is smaller than current one. + If there are no bif left or if the bif is largest than the current + termination target the np.inf is returned instead. + currentSec.stop_criteria["bif_term"] + ''' + current_length = np.abs(stop["term"] - stop["bif"]) + target_length = np.abs(currentTR - currentBF) + + if not self.bif or target_length==np.inf: + return np.inf + if target_length <= current_length: + return currentBF + for b in self.bif: + target_length = np.abs(currentTR - b) + if target_length <= current_length: + return b + return np.inf + + def get_stop_criteria(self, currentSec): + """Returns stop1 and stop2 that are the commonly + shared stop criteria for all TMDPath algorithms. + stop[bif_term] = {ref: the current path distance + bif: the smallest appropriate bifurcation path length + term: the appropriate termination path length + } + """ + currentBF = self.bif[0] if len(self.bif) > 1 else np.inf + + b1 = self.curate_bif(currentSec.stop_criteria["bif_term"], currentBF, + round_num(currentSec.stop_criteria["bif_term"]["term"])) + + stop1 = {"bif_term": {"ref": self.start_point[:3], + "bif": b1, + "term": round_num(currentSec.stop_criteria["bif_term"]["term"])}} + + b2 = self.curate_bif(currentSec.stop_criteria["bif_term"], currentBF, + round_num(self.bt_all[currentSec.stop_criteria["bif_term"]["bif"]])) + + stop2 = {"bif_term": {"ref": self.start_point[:3], + "bif": b2, + "term": round_num(self.bt_all[currentSec.stop_criteria["bif_term"]["bif"]])}} + + return (stop1, stop2) + def initialize(self): """Generates the data to be used for the initialization of the first section to be grown. Saves the extracted @@ -57,36 +104,45 @@ def bifurcate(self, currentSec): """ self.bif.remove(currentSec.stop_criteria["bif_term"]["bif"]) ang = self.angles[currentSec.stop_criteria["bif_term"]["bif"]] - dirs = self.bif_method(currentSec.direction, angles=ang) + dir1, dir2 = self.bif_method(currentSec.get_current_direction(), angles=ang) start_point = np.array(currentSec.points3D[-1]) - stops = [{"bif_term": {"ref": self.start_point[:3], - "bif": self.bif[0] if self.bif else np.inf, - "term": round_num(term)}} - for term in [currentSec.stop_criteria["bif_term"]["term"], - self.bt_all[currentSec.stop_criteria["bif_term"]["bif"]]]] + stop1, stop2 = self.get_stop_criteria(currentSec) - return [{'direction': direction, - 'start_point': start_point, - 'stop': stop, - 'process': currentSec.process} for direction, stop in zip(dirs, stops)] + s1 = {'direction': dir1, + 'start_point': start_point, + 'stop':stop1, + 'process': currentSec.process} + + s2 = {'direction': dir2, + 'start_point': start_point, + 'stop':stop2, + 'process': currentSec.process} + + return s1, s2 def terminate(self, currentSec): """When the growth of a section is terminated the "term" must be removed from the TMD grower """ self.term.remove(currentSec.stop_criteria["bif_term"]["term"]) + #print 'B: ', currentSec.stop_criteria["bif_term"]["bif"], + #print ' & removed T: ', currentSec.stop_criteria["bif_term"]["term"] def extend(self, currentSec): """Definition of stop criterion for the growth of the current section. """ bif_term = currentSec.stop_criteria["bif_term"] - bif_term["bif"] = round_num(self.bif[0]) if self.bif else np.inf - if bif_term["term"] not in self.term: - bif_term["term"] = round_num(self.term[0]) if self.term else np.inf + if bif_term["bif"] not in self.bif and bif_term["bif"] != np.inf: + currentSec.stop_criteria["bif_term"]["bif"] = self.curate_bif(currentSec.stop_criteria["bif_term"], + self.bif[0] if self.bif else np.inf, + round_num(bif_term["term"])) + + if bif_term["term"] not in self.term and bif_term["term"] != np.inf: + currentSec.stop_criteria["bif_term"]["term"] = np.min(self.term) if self.term else np.inf - return currentSec.generate() + return currentSec.next() class TMDApicalAlgo(TMDAlgo): @@ -116,19 +172,70 @@ def bifurcate(self, currentSec): process2 = 'secondary' start_point = np.array(currentSec.points3D[-1]) + stop1, stop2 = self.get_stop_criteria(currentSec) - stop1 = {"bif_term": {"ref": self.start_point[:3], - "bif": self.bif[0] if self.bif else np.inf, - "term": round_num(currentSec.stop_criteria["bif_term"]["term"])}} + s1 = {'direction': dir1, + 'start_point': start_point, + 'stop':stop1, + 'process':process1} - stop2 = {"bif_term": {"ref": self.start_point[:3], - "bif": self.bif[0] if self.bif else np.inf, - "term": round_num(self.bt_all[currentSec.stop_criteria["bif_term"]["bif"]])}} + s2 = {'direction': dir2, + 'start_point': start_point, + 'stop':stop2, + 'process': process2} + + return s1, s2 + +class TMDGradientAlgo(TMDAlgo): + """TreeGrower of TMD apical growth""" + + def bifurcate(self, currentSec): + """When the section bifurcates two new sections need to be created. + This method computes from the current state the data required for the + generation of two new sections and returns the corresponding dictionaries. + """ + self.bif.remove(currentSec.stop_criteria["bif_term"]["bif"]) + ang = self.angles[currentSec.stop_criteria["bif_term"]["bif"]] + + current_rd = np.linalg.norm(np.subtract(currentSec.points3D[-1], self.start_point)) + + if currentSec.process=='major': + dir1, dir2 = bif_methods['directional'](currentSec.direction, angles=ang) + if current_rd <= self.params['apical_distance']: + process1 = 'major' + process2 = 'secondary' + else: + process1 = 'secondary' + process2 = 'secondary' + else: + dir1, dir2 = self.bif_method(currentSec.get_current_direction(), angles=ang) + process1 = 'secondary' + process2 = 'secondary' + + start_point = np.array(currentSec.points3D[-1]) + + def majorize_process(stop, process, input_dir): + difference = np.abs(stop["bif_term"]["bif"] - stop["bif_term"]["term"]) + if difference > self.params['bias_length'] and difference != np.inf: + direction1 = (1.0 - self.params['bias']) * np.array(input_dir) + direction2 = self.params['bias'] * np.array(currentSec.direction) + direct = np.add(direction1, direction2).tolist() + return 'major', direct + else: + return process, input_dir + + stop1, stop2 = self.get_stop_criteria(currentSec) + + if process1 != 'major': + process1, dir1 = majorize_process(stop1, process1, dir1) + process2, dir2 = majorize_process(stop2, process2, dir2) + + start_point = np.array(currentSec.points3D[-1]) s1 = {'direction': dir1, 'start_point': start_point, 'stop':stop1, - 'process': process1} + 'process':process1} s2 = {'direction': dir2, 'start_point': start_point, diff --git a/tns/generate/algorithms/tmdgrower_path.py b/tns/generate/algorithms/tmdgrower_path.py new file mode 100644 index 00000000..5de83abc --- /dev/null +++ b/tns/generate/algorithms/tmdgrower_path.py @@ -0,0 +1,184 @@ +"""Basic class for TreeGrower Algorithms based on path distance""" + +import numpy as np + +from tns.morphmath import sample +from tns.basic import round_num +from tns.generate.algorithms.common import init_ph_angles, bif_methods +from tns.generate.algorithms.tmdgrower import TMDAlgo + + +class TMDAlgoPath(TMDAlgo): + """TreeGrower of TMD path dependent growth""" + + def initialize(self): + """Generates the data to be used for the initialization + of the first section to be grown. Saves the extracted + input data into the corresponding structures. + """ + bif, term, angles, bt_all = init_ph_angles(self.ph_angles) + + self.bif = bif + self.term = term + self.angles = angles + self.bt_all = bt_all + + stop = {"bif_term": {"ref": 0, + "bif": self.bif[0], + "term": self.term[-1]}} + + num_sec = len(self.ph_angles) + + return stop, num_sec + + def get_stop_criteria(self, currentSec): + """Returns stop1 and stop2 that are the commonly + shared stop criteria for all TMDPath algorithms. + stop[bif_term] = {ref: the current path distance + bif: the smallest appropriate bifurcation path length + term: the appropriate termination path length + } + """ + currentBF = self.bif[0] if len(self.bif) > 1 else np.inf + + b1 = self.curate_bif(currentSec.stop_criteria["bif_term"], currentBF, + round_num(currentSec.stop_criteria["bif_term"]["term"])) + + stop1 = {"bif_term": {"ref": currentSec.pathlength, + "bif": b1, + "term": round_num(currentSec.stop_criteria["bif_term"]["term"])}} + + b2 = self.curate_bif(currentSec.stop_criteria["bif_term"], currentBF, + round_num(self.bt_all[currentSec.stop_criteria["bif_term"]["bif"]])) + + stop2 = {"bif_term": {"ref": currentSec.pathlength, + "bif": b2, + "term": round_num(self.bt_all[currentSec.stop_criteria["bif_term"]["bif"]])}} + + return (stop1, stop2) + + def bifurcate(self, currentSec): + """When the section bifurcates two new sections need to be created. + This method computes from the current state the data required for the + generation of two new sections and returns the corresponding dictionaries. + """ + self.bif.remove(currentSec.stop_criteria["bif_term"]["bif"]) + + ang = self.angles[currentSec.stop_criteria["bif_term"]["bif"]] + dir1, dir2 = self.bif_method(currentSec.get_current_direction(), angles=ang) + + start_point = np.array(currentSec.points3D[-1]) + + stop1, stop2 = self.get_stop_criteria(currentSec) + + s1 = {'direction': dir1, + 'start_point': start_point, + 'stop':stop1, + 'process': currentSec.process} + + s2 = {'direction': dir2, + 'start_point': start_point, + 'stop':stop2, + 'process': currentSec.process} + + return s1, s2 + +class TMDApicalAlgoPath(TMDAlgoPath): + """TreeGrower of TMD apical growth""" + + def bifurcate(self, currentSec): + """When the section bifurcates two new sections need to be created. + This method computes from the current state the data required for the + generation of two new sections and returns the corresponding dictionaries. + """ + self.bif.remove(currentSec.stop_criteria["bif_term"]["bif"]) + ang = self.angles[currentSec.stop_criteria["bif_term"]["bif"]] + + current_rd = np.linalg.norm(np.subtract(currentSec.points3D[-1], self.start_point)) + + if currentSec.process=='major': + dir1, dir2 = bif_methods['directional'](currentSec.direction, angles=ang) + if current_rd <= self.params['apical_distance']: + process1 = 'major' + process2 = 'secondary' + else: + process1 = 'secondary' + process2 = 'secondary' + else: + dir1, dir2 = self.bif_method(currentSec.get_current_direction(), angles=ang) + process1 = 'secondary' + process2 = 'secondary' + + start_point = np.array(currentSec.points3D[-1]) + + stop1, stop2 = self.get_stop_criteria(currentSec) + + s1 = {'direction': dir1, + 'start_point': start_point, + 'stop':stop1, + 'process':process1} + + s2 = {'direction': dir2, + 'start_point': start_point, + 'stop':stop2, + 'process': process2} + + return s1, s2 + + +class TMDGradientAlgoPath(TMDAlgoPath): + """TreeGrower of TMD apical growth""" + + def bifurcate(self, currentSec): + """When the section bifurcates two new sections need to be created. + This method computes from the current state the data required for the + generation of two new sections and returns the corresponding dictionaries. + """ + self.bif.remove(currentSec.stop_criteria["bif_term"]["bif"]) + ang = self.angles[currentSec.stop_criteria["bif_term"]["bif"]] + current_rd = np.linalg.norm(np.subtract(currentSec.points3D[-1], self.start_point)) + + if currentSec.process=='major': + dir1, dir2 = bif_methods['directional'](currentSec.direction, angles=ang) + if current_rd <= self.params['apical_distance']: + process1 = 'major' + process2 = 'secondary' + else: + process1 = 'secondary' + process2 = 'secondary' + else: + dir1, dir2 = self.bif_method(currentSec.get_current_direction(), angles=ang) + process1 = 'secondary' + process2 = 'secondary' + + def majorize_process(stop, process, input_dir): + difference = np.abs(stop["bif_term"]["bif"] - stop["bif_term"]["term"]) + if difference == np.inf: + difference = np.abs(stop["bif_term"]["term"] - currentSec.pathlength) + if difference > self.params['bias_length']: + direction1 = (1.0 - self.params['bias']) * np.array(input_dir) + direction2 = self.params['bias'] * np.array(currentSec.direction) + direct = np.add(direction1, direction2).tolist() + return 'major', direct + else: + return process, input_dir + + stop1, stop2 = self.get_stop_criteria(currentSec) + + if process1 != 'major': + process1, dir1 = majorize_process(stop1, process1, dir1) + process2, dir2 = majorize_process(stop2, process2, dir2) + + start_point = np.array(currentSec.points3D[-1]) + + s1 = {'direction': dir1, + 'start_point': start_point, + 'stop':stop1, + 'process':process1} + + s2 = {'direction': dir2, + 'start_point': start_point, + 'stop':stop2, + 'process': process2} + + return s1, s2 diff --git a/tns/generate/grower.py b/tns/generate/grower.py index 629ed58f..64b1d4ae 100644 --- a/tns/generate/grower.py +++ b/tns/generate/grower.py @@ -38,7 +38,7 @@ def next(self): if grower.end(): self.active_neurites.remove(grower) else: - grower.next() + grower.next_point() def grow(self): """Generates a neuron according to the input_parameters diff --git a/tns/generate/section.py b/tns/generate/section.py index a0071289..22d71949 100644 --- a/tns/generate/section.py +++ b/tns/generate/section.py @@ -15,7 +15,7 @@ def __init__(self, randomness, targeting, process, - stop_criteria={"num_seg": 100}): + stop_criteria): '''A section is a list of points in 4D space (x, y, x, r) that are sequentially connected to each other. This process generates a tubular morphology that resembles a random walk. @@ -29,7 +29,6 @@ def __init__(self, "scale_prob": 1.0, "history": 1.0 - randomness - targeting} self.stop_criteria = stop_criteria - self.segs = 0 self.process = process def next_point(self, current_point): @@ -59,46 +58,11 @@ def next_point(self, current_point): return new_point - def check_stop_num_seg(self): + def check_stop(self): """Checks if any num_seg criteria is fullfiled. If it is it returns False and the growth stops. """ - if self.segs < self.stop_criteria["num_seg"]: - return True - else: - return False - - def check_stop_ph(self, prob_function): - """Checks if any of bif_term criteria is fullfiled. - If False the growth stops. - """ - crit = self.stop_criteria["bif_term"] - scale = self.params["scale_prob"] - - currd = np.linalg.norm(np.subtract(self.points3D[-1], crit["ref"])) - # compute expected path length for a given target radial distance - # pathtarget = self.segs > 1.3 * np.abs(crit["bif"] - crit["term"]) - - # Ensure that the section has at least two points - if len(self.points3D) < 2: - return True - - if ph_prob(prob_function, crit["bif"] - currd): - self.children = 2. - return False - elif ph_prob(prob_function, crit["term"] - currd): - self.children = 0. - return False - # Checks in too long path length is generated - # elif pathtarget: - # # If target bif smaller - # if crit["bif"] <= crit["term"]: - # self.children = 2. - # else: - # self.children = 0. - # return False - else: - return True + return len(self.points3D) < self.stop_criteria["num_seg"] def history(self, memory=5): '''Returns a combination of the sections history @@ -106,7 +70,6 @@ def history(self, memory=5): hist = np.array([0., 0., 0.]) for i in xrange(1, min(memory, len(self.points3D))): - hist = np.add(hist, np.exp(1. - i) * (self.points3D[-i] - self.points3D[-i - 1])) if np.linalg.norm(hist) != 0.0: @@ -114,48 +77,163 @@ def history(self, memory=5): else: return hist + def next(self): + '''Creates one point and returns the next state. + bifurcate, terminate or continue. + ''' + curr_point = self.points3D[-1] + point = self.next_point(curr_point) + self.points3D.append(np.array(point)) + + if self.check_stop(): + return 'continue' + elif self.children == 0: + return 'terminate' + else: + return 'bifurcate' + def generate(self): '''Creates a section with the selected parameters until at least one stop criterion is fulfilled. ''' - prob_function = stats.expon(loc=0, scale=self.params["scale_prob"]) - while self.check_stop_ph(prob_function): + while self.check_stop(): curr_point = self.points3D[-1] point = self.next_point(curr_point) self.points3D.append(np.array(point)) - self.segs = self.segs + 1 if self.children == 0: return 'terminate' else: return 'bifurcate' - def generate_nseg(self): + def get_current_direction(self): + return self.history(memory=10) + + +class SectionGrowerTMD(SectionGrower): + '''Class for the section + ''' + def __init__(self, + parent, + children, + start_point, + direction, + randomness, + targeting, + process, + stop_criteria): + '''A section is a list of points in 4D space (x, y, x, r) + that are sequentially connected to each other. This process + generates a tubular morphology that resembles a random walk. + ''' + super(SectionGrowerTMD, self).__init__(parent, + children, + start_point, + direction, + randomness, + targeting, + process, + stop_criteria) + + self.prob_function = stats.expon(loc=0, scale=self.params["scale_prob"]) + + def check_stop(self): + """Checks if any of bif_term criteria is fullfiled. + If False the growth stops. + """ + crit = self.stop_criteria["bif_term"] + + currd = np.linalg.norm(np.subtract(self.points3D[-1], crit["ref"])) + # Ensure that the section has at least two points + if len(self.points3D) < 2: + return True + + if ph_prob(self.prob_function, crit["bif"] - currd): + self.children = 2. + return False + elif ph_prob(self.prob_function, crit["term"] - currd): + self.children = 0. + return False + else: + return True + + +class SectionGrowerPath(SectionGrower): + '''Class for the section + ''' + def __init__(self, + parent, + children, + start_point, + direction, + randomness, + targeting, + process, + stop_criteria): + '''A section is a list of points in 4D space (x, y, x, r) + that are sequentially connected to each other. This process + generates a tubular morphology that resembles a random walk. + ''' + super(SectionGrowerPath, self).__init__(parent, + children, + start_point, + direction, + randomness, + targeting, + process, + stop_criteria) + + self.prob_function = stats.expon(loc=0, scale=self.params["scale_prob"]) + self.pathlength = 0 if parent is None else self.stop_criteria['bif_term']['ref'] + + def check_stop(self): + """Checks if any of bif_term criteria is fullfiled. + If False the growth stops. + """ + crit = self.stop_criteria["bif_term"] + + if len(self.points3D) < 2: + return True + if ph_prob(self.prob_function, crit["bif"] - self.pathlength): + self.children = 2. + return False + elif ph_prob(self.prob_function, crit["term"] - self.pathlength): + self.children = 0. + return False + else: + return True + + def next(self): + '''Creates one point and returns the next state. + bifurcate, terminate or continue. + ''' + curr_point = self.points3D[-1] + point = self.next_point(curr_point) + self.points3D.append(np.array(point)) + self.pathlength = self.pathlength + np.linalg.norm(np.subtract(curr_point, point)) + + if self.check_stop(): + return 'continue' + elif self.children == 0: + return 'terminate' + else: + return 'bifurcate' + + def generate(self): '''Creates a section with the selected parameters until at least one stop criterion is fulfilled. ''' - self.points3D.append(np.array(self.points3D[0])) - - while self.check_stop_num_seg(): + while self.check_stop(): curr_point = self.points3D[-1] point = self.next_point(curr_point) self.points3D.append(np.array(point)) - self.segs = self.segs + 1 + self.pathlength = self.pathlength + np.linalg.norm(np.subtract(curr_point, point)) if self.children == 0: return 'terminate' else: return 'bifurcate' - def get_current_direction(self): - - vect = np.subtract([self.points3D[-1][0], self.points3D[-1][1], self.points3D[-1][2]], - [self.points3D[0][0], self.points3D[0][1], self.points3D[0][2]]) - - if np.linalg.norm(vect) != 0.0: - return vect / np.linalg.norm(vect) - else: - return vect diff --git a/tns/generate/tree.py b/tns/generate/tree.py index 53f13f0c..423cf61b 100644 --- a/tns/generate/tree.py +++ b/tns/generate/tree.py @@ -7,15 +7,26 @@ import numpy as np from morphio import PointLevel, SectionType -from tns.generate.algorithms import basicgrower, tmdgrower -from tns.generate.section import SectionGrower +from tns.generate.algorithms import basicgrower, tmdgrower, tmdgrower_path +from tns.generate.section import SectionGrower, SectionGrowerTMD, SectionGrowerPath from tns.morphmath import random_tree as rd from tns.morphmath import rotation growth_algorithms = {'tmd': tmdgrower.TMDAlgo, 'tmd_apical': tmdgrower.TMDApicalAlgo, + 'tmd_gradient': tmdgrower.TMDGradientAlgo, + 'tmd-path': tmdgrower_path.TMDAlgoPath, + 'tmd_apical_path': tmdgrower_path.TMDApicalAlgoPath, + 'tmd_gradient_path': tmdgrower_path.TMDGradientAlgoPath, 'trunk': basicgrower.TrunkAlgo} +section_growers = {'tmd': SectionGrowerTMD, + 'tmd_apical': SectionGrowerTMD, + 'tmd_gradient': SectionGrowerTMD, + 'tmd-path': SectionGrowerPath, + 'tmd_apical_path':SectionGrowerPath, + 'tmd_gradient_path': SectionGrowerPath, + 'trunk': SectionGrower} class TreeGrower(object): """Tree class""" @@ -41,7 +52,7 @@ def __init__(self, self.type = parameters["tree_type"] # 2: axon, 3: basal, 4: apical, 5: other self.params = parameters self.distr = distributions - self.active_sections = set() + self.active_sections = list() grow_meth = growth_algorithms[self.params["growth_method"]] self.growth_algo = grow_meth(input_data=self.distr, @@ -62,23 +73,33 @@ def add_section(self, parent, direction, start_point, stop, process=None, childr from all the required information. The section is added to the neuron.sections and activated. """ - self.active_sections.add(SectionGrower(parent=parent, - start_point=start_point, - direction=direction, - randomness=self.params["randomness"], - targeting=self.params["targeting"], - children=children, - process=process, - stop_criteria=copy.deepcopy(stop))) + SGrower = section_growers[self.params['growth_method']] + + self.active_sections.append(SGrower(parent=parent, + start_point=start_point, + direction=direction, + randomness=self.params["randomness"], + targeting=self.params["targeting"], + children=children, + process=process, + stop_criteria=copy.deepcopy(stop))) def end(self): return not bool(self.active_sections) + def order_per_process(self, secs): + return np.copy(secs)[np.argsort([ss.process for ss in secs])] + + def order_per_bif(self, secs): + ordered_list = np.argsort([ss.stop_criteria['bif_term']['bif'] for ss in secs]) + return np.copy(secs)[ordered_list] + def next(self): '''Operates the tree growth according to the selected algorithm. ''' + ordered_sections = self.order_per_process(self.active_sections) - for section_grower in self.active_sections.copy(): + for section_grower in ordered_sections:#self.active_sections.copy(): # the current section_grower is generated # In here the stop criterion can be modified accordingly state = self.growth_algo.extend(section_grower) @@ -100,3 +121,33 @@ def next(self): # the current section_grower terminates self.growth_algo.terminate(section_grower) self.active_sections.remove(section_grower) + + def next_point(self): + '''Operates the tree growth according to the selected algorithm. + ''' + ordered_sections = self.order_per_bif(self.active_sections) + + for section_grower in ordered_sections:#np.copy(self.active_sections):#self.active_sections.copy(): + # the current section_grower is generated + # In here the stop criterion can be modified accordingly + state = self.growth_algo.extend(section_grower) + + if state != 'continue': + + section = self.neuron.append_section( + section_grower.parent, + PointLevel(np.array(section_grower.points3D).tolist(), + [self.params['radius'] * 2] * len(section_grower.points3D)), + SectionType(self.params['tree_type'])) + + if state == 'bifurcate': + # the current section_grower bifurcates + # Returns two section_grower dictionaries: (S1, S2) + for child_section in self.growth_algo.bifurcate(section_grower): + self.add_section(parent=section, **child_section) + self.active_sections.remove(section_grower) + + elif state == 'terminate': + # the current section_grower terminates + self.growth_algo.terminate(section_grower) + self.active_sections.remove(section_grower)