class TestCompartmentTree():
    def loadTTree(self):
        """
        Load the T-tree morphology in memory

          6--5--4--7--8
                |
                |
                1
        """
        fname = os.path.join(MORPHOLOGIES_PATH_PREFIX, 'Tsovtree.swc')
        self.tree = SOVTree(fname, types=[1, 3, 4])
        self.tree.fitLeakCurrent(-75., 10.)
        self.tree.setCompTree()
        # do SOV calculation
        self.tree.calcSOVEquations()

    def testTreeDerivation(self):
        self.loadTTree()
        # locations
        locs_soma = [(1, 0.5)]
        locs_prox = [(4, 0.2)]
        locs_bifur = [(4, 1.0)]
        locs_dist_nobifur = [(6., 0.5), (8., 0.5)]
        locs_dist_bifur = [(4, 1.0), (6., 0.5), (8., 0.5)]
        locs_dist_nroot = [(4, 1.0), (4, 0.5), (6., 0.5), (8., 0.5)]
        # test structures
        with pytest.raises(KeyError):
            self.tree.createCompartmentTree('set0')
        # test root (is soma) in set
        self.tree.storeLocs(locs_dist_bifur + locs_soma, 'set0')
        ctree = self.tree.createCompartmentTree('set0')
        assert ctree[0].loc_ind == 3
        assert ctree[1].loc_ind == 0
        cloc_inds = [cn.loc_ind for cn in ctree[1].child_nodes]
        assert 1 in cloc_inds and 2 in cloc_inds
        # test soma not in set (but common root)
        self.tree.storeLocs(locs_dist_bifur, 'set1')
        ctree = self.tree.createCompartmentTree('set1')
        assert ctree[0].loc_ind == 0
        cloc_inds = [cn.loc_ind for cn in ctree[0].child_nodes]
        assert 1 in cloc_inds and 2 in cloc_inds
        # test soma not in set and no common root
        self.tree.storeLocs(locs_dist_nobifur, 'set2')
        with pytest.warns(UserWarning):
            ctree = self.tree.createCompartmentTree('set2')
        assert self.tree.getLocs('set2')[0] == (4, 1.)
        cloc_inds = [cn.loc_ind for cn in ctree[0].child_nodes]
        assert 1 in cloc_inds and 2 in cloc_inds
        # test 2 locs on common root
        self.tree.storeLocs(locs_dist_nroot, 'set3')
        ctree = self.tree.createCompartmentTree('set3')
        assert ctree[0].loc_ind == 1
        assert ctree[1].loc_ind == 0

    def testFitting(self):
        self.loadTTree()
        # locations
        locs_soma = [(1, 0.5)]
        locs_prox = [(4, 0.2)]
        locs_bifur = [(4, 1.0)]
        locs_dist_nobifur = [(6., 0.5), (8., 0.5)]
        locs_dist_bifur = [(4, 1.0), (6., 0.5), (8., 0.5)]
        # store the locations
        self.tree.storeLocs(locs_soma + locs_prox, 'prox')
        self.tree.storeLocs(locs_soma + locs_bifur, 'bifur')
        self.tree.storeLocs(locs_soma + locs_dist_nobifur, 'dist_nobifur')
        self.tree.storeLocs(locs_soma + locs_dist_bifur, 'dist_bifur')
        # derive steady state impedance matrices
        z_mat_prox = self.tree.calcImpedanceMatrix(locarg='prox')
        z_mat_bifur = self.tree.calcImpedanceMatrix(locarg='bifur')
        z_mat_dist_nobifur = self.tree.calcImpedanceMatrix(
            locarg='dist_nobifur')
        z_mat_dist_bifur = self.tree.calcImpedanceMatrix(locarg='dist_bifur')
        # create the tree structures
        ctree_prox = self.tree.createCompartmentTree('prox')
        ctree_bifur = self.tree.createCompartmentTree('bifur')
        ctree_dist_nobifur = self.tree.createCompartmentTree('dist_nobifur')
        ctree_dist_bifur = self.tree.createCompartmentTree('dist_bifur')
        # test the tree structures
        assert len(ctree_prox) == len(locs_prox) + 1
        assert len(ctree_bifur) == len(locs_bifur) + 1
        assert len(ctree_dist_nobifur) == len(locs_dist_nobifur) + 1
        assert len(ctree_dist_bifur) == len(locs_dist_bifur) + 1
        # fit the steady state models
        ctree_prox.computeGMC(z_mat_prox)
        ctree_bifur.computeGMC(z_mat_bifur)
        ctree_dist_nobifur.computeGMC(z_mat_dist_nobifur)
        ctree_dist_bifur.computeGMC(z_mat_dist_bifur)
        # compute the fitted impedance matrices
        z_fit_prox = ctree_prox.calcImpedanceMatrix()
        z_fit_bifur = ctree_bifur.calcImpedanceMatrix()
        z_fit_dist_nobifur = ctree_dist_nobifur.calcImpedanceMatrix()
        z_fit_dist_bifur = ctree_dist_bifur.calcImpedanceMatrix()
        # test correctness
        assert np.allclose(z_fit_prox, z_mat_prox, atol=0.5)
        assert np.allclose(z_fit_bifur, z_mat_bifur, atol=0.5)
        assert not np.allclose(
            z_fit_dist_nobifur, z_mat_dist_nobifur, atol=0.5)
        assert np.allclose(z_fit_dist_bifur, z_mat_dist_bifur, atol=0.5)

    def testReordering(self):
        self.loadTTree()
        # test reordering
        locs_dist_badorder = [(1., 0.5), (8., 0.5), (4, 1.0)]
        self.tree.storeLocs(locs_dist_badorder, 'badorder')
        z_mat_badorder = self.tree.calcImpedanceMatrix(locarg='badorder')
        ctree_badorder = self.tree.createCompartmentTree('badorder')
        # check if location indices are assigned correctly
        assert [node.loc_ind for node in ctree_badorder] == [0, 2, 1]
        # check if reordering works
        z_mat_reordered = ctree_badorder._preprocessZMatArg(z_mat_badorder)
        assert np.allclose(z_mat_reordered,
                           z_mat_badorder[:, [0, 2, 1]][[0, 2, 1], :])
        # check if fitting is correct
        ctree_badorder.computeGMC(z_mat_badorder)
        z_fit_badorder = ctree_badorder.calcImpedanceMatrix()
        assert np.allclose(z_mat_badorder, z_fit_badorder, atol=0.5)
        assert not np.allclose(z_mat_reordered, z_fit_badorder)
        # test if equivalent locs are returned correctly
        locs_equiv = ctree_badorder.getEquivalentLocs()
        assert all([
            loc == loc_
            for loc, loc_ in zip(locs_equiv, [(0, .5), (2, .5), (1, .5)])
        ])

    def loadBallAndStick(self):
        self.greens_tree = GreensTree(file_n=os.path.join(
            MORPHOLOGIES_PATH_PREFIX, 'ball_and_stick.swc'))
        self.greens_tree.setPhysiology(0.8, 100. / 1e6)
        self.greens_tree.setLeakCurrent(100., -75.)
        self.greens_tree.setCompTree()
        # set the impedances
        self.freqs = np.array([0.]) * 1j
        self.greens_tree.setImpedance(self.freqs)
        # create sov tree
        self.sov_tree = self.greens_tree.__copy__(new_tree=SOVTree())
        self.sov_tree.calcSOVEquations(maxspace_freq=50.)

    def testLocationMapping(self, n_loc=20):
        self.loadBallAndStick()
        # define locations
        xvals = np.linspace(0., 1., n_loc + 1)[1:]
        locs_1 = [(1, 0.5)] + [(4, x) for x in xvals]
        locs_2 = [(1, 0.5)] + [(4, x) for x in xvals][::-1]
        locs_3 = [(4, x) for x in xvals] + [(1, 0.5)]
        # create compartment trees
        ctree_1 = self.greens_tree.createCompartmentTree(locs_1)
        ctree_2 = self.greens_tree.createCompartmentTree(locs_2)
        ctree_3 = self.greens_tree.createCompartmentTree(locs_3)
        # test location indices
        locinds_1 = np.array([node.loc_ind for node in ctree_1])
        locinds_2 = np.array([node.loc_ind for node in ctree_2])
        locinds_3 = np.array([node.loc_ind for node in ctree_3])
        # check consecutive
        assert np.allclose(locinds_1[:-1], locinds_1[1:] - 1)
        # check permutation
        assert np.allclose(locinds_1[1:], locinds_2[1:][::-1])
        assert np.allclose(locinds_1[:-1], locinds_3[1:])

    def testGSSFit(self, n_loc=20):
        self.loadBallAndStick()
        # define locations
        xvals = np.linspace(0., 1., n_loc + 1)[1:]
        locs_1 = [(1, 0.5)] + [(4, x) for x in xvals]
        locs_2 = [(1, 0.5)] + [(4, x) for x in xvals][::-1]
        locs_3 = [(4, x) for x in xvals] + [(1, 0.5)]
        locs_4 = random.sample(locs_1, k=len(locs_1))
        # calculate impedance matrices
        z_mat_1 = self.greens_tree.calcImpedanceMatrix(locs_1)[0].real
        z_mat_2 = self.greens_tree.calcImpedanceMatrix(locs_2)[0].real
        z_mat_3 = self.greens_tree.calcImpedanceMatrix(locs_3)[0].real
        z_mat_4 = self.greens_tree.calcImpedanceMatrix(locs_4)[0].real
        # create compartment trees
        ctree_1 = self.greens_tree.createCompartmentTree(locs_1)
        ctree_2 = self.greens_tree.createCompartmentTree(locs_2)
        ctree_3 = self.greens_tree.createCompartmentTree(locs_3)
        ctree_4 = self.greens_tree.createCompartmentTree(locs_4)
        # fit g_m and g_c
        ctree_1.computeGMC(z_mat_1, channel_names=['L'])
        ctree_2.computeGMC(z_mat_2, channel_names=['L'])
        ctree_3.computeGMC(z_mat_3, channel_names=['L'])
        ctree_4.computeGMC(z_mat_4, channel_names=['L'])
        # compare both models
        assert str(ctree_1) == str(ctree_2)
        assert str(ctree_1) == str(ctree_3)
        assert str(ctree_1) == str(ctree_4)
        # compare impedance matrices
        z_fit_1 = ctree_1.calcImpedanceMatrix(self.freqs)
        z_fit_2 = ctree_2.calcImpedanceMatrix(self.freqs)
        z_fit_3 = ctree_3.calcImpedanceMatrix(self.freqs)
        z_fit_4 = ctree_4.calcImpedanceMatrix(self.freqs)
        assert np.allclose(z_fit_1, z_mat_1, atol=1e-8)
        assert np.allclose(z_fit_2, z_mat_2, atol=1e-8)
        assert np.allclose(z_fit_3, z_mat_3, atol=1e-8)
        assert np.allclose(z_fit_4, z_mat_4, atol=1e-8)
        assert np.allclose(z_fit_1,
                           ctree_2.calcImpedanceMatrix(indexing='tree'))
        assert np.allclose(z_fit_1,
                           ctree_3.calcImpedanceMatrix(indexing='tree'))
        assert np.allclose(z_fit_1,
                           ctree_4.calcImpedanceMatrix(indexing='tree'))

    def testCFit(self, n_loc=20):
        self.loadBallAndStick()
        # define locations
        xvals = np.linspace(0., 1., n_loc + 1)[1:]
        locs = [(1, 0.5)] + [(4, x) for x in xvals]
        # create compartment tree
        ctree = self.greens_tree.createCompartmentTree(locs)
        # steady state fit
        z_mat = self.greens_tree.calcImpedanceMatrix(locs)[0].real
        ctree.computeGMC(z_mat)
        # get SOV constants for capacitance fit
        alphas, phimat, importance = self.sov_tree.getImportantModes(
            locarg=locs,
            sort_type='importance',
            eps=1e-12,
            return_importance=True)
        # fit the capacitances from SOV time-scales
        ctree.computeC(-alphas[0:1].real * 1e3,
                       phimat[0:1, :].real,
                       weights=importance[0:1])
        # check if equal to membrane time scale
        nds = [self.greens_tree[loc[0]] for loc in locs]
        taus_orig = np.array([n.c_m / n.currents['L'][0] for n in nds])
        taus_fit = np.array([n.ca / n.currents['L'][0] for n in ctree])
        assert np.allclose(taus_orig, taus_fit)

        # fit capacitances with experimental vector fit
        for n in ctree:
            n.ca = 1.
        self.greens_tree.setImpedance(freqs=ke.create_logspace_freqarray())
        z_mat = self.greens_tree.calcImpedanceMatrix(locs)
        # run the vector fit
        ctree.computeCVF(self.greens_tree.freqs, z_mat)
        taus_fit2 = np.array([n.ca / n.currents['L'][0] for n in ctree])
        assert np.allclose(taus_orig, taus_fit2, atol=.3)

    def fitBallAndStick(self, n_loc=20):
        self.loadBallAndStick()
        # define locations
        xvals = np.linspace(0., 1., n_loc + 1)[1:]
        np.random.shuffle(xvals)
        locs = [(1, 0.5)] + [(4, x) for x in xvals]
        # create compartment tree
        ctree = self.greens_tree.createCompartmentTree(locs)
        # steady state fit
        z_mat = self.greens_tree.calcImpedanceMatrix(locs)[0].real
        ctree.computeGMC(z_mat)
        # get SOV constants for capacitance fit
        alphas, phimat, importance = self.sov_tree.getImportantModes(
            locarg=locs,
            sort_type='importance',
            eps=1e-12,
            return_importance=True)
        # fit the capacitances from SOV time-scales
        ctree.computeC(-alphas[0:1].real * 1e3,
                       phimat[0:1, :].real,
                       weights=importance[0:1])
        self.ctree = ctree

    def testPasFunctionality(self, n_loc=10):
        self.fitBallAndStick(n_loc=n_loc)

        # test equilibrium potential setting
        e_eq = -75. + np.random.randint(10, size=n_loc + 1)
        # with tree indexing
        self.ctree.setEEq(e_eq, indexing='tree')
        assert np.allclose(e_eq, np.array([n.e_eq for n in self.ctree]))
        assert np.allclose(e_eq, self.ctree.getEEq(indexing='tree'))
        assert not np.allclose(e_eq, self.ctree.getEEq(indexing='locs'))
        # with loc indexing
        self.ctree.setEEq(e_eq, indexing='locs')
        assert not np.allclose(e_eq, np.array([n.e_eq for n in self.ctree]))
        assert not np.allclose(e_eq, self.ctree.getEEq(indexing='tree'))
        assert np.allclose(e_eq, self.ctree.getEEq(indexing='locs'))

        # conductance matrices
        gm1 = self.ctree.calcConductanceMatrix(indexing='locs')
        gm2 = self.ctree.calcSystemMatrix(indexing='locs',
                                          channel_names=['L'],
                                          with_ca=True,
                                          use_conc=False)
        gm3 = self.ctree.calcSystemMatrix(indexing='locs',
                                          channel_names=['L'],
                                          with_ca=False,
                                          use_conc=False)
        gm4 = self.ctree.calcSystemMatrix(indexing='locs',
                                          channel_names=['L'],
                                          with_ca=False,
                                          use_conc=True)
        gm5 = self.ctree.calcSystemMatrix(indexing='locs',
                                          with_ca=False,
                                          use_conc=True)
        gm6 = self.ctree.calcSystemMatrix(indexing='tree',
                                          with_ca=False,
                                          use_conc=True)
        assert np.allclose(gm1, gm2)
        assert np.allclose(gm1, gm3)
        assert np.allclose(gm1, gm4)
        assert np.allclose(gm1, gm5)
        assert not np.allclose(gm1, gm6)

        # eigenvalues
        alphas, phimat, phimat_inv = self.ctree.calcEigenvalues()
        ca_vec = np.array([1. / node.ca for node in self.ctree]) * 1e-3
        assert np.allclose(np.dot(phimat, phimat_inv), np.diag(ca_vec))
        assert np.allclose(
            np.array([n.ca / n.currents['L'][0] for n in self.ctree]),
            np.ones(len(self.ctree)) * np.max(1e-3 / np.abs(alphas)))

    def loadBall(self):
        self.greens_tree = GreensTree(
            file_n=os.path.join(MORPHOLOGIES_PATH_PREFIX, 'ball.swc'))
        # capacitance and axial resistance
        self.greens_tree.setPhysiology(0.8, 100. / 1e6)
        # ion channels
        k_chan = channelcollection.Kv3_1()
        self.greens_tree.addCurrent(k_chan, 0.766 * 1e6, -85.)
        na_chan = channelcollection.Na_Ta()
        self.greens_tree.addCurrent(na_chan, 1.71 * 1e6, 50.)
        # fit leak current
        self.greens_tree.fitLeakCurrent(-75., 10.)
        # set computational tree
        self.greens_tree.setCompTree()
        # set the impedances
        self.freqs = np.array([0.])
        self.greens_tree.setImpedance(self.freqs)
        # create sov tree
        self.sov_tree = self.greens_tree.__copy__(new_tree=SOVTree())
        self.sov_tree.calcSOVEquations(maxspace_freq=100.)

    def testChannelFit(self):
        self.loadBall()
        locs = [(1, 0.5)]
        e_eqs = [-75., -55., -35., -15.]
        # create compartment tree
        ctree = self.greens_tree.createCompartmentTree(locs)
        ctree.addCurrent(channelcollection.Na_Ta(), 50.)
        ctree.addCurrent(channelcollection.Kv3_1(), -85.)

        # create tree with only leak
        greens_tree_pas = self.greens_tree.__copy__()
        greens_tree_pas[1].currents = {'L': greens_tree_pas[1].currents['L']}
        greens_tree_pas.setCompTree()
        greens_tree_pas.setImpedance(self.freqs)
        # compute the passive impedance matrix
        z_mat_pas = greens_tree_pas.calcImpedanceMatrix(locs)[0]

        # create tree with only potassium
        greens_tree_k = self.greens_tree.__copy__()
        greens_tree_k[1].currents = {key: val for key, val in greens_tree_k[1].currents.items() \
                                               if key != 'Na_Ta'}
        # compute potassium impedance matrices
        z_mats_k = []
        for e_eq in e_eqs:
            greens_tree_k.setEEq(e_eq)
            greens_tree_k.setCompTree()
            greens_tree_k.setImpedance(self.freqs)
            z_mats_k.append(greens_tree_k.calcImpedanceMatrix(locs))

        # create tree with only sodium
        greens_tree_na = self.greens_tree.__copy__()
        greens_tree_na[1].currents = {key: val for key, val in greens_tree_na[1].currents.items() \
                                               if key != 'Kv3_1'}
        # create state variable expansion points
        svs = []
        e_eqs_ = []
        na_chan = greens_tree_na.channel_storage['Na_Ta']
        for e_eq1 in e_eqs:
            sv1 = na_chan.computeVarinf(e_eq1)
            for e_eq2 in e_eqs:
                e_eqs_.append(e_eq2)
                sv2 = na_chan.computeVarinf(e_eq2)
                svs.append({'m': sv2['m'], 'h': sv1['h']})
        # compute sodium impedance matrices
        z_mats_na = []
        for ii, sv in enumerate(svs):
            greens_tree_na.setEEq(e_eqs[ii % len(e_eqs)])
            greens_tree_na[1].setExpansionPoint('Na_Ta', sv)
            greens_tree_na.setCompTree()
            greens_tree_na.setImpedance(self.freqs)
            z_mats_na.append(greens_tree_na.calcImpedanceMatrix(locs))

        # compute combined impedance matrices
        z_mats_comb = []
        for e_eq in e_eqs:
            self.greens_tree.setEEq(e_eq)
            self.greens_tree.setCompTree()
            self.greens_tree.setImpedance(self.freqs)
            z_mats_comb.append(self.greens_tree.calcImpedanceMatrix(locs))

        # passive fit
        ctree.computeGMC(z_mat_pas)
        # get SOV constants for capacitance fit
        sov_tree = greens_tree_pas.__copy__(new_tree=SOVTree())
        sov_tree.setCompTree()
        sov_tree.calcSOVEquations()
        alphas, phimat, importance = sov_tree.getImportantModes(
            locarg=locs,
            sort_type='importance',
            eps=1e-12,
            return_importance=True)
        # fit the capacitances from SOV time-scales
        ctree.computeC(-alphas[0:1].real * 1e3,
                       phimat[0:1, :].real,
                       weights=importance[0:1])

        ctree1 = copy.deepcopy(ctree)
        ctree2 = copy.deepcopy(ctree)
        ctree3 = copy.deepcopy(ctree)
        ctree4 = copy.deepcopy(ctree)

        # fit paradigm 1 --> separate impedance matrices and separate fits
        # potassium channel fit
        for z_mat_k, e_eq in zip(z_mats_k, e_eqs):
            ctree1.computeGSingleChanFromImpedance('Kv3_1',
                                                   z_mat_k,
                                                   e_eq,
                                                   self.freqs,
                                                   other_channel_names=['L'])
        ctree1.runFit()
        # sodium channel fit
        for z_mat_na, e_eq, sv in zip(z_mats_na, e_eqs_, svs):
            ctree1.computeGSingleChanFromImpedance('Na_Ta',
                                                   z_mat_na,
                                                   e_eq,
                                                   self.freqs,
                                                   sv=sv,
                                                   other_channel_names=['L'])
        ctree1.runFit()

        # fit paradigm 2 --> separate impedance matrices, same fit
        for z_mat_k, e_eq in zip(z_mats_k, e_eqs):
            ctree2.computeGSingleChanFromImpedance(
                'Kv3_1',
                z_mat_k,
                e_eq,
                self.freqs,
                all_channel_names=['Kv3_1', 'Na_Ta'])
        for z_mat_na, e_eq, sv in zip(z_mats_na, e_eqs_, svs):
            ctree2.computeGSingleChanFromImpedance(
                'Na_Ta',
                z_mat_na,
                e_eq,
                self.freqs,
                sv=sv,
                all_channel_names=['Kv3_1', 'Na_Ta'])
        ctree2.runFit()

        # fit paradigm 3 --> same impedance matrices
        for z_mat_comb, e_eq in zip(z_mats_comb, e_eqs):
            ctree3.computeGChanFromImpedance(['Kv3_1', 'Na_Ta'], z_mat_comb,
                                             e_eq, self.freqs)
        ctree3.runFit()

        # fit paradigm 4 --> fit incrementally
        for z_mat_na, e_eq, sv in zip(z_mats_na, e_eqs_, svs):
            ctree4.computeGSingleChanFromImpedance('Na_Ta',
                                                   z_mat_na,
                                                   e_eq,
                                                   self.freqs,
                                                   sv=sv)
        ctree4.runFit()
        for z_mat_comb, e_eq in zip(z_mats_comb, e_eqs):
            ctree4.computeGSingleChanFromImpedance(
                'Kv3_1',
                z_mat_comb,
                e_eq,
                self.freqs,
                other_channel_names=['Na_Ta', 'L'])
        ctree4.runFit()

        # test if correct
        keys = ['L', 'Na_Ta', 'Kv3_1']
        # soma surface (cm) for total conductance calculation
        a_soma = 4. * np.pi * (self.greens_tree[1].R * 1e-4)**2
        conds = np.array(
            [self.greens_tree[1].currents[key][0] * a_soma for key in keys])
        # compartment models conductances
        cconds1 = np.array([ctree1[0].currents[key][0] for key in keys])
        cconds2 = np.array([ctree2[0].currents[key][0] for key in keys])
        cconds3 = np.array([ctree3[0].currents[key][0] for key in keys])
        cconds4 = np.array([ctree4[0].currents[key][0] for key in keys])
        assert np.allclose(conds, cconds1)
        assert np.allclose(conds, cconds2)
        assert np.allclose(conds, cconds3)
        assert np.allclose(conds, cconds4)

        # rename for further testing
        ctree = ctree1
        # frequency array
        ft = ke.FourrierTools(np.linspace(0., 50., 100))
        freqs = ft.s
        # compute impedance matrix
        v_h = -42.
        # original
        self.greens_tree.setEEq(v_h)
        self.greens_tree.setCompTree()
        self.greens_tree.setImpedance(freqs)
        z_mat_orig = self.greens_tree.calcImpedanceMatrix([(1., .5)])
        # potassium
        greens_tree_k.setEEq(v_h)
        greens_tree_k.setCompTree()
        greens_tree_k.setImpedance(freqs)
        z_mat_k = greens_tree_k.calcImpedanceMatrix([(1, .5)])
        # sodium
        greens_tree_na.removeExpansionPoints()
        greens_tree_na.setEEq(v_h)
        greens_tree_na.setCompTree()
        greens_tree_na.setImpedance(freqs)
        z_mat_na = greens_tree_na.calcImpedanceMatrix([(1, .5)])
        # passive
        greens_tree_pas.setCompTree()
        greens_tree_pas.setImpedance(freqs)
        z_mat_pas = greens_tree_pas.calcImpedanceMatrix([(1, .5)])

        # reduced impedance matrices
        ctree.removeExpansionPoints()
        ctree.setEEq(v_h)
        z_mat_fit = ctree.calcImpedanceMatrix(freqs=freqs)
        z_mat_fit_k = ctree.calcImpedanceMatrix(channel_names=['L', 'Kv3_1'],
                                                freqs=freqs)
        z_mat_fit_na = ctree.calcImpedanceMatrix(channel_names=['L', 'Na_Ta'],
                                                 freqs=freqs)
        z_mat_fit_pas = ctree.calcImpedanceMatrix(channel_names=['L'],
                                                  freqs=freqs)

        assert np.allclose(z_mat_orig, z_mat_fit)
        assert np.allclose(z_mat_k, z_mat_fit_k)
        assert np.allclose(z_mat_na, z_mat_fit_na)
        assert np.allclose(z_mat_pas, z_mat_fit_pas)

        # test total current, conductance
        sv = svs[-1]
        p_open = sv['m']**3 * sv['h']
        # with p_open given
        g1 = ctree[0].getGTot(ctree.channel_storage,
                              channel_names=['L', 'Na_Ta'],
                              p_open_channels={'Na_Ta': p_open})
        i1 = ctree[0].getGTot(ctree.channel_storage,
                              channel_names=['L', 'Na_Ta'],
                              p_open_channels={'Na_Ta': p_open})
        # with expansion point given
        ctree.setExpansionPoints({'Na_Ta': sv})
        g2 = ctree[0].getGTot(ctree.channel_storage,
                              channel_names=['L', 'Na_Ta'])
        i2 = ctree[0].getGTot(ctree.channel_storage,
                              channel_names=['L', 'Na_Ta'])
        # with e_eq given
        g3 = ctree[0].getGTot(ctree.channel_storage,
                              v=e_eqs[-1],
                              channel_names=['L', 'Na_Ta'])
        i3 = ctree[0].getGTot(ctree.channel_storage,
                              v=e_eqs[-1],
                              channel_names=['L', 'Na_Ta'])
        # with e_eq stored
        ctree.setEEq(e_eqs[-1])
        g4 = ctree[0].getGTot(ctree.channel_storage,
                              channel_names=['L', 'Na_Ta'])
        i4 = ctree[0].getGTot(ctree.channel_storage,
                              channel_names=['L', 'Na_Ta'])
        # check if correct
        assert np.abs(g1 - g2) < 1e-10
        assert np.abs(g1 - g3) < 1e-10
        assert np.abs(g1 - g4) < 1e-10
        assert np.abs(i1 - i2) < 1e-10
        assert np.abs(i1 - i3) < 1e-10
        assert np.abs(i1 - i4) < 1e-10
        # compare current, conductance
        g_ = ctree[0].getGTot(ctree.channel_storage, channel_names=['Na_Ta'])
        i_ = ctree[0].getITot(ctree.channel_storage, channel_names=['Na_Ta'])
        assert np.abs(g_ *
                      (e_eqs[-1] - ctree[0].currents['Na_Ta'][1]) - i_) < 1e-10

        # test leak fitting
        self.greens_tree.setEEq(-75.)
        self.greens_tree.setCompTree()
        ctree.setEEq(-75.)
        ctree.removeExpansionPoints()
        ctree.fitEL()
        assert np.abs(ctree[0].currents['L'][1] -
                      self.greens_tree[1].currents['L'][1]) < 1e-10
Exemple #2
0
class TestNeuron():
    def loadTTreePassive(self):
        """
        Load the T-tree morphology in memory with passive conductance

          6--5--4--7--8
                |
                |
                1
        """
        v_eq = -75.
        self.dt = 0.025
        self.tmax = 100.
        # for frequency derivation
        self.ft = ke.FourrierTools(np.arange(0., self.tmax, self.dt))
        # load the morphology
        fname = os.path.join(MORPHOLOGIES_PATH_PREFIX, 'Tsovtree.swc')
        self.greenstree = GreensTree(fname, types=[1, 3, 4])
        self.greenstree.fitLeakCurrent(v_eq, 10.)
        self.greenstree.setCompTree()
        self.greenstree.setImpedance(self.ft.s)
        # copy greenstree parameters into NEURON simulation tree
        self.neurontree = NeuronSimTree(dt=self.dt,
                                        t_calibrate=10.,
                                        v_init=v_eq,
                                        factor_lambda=25.)
        self.greenstree.__copy__(self.neurontree)
        self.neurontree.treetype = 'computational'

    def loadTTreeActive(self):
        """
        Load the T-tree morphology in memory with h-current

          6--5--4--7--8
                |
                |
                1
        """
        v_eq = -75.
        self.dt = 0.1
        self.tmax = 100.
        # for frequency derivation
        self.ft = ke.FourrierTools(np.arange(0., self.tmax, self.dt))
        # load the morphology
        h_chan = channelcollection.h()
        fname = os.path.join(MORPHOLOGIES_PATH_PREFIX, 'Tsovtree.swc')
        self.greenstree = GreensTree(fname, types=[1, 3, 4])
        self.greenstree.addCurrent(h_chan, 50., -43.)
        self.greenstree.fitLeakCurrent(v_eq, 10.)
        self.greenstree.setCompTree()
        self.greenstree.setImpedance(self.ft.s)
        # copy greenstree parameters into NEURON simulation tree
        self.neurontree = NeuronSimTree(dt=self.dt,
                                        t_calibrate=10.,
                                        v_init=v_eq,
                                        factor_lambda=25.)
        self.greenstree.__copy__(self.neurontree)
        self.neurontree.treetype = 'computational'

    def loadTTreeTestChannel(self):
        """
        Load the T-tree morphology in memory with h-current

          6--5--4--7--8
                |
                |
                1
        """
        v_eq = -75.
        self.dt = 0.025
        self.tmax = 100.
        # for frequency derivation
        self.ft = ke.FourrierTools(np.arange(0., self.tmax, self.dt))
        # load the morphology
        test_chan = channelcollection.TestChannel2()
        fname = os.path.join(MORPHOLOGIES_PATH_PREFIX, 'Tsovtree.swc')
        self.greenstree = GreensTree(fname, types=[1, 3, 4])
        self.greenstree.addCurrent(test_chan, 50., -23.)
        self.greenstree.fitLeakCurrent(v_eq, 10.)
        self.greenstree.setCompTree()
        self.greenstree.setImpedance(self.ft.s)
        # copy greenstree parameters into NEURON simulation tree
        self.neurontree = NeuronSimTree(dt=self.dt,
                                        t_calibrate=100.,
                                        v_init=v_eq,
                                        factor_lambda=25.)
        self.greenstree.__copy__(self.neurontree)
        self.neurontree.treetype = 'computational'

    def loadTTreeTestChannelSoma(self):
        """
        Load the T-tree morphology in memory with h-current

          6--5--4--7--8
                |
                |
                1
        """
        v_eq = -75.
        self.dt = 0.025
        self.tmax = 100.
        # for frequency derivation
        self.ft = ke.FourrierTools(np.arange(0., self.tmax, self.dt))
        # load the morphology
        test_chan = channelcollection.TestChannel2()
        fname = os.path.join(MORPHOLOGIES_PATH_PREFIX, 'Tsovtree.swc')
        self.greenstree = GreensTree(fname, types=[1, 3, 4])
        self.greenstree.addCurrent(test_chan,
                                   50.,
                                   23.,
                                   node_arg=[self.greenstree[1]])
        self.greenstree.fitLeakCurrent(v_eq, 10.)
        self.greenstree.setCompTree()
        self.greenstree.setImpedance(self.ft.s)
        # copy greenstree parameters into NEURON simulation tree
        self.neurontree = NeuronSimTree(dt=self.dt,
                                        t_calibrate=100.,
                                        v_init=v_eq,
                                        factor_lambda=25.)
        self.greenstree.__copy__(self.neurontree)
        self.neurontree.treetype = 'computational'

    def testPassive(self, pplot=False):
        self.loadTTreePassive()
        # set of locations
        locs = [(1, .5), (4, .5), (4, 1.), (5, .5), (6, .5), (7, .5), (8, .5)]
        # compute impedance matrix with Green's function
        zf_mat_gf = self.greenstree.calcImpedanceMatrix(locs)
        z_mat_gf = zf_mat_gf[self.ft.ind_0s].real
        # convert impedance matrix to time domain
        zk_mat_gf = np.zeros((len(self.ft.t), len(locs), len(locs)))
        for (ii, jj) in itertools.product(list(range(len(locs))),
                                          list(range(len(locs)))):
            zk_mat_gf[:, ii,
                      jj] = self.ft.ftInv(zf_mat_gf[:, ii, jj])[1].real * 1e-3
        # test the steady state impedance matrix
        z_mat_neuron = self.neurontree.calcImpedanceMatrix(locs)
        assert np.allclose(z_mat_gf, z_mat_neuron, atol=1.)
        # test the temporal matrix
        tk, zk_mat_neuron = self.neurontree.calcImpedanceKernelMatrix(locs)
        assert np.allclose(zk_mat_gf[int(2. / self.dt):, :, :],
                           zk_mat_neuron[int(2. / self.dt):, :, :],
                           atol=.2)
        if pplot:
            # plot kernels
            pl.figure()
            cc = 0
            for ii in range(len(locs)):
                jj = 0
                while jj <= ii:
                    pl.plot(tk,
                            zk_mat_neuron[:, ii, jj],
                            c=colours[cc % len(colours)])
                    pl.plot(tk,
                            zk_mat_gf[:, ii, jj],
                            ls='--',
                            lw=2,
                            c=colours[cc % len(colours)])
                    cc += 1
                    jj += 1
            pl.show()

    def testActive(self, pplot=False):
        self.loadTTreeActive()
        # set of locations
        locs = [(1, .5), (4, .5), (6, .5), (7, .5), (8, .5)]
        # compute impedance matrix with Green's function
        zf_mat_gf = self.greenstree.calcImpedanceMatrix(locs)
        z_mat_gf = zf_mat_gf[self.ft.ind_0s].real
        # convert impedance matrix to time domain
        zk_mat_gf = np.zeros((len(self.ft.t), len(locs), len(locs)))
        for (ii, jj) in itertools.product(list(range(len(locs))),
                                          list(range(len(locs)))):
            zk_mat_gf[:, ii,
                      jj] = self.ft.ftInv(zf_mat_gf[:, ii, jj])[1].real * 1e-3
        # test the steady state impedance matrix
        z_mat_neuron = self.neurontree.calcImpedanceMatrix(locs, t_dur=500.)
        assert np.allclose(z_mat_gf, z_mat_neuron, atol=5.)
        # test the temporal matrix
        tk, zk_mat_neuron = self.neurontree.calcImpedanceKernelMatrix(locs)
        assert np.allclose(zk_mat_gf[int(2. / self.dt):, :, :],
                           zk_mat_neuron[int(2. / self.dt):, :, :],
                           atol=.5)
        if pplot:
            # plot kernels
            pl.figure()
            cc = 0
            for ii in range(len(locs)):
                jj = 0
                while jj <= ii:
                    pl.plot(tk,
                            zk_mat_neuron[:, ii, jj],
                            c=colours[cc % len(colours)])
                    pl.plot(tk,
                            zk_mat_gf[:, ii, jj],
                            ls='--',
                            lw=2,
                            c=colours[cc % len(colours)])
                    cc += 1
                    jj += 1
            pl.show()

    def testChannelRecording(self):
        self.loadTTreeTestChannel()
        # set of locations
        locs = [(1, .5), (4, .5), (4, 1.), (5, .5), (6, .5), (7, .5), (8, .5)]
        # create simulation tree
        self.neurontree.initModel(t_calibrate=10., factor_lambda=10.)
        self.neurontree.storeLocs(locs, name='rec locs')
        # run test simulation
        res = self.neurontree.run(1., record_from_channels=True)
        # check if results are stored correctly
        assert set(res['chan']['TestChannel2'].keys()) == {
            'a00', 'a01', 'a10', 'a11', 'p_open'
        }
        # check if values are correct
        assert np.allclose(res['chan']['TestChannel2']['a00'], .3)
        assert np.allclose(res['chan']['TestChannel2']['a01'], .5)
        assert np.allclose(res['chan']['TestChannel2']['a10'], .4)
        assert np.allclose(res['chan']['TestChannel2']['a11'], .6)
        assert np.allclose(res['chan']['TestChannel2']['p_open'],
                           .9 * .3**3 * .5**2 + .1 * .4**2 * .6**1)
        # check if shape is correct
        n_loc, n_step = len(locs), len(res['t'])
        assert res['chan']['TestChannel2']['a00'].shape == (n_loc, n_step)
        assert res['chan']['TestChannel2']['a01'].shape == (n_loc, n_step)
        assert res['chan']['TestChannel2']['a10'].shape == (n_loc, n_step)
        assert res['chan']['TestChannel2']['a11'].shape == (n_loc, n_step)
        assert res['chan']['TestChannel2']['p_open'].shape == (n_loc, n_step)
        # channel only at soma
        self.loadTTreeTestChannelSoma()
        # create simulation tree
        self.neurontree.initModel(t_calibrate=100., factor_lambda=10.)
        self.neurontree.storeLocs(locs, name='rec locs')
        # run test simulation
        res = self.neurontree.run(10., record_from_channels=True)
        # check if results are stored correctly
        assert set(res['chan']['TestChannel2'].keys()) == {
            'a00', 'a01', 'a10', 'a11', 'p_open'
        }
        # check if values are correct
        assert np.allclose(res['chan']['TestChannel2']['a00'][0, :], .3)
        assert np.allclose(res['chan']['TestChannel2']['a01'][0, :], .5)
        assert np.allclose(res['chan']['TestChannel2']['a10'][0, :], .4)
        assert np.allclose(res['chan']['TestChannel2']['a11'][0, :], .6)
        assert np.allclose(res['chan']['TestChannel2']['p_open'][0, :],
                           .9 * .3**3 * .5**2 + .1 * .4**2 * .6**1)
        assert np.allclose(res['chan']['TestChannel2']['a00'][1:, :], 0.)
        assert np.allclose(res['chan']['TestChannel2']['a01'][1:, :], 0.)
        assert np.allclose(res['chan']['TestChannel2']['a10'][1:, :], 0.)
        assert np.allclose(res['chan']['TestChannel2']['a11'][1:, :], 0.)
        assert np.allclose(res['chan']['TestChannel2']['p_open'][1:, :], 0.)
        # check if shape is correct
        n_loc, n_step = len(locs), len(res['t'])
        assert res['chan']['TestChannel2']['a00'].shape == (n_loc, n_step)
        assert res['chan']['TestChannel2']['a01'].shape == (n_loc, n_step)
        assert res['chan']['TestChannel2']['a10'].shape == (n_loc, n_step)
        assert res['chan']['TestChannel2']['a11'].shape == (n_loc, n_step)
        assert res['chan']['TestChannel2']['p_open'].shape == (n_loc, n_step)
Exemple #3
0
class TestCNET():
    def createPointNeurons(self, v_eq=-75.):
        self.v_eq = v_eq
        self.dt = .025
        gh, eh = 50., -43.
        h_chan = channelcollection.h()

        self.greens_tree = GreensTree(
            file_n=os.path.join(MORPHOLOGIES_PATH_PREFIX, 'ball.swc'))
        self.greens_tree.setPhysiology(1., 100. / 1e6)
        self.greens_tree.addCurrent(h_chan, gh, eh)
        self.greens_tree.fitLeakCurrent(v_eq, 10.)
        self.greens_tree.setEEq(v_eq)
        self.greens_tree_pas = self.greens_tree.__copy__(new_tree=GreensTree())
        self.greens_tree_pas.asPassiveMembrane()
        self.sim_tree = self.greens_tree.__copy__(new_tree=NeuronSimTree())
        # set the impedances
        self.greens_tree_pas.setCompTree()
        self.freqs = np.array([0.])
        self.greens_tree_pas.setImpedance(self.freqs)
        # create sov tree
        self.sov_tree = self.greens_tree_pas.__copy__(new_tree=SOVTree())
        self.sov_tree.calcSOVEquations(maxspace_freq=50.)

        z_inp = self.greens_tree_pas.calcZF((1, .5), (1, .5))[0]
        alphas, gammas = self.sov_tree.getSOVMatrices(locarg=[(1., .5)])
        # create NET
        node_0 = NETNode(0, [0], [0], z_kernel=(alphas, gammas[:, 0]**2))
        net_py = NET()
        net_py.setRoot(node_0)
        # check if correct
        assert np.abs(gammas[0, 0]**2 / np.abs(alphas[0]) - z_inp) < 1e-10
        assert np.abs(node_0.z_bar - z_inp) < 1e-10

        # to initialize neuron tree
        self.sim_tree.initModel(dt=self.dt)
        # add ion channel to NET simulator
        a_soma = 4. * np.pi * (self.sim_tree[1].R * 1e-4)**2
        self.cnet = netsim.NETSim(net_py, v_eq=self.v_eq)

        hchan = channelcollection.h()
        self.cnet.addChannel(hchan, 0, gh * a_soma, eh)

        # add the synapse
        # to neuron tree
        self.sim_tree.addDoubleExpSynapse((1, .5), .2, 3., 0.)
        self.sim_tree.setSpikeTrain(0, 0.001, [5.])
        # to net sim
        self.cnet.addSynapse(0, {
            'tau_r': .2,
            'tau_d': 3.,
            'e_r': 0.
        },
                             g_max=0.001)
        self.cnet.setSpikeTimes(0, [5. + self.dt])

    def createTree(self, reinitialize=1, v_eq=-75.):
        """
        Create simple NET structure

        2     3
        |     |
        |     |
        ---1---
           |
           |
           0
           |
        """
        self.v_eq = v_eq
        loc_ind = np.array([0, 1, 2])

        # kernel constants
        alphas = 1. / np.array([.5, 8.])
        gammas = np.array([-1., 1.])
        alphas_ = 1. / np.array([1.])
        gammas_ = np.array([1.])
        # nodes
        node_0 = NETNode(0, [0, 1, 2], [], z_kernel=(alphas, gammas))
        node_1 = NETNode(1, [0, 1, 2], [0], z_kernel=(alphas_, gammas_))
        node_2 = NETNode(2, [1], [1], z_kernel=(alphas_, gammas_))
        node_3 = NETNode(3, [2], [2], z_kernel=(alphas_, gammas_))
        # add nodes to tree
        net_py = NET()
        net_py.setRoot(node_0)
        net_py.addNodeWithParent(node_1, node_0)
        net_py.addNodeWithParent(node_2, node_1)
        net_py.addNodeWithParent(node_3, node_1)
        # store
        self.net_py = net_py
        self.cnet = netsim.NETSim(net_py, v_eq=self.v_eq)

    def createTree2(self, reinitialize=1, add_lin=True, v_eq=-75.):
        """
        Create simple NET structure

                3     4
                |     |
                |     |
                ---2---
             1     |
             |     |
             ---0---
                |
        """
        self.v_eq = v_eq
        loc_ind = np.array([0, 1, 2])

        # kernel constants
        alphas = 1. / np.array([1.])
        gammas = np.array([1.])
        # nodes
        node_0 = NETNode(0, [0, 1, 2], [], z_kernel=(alphas, gammas))
        node_1 = NETNode(1, [0], [0], z_kernel=(alphas, gammas))
        node_2 = NETNode(2, [1, 2], [], z_kernel=(alphas, gammas))
        node_3 = NETNode(3, [1], [1], z_kernel=(alphas, gammas))
        node_4 = NETNode(4, [2], [2], z_kernel=(alphas, gammas))
        # add nodes to tree
        net_py = NET()
        net_py.setRoot(node_0)
        net_py.addNodeWithParent(node_1, node_0)
        net_py.addNodeWithParent(node_2, node_0)
        net_py.addNodeWithParent(node_3, node_2)
        net_py.addNodeWithParent(node_4, node_2)
        # linear terms
        alphas = 1. / np.array([1.])
        gammas = np.array([1.])
        self.lin_terms = {
            1: Kernel((alphas, gammas)),
            2: Kernel((alphas, gammas))
        } if add_lin else {}
        # store
        self.net_py = net_py
        self.cnet = netsim.NETSim(net_py,
                                  lin_terms=self.lin_terms,
                                  v_eq=self.v_eq)

    def createTree3(self, reinitialize=1, add_lin=True, v_eq=-75.):
        """
        Create simple NET structure

                         6
                4     5  |
                |     |  |
                |     |  |
             2  ---3---  |
             |     |     |
             ---1---     |
                   |     |
                   0------
                   |
        """
        self.v_eq = v_eq

        # kernel constants
        alphas = 1. / np.array([1.])
        gammas = np.array([1.])
        # nodes
        node_0 = NETNode(0, [0, 1, 2, 3], [], z_kernel=(alphas, gammas))
        node_1 = NETNode(1, [0, 1, 2], [], z_kernel=(alphas, gammas))
        node_2 = NETNode(2, [0], [0], z_kernel=(alphas, gammas))
        node_3 = NETNode(3, [1, 2], [], z_kernel=(alphas, gammas))
        node_4 = NETNode(4, [1], [1], z_kernel=(alphas, gammas))
        node_5 = NETNode(5, [2], [2], z_kernel=(alphas, gammas))
        node_6 = NETNode(6, [3], [3], z_kernel=(alphas, gammas))
        # add nodes to tree
        net_py = NET()
        net_py.setRoot(node_0)
        net_py.addNodeWithParent(node_1, node_0)
        net_py.addNodeWithParent(node_2, node_1)
        net_py.addNodeWithParent(node_3, node_1)
        net_py.addNodeWithParent(node_4, node_3)
        net_py.addNodeWithParent(node_5, node_3)
        net_py.addNodeWithParent(node_6, node_0)
        # linear terms
        alphas = 1. / np.array([1.])
        gammas = np.array([1.])
        self.lin_terms = {
            1: Kernel((alphas, gammas)),
            2: Kernel((alphas, gammas)),
            3: Kernel((alphas, gammas))
        } if add_lin else {}
        # store
        self.net_py = net_py
        self.cnet = netsim.NETSim(net_py, lin_terms=self.lin_terms)

    def testIOFunctions(self):
        self.createTree()
        # storing and reading voltages from node voltage
        vnode = np.array([8., 10., 12., 14.])
        self.cnet.setVNodeFromVNode(vnode)
        vnode_back1 = self.cnet.getVNode()
        vnode_back2 = np.zeros(4)
        self.cnet.addVNodeToArr(vnode_back2)
        assert np.allclose(vnode_back1, vnode)
        assert np.allclose(vnode_back2, vnode)
        vloc_back1 = self.cnet.getVLoc()
        vloc_back2 = np.zeros(3)
        self.cnet.addVLocToArr(vloc_back2)
        assert np.allclose(vloc_back1, np.array([18., 30., 32.]) + self.v_eq)
        assert np.allclose(vloc_back2, np.array([18., 30., 32.]) + self.v_eq)
        with pytest.raises(ValueError):
            self.cnet.setVNodeFromVNode(np.zeros(3))
        with pytest.raises(ValueError):
            self.cnet.addVNodeToArr(np.zeros(3))
        with pytest.raises(ValueError):
            self.cnet.setVNodeFromVLoc(np.zeros(4))
        with pytest.raises(ValueError):
            self.cnet.addVLocToArr(np.zeros(4))
        # storing and reading voltages from location voltage
        vloc = np.array([12., 14., 16.]) + self.v_eq
        self.cnet.setVNodeFromVLoc(vloc)
        vnode_back1 = self.cnet.getVNode()
        vnode_back2 = np.zeros(4)
        self.cnet.addVNodeToArr(vnode_back2)
        assert np.allclose(vnode_back1, np.array([0., 12., 2., 4.]))
        assert np.allclose(vnode_back2, np.array([0., 12., 2., 4.]))
        vloc_back1 = self.cnet.getVLoc()
        vloc_back2 = np.zeros(3)
        self.cnet.addVLocToArr(vloc_back2)
        assert np.allclose(vloc_back1, vloc)
        assert np.allclose(vloc_back2, vloc)
        with pytest.raises(ValueError):
            self.cnet.setVNodeFromVNode(np.zeros(3))
        with pytest.raises(ValueError):
            self.cnet.addVNodeToArr(np.zeros(3))
        with pytest.raises(ValueError):
            self.cnet.setVNodeFromVLoc(np.zeros(4))
        with pytest.raises(ValueError):
            self.cnet.addVLocToArr(np.zeros(4))

    def testSolver(self):
        self.createTree()
        netp = self.net_py
        # test if single AMPA synapse agrees with analytical solution
        # add synapse
        self.cnet.addSynapse(1, "AMPA")
        g_syn = 1.
        g_list = [np.array([]), np.array([g_syn]), np.array([])]
        # solve numerically
        v_loc = self.cnet.solveNewton(g_list)
        v_node = self.cnet.getVNode()
        # solve analytically
        g_rescale = g_syn / (1. + netp[2].z_bar * g_syn)
        z_0plus1 = netp[0].z_bar + netp[1].z_bar
        v_0plus1 = z_0plus1 * g_rescale / (1. + z_0plus1 * g_rescale) * \
                   (0. - self.v_eq)
        v_2 = netp[2].z_bar * g_rescale * (0. - self.v_eq - v_0plus1)
        # test if both solutions agree
        assert np.abs(v_node[0] + v_node[1] - v_0plus1) < 1e-9
        assert np.abs(v_node[2] - v_2) < 1e-9
        assert np.abs(v_node[3] - 0.0) < 1e-9
        assert np.abs(v_loc[0] - self.v_eq - v_0plus1) < 1e-9
        assert np.abs(v_loc[1] - self.v_eq - v_0plus1 - v_2) < 1e-9
        assert np.abs(v_loc[2] - self.v_eq - v_0plus1) < 1e-9
        # test if AMPA and GABA synapses agree with analytical solution
        # add synapse
        self.cnet.addSynapse(2, "GABA")
        g_exc = 1.
        g_inh = 1.
        g_list = [np.array([]), np.array([g_exc]), np.array([g_inh])]
        # solve numerically
        v_loc = self.cnet.solveNewton(g_list)
        v_node = self.cnet.getVNode()
        # solve analytically
        g_exc_ = g_exc / (1. + netp[2].z_bar * g_exc)
        g_inh_ = g_inh / (1. + netp[3].z_bar * g_inh)
        z_0plus1 = netp[0].z_bar + netp[1].z_bar
        v_0plus1 = z_0plus1 * g_exc_ / (1. + z_0plus1 * (g_exc_ + g_inh_)) * \
                   (0. - self.v_eq) + \
                   z_0plus1 * g_inh_ / (1. + z_0plus1 * (g_exc_ + g_inh_)) * \
                   (-80. - self.v_eq)
        v_2 = netp[2].z_bar * g_exc_ * (0. - self.v_eq - v_0plus1)
        v_3 = netp[3].z_bar * g_inh_ * (-80. - self.v_eq - v_0plus1)
        # test if both solutions agree
        assert np.abs(v_node[0] + v_node[1] - v_0plus1) < 1e-9
        assert np.abs(v_node[2] - v_2) < 1e-9
        assert np.abs(v_node[3] - v_3) < 1e-9
        assert np.abs(v_loc[0] - self.v_eq - v_0plus1) < 1e-9
        assert np.abs(v_loc[1] - self.v_eq - v_0plus1 - v_2) < 1e-9
        assert np.abs(v_loc[2] - self.v_eq - v_0plus1 - v_3) < 1e-9
        # test if NMDA synapse is solved correctly
        # check if removing synapse works correctly
        self.cnet.removeSynapseFromLoc(1, 0)
        self.cnet.removeSynapseFromLoc(2, 0)
        with pytest.raises(IndexError):
            self.cnet.removeSynapseFromLoc(3, 0)
        with pytest.raises(IndexError):
            self.cnet.removeSynapseFromLoc(1, 2)
        # create NMDA synapse
        self.cnet.addSynapse(1, "NMDA")
        # solve for low conductance
        g_syn_low = 1.
        g_list = [np.array([]), np.array([g_syn_low]), np.array([])]
        # solve numerically
        v_loc_low = self.cnet.solveNewton(g_list)
        v_node_low = self.cnet.getVNode()
        # solve for high conductance
        g_syn_high = 4.
        g_list = [np.array([]), np.array([g_syn_high]), np.array([])]
        # solve numerically
        v_loc_high = self.cnet.solveNewton(g_list)
        v_node_high = self.cnet.getVNode()
        # solve for moderate conductance
        g_syn_middle = 2.
        g_list = [np.array([]), np.array([g_syn_middle]), np.array([])]
        # solve numerically
        v_loc_middle_0 = self.cnet.solveNewton(g_list)
        v_node_middle_0 = self.cnet.getVNode()
        v_loc_middle_1 = self.cnet.solveNewton(g_list,
                                               v_0=np.array([0., 0., 0.]),
                                               v_alt=self.v_eq * np.ones(3))
        v_node_middle_1 = self.cnet.getVNode()
        # check if correct
        z_sum = netp[0].z_bar + netp[1].z_bar + netp[2].z_bar
        checkfun = lambda vv: (0. - vv) / (1. + 0.3 * np.exp(-.1 * vv))
        vv = v_loc_low[1]
        assert np.abs(vv - self.v_eq - z_sum * g_syn_low * checkfun(vv)) < 1e-3
        vv = v_loc_high[1]
        assert np.abs(vv - self.v_eq -
                      z_sum * g_syn_high * checkfun(vv)) < 1e-3
        vv = v_loc_middle_0[1]
        assert np.abs(vv - self.v_eq -
                      z_sum * g_syn_middle * checkfun(vv)) < 1e-3
        vv = v_loc_middle_1[1]
        assert np.abs(vv - self.v_eq -
                      z_sum * g_syn_middle * checkfun(vv)) < 1e-3
        assert np.abs(v_loc_middle_0[1] - v_loc_middle_1[1]) > 10.

    def testIntegration(self):
        tmax = 1000.
        dt = 0.025
        self.createTree()
        # add synapse and check additional synapse functions
        self.cnet.addSynapse(1, "AMPA", g_max=dt * 0.1)
        self.cnet.addSynapse(1, "AMPA+NMDA", g_max=1., nmda_ratio=5.)
        assert self.cnet.syn_map_py[0] == {
            'loc_index': 1,
            'syn_index_at_loc': 0,
            'n_syn_at_loc': 1,
            'g_max': [dt * 0.1]
        }
        assert self.cnet.syn_map_py[1] == {
            'loc_index': 1,
            'syn_index_at_loc': 1,
            'n_syn_at_loc': 2,
            'g_max': [1., 5.]
        }
        assert self.cnet.n_syn[1] == 3
        with pytest.raises(ValueError):
            self.cnet.addSynapse(1, "NONSENSE")
        with pytest.raises(IndexError):
            self.cnet.addSynapse(8, "AMPA")
        with pytest.raises(TypeError):
            self.cnet.addSynapse(1, ["NONSENSE LIST"])
        # check if synapse is correctly removed
        self.cnet.removeSynapse(1)
        assert len(self.cnet.syn_map_py) == 1
        # add spike times
        self.cnet.setSpikeTimes(0, np.arange(dt / 2., tmax, dt))
        # run sim
        res = self.cnet.runSim(tmax,
                               dt,
                               step_skip=1,
                               rec_v_node=True,
                               rec_g_syn_inds=[0])
        v_loc_sim = res['v_loc'][:, -1]
        # solve newton
        v_loc_newton = self.cnet.solveNewton([res['g_syn'][0][0][-1]])
        # compare
        assert np.allclose(v_loc_sim, v_loc_newton, atol=0.5)
        # do again with other synapses
        self.cnet.removeSynapse(0)
        self.cnet.addSynapse(2, "GABA", g_max=dt * 0.1)
        self.cnet.addSynapse(1, "AMPA", g_max=dt * 0.1)
        # add spike times
        self.cnet.setSpikeTimes(0, np.arange(dt / 2., tmax,
                                             dt))  # GABA synapse
        self.cnet.setSpikeTimes(1, np.arange(dt / 2., tmax,
                                             dt))  # AMPA synapse
        # run sim
        res = self.cnet.runSim(tmax,
                               dt,
                               step_skip=1,
                               rec_v_node=True,
                               rec_g_syn_inds=[0, 1])
        v_loc_sim = res['v_loc'][:, -1]
        g_newton = [res['g_syn'][ii][0][-1] for ii in [0, 1]]
        # solve newton
        v_loc_newton = self.cnet.solveNewton(g_newton)
        # compare
        assert np.allclose(v_loc_sim, v_loc_newton, atol=0.5)

        # add NMDA synapse
        self.cnet.addSynapse(0, "AMPA+NMDA", g_max=dt * 0.1, nmda_ratio=5.)
        self.cnet.addSynapse(1, "AMPA+NMDA", g_max=dt * 0.1, nmda_ratio=5.)
        # set spiketimes for second synapse
        self.cnet.setSpikeTimes(3, np.arange(dt / 2., tmax,
                                             dt))  # AMPA+NMDA synapse
        # remove first AMPA+NMDA synapse to see if spike times are correctly re-allocated
        assert len(self.cnet.spike_times_py[2]) == 0
        self.cnet.removeSynapse(2)
        for spk_tm in self.cnet.spike_times_py:
            assert len(spk_tm) > 0
        # run sim
        res = self.cnet.runSim(tmax,
                               dt,
                               step_skip=1,
                               rec_v_node=True,
                               rec_g_syn_inds=[0, 1, 2])
        v_loc_sim = res['v_loc'][:, -1]
        g_newton = [res['g_syn'][ii][0][-1] for ii in [0, 1, 2]]
        # solve newton
        v_loc_newton = self.cnet.solveNewton(g_newton)
        # compare
        assert np.allclose(v_loc_sim, v_loc_newton, atol=.5)

        # test whether sparse storage works
        ss = 33  # number of timesteps not a multiple of storage step
        # set spiketimes
        self.cnet.setSpikeTimes(0, np.array([5.]))
        self.cnet.setSpikeTimes(1, np.array([10.]))
        # run sim
        res1 = self.cnet.runSim(tmax,
                                dt,
                                step_skip=1,
                                rec_v_node=True,
                                rec_g_syn_inds=[0, 1, 2])
        # set spiketimes
        self.cnet.setSpikeTimes(0, np.array([5.]))
        self.cnet.setSpikeTimes(1, np.array([10.]))
        # run sim
        res2 = self.cnet.runSim(tmax,
                                dt,
                                step_skip=ss,
                                rec_v_node=True,
                                rec_g_syn_inds=[0, 1, 2])
        # check if results are idendtical
        assert len(res2['t']) == len(res2['v_loc'][0])
        np.allclose(res1['v_loc'][0][ss - 1:][::ss], res2['v_loc'][0])
        np.allclose(res1['v_node'][0][ss - 1:][::ss], res2['v_node'][0])
        np.allclose(res1['g_syn'][0][0][ss - 1:][::ss], res2['g_syn'][0])
        # test whether sparse storage works
        ss = 20  # number of timesteps a multiple of storage step
        # set spiketimes
        self.cnet.setSpikeTimes(0, np.array([5.]))
        self.cnet.setSpikeTimes(1, np.array([10.]))
        # run sim
        res1 = self.cnet.runSim(tmax,
                                dt,
                                step_skip=1,
                                rec_v_node=True,
                                rec_g_syn_inds=[0, 1, 2])
        # set spiketimes
        self.cnet.setSpikeTimes(0, np.array([5.]))
        self.cnet.setSpikeTimes(1, np.array([10.]))
        # run sim
        res2 = self.cnet.runSim(tmax,
                                dt,
                                step_skip=ss,
                                rec_v_node=True,
                                rec_g_syn_inds=[0, 1, 2])
        # check if results are idendtical
        assert len(res2['t']) == len(res2['v_loc'][0])
        np.allclose(res1['v_loc'][0][ss - 1:][::ss], res2['v_loc'][0])
        np.allclose(res1['v_node'][0][ss - 1:][::ss], res2['v_node'][0])
        np.allclose(res1['g_syn'][0][0][ss - 1:][::ss], res2['g_syn'][0])

    def testInversion(self):
        dt = 0.1

        # tests without linear terms
        # test with two non-leafs that integrate soma
        self.createTree2(add_lin=False)
        # add synapses
        self.cnet.addSynapse(0, "AMPA", g_max=dt * 0.1)
        self.cnet.addSynapse(1, "AMPA", g_max=dt * 0.1)
        self.cnet.addSynapse(2, "AMPA", g_max=dt * 0.1)
        # initialize
        self.cnet.initialize(dt=dt, mode=1)
        # construct inputs
        g_in = self.cnet.recastInput([1., 1., 1.])
        self.cnet._constructInput(np.array([self.v_eq, self.v_eq, self.v_eq]),
                                  g_in)
        # recursive matrix inversion
        self.cnet.invertMatrix()
        v_node = self.cnet.getVNode()
        # construct full matrix
        mat, vec = self.cnet.getMatAndVec(dt=dt)
        # full matrix inversion
        v_sol = np.linalg.solve(mat, vec)
        # test
        assert np.allclose(v_sol, v_node)
        # test with two non-leafs that integrate soma
        self.createTree3(add_lin=False)
        # add synapses
        self.cnet.addSynapse(0, "AMPA", g_max=dt * 0.1)
        self.cnet.addSynapse(1, "AMPA", g_max=dt * 0.1)
        self.cnet.addSynapse(2, "AMPA", g_max=dt * 0.1)
        self.cnet.addSynapse(3, "AMPA", g_max=dt * 0.1)
        # initialize
        self.cnet.initialize(dt=dt, mode=1)
        # construct inputs
        g_in = self.cnet.recastInput(np.ones(4))
        self.cnet._constructInput(self.v_eq * np.ones(4), g_in)
        # recursive matrix inversion
        self.cnet.invertMatrix()
        v_node = self.cnet.getVNode()
        # construct full matrix
        mat, vec = self.cnet.getMatAndVec(dt=dt)
        # full matrix inversion
        v_sol = np.linalg.solve(mat, vec)
        # test
        assert np.allclose(v_sol, v_node)

        # tests with linear terms
        # test with one non-leafs that integrate soma
        self.createTree2(add_lin=True)
        # add synapses
        self.cnet.addSynapse(0, "AMPA", g_max=dt * 0.1)
        self.cnet.addSynapse(1, "AMPA", g_max=dt * 0.1)
        self.cnet.addSynapse(2, "AMPA", g_max=dt * 0.1)
        # initialize
        self.cnet.initialize(dt=dt, mode=1)
        # construct inputs
        g_in = self.cnet.recastInput([1., 1., 1.])
        self.cnet._constructInput(np.array([self.v_eq, self.v_eq, self.v_eq]),
                                  g_in)
        # recursive matrix inversion
        self.cnet.invertMatrix()
        v_node = self.cnet.getVNode()
        # construct full matrix
        mat, vec = self.cnet.getMatAndVec(dt=dt)
        # full matrix inversion
        v_sol = np.linalg.solve(mat, vec)
        # test
        assert np.allclose(v_sol, v_node)
        # test with two non-leafs that integrate soma
        self.createTree3(add_lin=True)
        # add synapses
        self.cnet.addSynapse(0, "AMPA", g_max=dt * 0.1)
        self.cnet.addSynapse(1, "AMPA", g_max=dt * 0.1)
        self.cnet.addSynapse(2, "AMPA", g_max=dt * 0.1)
        self.cnet.addSynapse(3, "AMPA", g_max=dt * 0.1)
        # initialize
        self.cnet.initialize(dt=dt, mode=1)
        # construct inputs
        g_in = self.cnet.recastInput(np.ones(4))
        self.cnet._constructInput(self.v_eq * np.ones(4), g_in)
        # recursive matrix inversion
        self.cnet.invertMatrix()
        v_node = self.cnet.getVNode()
        # construct full matrix
        mat, vec = self.cnet.getMatAndVec(dt=dt)
        # full matrix inversion
        v_sol = np.linalg.solve(mat, vec)
        # test
        assert np.allclose(v_sol, v_node)

    def testChannel(self):
        self.createPointNeurons()
        # simulate neuron and NET model
        res_neuron = self.sim_tree.run(100)
        res_net = self.cnet.runSim(100., self.dt)
        # test if traces equal
        assert np.allclose(res_neuron['v_m'][0, :-1],
                           res_net['v_loc'][0, :],
                           atol=.1)
Exemple #4
0
class TestNeuron():
    def loadTTreePassive(self):
        '''
        Load the T-tree morphology in memory with passive conductance

          6--5--4--7--8
                |
                |
                1
        '''
        v_eq = -75.
        self.dt = 0.025
        self.tmax = 100.
        # for frequency derivation
        self.ft = ke.FourrierTools(np.arange(0., self.tmax, self.dt))
        # load the morphology
        print '>>> loading T-tree <<<'
        fname = 'test_morphologies/Tsovtree.swc'
        self.greenstree = GreensTree(fname, types=[1,3,4])
        self.greenstree.fitLeakCurrent(e_eq_target=v_eq, tau_m_target=10.)
        self.greenstree.setCompTree()
        self.greenstree.setImpedance(self.ft.s)
        # copy greenstree parameters into NEURON simulation tree
        self.neurontree = neurm.NeuronSimTree(dt=self.dt, t_calibrate=10., v_eq=v_eq,
                                              factor_lambda=25.)
        self.greenstree.__copy__(self.neurontree)
        self.neurontree.treetype = 'computational'

    def loadTTreeActive(self):
        '''
        Load the T-tree morphology in memory with h-current

          6--5--4--7--8
                |
                |
                1
        '''
        v_eq = -75.
        self.dt = 0.025
        self.tmax = 100.
        # for frequency derivation
        self.ft = ke.FourrierTools(np.arange(0., self.tmax, self.dt))
        # load the morphology
        print '>>> loading T-tree <<<'
        fname = 'test_morphologies/Tsovtree.swc'
        self.greenstree = GreensTree(fname, types=[1,3,4])
        self.greenstree.addCurrent('h', 50., -43.)
        self.greenstree.fitLeakCurrent(e_eq_target=v_eq, tau_m_target=10.)
        # for node in self.greenstree:
        #     print node.getGTot(channel_storage=self.greenstree.channel_storage)
        #     print node.currents
        self.greenstree.setCompTree()
        self.greenstree.setImpedance(self.ft.s)
        # copy greenstree parameters into NEURON simulation tree
        self.neurontree = neurm.NeuronSimTree(dt=self.dt, t_calibrate=10., v_eq=v_eq,
                                              factor_lambda=25.)
        self.greenstree.__copy__(self.neurontree)
        self.neurontree.treetype = 'computational'

    def testPassive(self, pplot=False):
        self.loadTTreePassive()
        # set of locations
        locs = [(1, .5), (4, .5), (4, 1.), (5, .5), (6, .5), (7, .5), (8, .5)]
        # compute impedance matrix with Green's function
        zf_mat_gf = self.greenstree.calcImpedanceMatrix(locs)
        z_mat_gf = zf_mat_gf[self.ft.ind_0s].real
        # convert impedance matrix to time domain
        zk_mat_gf = np.zeros((len(self.ft.t), len(locs), len(locs)))
        for (ii, jj) in itertools.product(range(len(locs)), range(len(locs))):
            zk_mat_gf[:,ii,jj] = self.ft.FT_inv(zf_mat_gf[:,ii,jj])[1].real * 1e-3
        # test the steady state impedance matrix
        z_mat_neuron = self.neurontree.calcImpedanceMatrix(locs)
        assert np.allclose(z_mat_gf, z_mat_neuron, atol=1.)
        # test the temporal matrix
        tk, zk_mat_neuron = self.neurontree.calcImpedanceKernelMatrix(locs)
        assert np.allclose(zk_mat_gf[int(2./self.dt):,:,:],
                           zk_mat_neuron[int(2./self.dt):,:,:], atol=.2)
        if pplot:
            # plot kernels
            pl.figure()
            cc = 0
            for ii in range(len(locs)):
                jj = 0
                while jj <= ii:
                    pl.plot(tk, zk_mat_neuron[:,ii,jj], c=colours[cc%len(colours)])
                    pl.plot(tk, zk_mat_gf[:,ii,jj], ls='--', lw=2, c=colours[cc%len(colours)])
                    cc += 1
                    jj += 1
            pl.show()

    def testActive(self, pplot=False):
        self.loadTTreeActive()
        # set of locations
        locs = [(1, .5), (4, .5), (4, 1.), (5, .5), (6, .5), (7, .5), (8, .5)]
        # compute impedance matrix with Green's function
        zf_mat_gf = self.greenstree.calcImpedanceMatrix(locs)
        z_mat_gf = zf_mat_gf[self.ft.ind_0s].real
        # convert impedance matrix to time domain
        zk_mat_gf = np.zeros((len(self.ft.t), len(locs), len(locs)))
        for (ii, jj) in itertools.product(range(len(locs)), range(len(locs))):
            zk_mat_gf[:,ii,jj] = self.ft.FT_inv(zf_mat_gf[:,ii,jj])[1].real * 1e-3
        # test the steady state impedance matrix
        z_mat_neuron = self.neurontree.calcImpedanceMatrix(locs, t_dur=1300.)
        print z_mat_gf
        print z_mat_neuron
        assert np.allclose(z_mat_gf, z_mat_neuron, atol=5.)
        # test the temporal matrix
        tk, zk_mat_neuron = self.neurontree.calcImpedanceKernelMatrix(locs)
        assert np.allclose(zk_mat_gf[int(2./self.dt):,:,:],
                           zk_mat_neuron[int(2./self.dt):,:,:], atol=.3)
        if pplot:
            # plot kernels
            pl.figure()
            cc = 0
            for ii in range(len(locs)):
                jj = 0
                while jj <= ii:
                    pl.plot(tk, zk_mat_neuron[:,ii,jj], c=colours[cc%len(colours)])
                    pl.plot(tk, zk_mat_gf[:,ii,jj], ls='--', lw=2, c=colours[cc%len(colours)])
                    cc += 1
                    jj += 1
            pl.show()