Source code for psctb.analyse._symca.ccobjects

from __future__ import division, print_function
from __future__ import absolute_import
from __future__ import unicode_literals

import numpy as np
from numpy import array, nanmin, nanmax
from sympy import Symbol
from pysces import ModelMap, Scanner, ParScanner
from numpy import NaN, abs

from ...utils.model_graph import ModelGraph
from ...utils.misc import silence_print, DotDict, formatter_factory, \
    do_safe_state, find_min, find_max, get_value, stringify, \
    scanner_range_setup

from ...utils.plotting import Data2D


[docs]def cctype(obj): return 'ccobjects' in str(type(obj))
[docs]@silence_print def get_state(mod, do_state=False): if do_state: mod.doState() ss = [getattr(mod, 'J_' + r) for r in mod.reactions] + \ [getattr(mod, s + '_ss') for s in mod.species] return ss
[docs]class CCBase(object): """The base object for the control coefficients and control patterns""" def __init__(self, mod, name, expression, ltxe): super(CCBase, self).__init__() self.expression = expression self.mod = mod self._ltxe = ltxe self.name = name self._latex_name = '\\Sigma' self._analysis_method = 'symca' self._str_expression_ = None self._value = None self._latex_expression = None @property def latex_expression(self): if not self._latex_expression: self._latex_expression = self._ltxe.expression_to_latex( self.expression ) return self._latex_expression @property def latex_name(self): return self._latex_name @property def _str_expression(self): if not self._str_expression_: self._str_expression_ = str(self.expression) return self._str_expression_ @property def value(self): """The value property. Calls self._calc_value() when self._value is None and returns self._value""" self._calc_value() return self._value def _repr_latex_(self): return '$%s = %s = %.3f$' % (self.latex_name, self.latex_expression, self.value) def _calc_value(self): """Calculates the value of the expression""" keys = self.expression.atoms(Symbol) subsdict = {} for key in keys: str_key = str(key) subsdict[str_key] = getattr(self.mod, str_key) self._value = get_value(self._str_expression, subsdict) def __repr__(self): return self.expression.__repr__() def __add__(self, other): if cctype(other): return self.expression.__add__(other.expression) else: return self.expression.__add__(other) def __mul__(self, other): if cctype(other): return self.expression.__mul__(other.expression) else: return self.expression.__mul__(other) def __div__(self, other): if cctype(other): return self.expression.__div__(other.expression) else: return self.expression.__div__(other) def __pow__(self, other): if cctype(other): return self.expression.__pow__(other.expression) else: return self.expression.__pow__(other)
[docs]class CCoef(CCBase): """The object the stores control coefficients. Inherits from CCBase""" def __init__(self, mod, name, expression, denominator, ltxe): super(CCoef, self).__init__(mod, name, expression, ltxe) self.numerator = expression self.denominator = denominator.expression self.expression = self.numerator / denominator.expression self.denominator_object = denominator self._latex_numerator = None self._latex_expression_full = None self._latex_expression = None self._latex_name = None self._abs_value = None self.control_patterns = None self._set_control_patterns() @property def abs_value(self): self._calc_abs_value() return self._abs_value @property def latex_numerator(self): if not self._latex_numerator: self._latex_numerator = self._ltxe.expression_to_latex( self.numerator ) return self._latex_numerator @property def latex_expression_full(self): if not self._latex_expression_full: full_expr = '\\frac{' + self.latex_numerator + '}{' \ + self.denominator_object.latex_expression + '}' self._latex_expression_full = full_expr return self._latex_expression_full @property def latex_expression(self): if not self._latex_expression: self._latex_expression = '(' + \ self.latex_numerator + ')' + '/~\\Sigma' return self._latex_expression @property def latex_name(self): if not self._latex_name: self._latex_name = self._ltxe.expression_to_latex( self.name ) return self._latex_name def _perscan_legacy(self, parameter, scan_range): scan_res = [list() for i in range(len(list(self.control_patterns.values())) + 1)] scan_res[0] = scan_range for parvalue in scan_range: state_valid = do_safe_state( self.mod, parameter, parvalue, type='mca') cc_abs_value = 0 for i, cp in enumerate(self.control_patterns.values()): if state_valid: cp_abs = abs(cp.value) scan_res[i + 1].append(cp_abs) cc_abs_value += cp_abs else: scan_res[i + 1].append(NaN) for i, cp in enumerate(self.control_patterns.values()): if state_valid: scan_res[i + 1][-1] = (scan_res[i + 1] [-1] / cc_abs_value) * 100 return scan_res def _valscan_legacy(self, parameter, scan_range): control_pattern_range = list(range(len(list(self.control_patterns.values())) + 2)) scan_res = [list() for i in control_pattern_range] scan_res[0] = scan_range for parvalue in scan_range: state_valid = do_safe_state(self.mod, parameter, parvalue, type='mca') cc_value = 0 for i, cp in enumerate(self.control_patterns.values()): if state_valid: cp_value = cp.value scan_res[i + 1].append(cp_value) cc_value += cp_value else: scan_res[i + 1].append(NaN) if state_valid: scan_res[i + 2].append(cc_value) else: scan_res[i + 2].append(NaN) return scan_res def _perscan(self, parameter, scan_range, par_scan=False, par_engine='multiproc'): val_scan_res = self._valscan(parameter, scan_range, par_scan, par_engine) points = len(scan_range) parameter = val_scan_res[:, 0].reshape(points, 1) cp_abs_vals = np.abs(val_scan_res[:, 1:-1]) cp_abs_sum = np.sum(cp_abs_vals, 1).reshape(points, 1) cp_abs_perc = (cp_abs_vals / cp_abs_sum) * 100 scan_res = np.hstack([parameter, cp_abs_perc]) return scan_res def _valscan(self, parameter, scan_range, par_scan=False, par_engine='multiproc'): needed_symbols = [parameter] + \ stringify(list(self.expression.atoms(Symbol))) # This is experimental if par_scan: scanner = ParScanner(self.mod, par_engine) else: scanner = Scanner(self.mod) scanner.quietRun = True start, end, points, log = scanner_range_setup(scan_range) scanner.addScanParameter(parameter, start=start, end=end, points=points, log=log) scanner.addUserOutput(*needed_symbols) scanner.Run() subs_dict = {} for i, symbol in enumerate(scanner.UserOutputList): subs_dict[symbol] = scanner.UserOutputResults[:, i] control_pattern_names = list(self.control_patterns.keys()) denom_expr = str(self.denominator) cp_numerators = [self.control_patterns[cp_name].numerator for cp_name in control_pattern_names] column_exprs = stringify(cp_numerators) parameter = subs_dict[parameter].reshape(points, 1) scan_res = [] denom_val = get_value(denom_expr, subs_dict) for expr in column_exprs: scan_res.append(get_value(expr, subs_dict) / denom_val) scan_res = np.array(scan_res).transpose() cc_vals = np.sum(scan_res, 1).reshape(points, 1) scan_res = np.hstack([parameter, scan_res, cc_vals]) return scan_res
[docs] def do_par_scan(self, parameter, scan_range, scan_type='percentage', init_return=True, par_scan=False, par_engine='multiproc', force_legacy=False): assert scan_type in ['percentage', 'value'] init = getattr(self.mod, parameter) column_names = [parameter] + \ [cp.name for cp in list(self.control_patterns.values())] if scan_type == 'percentage': y_label = 'Control pattern percentage contribution' try: assert not force_legacy, 'Legacy scan requested' scan_res = self._perscan(parameter, scan_range, par_scan, par_engine) data_array = scan_res except Exception as exception: print('The parameter scan yielded the following error:') print(exception) print('Switching over to slower scan method and replacing') print('invalid steady states with NaN values.') scan_res = self._perscan_legacy(parameter, scan_range) data_array = array(scan_res, dtype=np.float).transpose() ylim = [nanmin(data_array[:, 1:]), nanmax(data_array[:, 1:]) * 1.1] elif scan_type == 'value': column_names = column_names + [self.name] y_label = 'Control coefficient/pattern value' try: assert not force_legacy, 'Legacy scan requested' scan_res = self._valscan(parameter, scan_range, par_scan, par_engine) data_array = scan_res except Exception as exception: print('The parameter scan yielded the following error:') print(exception) print('Switching over to slower scan method and replacing') print('invalid steady states with NaN values.') scan_res = self._valscan_legacy(parameter, scan_range) data_array = array(scan_res, dtype=np.float).transpose() ylim = [nanmin(data_array[:, 1:]), nanmax(data_array[:, 1:]) * 1.1] # print data_array.shape if init_return: self.mod.SetQuiet() setattr(self.mod, parameter, init) self.mod.doMca() self.mod.SetLoud() mm = ModelMap(self.mod) species = mm.hasSpecies() if parameter in species: x_label = '[%s]' % parameter.replace('_', ' ') else: x_label = parameter ax_properties = {'ylabel': y_label, 'xlabel': x_label, 'xscale': 'linear', 'yscale': 'linear', 'xlim': [find_min(scan_range), find_max(scan_range)], 'ylim': ylim} data = Data2D(mod=self.mod, column_names=column_names, data_array=data_array, ltxe=self._ltxe, analysis_method='symca', ax_properties=ax_properties, file_name=self.name) return data
def _calc_abs_value(self): """Calculates the absolute numeric value of the control coefficient from the values of its control patterns.""" keys = self.expression.atoms(Symbol) subsdict = {} if len(keys) == 0: subsdict = None for key in keys: str_key = str(key) subsdict[str_key] = getattr(self.mod, str_key) for pattern in list(self.control_patterns.values()): pattern._calc_value(subsdict) self._abs_value = sum( [abs(pattern._value) for pattern in list(self.control_patterns.values())]) def _calc_value(self): """Calculates the numeric value of the control coefficient from the values of its control patterns.""" keys = self.expression.atoms(Symbol) subsdict = {} if len(keys) == 0: subsdict = None for key in keys: str_key = str(key) subsdict[str_key] = getattr(self.mod, str_key) for pattern in list(self.control_patterns.values()): pattern._calc_value(subsdict) self._value = sum( [pattern._value for pattern in list(self.control_patterns.values())]) def _set_control_patterns(self): """Divides control coefficient into control patterns and saves results in self.CPx where x is a number is the number of the control pattern as it appears in in control coefficient expression""" patterns = self.numerator.as_coeff_add()[1] if len(patterns) == 0: patterns = [self.numerator.as_coeff_add()[0]] cps = DotDict() cps._make_repr('v.name', 'v.value', formatter_factory()) for i, pattern in enumerate(patterns): name = 'CP{:3}'.format(i + 1).replace(' ', '0') cp = CPattern(self.mod, name, pattern, self.denominator_object, self, self._ltxe) setattr(self, name, cp) cps[name] = cp self.control_patterns = cps # assert self._check_control_patterns == True def _check_control_patterns(self): """Checks that all control patterns are either positive or negative""" all_same = False poscomp = [i.value > 0 for i in list(self.control_patterns.values())] negcomp = [i.value < 0 for i in list(self.control_patterns.values())] if all(poscomp): all_same = True elif all(negcomp): all_same = True return all_same
[docs] def highlight_patterns(self, width=None, height=None, show_dummy_sinks=False, show_external_modifier_links=False, pos_dic=None): mg = ModelGraph(mod=self.mod, pos_dic=pos_dic, analysis_method=self._analysis_method) if height: mg.height = height if width: mg.width = width mg.highlight_cc(self, show_dummy_sinks, show_external_modifier_links)
[docs]class CPattern(CCBase): """docstring for CPattern""" def __init__(self, mod, name, expression, denominator, parent, ltxe): super(CPattern, self).__init__(mod, name, expression, ltxe) self.numerator = expression self.denominator = denominator.expression self.expression = self.numerator / denominator.expression self.denominator_object = denominator self.parent = parent self._latex_numerator = None self._latex_expression_full = None self._latex_expression = None self._latex_name = None self._percentage = None def _calc_value(self, subsdict=None): """Calculates the value of the expression""" if subsdict is None: keys = self.expression.atoms(Symbol) subsdict = {} for key in keys: str_key = str(key) subsdict[str_key] = getattr(self.mod, str_key) self._value = get_value(self._str_expression, subsdict) @property def latex_numerator(self): if not self._latex_numerator: self._latex_numerator = self._ltxe.expression_to_latex( self.numerator ) return self._latex_numerator @property def latex_expression_full(self): if not self._latex_expression_full: full_expr = '\\frac{' + self.latex_numerator + '}{' \ + self.denominator_object.latex_expression + '}' self._latex_expression_full = full_expr return self._latex_expression_full @property def latex_expression(self): if not self._latex_expression: self._latex_expression = self.latex_numerator + '/~\\Sigma' return self._latex_expression @property def latex_name(self): if not self._latex_name: self._latex_name = self.name return self._latex_name @property def percentage(self): self._percentage = (abs(self.value) / self.parent.abs_value) * 100 return self._percentage