def test_condense_simple(self): complexes, reactions = read_pil(""" # File generated by peppercorn-v0.5.0 # Domain Specifications length d1 = 15 length t0 = 5 # Resting-set Complexes c1 = t0 d1 c2 = d1( + ) t0* c4 = t0( d1( + ) ) c5 = d1 # Transient Complexes c3 = t0( d1 + d1( + ) ) # Detailed Reactions reaction [bind21 = 100 /M/s ] c1 + c2 -> c3 reaction [open = 50 /s ] c3 -> c1 + c2 reaction [branch-3way = 50 /s ] c3 -> c4 + c5 """) # (rs1) c1 c4 (rs3) # \ / # <---> c3 ----> # / \ # (rs2) c2 c5 (rs4) enum = Enumerator(complexes.values(), reactions) enum.enumerate() enum.condense() for con in enum.condensed_reactions: assert con.rate_constant[0] == 50 del enum # old interface ... c1 = PepperMacrostate([complexes['c1']]) c2 = PepperMacrostate([complexes['c2']]) c4 = PepperMacrostate([complexes['c4']]) c5 = PepperMacrostate([complexes['c5']]) cond_react = PepperReaction([c1, c2], [c4, c5], 'condensed') cond_react.rate_constant = 20, None enum = Enumerator(complexes.values(), reactions) enum.dry_run() enumRG = PepperCondensation(enum) enumRG.condense() for con in enumRG.condensed_reactions: assert con.rate_constant[0] == 20
def compute_fates(self, scc): """ Processes a single SCC neighborhood, generating resting set multisets for each complex, and storing the mappings in the outer-scope `complex_fates` dict """ complex_fates = self._complex_fates if scc[0] in complex_fates: # Dirty check to see if the scc has been processed before. return # Convert to a set for fast lookup scc_set = frozenset(scc) outgoing_reactions = [] for c in scc: for r in self.reactions_consuming(c): if self.is_fast(r) and is_outgoing(r, scc_set): outgoing_reactions.append(r) # If this SCC is a resting set: if len(outgoing_reactions) == 0: # build new resting set try: resting_set = PepperMacrostate(scc) except DSDDuplicationError, e: resting_set = e.existing self._set_to_fate[scc_set] = resting_set # calculate stationary distribution self._stationary_distributions[ resting_set] = self.get_stationary_distribution(scc) # assign fate to each complex in the SCC fate = (resting_set, ) # needs to be iterable.. fates = frozenset([fate]) for c in scc: if c in complex_fates: raise CondensationError( 'complex should not be assigned yet') complex_fates[c] = SetOfFates(fates) # all complexes in this SCC decay to this SCC with probability 1, # e.g. P(c -> SCC) = 1 for all c in this SCC self._decay_probabilities[(c, fate)] = 1.0
def rs(self, names): return PepperMacrostate(map(self.cplx, names), memorycheck=False)
def test_cooperative_binding(self): # cooperative binding with k-fast 25 complexes, reactions = read_pil(""" # File generated by peppercorn-v0.5.0 # Domain Specifications length a = 5 length b = 5 length x = 10 length y = 10 # Resting-set Complexes C = x( y( + b* ) ) a* CR = x( y( + y b( + ) ) ) a* CRF = x( y + y( b( + ) ) ) a* L = a x LC = a( x + x( y( + b* ) ) ) LCF = a( x( + x y( + b* ) ) ) LR = a( x( + y( b( + ) ) ) ) R = y b T = x y # Transient Complexes LCR = a( x + x( y( + y b( + ) ) ) ) LCRF1 = a( x( + x y( + y b( + ) ) ) ) LCRF2 = a( x + x( y + y( b( + ) ) ) ) # Detailed Reactions reaction [bind21 = 1.5e+06 /M/s ] C + L -> LC reaction [bind21 = 1.5e+06 /M/s ] C + R -> CR reaction [open = 20 /s ] CR -> C + R reaction [branch-3way = 30 /s ] CR -> CRF reaction [branch-3way = 30 /s ] CRF -> CR reaction [bind21 = 1.5e+06 /M/s ] L + CR -> LCR reaction [bind21 = 1.5e+06 /M/s ] L + CRF -> LCRF2 reaction [open = 20 /s ] LC -> C + L reaction [branch-3way = 30 /s ] LC -> LCF reaction [branch-3way = 30 /s ] LCF -> LC reaction [branch-3way = 30 /s ] LCR -> LCRF1 reaction [branch-3way = 30 /s ] LCR -> LCRF2 reaction [branch-3way = 30 /s ] LCRF1 -> LCR reaction [branch-3way = 30 /s ] LCRF1 -> T + LR reaction [branch-3way = 30 /s ] LCRF2 -> LCR reaction [branch-3way = 30 /s ] LCRF2 -> T + LR reaction [bind21 = 1.5e+06 /M/s ] R + LC -> LCR reaction [bind21 = 1.5e+06 /M/s ] R + LCF -> LCRF1 """) L = complexes['L'] C = complexes['C'] R = complexes['R'] T = complexes['T'] LR = complexes['LR'] LC = complexes['LC'] CR = complexes['CR'] CRF = complexes['CRF'] LCF = complexes['LCF'] LCR = complexes['LCR'] LCRF1 = complexes['LCRF1'] LCRF2 = complexes['LCRF2'] # always resting sets rs1 = PepperMacrostate([L], memorycheck=False) rs2 = PepperMacrostate([C], memorycheck=False) rs3 = PepperMacrostate([R], memorycheck=False) rs4 = PepperMacrostate([T], memorycheck=False) rs5 = PepperMacrostate([LR], memorycheck=False) rs6 = PepperMacrostate([CR, CRF], memorycheck=False) rs7 = PepperMacrostate([LC, LCF], memorycheck=False) cplx_to_fate = { # maps Complex to its SetOfFates L: SetOfFates([[rs1]]), C: SetOfFates([[rs2]]), R: SetOfFates([[rs3]]), T: SetOfFates([[rs4]]), LR: SetOfFates([[rs5]]), CR: SetOfFates([[rs6]]), CRF: SetOfFates([[rs6]]), LC: SetOfFates([[rs7]]), LCF: SetOfFates([[rs7]]), #NOTE: only rs4 and rs5 bec. the other unimolecular reactions are now slow!! LCR: SetOfFates([[rs4, rs5]]), LCRF1: SetOfFates([[rs4, rs5]]), LCRF2: SetOfFates([[rs4, rs5]]) } cr1 = PepperReaction([rs1, rs2], [rs7], 'condensed', rate=1.5e6, memorycheck=False) cr2 = PepperReaction([rs2, rs3], [rs6], 'condensed', rate=1.5e6, memorycheck=False) # not sure how these rates were computed... cr1r = PepperReaction([rs7], [rs1, rs2], 'condensed', rate=10.0, memorycheck=False) cr2r = PepperReaction([rs6], [rs2, rs3], 'condensed', rate=10.0, memorycheck=False) cr3 = PepperReaction([rs1, rs6], [rs5, rs4], 'condensed', rate=3e6 / 2, memorycheck=False) cr4 = PepperReaction([rs3, rs7], [rs5, rs4], 'condensed', rate=3e6 / 2, memorycheck=False) enum = Enumerator(complexes.values(), reactions) enum.k_fast = 25 #enum.enumerate() # or enum.dry_run() enum.dry_run() # or enum.dry_run() enumRG = PepperCondensation(enum) enumRG.condense() # Works... self.assertEqual(enum.k_fast, enumRG.k_fast) self.assertEqual(sorted([rs1, rs2, rs3, rs4, rs5, rs6, rs7]), sorted(enumRG.resting_sets)) self.assertDictEqual(cplx_to_fate, enumRG.cplx_to_fate) self.assertEqual(sorted([cr1, cr1r, cr2, cr2r, cr3, cr4]), sorted(enumRG.condensed_reactions)) for (r1, r2) in zip(sorted([cr1, cr1r, cr2, cr2r, cr3, cr4]), sorted(enumRG.condensed_reactions)): self.assertEqual(r1, r2) self.assertAlmostEqual(r1.rate, r2.rate)
def test_zhang_cooperative_binding(self): complexes, reactions = read_pil(""" # Figure 1 of David Yu Zhang, "Cooperative hybridization of oligonucleotides", JACS, 2012 # File generated by peppercorn-v0.5.0 # Domain Specifications length d1 = 8 length d2 = 18 length d3 = 18 length d4 = 8 # Resting-set Complexes C1 = d2( d3( + d4* ) ) d1* L1 = d1( d2 + d2( d3( + d4* ) ) ) L2 = d1( d2( + d2 d3( + d4* ) ) ) Out = d2 d3 R1 = d2( d3( + d3 d4( + ) ) ) d1* R2 = d2( d3 + d3( d4( + ) ) ) d1* T1 = d1 d2 T2 = d3 d4 Waste = d1( d2( + d3( d4( + ) ) ) ) # Transient Complexes L1R1 = d1( d2 + d2( d3( + d3 d4( + ) ) ) ) L1R2 = d1( d2 + d2( d3 + d3( d4( + ) ) ) ) L2R1 = d1( d2( + d2 d3( + d3 d4( + ) ) ) ) # Detailed Reactions reaction [bind21 = 2.4e+06 /M/s ] C1 + T2 -> R1 reaction [bind21 = 2.4e+06 /M/s ] L1 + T2 -> L1R1 reaction [branch-3way = 18.5185 /s ] L1 -> L2 reaction [branch-3way = 18.5185 /s ] L1R1 -> L1R2 reaction [branch-3way = 18.5185 /s ] L1R1 -> L2R1 reaction [branch-3way = 18.5185 /s ] L1R2 -> L1R1 reaction [branch-3way = 18.5185 /s ] L1R2 -> Waste + Out reaction [bind21 = 2.4e+06 /M/s ] L2 + T2 -> L2R1 reaction [branch-3way = 18.5185 /s ] L2 -> L1 reaction [branch-3way = 18.5185 /s ] L2R1 -> L1R1 reaction [branch-3way = 18.5185 /s ] L2R1 -> Waste + Out reaction [branch-3way = 18.5185 /s ] R1 -> R2 reaction [branch-3way = 18.5185 /s ] R2 -> R1 reaction [bind21 = 2.4e+06 /M/s ] T1 + C1 -> L1 reaction [bind21 = 2.4e+06 /M/s ] T1 + R1 -> L1R1 reaction [bind21 = 2.4e+06 /M/s ] T1 + R2 -> L1R2 """) enum = Enumerator(complexes.values(), reactions) enum.k_fast = 0.01 enum.release_cutoff = 10 #enum.enumerate() # or enum.dry_run() enum.dry_run() enumRG = PepperCondensation(enum) enumRG.condense() """ macrostate rC1 = [C1] macrostate rL2 = [L2, L1] macrostate rOut = [Out] macrostate rR1 = [R1, R2] macrostate rT1 = [T1] macrostate rT2 = [T2] macrostate rWaste = [Waste] reaction [condensed = 2.4e+06 /M/s ] rT1 + rC1 -> rL2 reaction [condensed = 2.4e+06 /M/s ] rL2 + rT2 -> rWaste + rOut reaction [condensed = 2.4e+06 /M/s ] rC1 + rT2 -> rR1 reaction [condensed = 2.4e+06 /M/s ] rT1 + rR1 -> rWaste + rOut reaction [condensed = 0.00316623 /s ] rL2 -> rT1 + rC1 reaction [condensed = 0.00316623 /s ] rR1 -> rC1 + rT2 """ L1 = complexes['L1'] L2 = complexes['L2'] rL2 = PepperMacrostate([L2, L1], memorycheck=False) Out = complexes['Out'] rOut = PepperMacrostate([Out], memorycheck=False) Waste = complexes['Waste'] rWaste = PepperMacrostate([Waste], memorycheck=False) T2 = complexes['T2'] rT2 = PepperMacrostate([T2], memorycheck=False) # calculated by hand... cr1 = PepperReaction([rL2, rT2], [rWaste, rOut], 'condensed', rate=2.4e6, memorycheck=False) found = False for r in enumRG.condensed_reactions: if r == cr1: found = True self.assertAlmostEqual(r.rate, cr1.rate) self.assertTrue(found)
def test_condense_simple(self): complexes, reactions = read_pil(""" # File generated by peppercorn-v0.5.0 # Domain Specifications length d1 = 15 length t0 = 5 # Resting-set Complexes c1 = t0 d1 c2 = d1( + ) t0* c4 = t0( d1( + ) ) c5 = d1 # Transient Complexes c3 = t0( d1 + d1( + ) ) # Detailed Reactions reaction [bind21 = 100 /M/s ] c1 + c2 -> c3 reaction [open = 50 /s ] c3 -> c1 + c2 reaction [branch-3way = 50 /s ] c3 -> c4 + c5 """) # (rs1) c1 c4 (rs3) # \ / # <---> c3 ----> # / \ # (rs2) c2 c5 (rs4) # RestingSet representation rs1 = PepperMacrostate([complexes['c1']], memorycheck=False) rs2 = PepperMacrostate([complexes['c2']], memorycheck=False) rs3 = PepperMacrostate([complexes['c4']], memorycheck=False) rs4 = PepperMacrostate([complexes['c5']], memorycheck=False) # Frozensets instead of RestingMacrostates fs1 = frozenset([complexes['c1']]) fs2 = frozenset([complexes['c2']]) fs3 = frozenset([complexes['c4']]) fs4 = frozenset([complexes['c5']]) cplx_to_state = { # maps Complex to its RestingMacrostate complexes['c1']: rs1, complexes['c2']: rs2, complexes['c4']: rs3, complexes['c5']: rs4 } cplx_to_fate = { # maps Complex to its SetOfFates complexes['c1']: SetOfFates([[rs1]]), complexes['c2']: SetOfFates([[rs2]]), complexes['c3']: SetOfFates([[rs1, rs2], [rs3, rs4]]), complexes['c4']: SetOfFates([[rs3]]), complexes['c5']: SetOfFates([[rs4]]) } cplx_to_set = { # maps Complex to its frozenset complexes['c1']: fs1, complexes['c2']: fs2, complexes['c4']: fs3, complexes['c5']: fs4 } set_to_fate = { # maps frozenset to the RestingMacrostate fs1: rs1, fs2: rs2, fs3: rs3, fs4: rs4 } cond_react = PepperReaction([rs1, rs2], [rs3, rs4], 'condensed', memorycheck=False) cond_react.rate = 100 * (float(50) / (50 + 50)) enum = Enumerator(complexes.values(), reactions) enum.dry_run() # does not change the rates! enumRG = PepperCondensation(enum) enumRG.condense() self.assertEqual(sorted([rs1, rs2, rs3, rs4]), sorted(enumRG.resting_sets)) self.assertDictEqual(set_to_fate, enumRG.set_to_fate) self.assertDictEqual(cplx_to_fate, enumRG.cplx_to_fate) #self.assertDictEqual(cplx_to_set, info['complexes_to_resting_set']) self.assertEqual([cond_react], enumRG.condensed_reactions) self.assertEqual(cond_react.rate, enumRG.condensed_reactions[0].rate) self.assertEqual(enumRG.condensed_reactions[0].rate, 50)
def rs(self, names): return PepperMacrostate(list(map(self.cplx, names)))
def test_zhang_cooperative_binding(self): complexes, reactions = read_pil(""" # Figure 1 of David Yu Zhang, "Cooperative hybridization of oligonucleotides", JACS, 2012 # File generated by peppercorn-v0.5.0 # Domain Specifications length d1 = 8 length d2 = 18 length d3 = 18 length d4 = 8 # Resting-set Complexes C1 = d2( d3( + d4* ) ) d1* L1 = d1( d2 + d2( d3( + d4* ) ) ) L2 = d1( d2( + d2 d3( + d4* ) ) ) Out = d2 d3 R1 = d2( d3( + d3 d4( + ) ) ) d1* R2 = d2( d3 + d3( d4( + ) ) ) d1* T1 = d1 d2 T2 = d3 d4 Waste = d1( d2( + d3( d4( + ) ) ) ) # Transient Complexes L1R1 = d1( d2 + d2( d3( + d3 d4( + ) ) ) ) L1R2 = d1( d2 + d2( d3 + d3( d4( + ) ) ) ) L2R1 = d1( d2( + d2 d3( + d3 d4( + ) ) ) ) # Detailed Reactions reaction [bind21 = 2.4e+06 /M/s ] C1 + T2 -> R1 reaction [bind21 = 2.4e+06 /M/s ] L1 + T2 -> L1R1 reaction [branch-3way = 18.5185 /s ] L1 -> L2 reaction [branch-3way = 18.5185 /s ] L1R1 -> L1R2 reaction [branch-3way = 18.5185 /s ] L1R1 -> L2R1 reaction [branch-3way = 18.5185 /s ] L1R2 -> L1R1 reaction [branch-3way = 18.5185 /s ] L1R2 -> Waste + Out reaction [bind21 = 2.4e+06 /M/s ] L2 + T2 -> L2R1 reaction [branch-3way = 18.5185 /s ] L2 -> L1 reaction [branch-3way = 18.5185 /s ] L2R1 -> L1R1 reaction [branch-3way = 18.5185 /s ] L2R1 -> Waste + Out reaction [branch-3way = 18.5185 /s ] R1 -> R2 reaction [branch-3way = 18.5185 /s ] R2 -> R1 reaction [bind21 = 2.4e+06 /M/s ] T1 + C1 -> L1 reaction [bind21 = 2.4e+06 /M/s ] T1 + R1 -> L1R1 reaction [bind21 = 2.4e+06 /M/s ] T1 + R2 -> L1R2 """) enum = Enumerator(complexes.values(), reactions) enum.k_fast = 0.01 enum.release_cutoff = 10 enum.enumerate() # or enum.dry_run() enumRG = PepperCondensation(enum) enumRG.condense() """ macrostate rC1 = [C1] macrostate rL2 = [L2, L1] macrostate rOut = [Out] macrostate rR1 = [R1, R2] macrostate rT1 = [T1] macrostate rT2 = [T2] macrostate rWaste = [Waste] reaction [condensed = 2.4e+06 /M/s ] rT1 + rC1 -> rL2 reaction [condensed = 2.4e+06 /M/s ] rL2 + rT2 -> rWaste + rOut reaction [condensed = 2.4e+06 /M/s ] rC1 + rT2 -> rR1 reaction [condensed = 2.4e+06 /M/s ] rT1 + rR1 -> rWaste + rOut reaction [condensed = 0.00316623 /s ] rL2 -> rT1 + rC1 reaction [condensed = 0.00316623 /s ] rR1 -> rC1 + rT2 """ try: L1 = complexes['L1'] L2 = complexes['L2'] L = PepperMacrostate([L1, L2]) except SingletonError as err: L = err.existing O = PepperMacrostate([complexes['Out']]) W = PepperMacrostate([complexes['Waste']]) T = PepperMacrostate([complexes['T2']]) # calculated by hand... cr1 = PepperReaction([L, T], [W, O], 'condensed') assert cr1 in enumRG.condensed_reactions assert cr1.rate_constant == (2.4e6, '/M/s')
def segment_neighborhood(complexes, reactions, p_min=None): """ Segmentation of a potentially incomplete neighborhood. That means only the specified complexes are interesting, all others should not be returned. Beware: Complexes must contain all reactants in reactions *and* there must not be any incoming fast reactions into complexes, other than those specified in reactions. Thus, we can be sure that the SCCs found here are consistent with SCCs found in a previous iteration. Args: complexes(list[:obj:`PepperComplex`]) """ index = 0 S = [] SCCs = [] total = complexes[:] for rxn in reactions: total += rxn.products total = list(set(total)) # Set up for Tarjan's algorithm for c in total: c._index = None # filters reaction products such that there are only species from within complexes # this is ok, because it must not change the assignment of SCCs. rxns_within = {k: [] for k in complexes} rxns_consuming = {k: [r for r in reactions if (k in r.reactants)] for k in total} for rxn in reactions: assert len(rxn.reactants) == 1 for product in rxn.products: if product in complexes: rxns_within[rxn.reactants[0]].append(product) rxns_consuming[rxn.reactants[0]] def tarjans_scc(cplx, index): """ Executes an iteration of Tarjan's algorithm (a modified DFS) starting at the given node. """ # Set this node's tarjan numbers cplx._index = index cplx._lowlink = index index += 1 S.append(cplx) for product in rxns_within[cplx]: # Product hasn't been traversed; recurse if product._index is None : index = tarjans_scc(product, index) cplx._lowlink = min(cplx._lowlink, product._lowlink) # Product is in the current neighborhood elif product in S: cplx._lowlink = min(cplx._lowlink, product._index) if cplx._lowlink == cplx._index: scc = [] while True: next = S.pop() scc.append(next) if next == cplx: break SCCs.append(scc) return index # We now perform Tarjan's algorithm, marking nodes as appropriate for cplx in complexes: if cplx._index is None: tarjans_scc(cplx, index) resting_macrostates = [] transient_macrostates = [] resting_complexes = [] transient_complexes = [] for scc in SCCs: try: ms = PepperMacrostate(scc[:], prefix='') except DSDDuplicationError, e: assert set(e.existing.complexes) == set(scc) ms = e.existing #except DSDObjectsError, e: # assert set(e.existing.complexes) <= set(scc) # del PepperMacrostate.MEMORY[e.existing.canonical_form] # del PepperMacrostate.NAMES[e.existing.name] # ms = PepperMacrostate(scc[:], prefix='') for c in scc: for rxn in rxns_consuming[c]: ms.add_reaction(rxn) if ms.is_transient: transient_complexes += (scc) else : resting_macrostates.append(ms) if p_min: for (c, s) in ms.get_stationary_distribution(): if s < p_min: transient_complexes.append(c) else : resting_complexes.append(c) else: resting_complexes += (scc)