コード例 #1
0
    def testSegmentNeighborhood2(self):
        enum = self.SLC_enumerator
        complex_set = [
            self.complexes['Cat'], self.complexes['C2'], self.complexes['I1'],
            self.complexes['I2'], self.complexes['I3'], self.complexes['SP']
        ]
        reaction_set = []
        # multi-molecular reactions never appear in segment neighborhood arguments
        #reaction_set.append(ReactionPathway('bind21', [self.complexes['Cat'], self.complexes['C2']], [self.complexes['I1']]))
        #reaction_set.append(ReactionPathway('bind21', [self.complexes['I3'], self.complexes['SP']], [self.complexes['I2']]))
        reaction_set.append(
            ReactionPathway('open', [self.complexes['I1']],
                            [self.complexes['Cat'], self.complexes['C2']]))
        reaction_set.append(
            ReactionPathway('open', [self.complexes['I2']],
                            [self.complexes['I3'], self.complexes['SP']]))
        reaction_set.append(
            ReactionPathway('branch_3way', [self.complexes['I1']],
                            [self.complexes['I2']]))
        reaction_set.append(
            ReactionPathway('branch_3way', [self.complexes['I2']],
                            [self.complexes['I1']]))

        res = enum.segment_neighborhood(complex_set, reaction_set)
        exp = {
            'resting_states':
            sorted([
                RestingState('0', [self.complexes['Cat']]),
                RestingState('1', [self.complexes['C2']]),
                RestingState('2', [self.complexes['SP']]),
                RestingState('3', [self.complexes['I3']])
            ]),
            'resting_state_complexes':
            sorted([
                self.complexes['Cat'], self.complexes['C2'],
                self.complexes['SP'], self.complexes['I3']
            ]),
            'transient_state_complexes':
            sorted([self.complexes['I1'], self.complexes['I2']])
        }

        assert res == exp
コード例 #2
0
        def rxn(string):
            """
            Dummy function for generating reactions between formal species
            """
            reactants, products = string.split('->')
            reactants = reactants.split('+')
            products = products.split('+')

            reactants = [cplx(x) for x in reactants]
            products = [cplx(x) for x in products]
            reactions.add(ReactionPathway('dummy', reactants, products))
コード例 #3
0
    def testSegmentNeighborhood3(self):
        enum = self.three_arm_enumerator
        complex_set = [
            self.complexes2['IABC'], self.complexes2['I'],
            self.complexes2['ABC']
        ]
        reaction_set = []
        reaction_set.append(
            ReactionPathway('branch_3way', [self.complexes2['IABC']],
                            [self.complexes2['I'], self.complexes2['IABC']]))

        res = enum.segment_neighborhood(complex_set, reaction_set)
        exp = {
            'resting_states':
            sorted([
                RestingState('0', [self.complexes2['I']]),
                RestingState('1', [self.complexes2['ABC']])
            ]),
            'resting_state_complexes':
            sorted([self.complexes2['I'], self.complexes2['ABC']]),
            'transient_state_complexes':
            sorted([self.complexes2['IABC']])
        }
        assert res == exp
コード例 #4
0
    def testEnumeration3(self):
        enum = Enumerator(self.SLC_enumerator.domains,
                          self.SLC_enumerator.strands, [
                              self.complexes['Cat'], self.complexes['C1'],
                              self.complexes['C2']
                          ])
        enum.enumerate()

        # shortcut because of large number of uses
        c = self.complexes

        exp_initial_complexes = sorted([
            self.complexes['Cat'], self.complexes['C1'], self.complexes['C2']
        ])
        res_initial_complexes = sorted(enum.initial_complexes)

        assert exp_initial_complexes == res_initial_complexes

        reaction_set = []
        reaction_set.append(
            ReactionPathway('bind21', [c['Cat'], c['C2']], [c['I1']]))
        reaction_set.append(
            ReactionPathway('open', [c['I1']], [c['Cat'], c['C2']]))
        reaction_set.append(
            ReactionPathway('branch_3way', [c['I1']], [c['I2']]))
        reaction_set.append(
            ReactionPathway('branch_3way', [c['I2']], [c['I1']]))
        reaction_set.append(
            ReactionPathway('open', [c['I2']], [c['SP'], c['I3']]))
        reaction_set.append(
            ReactionPathway('bind21', [c['SP'], c['I3']], [c['I2']]))
        reaction_set.append(
            ReactionPathway('bind21', [c['I3'], c['C1']], [c['I4']]))
        reaction_set.append(
            ReactionPathway('open', [c['I4']], [c['I3'], c['C1']]))
        reaction_set.append(
            ReactionPathway('branch_3way', [c['I4']], [c['OP'], c['I5']]))

        I6 = copy.deepcopy(c['I5'])
        Cat_index = I6.strand_index('Cat')
        BS_index = I6.strand_index('BS')
        PS_index = I6.strand_index('PS')

        I6.structure[Cat_index][0] = None
        I6.structure[BS_index][1] = (PS_index, 4)
        I6.structure[PS_index][4] = (BS_index, 1)

        self.complexes['I6'] = I6
        I6._name = 'I6'

        reaction_set.append(
            ReactionPathway('branch_3way', [c['I5']], [c['I6']]))
        reaction_set.append(
            ReactionPathway('branch_3way', [c['I6']], [c['I5']]))
        reaction_set.append(
            ReactionPathway('open', [c['I6']], [c['W'], c['Cat']]))
        reaction_set.append(
            ReactionPathway('bind21', [c['W'], c['Cat']], [c['I6']]))

        I7 = copy.deepcopy(c['I4'])
        Cat_index = I7.strand_index('Cat')
        BS_index = I7.strand_index('BS')
        PS_index = I7.strand_index('PS')

        I7.structure[Cat_index][0] = None
        I7.structure[BS_index][1] = (PS_index, 4)
        I7.structure[PS_index][4] = (BS_index, 1)

        self.complexes['I7'] = I7
        I7._name = 'I7'

        reaction_set.append(
            ReactionPathway('branch_3way', [c['I4']], [c['I7']]))
        reaction_set.append(
            ReactionPathway('branch_3way', [c['I7']], [c['I4']]))
        reaction_set.append(
            ReactionPathway('branch_3way', [c['I7']], [c['OP'], c['I6']]))

        I8 = Complex(
            'I8', [self.strands['BS'], self.strands['OP'], self.strands['PS']],
            [[None, (2, 4),
              (2, 3), None, None, None], [(2, 2), (2, 1), (2, 0), None],
             [(1, 2), (1, 1), (1, 0), (0, 2), (0, 1)]])
        self.complexes['I8'] = I8
        I8._name = 'I8'

        reaction_set.append(
            ReactionPathway('open', [c['I7']], [c['I8'], c['Cat']]))
        reaction_set.append(
            ReactionPathway('branch_3way', [c['I8']], [c['OP'], c['W']]))

        exp_reactions = sorted(reaction_set)
        res_reactions = sorted(enum.reactions)

        assert exp_reactions == res_reactions

        exp_resting_states = sorted([
            RestingState('0', [self.complexes['Cat']]),
            RestingState('1', [self.complexes['C2']]),
            RestingState('2', [self.complexes['SP']]),
            RestingState('3', [self.complexes['I3']]),
            RestingState('4', [self.complexes['C1']]),
            RestingState('5', [self.complexes['OP']]),
            RestingState('6', [self.complexes['W']])
        ])
        res_resting_states = sorted(enum.resting_states)

        assert exp_resting_states == res_resting_states

        exp_resting_complexes = sorted([
            self.complexes['Cat'], self.complexes['C2'], self.complexes['SP'],
            self.complexes['I3'], self.complexes['C1'], self.complexes['OP'],
            self.complexes['W']
        ])

        assert sorted(enum.resting_complexes) == exp_resting_complexes

        exp_transient_complexes = sorted([
            self.complexes['I1'], self.complexes['I2'], self.complexes['I4'],
            self.complexes['I5'], self.complexes['I6'], self.complexes['I7'],
            self.complexes['I8']
        ])
        res_transient_complexes = sorted(enum.transient_complexes)

        assert res_transient_complexes == exp_transient_complexes

        assert sorted(exp_transient_complexes +
                      exp_resting_complexes) == sorted(enum.complexes)
コード例 #5
0
    def testEnumeration2(self):
        enum = Enumerator(self.SLC_enumerator._domains,
                          self.SLC_enumerator._strands, [self.complexes['I1']])
        enum.enumerate()

        exp_initial_complexes = [self.complexes['I1']]
        assert exp_initial_complexes == enum.initial_complexes

        reaction_set = []
        reaction_set.append(
            ReactionPathway('bind21',
                            [self.complexes['Cat'], self.complexes['C2']],
                            [self.complexes['I1']]))
        reaction_set.append(
            ReactionPathway('bind21',
                            [self.complexes['I3'], self.complexes['SP']],
                            [self.complexes['I2']]))
        reaction_set.append(
            ReactionPathway('open', [self.complexes['I1']],
                            [self.complexes['Cat'], self.complexes['C2']]))
        reaction_set.append(
            ReactionPathway('open', [self.complexes['I2']],
                            [self.complexes['I3'], self.complexes['SP']]))
        reaction_set.append(
            ReactionPathway('branch_3way', [self.complexes['I1']],
                            [self.complexes['I2']]))
        reaction_set.append(
            ReactionPathway('branch_3way', [self.complexes['I2']],
                            [self.complexes['I1']]))
        reaction_set.sort()

        exp_reactions = reaction_set
        res_reactions = sorted(enum.reactions)

        assert exp_reactions == res_reactions

        exp_resting_states = sorted([
            RestingState('0', [self.complexes['Cat']]),
            RestingState('1', [self.complexes['C2']]),
            RestingState('2', [self.complexes['SP']]),
            RestingState('3', [self.complexes['I3']])
        ])

        assert exp_resting_states == enum.resting_states

        exp_complexes = sorted([
            self.complexes['I1'], self.complexes['I2'], self.complexes['Cat'],
            self.complexes['C2'], self.complexes['SP'], self.complexes['I3']
        ])
        res_complexes = sorted(enum.complexes)

        assert res_complexes == exp_complexes

        exp_resting_complexes = sorted([
            self.complexes['Cat'], self.complexes['C2'], self.complexes['SP'],
            self.complexes['I3']
        ])
        res_resting_complexes = sorted(enum.resting_complexes)
        assert res_resting_complexes == exp_resting_complexes

        exp_transient_complexes = sorted(
            [self.complexes['I1'], self.complexes['I2']])
        res_transient_complexes = sorted(enum.transient_complexes)
        assert res_transient_complexes == exp_transient_complexes
コード例 #6
0
    def setUp(self):

        self.domains = domains = {
            'a': Domain('a', 'long'),
            'b': Domain('b', 'long'),
            'c': Domain('c', 'long'),
            'd': Domain('d', 'long'),
            'e': Domain('e', 'long'),
            'f': Domain('f', 'long'),
            'g': Domain('g', 'long'),
            'h': Domain('h', 'long'),
            'i': Domain('i', 'long'),
            'j': Domain('j', 'long'),
        }
        self.strands = strands = {
            's1': Strand('s1', [domains['a']]),
            's2': Strand('s2', [domains['b']]),
            's3': Strand('s3', [domains['c']]),
            's4': Strand('s4', [domains['d']]),
            's5': Strand('s5', [domains['e']]),
            's6': Strand('s6', [domains['f']]),
            's7': Strand('s7', [domains['g']]),
            's8': Strand('s8', [domains['h']]),
            's9': Strand('s9', [domains['i']]),
            's10': Strand('s10', [domains['j']]),
        }
        self.complexes = complexes = {
            'A': Complex('A', [strands['s1']], [[None]]),
            'B': Complex('B', [strands['s2']], [[None]]),
            'C': Complex('C', [strands['s3']], [[None]]),
            'D': Complex('D', [strands['s4']], [[None]]),
            'E': Complex('E', [strands['s5']], [[None]]),
            'F': Complex('F', [strands['s6']], [[None]]),
            'G': Complex('G', [strands['s7']], [[None]]),
            'H': Complex('H', [strands['s8']], [[None]]),
            'I': Complex('I', [strands['s9']], [[None]]),
            'J': Complex('J', [strands['s10']], [[None]])
        }
        self.reactions = reactions = {

            # cycle 1: A -> B -> C -> D -> A
            'A->B':
            ReactionPathway('bind11', [complexes['A']], [complexes['B']]),
            'B->C':
            ReactionPathway('bind11', [complexes['B']], [complexes['C']]),
            'C->D':
            ReactionPathway('bind11', [complexes['C']], [complexes['D']]),
            'D->A':
            ReactionPathway('bind11', [complexes['D']], [complexes['A']]),

            # cycle 2: E -> F -> G ->E
            'E->F':
            ReactionPathway('bind11', [complexes['E']], [complexes['F']]),
            'F->G':
            ReactionPathway('bind11', [complexes['F']], [complexes['G']]),
            'G->E':
            ReactionPathway('bind11', [complexes['G']], [complexes['E']]),

            # fast transition: cycle 1 -> cycle 2
            'D->E':
            ReactionPathway('bind11', [complexes['D']], [complexes['E']]),

            # non-deterministic choice:
            'E->H':
            ReactionPathway('bind11', [complexes['E']], [complexes['H']]),
            'E->I':
            ReactionPathway('bind11', [complexes['E']], [complexes['I']]),

            # bimolecular reaction
            'C+D->E':
            ReactionPathway('bind21', [complexes['C'], complexes['D']],
                            [complexes['E']]),

            # reversible reactions
            'B->A':
            ReactionPathway('bind11', [complexes['B']], [complexes['A']]),
            'C->B':
            ReactionPathway('bind11', [complexes['C']], [complexes['B']]),
            'D->C':
            ReactionPathway('bind11', [complexes['D']], [complexes['C']]),
            'A->D':
            ReactionPathway('bind11', [complexes['A']], [complexes['D']]),
            'A->E':
            ReactionPathway('bind11', [complexes['A']], [complexes['E']]),
            'E->A':
            ReactionPathway('bind11', [complexes['E']], [complexes['A']]),
            'E->C':
            ReactionPathway('bind11', [complexes['E']], [complexes['C']]),
            'E->F':
            ReactionPathway('bind11', [complexes['E']], [complexes['F']]),
            'F->D':
            ReactionPathway('bind11', [complexes['F']], [complexes['D']])
        }

        enum = Enum(complexes.values(), reactions.values())
        self.enumerator = self.enum = enum
        self.neighborhood_abcd = pluck(complexes, ['A', 'B', 'C', 'D'])
        self.neighborhood_e = [complexes['E']]
コード例 #7
0
    def testCondenseGraph6(self):

        self.fate_example = input.input_pil(
            'tests/files/examples/fate-example.pil')

        self.fate_example.MAX_COMPLEX_SIZE = 10
        self.fate_example.MAX_REACTION_COUNT = 1000
        self.fate_example.MAX_COMPLEX_COUNT = 200
        self.fate_example.RELEASE_CUTOFF = 7

        self.fate_example.enumerate()

        enumerator = self.fate_example
        condensed = condense_resting_states(self.fate_example)

        # Domains
        domains = {
            '2': Domain('2', 8, is_complement=False, sequence='NNNNNNNN'),
            '2*': Domain('2', 8, is_complement=True, sequence='NNNNNNNN'),
            '3': Domain('3', 8, is_complement=False, sequence='NNNNNNNN'),
            '3*': Domain('3', 8, is_complement=True, sequence='NNNNNNNN'),
            'a': Domain('a', 8, is_complement=False, sequence='NNNNNNNN'),
            'a*': Domain('a', 8, is_complement=True, sequence='NNNNNNNN'),
            'b': Domain('b', 8, is_complement=False, sequence='NNNNNNNN'),
            'b*': Domain('b', 8, is_complement=True, sequence='NNNNNNNN'),
            'c': Domain('c', 8, is_complement=False, sequence='NNNNNNNN'),
            'c*': Domain('c', 8, is_complement=True, sequence='NNNNNNNN'),
            't': Domain('t', 4, is_complement=False, sequence='NNNN'),
            't*': Domain('t', 4, is_complement=True, sequence='NNNN')
        }
        assert set(domains.values()) == set(enumerator.domains)

        # Strands
        strands = {
            '3a':
            Strand('3a', [domains['3*'], domains['a*']]),
            '23':
            Strand('23', [domains['2'], domains['3']]),
            'gate':
            Strand('gate', [
                domains['a*'], domains['b*'], domains['c'], domains['b'],
                domains['a'], domains['2*'], domains['t*']
            ]),
            't23':
            Strand('t23', [domains['t'], domains['2'], domains['3']])
        }
        assert set(strands.values()) == set(enumerator.strands)

        # Complexes
        complexes = {
            '2':
            Complex('2', [strands['23'], strands['3a'], strands['gate']],
                    [[(2, 5), (1, 0)], [(0, 1), None],
                     [(2, 4), (2, 3), None, (2, 1), (2, 0), (0, 0), None]]),
            '5':
            Complex('5', [
                strands['23'], strands['3a'], strands['gate'], strands['t23']
            ], [[(2, 5), (1, 0)], [(0, 1), (2, 4)],
                [None, (2, 3), None, (2, 1), (1, 1), (0, 0),
                 (3, 0)], [(2, 6), None, None]]),
            '6':
            Complex('6', [
                strands['23'], strands['3a'], strands['gate'], strands['t23']
            ], [[(2, 5), (1, 0)], [(0, 1), None],
                [(2, 4), (2, 3), None, (2, 1), (2, 0), (0, 0),
                 (3, 0)], [(2, 6), None, None]]),
            '12':
            Complex('12', [strands['gate'], strands['t23']],
                    [[(0, 4), (0, 3), None, (0, 1), (0, 0), (1, 1),
                      (1, 0)], [(0, 6), (0, 5), None]]),
            '13':
            Complex('13', [strands['23'], strands['3a']],
                    [[None, (1, 0)], [(0, 1), None]]),
            '17':
            Complex('17', [
                strands['23'], strands['3a'], strands['gate'], strands['t23']
            ], [[None, (1, 0)], [(0, 1), (2, 4)],
                [None, (2, 3), None, (2, 1), (1, 1), (3, 1),
                 (3, 0)], [(2, 6), (2, 5), None]]),
            '20':
            Complex('20', [strands['3a'], strands['gate'], strands['t23']],
                    [[(2, 2), (1, 4)],
                     [None, (1, 3), None, (1, 1), (0, 1), (2, 1),
                      (2, 0)], [(1, 6), (1, 5), (0, 0)]]),
            '21':
            Complex('21', [strands['23']], [[None, None]]),
            '26':
            Complex('26', [strands['3a'], strands['gate'], strands['t23']],
                    [[(2, 2), None],
                     [(1, 4), (1, 3), None, (1, 1), (1, 0), (2, 1),
                      (2, 0)], [(1, 6), (1, 5), (0, 0)]]),
            'gate':
            Complex('gate', [strands['23'], strands['3a'], strands['gate']],
                    [[(2, 5), (1, 0)], [(0, 1), (2, 4)],
                     [None, (2, 3), None, (2, 1), (1, 1), (0, 0), None]]),
            't23':
            Complex('t23', [strands['t23']], [[None, None, None]])
        }
        assert set(complexes.values()) == set(enumerator.complexes)

        # Reactions
        reactions = {
            ReactionPathway('bind21', [complexes['gate'], complexes['t23']],
                            [complexes['5']]),
            ReactionPathway('bind21', [complexes['2'], complexes['t23']],
                            [complexes['6']]),
            ReactionPathway('branch_3way', [complexes['gate']],
                            [complexes['2']]),
            ReactionPathway('branch_3way', [complexes['2']],
                            [complexes['gate']]),
            ReactionPathway('branch_3way', [complexes['6']],
                            [complexes['13'], complexes['12']]),
            ReactionPathway('branch_3way', [complexes['6']], [complexes['5']]),
            ReactionPathway('branch_3way', [complexes['5']],
                            [complexes['17']]),
            ReactionPathway('branch_3way', [complexes['5']], [complexes['6']]),
            ReactionPathway('branch_3way', [complexes['17']],
                            [complexes['21'], complexes['20']]),
            ReactionPathway('branch_3way', [complexes['17']],
                            [complexes['13'], complexes['12']]),
            ReactionPathway('branch_3way', [complexes['17']],
                            [complexes['5']]),
            ReactionPathway('branch_3way', [complexes['20']],
                            [complexes['26']]),
            ReactionPathway('branch_3way', [complexes['26']],
                            [complexes['20']]),
            ReactionPathway('open', [complexes['6']],
                            [complexes['2'], complexes['t23']]),
            ReactionPathway('open', [complexes['5']],
                            [complexes['gate'], complexes['t23']])
        }
        assert set(reactions) == set(enumerator.reactions)

        # Resting states
        resting_states = {
            '40': RestingState('40', [complexes['12']]),
            '42': RestingState('42', [complexes['13']]),
            '43': RestingState('43', [complexes['21']]),
            '41': RestingState('41', [complexes['26'], complexes['20']]),
            '38': RestingState('38', [complexes['2'], complexes['gate']]),
            '39': RestingState('39', [complexes['t23']])
        }
        assert set(resting_states.values()) == set(condensed['resting_states'])

        # Condensed Reactions
        condensed_reactions = {
            ReactionPathway('condensed',
                            [resting_states['38'], resting_states['39']],
                            [resting_states['43'], resting_states['41']]),
            ReactionPathway('condensed',
                            [resting_states['38'], resting_states['39']],
                            [resting_states['42'], resting_states['40']])
        }
        assert set(condensed_reactions) == set(condensed['reactions'])
コード例 #8
0
    def testCondenseGraph2(self):
        reactions = pluck(
            self.reactions,
            ['A->B', 'B->C', 'C->D', 'D->A', 'E->F', 'F->G', 'G->E', 'C+D->E'])
        complexes = pluck(self.complexes, ['A', 'B', 'C', 'D', 'E', 'F', 'G'])
        enum = Enum(complexes, reactions)
        out = condense_graph(enum)
        resting_states = out['resting_state_map']
        resting_state_targets = out['resting_state_targets']
        reactions = out['condensed_reactions']

        # Resting states:
        print "Resting states: "
        print str(resting_states)
        print

        resting_state_A = (RestingState('3', [
            self.complexes['A'], self.complexes['B'], self.complexes['C'],
            self.complexes['D']
        ]), )
        resting_state_E = (RestingState(
            '4',
            [self.complexes['E'], self.complexes['F'], self.complexes['G']]), )

        expected_resting_states = {
            frozenset([
                self.complexes['B'], self.complexes['C'], self.complexes['D'], self.complexes['A']
            ]):
            RestingState('3', [
                self.complexes['A'], self.complexes['B'], self.complexes['C'],
                self.complexes['D']
            ]),
            frozenset([
                self.complexes['G'], self.complexes['E'], self.complexes['F']
            ]):
            RestingState('4', [
                self.complexes['E'], self.complexes['F'], self.complexes['G']
            ])
        }
        assert resting_states == expected_resting_states

        # Resting state targets:
        print "Resting state targets: "
        print_dict(resting_state_targets)
        print

        print "Expected resting state targets: "
        expected_resting_state_targets = {
            self.complexes['A']: SetOfFates([resting_state_A]),
            self.complexes['B']: SetOfFates([resting_state_A]),
            self.complexes['C']: SetOfFates([resting_state_A]),
            self.complexes['D']: SetOfFates([resting_state_A]),
            self.complexes['E']: SetOfFates([resting_state_E]),
            self.complexes['F']: SetOfFates([resting_state_E]),
            self.complexes['G']: SetOfFates([resting_state_E]),
        }
        print_dict(expected_resting_state_targets)
        print

        assert_dict_eq(resting_state_targets, expected_resting_state_targets)
        assert resting_state_targets == expected_resting_state_targets

        # Reactions:
        print "Reactions: "
        print_set(reactions, fmt=repr)
        print "Expected Reactions: "
        expected_reactions = set([
            ReactionPathway('condensed',
                            [resting_state_A[0], resting_state_A[0]],
                            [resting_state_E[0]])
        ])
        print_set(expected_reactions, fmt=repr)
        print
        assert sorted(reactions) == sorted(expected_reactions)
コード例 #9
0
def condense_graph(enumerator, compute_rates=True, k_fast=0.0):
    """
    Condenses the reaction graph for the given `enumerator`.

    :param enumerator.Enumerator enumerator: The enumerator object to condense; `enumerate` must already have been called.
    :param bool compute_rates: True to compute rates for the condensed reactions; requires numpy and slows calculation.

    Returns an object of the following form::

        {
            'resting_states': list of resting states
            'resting_state_map': dict mapping names to resting states,
            'resting_state_targets': dict mapping each complex to its fate,
            'condensed_reactions': list of ReactionPathway objects representing the condensed reactions,
            'reactions': same as 'condensed_reactions'
         }

    """
    # Approach: compute SCCs using Tarjan's algorithm, including only fast
    # 1-1 reactions as edges. For each complex in the SCC, compute the
    # set of _fates_ of that complex (a fate is a multiset of resting states
    # that are reachable from that complex by fast reactions). For each
    # detailed reaction, generate one condensed reaction for each combination
    # of the fates of the products.
    #
    # Detailed description:
    # A fate of a complex is the multiset of resting states that can be reached
    # from the complex by a series of fast reactions. For instance, in the
    # network A -> B -> C, C -> D, D -> E + F where all reactions are fast,
    # there are three resting states: ^D = { D }, ^E = { E }, ^F = { F }, and
    # the complex A has two fates: { ^D } and { ^E, ^F }. We define F(x) for
    # some complex x to be the _set_ of fates of x; therefore
    # F(A) = { { ^D }, { ^E, ^F } }.
    #
    # Relatedly, for some reaction r = (A, (b1, b2, ...)) where A is the set of
    # reactants and B is the set of products, let the fate of the reaction
    # R(r) = F(b1) [+] F(b2) [+] ..., where [+] is the cartesian sum. Finally,
    # let S(x) be the SCC containing complex x. Note that S(x) may or may not
    # be a resting state.
    #
    # With these definitions, we can calculate F(x) by a recursion;
    #
    # F(x) = { { S(x) }            : if S(x) is a resting state
    #        { U_{r in R_o} R(r)   : if S(x) is not a resting state and R_o
    #                                represents the set of outgoing reactions
    #                                from S(x)
    #
    # Once we have calculated F(x) (which can be done by a DFS on the DAG formed
    # by the SCCs of the fast, detailed, 1-1 reactions---see the paper for
    # details on why this forms a DAG and therefore F(x) can be calculated in
    # finite time), the set of condensed reactions can be easily computed. We
    # generate one condensed reaction for each combination of fates of the
    # products.
    #
    # ------------------------------------------------------------------------

    # F(x) : Stores mapping between a complex x and the set of its possible
    # fates
    complex_fates = {}

    # Stores mappings between an SCC and the associated resting state
    resting_states = {}

    # S(x) : Stores mappings between each complex x and the SCC S(x) which
    # contains x
    SCC_containing = {}

    # Remembers which SCCs we've processed (those SCCs for which we've
    # computed the fates)
    processed_SCCs = set()

    # The following dicts map SCCs to the various matrices used to
    # calculate the condensed reaction rates:

    # For resting state SCCs:
    # stationary_distributions[SCC] =  s^ : maps complexes to stationary
    # probabilities
    stationary_distributions = {}

    # For transient SCCs:
    # exit_probabilities[SCC] = B : maps (incoming complex, outgoing reaction) tuples to exit
    # probabilities
    exit_probabilities = {}

    # decay_probabilities[(x,F)] = P( x decays to F ) = P( x -> F ), where F is a fate of
    # complex x
    decay_probabilities = collections.defaultdict(
        float)  # default to zero if no entry

    # reaction_decay_probabilities[(r,F)] = P( products of r decay to F ) = P( r -> F )
    # where F is a fate of reaction r
    reaction_decay_probabilities = collections.defaultdict(
        float)  # default to zero if no entry

    # Define helper functions for calculating condensed reaction rates
    def get_stationary_distribution(scc):
        scc_set = frozenset(scc)
        scc_list = sorted(scc)
        L = len(scc)

        # assign a numerical index to each complex
        complex_indices = {c: i for (i, c) in enumerate(scc_list)}

        # find all reactions between complexes in this SCC (non-outgoing 1,1
        # reactions)
        reactions = [
            r for c in scc for r in reactions_consuming[c]
            if (r.arity == (1, 1) and not is_outgoing(r, scc_set))
        ]

        # T is the transition rate matrix, defined as follows:
        # T_{ij} = { rate(j -> i)       if  i != j
        #          { - sum_{j'} T_{j'i} if  i == j
        T = np.zeros((L, L))

        # add transition rates for each reaction to T
        for r in reactions:
            # r : a -> b
            # T_{b,a} = rate(r : a -> b)
            a = r.reactants[0]
            b = r.products[0]
            T[complex_indices[b]][complex_indices[a]] = r.rate()

        T0 = np.copy(T)

        # compute diagonal elements of T
        T_diag = np.sum(T, axis=0)  # sum over columns
        for i in xrange(L):
            T[i][i] = -T_diag[i]

        # calculate eigenvalues
        (w, v) = np.linalg.eig(T)
        # w is array of eigenvalues
        # v is array of eigenvectors, where column v[:,i] is eigenvector
        # corresponding to the eigenvalue w[i].

        # find eigenvector corresponding to eigenvalue zero (or nearly 0)
        epsilon = 1e-5
        i = np.argmin(np.abs(w))
        if abs(w[i]) > epsilon:
            logging.warn((
                "Bad stationary distribution for resting state transition matrix. "
                +
                "Eigenvalue found %f has magnitude greater than epsilon = %f. "
                +
                "Markov chain may be periodic, or epsilon may be too high. Eigenvalues: %s"
            ) % (w(i), epsilon, str(w)))
        s = v[:, i]

        # check that the stationary distribution is good
        assert (s >= 0).all() or (s <= 0).all(
        ), "Stationary distribution of resting state complex should not be an eigenvector of mixed sign."
        s = s / np.sum(s)
        assert abs(
            np.sum(s) - 1
        ) < epsilon, "Stationary distribution of resting state complex should sum to 1 after normalization"

        # return dict mapping complexes to stationary probabilities
        return {c: s[i] for (c, i) in complex_indices.iteritems()}

    def get_exit_probabilities(scc):
        # build set and list of elements in SCC; assign a numerical index to
        # each complex
        scc_set = frozenset(scc)
        scc_list = sorted(scc)
        complex_indices = {c: i for (i, c) in enumerate(scc_list)}

        # find all fast reactions involving complexes in this SCC
        reactions = [
            r for c in scc for r in reactions_consuming[c] if is_fast(r)
        ]

        # sort reactions into internal and outgoing; assign a numerical index
        # to each reaction
        r_outgoing = sorted([r for r in reactions if is_outgoing(r, scc_set)])
        r_internal = sorted(
            [r for r in reactions if not is_outgoing(r, scc_set)])
        exit_indices = {r: i for (i, r) in enumerate(r_outgoing)}

        # L = # of complexes in SCC
        L = len(scc)

        # e = # of exit pathways
        e = len(r_outgoing)

        # add transition rates for each internal reaction
        T = np.zeros((L, L))
        for r in r_internal:
            a = r.reactants[0]
            b = r.products[0]
            T[complex_indices[a]][complex_indices[b]] = r.rate()

        # add transition rates for each outgoing reaction
        Te = np.zeros((L, e))
        for r in r_outgoing:
            a = r.reactants[0]
            Te[complex_indices[a]][exit_indices[r]] = r.rate()

        # the full transition matrix P_{L+e x L+e} would be
        #
        # P = ( Q_{LxL}  R_{Lxe}   )
        #     ( 0_{exL}  I_{exe}   )
        #
        # but we really only care about Q to calculate the fundamental matrix,
        # so we construct
        #
        # P = (T_{LxL} Te_{Lxe})
        P = np.hstack((T, Te))

        # then normalize P along each row, to get the overall transition
        # probabilities, e.g. P_ij = P(i -> j), where i,j in 0...L+e
        P = P / np.sum(P, 1)[:, np.newaxis]

        # extract the interior transition probabilities (Q_{LxL})
        Q = P[:, 0:L]

        # extract the exit probabilities (R_{Lxe})
        R = P[:, L:L + e]

        # calculate the fundamental matrix (N = (I_L - Q)^-1)
        N = np.linalg.inv(np.eye(L) - Q)

        # make sure all elements of fundamental matrix are >= 0
        assert (N >= 0).all()  # --- commented out by EW (temporarily)

        # calculate the absorption matrix (B = NR)
        B = np.dot(N, R)

        # --- added by EW as a weaker surrugate for the above, when necessary
        assert (B >= 0).all()

        # return dict mapping tuples of (incoming complex, outgoing reaction)
        # to exit probabilities
        return {(c, r): B[i, j]
                for (c, i) in complex_indices.iteritems()
                for (r, j) in exit_indices.iteritems()}

    # Define helper functions
    def is_fast(reaction):
        """
        Current heuristic to determine if reaction is fast: unimolecular in reactants
        AND rate constant > k_fast
        """
        # return (reaction.arity == (1,1) or reaction.arity == (1,2)) and
        # reaction.rate() > k_fast
        return (reaction.arity[0] == 1) and reaction.rate() > k_fast

    def get_fates(complex):
        """
        Returns the set of possible fates for this complex, calling
        compute_fates if necessary
        """
        if (complex not in complex_fates):
            compute_fates(SCC_containing[complex])
        return complex_fates[complex]

    def get_resting_state(complex):
        """
        Returns the resting state associated with this complex, if
        one exists.
        """
        scc = SCC_containing[complex]
        scc_set = frozenset(scc)
        if (scc_set not in resting_states):
            compute_fates(scc)
        return resting_states[scc_set]

    def calculate_reaction_decay_probabilities(r, fate, combinations=None):
        if combinations is None:
            combinations = cartesian_product(map(get_fates, r.products))

        for fates in (combination for combination in combinations
                      if tuple_sum(combination) == fate):

            # each combination (`fates`) that sums to `fate`
            # constitutes a possible way this reaction can
            # decay to `fate`, and therefore contributes to
            # P(r -> fate). This contribution is the joint
            # probability that each product `d` of r decays to
            # the corresponding fate `f` in `fates`.
            reaction_decay_probabilities[(r, fate)] += times(
                decay_probabilities[(d, f)]
                for (d, f) in zip(r.products, fates))

    def compute_fates(scc):
        """
        Processes a single SCC neighborhood, generating resting state multisets
        for each complex, and storing the mappings in the outer-scope
        `complex_fates` dict
        """

        # Convert to a set for fast lookup
        scc_set = frozenset(scc)

        # Check that we haven't been here already
        if (scc_set in processed_SCCs):
            return

        outgoing_reactions = [
            r for c in scc for r in reactions_consuming[c]
            if (is_fast(r) and is_outgoing(r, scc_set))
        ]

        # Remember that we've processed this neighborhood
        processed_SCCs.add(scc_set)

        # If this SCC is a resting state:
        if (len(outgoing_reactions) == 0):

            # build new resting state
            resting_state = RestingState(get_auto_name(), scc)
            resting_states[scc_set] = resting_state

            # calculate stationary distribution
            if compute_rates:
                stationary_distributions[
                    resting_state] = get_stationary_distribution(scc)

            # assign fate to each complex in the SCC
            fate = (resting_state, )
            fates = frozenset([fate])
            for c in scc:
                if c in complex_fates:
                    raise Exception()
                # frozenset([ (get_resting_state(c),) ])
                complex_fates[c] = 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
                decay_probabilities[(c, fate)] = 1.0

        # Otherwise, if there are outgoing fast reactions:
        else:

            # Compute all possible combinations of the fates of each product of
            # r
            reaction_fate_combinations = [
                cartesian_product(map(get_fates, r.products))
                for r in outgoing_reactions
            ]

            # Compute the fates of each of the outgoing reactions by summing each element above
            # This is a list, with each element corresponding to the frozenset
            # of fates for one reaction.
            reaction_fates = [
                sorted(map(tuple_sum_sort, combination))
                for combination in reaction_fate_combinations
            ]

            # note that these two are equivalent; the intermediate reaction_fate_combinations is only
            # calculated for the sake of the rates
            assert reaction_fates == [
                sorted(cartesian_sum(map(get_fates, r.products)))
                for r in outgoing_reactions
            ]

            if compute_rates:
                # calculate the exit probabilities
                exit_probabilities[scc_set] = get_exit_probabilities(scc)

                # calculate the probability of each fate
                # for each outgoing reaction, `r`
                for (i, r) in enumerate(outgoing_reactions):
                    # for each possible `fate` of that reaction `r`
                    for fate in reaction_fates[i]:

                        fate = tuple(sorted(fate))

                        # calculate the probability that the products of `r`
                        # decay to `fate`, e.g. P(r -> fate)

                        # not necessary since using defaultdict
                        # # initialize to zero
                        # if (r,fate) not in reaction_decay_probabilities:
                        #     reaction_decay_probabilities[(r,fate)] = 0

                        # iterate over all combinations of the fates of `r`
                        # that sum to equal `fate`

                        combinations = reaction_fate_combinations[i]
                        calculate_reaction_decay_probabilities(
                            r, fate, combinations)
                        # calculate_reaction_decay_probabilities(r,fate)

                        # the decay probability will be different for different
                        # complexes in the SCC
                        for c in scc:
                            # P(x decays to F) = P(SCC exits via r | SCC was
                            # entered via c) * P(r decays to F)

                            if (c, fate) not in decay_probabilities:
                                decay_probabilities[(c, fate)] = 0

                            decay_probabilities[(
                                c, fate)] += exit_probabilities[scc_set][
                                    (c, r)] * reaction_decay_probabilities[
                                        (r, fate)]

            # The set of fates for the complexes in this SCC is the union of
            # the fates for all outgoing reactions.

            # Note that frozenset().union(*X) === X[0] U X[1] U X[2] ...
            # where X[i] is a frozenset and a U b represents the union of
            # frozensets a and b
            set_of_fates = frozenset().union(*reaction_fates)

            for c in scc:
                if c in complex_fates:
                    raise Exception()
                complex_fates[c] = set_of_fates

    # Cache some things for speed
    reactions_consuming = get_reactions_consuming(enumerator.complexes,
                                                  enumerator.reactions)

    # Compute list of SCCs
    SCCs = tarjans(enumerator.complexes, enumerator.reactions,
                   reactions_consuming, is_fast)

    # Map each complex to the SCC which contains the complex (each complex
    # should be in 1 SCC)
    SCC_containing.update({c: scc for scc in SCCs for c in scc})

    # Generate resting state multisets by processing each SCC. Outer loop
    # guarantees that all SCCs will be processed even if DFS recursion
    # misses some
    for scc in SCCs:
        compute_fates(scc)

    # Map each complex to the set of multisets of resting states which it can
    # reach
    complex_fates = {
        complex: SetOfFates(complex_fates[complex])
        for complex in complex_fates
    }

    # Generate condensed reactions
    condensed_reactions = set()
    for reaction in enumerator.reactions:

        # Filter reactions which are fast (these have been handled by the
        # fates)
        if (is_fast(reaction)):
            continue

        # Filter reactions with no products/reactants
        if (len(reaction.reactants) == 0) and (len(reaction.products) == 0):
            continue

        reactant_fates = map(get_fates, reaction.reactants)
        product_fates = map(get_fates, reaction.products)

        # Take cartesian sum of reactant fates
        new_reactant_combinations = cartesian_sum(reactant_fates)

        # Take cartesian sum of reaction product fates to get all
        # combinations of resting states
        new_product_combinations = cartesian_sum(product_fates)

        # make sure each reactant is a resting state (F(xn) is a singleton for
        # each reactant xn)
        for (i, f_xn) in enumerate(reactant_fates):
            if (not f_xn.is_singleton()):
                logging.error(
                    "Cannot condense reaction %r: reactant %s has multiple fates: F(%s) = %s"
                    % (reaction, str(reaction.reactants[i]),
                       str(reaction.reactants[i]), str(f_xn)))
                raise Exception()

        # Now, we'll take all of the combinations of reactants and products
        new_reactant_product_combinations = it.product(
            new_reactant_combinations, new_product_combinations)

        # And generate new reactions with each combination which is not trivial
        # (reactants == products)
        for (reactants, products) in new_reactant_product_combinations:
            reactants = tuple(sorted(reactants))
            products = tuple(sorted(products))

            # Prune trivial reactions
            if (reactants != products):
                reaction = ReactionPathway('condensed', list(reactants),
                                           list(products))

                if compute_rates:

                    # calculate reaction rate by summing over all
                    # representative detailed reactions
                    detailed_reactions_consuming = set([
                        r for reactant in reactants for c in reactant.complexes
                        for r in reactions_consuming[c]
                    ])
                    reaction_rate = 0.0
                    for r in detailed_reactions_consuming:

                        # calculate the probability that this detailed reaction decays to the products
                        # P(r -> F), where F = products. Used in next step
                        calculate_reaction_decay_probabilities(r, products)

                        # probability that the products of this detailed reaction decay into fates
                        # that yield the condensed reaction
                        product_probability = reaction_decay_probabilities[(
                            r, products)]
                        assert product_probability >= 0

                        # probability that the resting states comprising the reactants of the condensed reaction will be in the right
                        # configuration to perform the detailed reaction
                        reactant_probabilities = times(
                            stationary_distributions[resting_states[frozenset(
                                SCC_containing[a])]][a] for a in r.reactants)
                        assert reactant_probabilities >= 0

                        # rate of the detailed reaction
                        k = r.rate()
                        assert k >= 0

                        # overall contribution of detailed reaction r to rate of the condensed reaction ^r =
                        # P(reactants of ^r are present as reactants of r) *
                        # k_r * P(products of r decay to products of ^r)
                        reaction_rate += reactant_probabilities * k * product_probability

                        if isinstance(reaction_rate, complex):
                            if reaction_rate.imag > 0:
                                logging.warn((
                                    "Detailed reaction %s contributes a complex rate of %f + %fj "
                                    + " to condensed reaction %s.") %
                                             (r, reaction_rate.real,
                                              reaction_rate.imag, reaction))

                    reaction._const = reaction_rate
                condensed_reactions.add(reaction)

    return {
        'resting_states':
        resting_states.values(),
        'resting_state_map':
        resting_states,
        'resting_state_targets':
        complex_fates,
        'condensed_reactions':
        list(condensed_reactions),
        'reactions':
        list(condensed_reactions),
        'complexes_to_resting_states':
        dict((c, rs) for rs in resting_states for c in rs)
    }
コード例 #10
0
ファイル: input.py プロジェクト: brezal/peppercornenumerator
def load_json(filename):
    """
    Loads a saved enumerator from a JSON output file at filename.
    """

    fin = open(filename, 'r')
    saved = json.load(fin)
    saved_domains = saved['domains']

    domains = {}
    for saved_domain in saved_domains:
        if 'sequence' not in saved_domain:
            saved_domain['sequence'] = None
        if (saved_domain['is_complement']):
            saved_domain['name'] = saved_domain['name'][:-1]
        new_dom = utils.Domain(saved_domain['name'],
                               saved_domain['length'],
                               is_complement=saved_domain['is_complement'],
                               sequence=saved_domain['sequence'])
        domains[new_dom.name] = new_dom

    saved_strands = saved['strands']

    strands = {}
    for saved_strand in saved_strands:
        doms = []
        for domain in saved_strand['domains']:
            doms.append(domains[domain])

        new_strand = utils.Strand(saved_strand['name'], doms)
        strands[saved_strand['name']] = new_strand

    complexes = {}
    resting_complexes = {}

    saved_resting_complexes = saved['resting_complexes']
    for saved_complex in saved_resting_complexes:
        c_strands = []
        for strand in saved_complex['strands']:
            c_strands.append(strands[strand])
        new_structure = []
        for strand in saved_complex['structure']:
            new_strand = []
            for tup in strand:
                if (tup is None):
                    new_strand.append(None)
                else:
                    new_strand.append(tuple(tup))
            new_structure.append(new_strand)
        new_complex = utils.Complex(saved_complex['name'], c_strands,
                                    new_structure)
        resting_complexes[saved_complex['name']] = new_complex
        complexes[saved_complex['name']] = new_complex

    transient_complexes = {}

    saved_transient_complexes = saved['transient_complexes']
    for saved_complex in saved_transient_complexes:
        c_strands = []
        for strand in saved_complex['strands']:
            c_strands.append(strands[strand])
        new_structure = []
        for strand in saved_complex['structure']:
            new_strand = []
            for tup in strand:
                if (tup is None):
                    new_strand.append(None)
                else:
                    new_strand.append(tuple(tup))
            new_structure.append(new_strand)
        new_complex = utils.Complex(saved_complex['name'], c_strands,
                                    new_structure)
        transient_complexes[saved_complex['name']] = new_complex
        complexes[saved_complex['name']] = new_complex

    saved_reactions = saved['reactions']

    reactions = []

    for saved_reaction in saved_reactions:
        reactants = []
        for reactant in saved_reaction['reactants']:
            reactants.append(complexes[reactant])
        products = []
        for product in saved_reaction['products']:
            products.append(complexes[product])

        reaction = ReactionPathway(saved_reaction['name'], reactants, products)
        reactions.append(reaction)

    resting_states = []

    for resting_state in saved['resting_states']:
        comps = []
        for complex in resting_state['complexes']:
            comps.append(complexes[complex])
        resting_states.append(utils.RestingState(resting_state['name'], comps))

    initial_complexes = {}
    for saved_complex in saved['initial_complexes']:
        c_strands = []
        for strand in saved_complex['strands']:
            c_strands.append(strands[strand])
        new_structure = []
        for strand in saved_complex['structure']:
            new_strand = []
            for tup in strand:
                if (tup is None):
                    new_strand.append(None)
                else:
                    new_strand.append(tuple(tup))
            new_structure.append(new_strand)
        new_complex = utils.Complex(saved_complex['name'], c_strands,
                                    new_structure)
        initial_complexes[saved_complex['name']] = new_complex

    enumerator = peppercornenumerator.Enumerator(domains.values(),
                                                 strands.values(),
                                                 initial_complexes.values())
    enumerator._complexes = complexes.values()
    enumerator._resting_states = resting_states
    enumerator._transient_complexes = transient_complexes.values()
    enumerator._resting_complexes = resting_complexes.values()
    enumerator._reactions = reactions

    return enumerator