def _sensitivity(exposure, workspace, protocol, statistic, par=None, seed=None): """ Sensitivity analysis. >>> _sensitivity("11df840d0150d34c9716cd4cbdd164c8", "bondarenko_szigeti_bett_kim_rasmusson_2004_apical", "protocol", "apd90", ("g_Na", "Nao"), seed=1) TODO: Make optional arguments of exposure, lower, upper, etc. TODO: Accept json dict of model_kwargs, morris_kwargs """ m = Model(exposure + "/" + workspace, maxsteps=1e6, chunksize=1e5, reltol=1e-8) phenotypes(m, m.name) # initialize and cache default par = [str(i) for i in par] # Numpy cannot handle Unicode names if not par: # Default: sample within plus/minus 50% of all nonzero parameters par = [k for k in m.dtype.p.names if m.pr[k] != 0] baseline = unstruct(m.pr[par]).squeeze() lower = 0.5 * baseline upper = 1.5 * baseline if seed is not None: r.set_seed(seed) r("fun <- function(func, x) func(x)") raise Exception(str(r.fun(r.sum, baseline))) fun = scalar_pheno(m, statistic) r.as_vector(baseline) return r.morris(fun, factors=par, r=2, design={"type": "oat", "levels": 10, "grid.jump": 5}, binf=lower, bsup=upper)
def cycle(self, index=0, tmax=None, tol=1e-4, n=None): """ Find limit cycle (integrate until successive extrema are almost equal). :param str_or_int index: Index of state variable to base search on. :param float tmax: Time limit for aborting search for limit cycle. :param float tol: Tolerance for weighted root-mean-square norm of change in state vector between successive extrema. :param int n: Keep history of up to n extrema while searching for limit cycle. .. plot:: :include-source: >>> from matplotlib import pyplot as plt >>> from cgp.cvodeint.namedcvodeint import Namedcvodeint >>> from cgp.utils.unstruct import unstruct >>> from cgp.phenotyping.attractor import AttractorMixin >>> class Test(Namedcvodeint, AttractorMixin): ... '''Inherits van der Pol equations as default example.''' ... pass >>> test = Test(t=[0, 10000]) >>> t, y, period = test.cycle() >>> period 6.663... >>> h = plt.plot(t, unstruct(y), '-') """ if tmax is None: tmax = self.t[-1] extrema = deque([self.next_extremum(tmax, index)], maxlen=n) while True: t, y, (te, ye) = tup = self.next_extremum(tmax, index) extrema.appendleft(tup) # pylint:disable=W0612,C0301 for lag, (t_, y_, (te_, ye_)) in enumerate(extrema): #@UnusedVariable if lag == 0: continue diff = unstruct(ye_) - unstruct(ye) if self.weighted_rms(diff) < tol: L = reversed(list(extrema)[:lag]) t, y, _ = catrec(*L, globalize_time=False) period = te - te_ return t, y.squeeze(), period
def test_dimensions(): """Check relationship between dimensions of x and unstruct(x).""" # x has 2 fields and dimensions (3, 4) x = np.arange(24).view(dtype=[(i, int) for i in "ab"]).reshape(3, 4) # u has dimensions (3, 4, 2) u = unstruct(x) # change to u affects x u[:] += 10 # Last dimension of u corresponds to fields of x np.testing.assert_equal(x["a"], u[..., 0]) # First dimensions of u corresponds to first dimensions of x np.testing.assert_equal(x[0][0].item(), u[0][0])
def test_zero_rank(): """ Handle intricacies of zero-rank arrays. `Zero-rank arrays <http://projects.scipy.org/numpy/wiki/ZeroRankArray>`_ are tricky; they can be structured, yet be of type numpy.void. Here, x0 and x1 are almost, but not completely, the same: >>> fieldtype = np.int32 # ensure same result on 32- and 64-bit platforms >>> dtype = [("a", fieldtype), ("b", fieldtype)] >>> x0 = np.array([(0, 1)], dtype=dtype)[0] >>> x1 = np.array((0, 1), dtype=dtype) Despite a lot of equalities below, x0 and x1 are of different type. >>> (x0 == x1) and (x0.shape == x1.shape) and (x0.dtype == x1.dtype) True >>> x0 (0, 1) >>> x1 array((0, 1), dtype=[('a', '<i4'), ('b', '<i4')]) >>> type(x0), type(x1) (<type 'numpy.void'>, <type 'numpy.ndarray'>) Unstructuring them was tricky, but finally works. >>> unstruct(x0) array([0, 1]) >>> unstruct(x1) array([0, 1]) """ fieldtype = np.int32 # ensure same result on 32- and 64-bit platforms dtype = [("a", fieldtype), ("b", fieldtype)] x0 = np.array([(0, 1)], dtype=dtype)[0] x1 = np.array((0, 1), dtype=dtype) np.testing.assert_equal(unstruct(x0), unstruct(x1))
def do_sensitivity(exposure, workspace, protocol, statistic, par=None, seed=None, model=None, changeset=None, variant=None): """ Sensitivity analysis. Callback from R. >>> r("fun <- function(func, x) func(x)") RClosure with name <return value from eval>: <R function> >>> r.fun(r.sum, range(5)) array([10]) (R-style, sealed) >>> m = Model(workspace="beeler_reuter_1977", rename=dict( ... p=dict(IstimPeriod="stim_period", IstimAmplitude="stim_amplitude", ... IstimPulseDuration="stim_duration", IstimStart="stim_start")), ... reltol=1e-10, maxsteps=1e6, chunksize=100000) >>> m.pr.IstimStart = 0 >>> print "Result:", do_sensitivity("", "", "protocol", "apbase", ("C", "g_Na"), seed=1, model=m) Result:... Model runs: 6 mu mu.star sigma C 0.03672701 0.03672701 0.05130616 g_Na -0.41200934 0.41200934 0.04219007 TODO: Make optional arguments of exposure, lower, upper, etc. TODO: Accept json dict of model_kwargs, morris_kwargs """ if model is None: m = Model(workspace, exposure, changeset, variant, maxsteps=1e6, chunksize=1e5, reltol=1e-8) m.pr.stim_start = 0 else: m = model phenotypes(m) # initialize and cache default factors = [str(i) for i in par] # Numpy cannot handle Unicode names if not factors: # Default: sample within plus/minus 50% of all nonzero parameters factors = [k for k in m.dtype.p.names if m.pr[k] != 0] baseline = unstruct(m.pr[factors]).squeeze() lower = 0.5 * baseline upper = 1.5 * baseline if seed is not None: r.set_seed(seed) fun = scalar_pheno(statistic, m, factors) design = {"type": "oat", "levels": 10, "grid.jump": 5} return r.morris(fun, factors=factors, r=2, design=design, binf=lower, bsup=upper)
def shoehorn_recarray(x, ndim=1): """ Shoehorn multi-dimensional record array into HDF-compatible form. Pytables does not allow table rows to be arrays. Thus, a record array with dimension > 1 cannot be converted to an HDF table. However, it will accept a 1-D record array whose *fields* are arrays. This function rearranges the dimensions of the entire array and its fields, retaining *ndim* dimensions and pushing the rest into the fields. One dimension is suitable for creating a table, whereas zero dimensions is suitable for a table row. >>> x = np.arange(24.0).view([("a", float), ("b", float)]).reshape(4, 3) >>> y = shoehorn_recarray(x) >>> np.testing.assert_equal(x["a"], y["a"]) >>> x.shape (4, 3) >>> x.dtype dtype([('a', '<f8'), ('b', '<f8')]) >>> y.shape (4,) >>> y.dtype dtype([('a', '<f8', (3,)), ('b', '<f8', (3,))]) >>> z = shoehorn_recarray(x, ndim=0) >>> np.testing.assert_equal(x["a"], z["a"]) >>> z.shape () >>> z.dtype dtype([('a', '<f8', (4, 3)), ('b', '<f8', (4, 3))]) """ u = unstruct(x) # The shape of u is x.shape + (#fields,) + fieldshape # Roll the #fields dimension to position ndim (note zero-based indexing) r = np.rollaxis(u, len(x.shape), ndim) dtype = [i[:2] + (r.shape[1 + ndim:],) for i in ast.literal_eval(str(x.dtype))] return r.flatten().view(dtype).squeeze()
def restruct(a, axes=0): """ Convert shape dimensions to field dimensions. Converting a record array of shape (3,) with two scalar fields to one of size () whose fields are shape (3,). >>> restruct(np.array([(0, 1), (2, 3), (4, 5)], ... dtype=[("a", "|i1"), ("b", "|i1")])) array(([0, 2, 4], [1, 3, 5]), dtype=[('a', '|i1', (3,)), ('b', '|i1', (3,))]) This is an array of shape (2, 3) with two scalar fields. >>> a = np.array([[(0, 1), (2, 3), (4, 5)], ... [(6, 7), (8, 9), (10, 11)]], ... dtype=[('a', '|i1'), ('b', '|i1')]) Folding the first dimension into each field gives an array of shape (3,) whose fields have shape (2,). >>> restruct(a) array([([0, 6], [1, 7]), ([2, 8], [3, 9]), ([4, 10], [5, 11])], dtype=[('a', '|i1', (2,)), ('b', '|i1', (2,))]) """ axes = np.atleast_1d(axes) u = unstruct(a) ax = range(u.ndim) # Move the given axes to the end for i in axes: ax.remove(i) ax.append(i) ut = u.transpose(ax).ravel() dtype = [(k, u.dtype, a.shape[axes]) for k in a.dtype.names] result = ut.view(dtype) result.shape = result.shape[:a.ndim - len(axes)] return result
ph = np.concatenate([phenotypes(i) for i in mat2par(mat)]) result = ph["apd90"] result[np.isnan(result)] = 0 return py2ri(result) @ri.rternalize def appeak(mat): ph = np.concatenate([phenotypes(i) for i in mat2par(mat)]) result = ph["appeak"] result[np.isnan(result)] = 0 return py2ri(result) # mu mu.star sigma #Cm -0.4348831 0.6603604 0.9338907 #Vmyo 0.1264869 0.1264869 0.1657938 # mu mu.star sigma #Cm 0.6716927 0.6716927 0.3328038 #Vmyo -0.3135712 0.3135712 0.3033346 if __name__ == "__main__": binf = 0.5 * unstruct(m.pr[factors]) bsup = 1.5 * unstruct(m.pr[factors]) r.pdf("morrisplot.pdf") r.plot(r.morris(apd90, factors=factors, r=2, design={"type": "oat", "levels": 10, "grid.jump": 5}, binf=binf, bsup=bsup)) r.dev_off() # saved = np.load("/tmp/saveme.npy") # ph = [phenotypes(i) for i in saved] # fail = [np.isnan(i).all() for i in ph] # result = np.logical_not(fail).astype(float)
return par def scalar_pheno(field): """Make a function to return a named field of the phenotype array.""" @ri.rternalize def fun(rmatrix): """Scalar function for use with R's sensitivity::morris().""" ph = np.concatenate([phenotypes(i) for i in mat2par(rmatrix)]) return py2ri(ph[field]) return fun if __name__ == "__main__": # Sensitivity analysis baseline = unstruct(m.pr[factors]) lower = 0.5 * baseline upper = 1.5 * baseline result = dict() for field in "appeak", "apd90", "ctpeak", "ctbase", "ctd90": r.set_seed(20120221) # repeatable random sampling result[field] = r.morris(scalar_pheno(field), factors=factors, r=2, design={"type": "oat", "levels": 10, "grid.jump": 5}, binf=lower, bsup=upper) # Print and visualize results r.png("sensitivity.png", width=1024, height=768, pointsize=24) r.par(mfrow=(2, 3)) # multiple figures in two rows, three columns for k, v in result.items(): print "====================================================" print k print v
def test_dtypes(): """Verify that unstruct() handles different dtypes correctly.""" for fieldtype in np.int8, np.int32, float: dtype = [(i, fieldtype) for i in "ab"] x = np.arange(4, dtype=fieldtype).view(dtype) yield np.testing.assert_equal, unstruct(x), [[0, 1], [2, 3]]