Cycle fluxes in receptor models

Cycle fluxes in receptor models#

UNDER CONSTRUCTION#

%%capture
%run receptor_tools.ipynb

As discusssed earlier earlier, the Markov chain tree theorem (also called Hill’s diagrammatic method) provides algebraic expressions for the steady-state probabilities of a receptor model. The function hill_diagramatic_method() below takes a symbolic generator matrix for such a Markov chain as input and, using the Markov chain tree theorem, calculates the steady-state probability of each state.

The receptor models considered here have state-transition diagrams with the topology of a simple symmetric directed graph (connected, no loops). The Markov chain tree theorem always applies to receptor models with these properties. However, there are Markov chains that can not be analyzed in this way (e.g., Markov chains with an infinite number of states).

Receptor model with topology of the House Graph#

Consider receptor model that has more than one cycle, namely, the house graph.

var('p0 p1 p2 p3 p4 a01 a10 a02 a20 a04 a40 a12 a21 a23 a32 a34 a43')
d = {0: {1:a01, 2:a02, 4:a04}, 1: {0:a10, 2:a12}, 2: {1:a21, 0:a20, 3:a23}, 3: {2:a32, 4:a34}, 4: {3:a43, 0:a40}};
G = DiGraph(d,weighted=True)
vertex_positions = {0: (0, 0), 1: (1, 1.41), 2: (2, 0), 3: (2,-2), 4: (0,-2)}
G.plot(figsize=8,edge_labels=True,pos=vertex_positions,graph_border=True)
_images/f6f957fd0d661c557d5e34de4ca9c066e51773938c68be31ed5abead0858deff.png
Q = generator(G.weighted_adjacency_matrix(sparse=False))
z = hill_diagramatic_method(Q)
for i in range(5):
    print('z[%s] ='%i, f'{expand(z[i])}')
z[0] = a10*a20*a32*a40 + a12*a20*a32*a40 + a10*a21*a32*a40 + a10*a20*a34*a40 + a12*a20*a34*a40 + a10*a21*a34*a40 + a10*a23*a34*a40 + a12*a23*a34*a40 + a10*a20*a32*a43 + a12*a20*a32*a43 + a10*a21*a32*a43
z[1] = a01*a20*a32*a40 + a01*a21*a32*a40 + a02*a21*a32*a40 + a01*a20*a34*a40 + a01*a21*a34*a40 + a02*a21*a34*a40 + a01*a23*a34*a40 + a01*a20*a32*a43 + a01*a21*a32*a43 + a02*a21*a32*a43 + a04*a21*a32*a43
z[2] = a02*a10*a32*a40 + a01*a12*a32*a40 + a02*a12*a32*a40 + a02*a10*a34*a40 + a01*a12*a34*a40 + a02*a12*a34*a40 + a02*a10*a32*a43 + a04*a10*a32*a43 + a01*a12*a32*a43 + a02*a12*a32*a43 + a04*a12*a32*a43
z[3] = a02*a10*a23*a40 + a01*a12*a23*a40 + a02*a12*a23*a40 + a04*a10*a20*a43 + a04*a12*a20*a43 + a04*a10*a21*a43 + a02*a10*a23*a43 + a04*a10*a23*a43 + a01*a12*a23*a43 + a02*a12*a23*a43 + a04*a12*a23*a43
z[4] = a04*a10*a20*a32 + a04*a12*a20*a32 + a04*a10*a21*a32 + a04*a10*a20*a34 + a04*a12*a20*a34 + a04*a10*a21*a34 + a02*a10*a23*a34 + a04*a10*a23*a34 + a01*a12*a23*a34 + a02*a12*a23*a34 + a04*a12*a23*a34

In this case, each of the five relative probabilities is a multinomial with 10 terms because there are 10 rooted spanning trees associated to each state (vertex). Each term has 4 factors because any spanning tree of the house graph has 4 edges.

def myHouseGraph():
    H = DiGraph()
    vertex_vars = ['a0','a1','a2','a3','a4']
    edge_vars = ['a','b','c','d','e','f','A','B','C','D','E','F']
    V=PolynomialRing(ZZ,names=vertex_vars,order='invlex')
    V.inject_variables()
    E=PolynomialRing(ZZ,names=edge_vars,order='lex')
    E.inject_variables()
    H.add_edge(a0,a1,E.gen(edge_vars.index('a')))
    H.add_edge(a1,a0,E.gen(edge_vars.index('A')))
    H.add_edge(a0,a2,E.gen(edge_vars.index('c')))
    H.add_edge(a2,a0,E.gen(edge_vars.index('C')))
    H.add_edge(a0,a4,E.gen(edge_vars.index('f')))
    H.add_edge(a4,a0,E.gen(edge_vars.index('F')))
    H.add_edge(a1,a2,E.gen(edge_vars.index('b')))
    H.add_edge(a2,a1,E.gen(edge_vars.index('B')))
    H.add_edge(a2,a3,E.gen(edge_vars.index('d')))
    H.add_edge(a3,a2,E.gen(edge_vars.index('D')))
    H.add_edge(a3,a4,E.gen(edge_vars.index('e')))
    H.add_edge(a4,a3,E.gen(edge_vars.index('E')))
    return H, E
H, P = myHouseGraph()
H.show(edge_labels=True,layout='spring')
Defining a0, a1, a2, a3, a4
Defining a, b, c, d, e, f, A, B, C, D, E, F
_images/1ed7f8f52a736a8b34a42079d5d580e182138bb79108df6e65e0d4fe4b331d9d.png
def tree_polynomial(G,P=None,root=None):
    if P==None:
        edge_vars = []
        f = 0
        #edge_coeff = []
        for e in G.edges(sort=True):
            f += e[2]
            #print(e,e[2].monomials()[0],list(e[2].monomials()[0]))
        print(f.variables())
        edge_vars = list(f.variables()) 
        #    edge_coeff.append(e[2]/e[2].monomials()[0])
        P = PolynomialRing(ZZ,names=edge_vars)
    Trees = []
    if G.is_directed():
        if root != None:
            T = P(0)
            for b in G.in_branchings(source=root,spanning=True):
                t = P(1)
                for e in b.edges(sort=True):
                    t *= P(e[2])
                T += t
            return T  
        else:  
            for r in G.vertices(sort=True):
                #print('r=',r)
                T = P(0)
                for b in G.in_branchings(source=r,spanning=True):
                    #b.show(figsize=10,edge_labels=true)
                    t = P(1)
                    for e in b.edges(sort=True):
                        #print(e)
                        #t *= P(e[2].monomials()[0])
                        t *= P(e[2])
                    T += t
                Trees.append(T)
                #return Trees
            return Trees
    else:
        T = 0
        for b in G.spanning_trees():
            t = 1
            for e in b.edges(sort=True):
                t *= G.edge_label(e[0],e[1])
            T += t
        return P(T) 
TT = tree_polynomial(H,P)
T = tree_polynomial(H,P,a0)
show(T)
\(\displaystyle b d e F + b e C F + b C D E + b C D F + d e A F + e A B F + e A C F + A B D E + A B D F + A C D E + A C D F\)
for v in H.vertices(sort=True):
    T = tree_polynomial(H,P,v)
    print(v,':',T)
a0 : b*d*e*F + b*e*C*F + b*C*D*E + b*C*D*F + d*e*A*F + e*A*B*F + e*A*C*F + A*B*D*E + A*B*D*F + A*C*D*E + A*C*D*F
a1 : a*d*e*F + a*e*B*F + a*e*C*F + a*B*D*E + a*B*D*F + a*C*D*E + a*C*D*F + c*e*B*F + c*B*D*E + c*B*D*F + f*B*D*E
a2 : a*b*e*F + a*b*D*E + a*b*D*F + b*c*e*F + b*c*D*E + b*c*D*F + b*f*D*E + c*e*A*F + c*A*D*E + c*A*D*F + f*A*D*E
a3 : a*b*d*E + a*b*d*F + b*c*d*E + b*c*d*F + b*d*f*E + b*f*C*E + c*d*A*E + c*d*A*F + d*f*A*E + f*A*B*E + f*A*C*E
a4 : a*b*d*e + b*c*d*e + b*d*e*f + b*e*f*C + b*f*C*D + c*d*e*A + d*e*f*A + e*f*A*B + e*f*A*C + f*A*B*D + f*A*C*D
def cycle_fluxes(G,P,verbose=True):
    Cvert3 = []  
    Cvert2 = [] 
    for C in G.all_simple_cycles():
        if len(C) > 3:
            Cvert3.append(C)
        else:
            Cvert2.append(C)
    Cvert2.sort()
    Cvert3Sort = Cvert3.copy()
    Cvert3Sort.sort()
    Cvert3SortP = Cvert3Sort.copy()
    for i in range(len(Cvert3Sort)/2):
        Cvert3SortP.remove(Cvert3SortP[i][::-1])
    Cvert3SortP2 = []
    for i in range(3,100): # arbitrary max cycle length 
        Cvert3SortP2 += [ C for C in Cvert3SortP if len(C)==i ]
    Cvert3SortP = Cvert3SortP2.copy()
    Cvert3SortM = []
    for i in range(len(Cvert3SortP)):  
        Cvert3SortM.append(Cvert3SortP[i][::-1])
    CvertP = Cvert3SortP + Cvert2
    CvertM = Cvert3SortM + Cvert2
    ncycles = len(CvertP)

    CedgeP = []
    CedgeM = []
    ThetaP = []
    ThetaM = []
    for i in range(ncycles):
        CPe = [ G.edge_label(CvertP[i][j],CvertP[i][j+1]) for j in range(len(CvertP[i])-1)]
        CMe = [ G.edge_label(CvertM[i][j],CvertM[i][j+1]) for j in range(len(CvertM[i])-1)]
        TPe = prod(CPe)
        TMe = prod(CMe)
        CedgeP.append(CPe)
        ThetaP.append(TPe)
        CedgeM.append(CMe)
        ThetaM.append(TMe)

    Tkappa = []
    ThetaPMTkappa = []
    for i in range(ncycles):
        CP=CvertP[i]
        #print('CP=',CP)
        H = G.copy()
        #show(H,edge_labels=True) ######
        H.allow_multiple_edges(True)
        #print([None]+CP)
        H.merge_vertices([None]+CP) # None becomes vertex 0 (as opposed to a0, a1, ...)
        if H.order():
            #show(H,edge_labels=True) ######
            T = tree_polynomial(H,P,root=0) 
        else:
            T = P(1)
        Tkappa.append(T)
        ThetaPMTkappa.append((ThetaP[i]-ThetaM[i])*Tkappa[i])

    if verbose:
        print('\ntotal of',len(Cvert2),'+ 2 *',len(Cvert3)/2,'cycles.\n') 
        for i in range(ncycles):
            H = G.copy()
            for e in H.edges(sort=True):
                H.set_edge_label(e[0],e[1],0)
            for j in range(len(CvertP[i])-1):
                H.set_edge_label(CvertP[i][j],CvertP[i][j+1],1)
                H.set_edge_label(CvertM[i][j],CvertM[i][j+1],-1)
            if ThetaP[i] == ThetaM[i]: 
                print(i,'=>',CvertP[i],'-',CvertM[i],'=>',CedgeP[i],'-',CedgeM[i],'=>',ThetaP[i]-ThetaM[i],'@ (',Tkappa[i],')')
            else:
                print(i,'=>',CvertP[i],'-',CvertM[i],'=>',CedgeP[i],'-',CedgeM[i],'=>',(ThetaP[i]-ThetaM[i]).factor(),'@ (',Tkappa[i],')')
            H.show(color_by_label=True,edge_labels=True)
            
    return ncycles, CedgeP, CedgeM, ThetaP, ThetaM, Tkappa, ThetaPMTkappa
Trees = tree_polynomial(H)
T = [0]*len(Trees)
for i in range(len(Trees)):
    T[i] = Trees[i]
    print('T[%s] = ' %(i),T[i])
TT = sum(T)
(a, b, c, d, e, f, A, B, C, D, E, F)
T[0] =  A*B*D*E + b*C*D*E + A*C*D*E + b*d*e*F + d*e*A*F + e*A*B*F + b*e*C*F + e*A*C*F + A*B*D*F + b*C*D*F + A*C*D*F
T[1] =  a*B*D*E + c*B*D*E + f*B*D*E + a*C*D*E + a*d*e*F + a*e*B*F + c*e*B*F + a*e*C*F + a*B*D*F + c*B*D*F + a*C*D*F
T[2] =  a*b*D*E + b*c*D*E + b*f*D*E + c*A*D*E + f*A*D*E + a*b*e*F + b*c*e*F + c*e*A*F + a*b*D*F + b*c*D*F + c*A*D*F
T[3] =  a*b*d*E + b*c*d*E + b*d*f*E + c*d*A*E + d*f*A*E + f*A*B*E + b*f*C*E + f*A*C*E + a*b*d*F + b*c*d*F + c*d*A*F
T[4] =  a*b*d*e + b*c*d*e + b*d*e*f + c*d*e*A + d*e*f*A + e*f*A*B + b*e*f*C + e*f*A*C + f*A*B*D + b*f*C*D + f*A*C*D
ncycles, CedgeP, CedgeM, ThetaP, ThetaM, Tkappa, ThetaPMTkappa = cycle_fluxes(H,P,verbose=True)
for i in range(ncycles):
    print('(',ThetaP[i],'-',ThetaM[i],') @ (',Tkappa[i],')')
total of 6 + 2 * 3 cycles.

0 => [a0, a1, a2, a0] - [a0, a2, a1, a0] => [a, b, C] - [c, B, A] => (-1) * (-a*b*C + c*A*B) @ ( e*F + D*E + D*F )
_images/091622cef14c26950d9d4dadf150c0e90679f39595b1a437a2bc2649945079fa.png
1 => [a0, a2, a3, a4, a0] - [a0, a4, a3, a2, a0] => [c, d, e, F] - [f, E, D, C] => (-1) * (-c*d*e*F + f*C*D*E) @ ( b + A )
_images/6f2704d21f6cb0c905eb9e50d7f34c0a61a3352ca022d82cbeb6923171929009.png
2 => [a0, a1, a2, a3, a4, a0] - [a0, a4, a3, a2, a1, a0] => [a, b, d, e, F] - [f, E, D, B, A] => (-1) * (-a*b*d*e*F + f*A*B*D*E) @ ( 1 )
_images/3707dbb7af5566355eec82defd4cb0e50f51087341dfdf67cdd982da74c1c86b.png
3 => [a0, a1, a0] - [a0, a1, a0] => [a, A] - [a, A] => 0 @ ( d*e*F + e*B*F + e*C*F + B*D*E + B*D*F + C*D*E + C*D*F )
_images/32e85e7a41e0793783183e970fb061b6a4ae400ffd0b48e39a565674ff1c3a1d.png
4 => [a0, a2, a0] - [a0, a2, a0] => [c, C] - [c, C] => 0 @ ( b*e*F + b*D*E + b*D*F + e*A*F + A*D*E + A*D*F )
_images/675b2d32af4bf2a2deca7b2851dca699e78c8398bcf40e4e201626b84b9964ba.png
5 => [a0, a4, a0] - [a0, a4, a0] => [f, F] - [f, F] => 0 @ ( b*d*e + b*e*C + b*C*D + d*e*A + e*A*B + e*A*C + A*B*D + A*C*D )
_images/a56a20314acda71b2e6079178f18d5511b25b6a2d1e2615a8cbf8a9484fd3ebe.png
6 => [a1, a2, a1] - [a1, a2, a1] => [b, B] - [b, B] => 0 @ ( a*e*F + a*D*E + a*D*F + c*e*F + c*D*E + c*D*F + f*D*E )
_images/78b8ac1cc79b9141b8d2eb8d393dfc114cc4acdf6eb4935f413be2488f58b215.png
7 => [a2, a3, a2] - [a2, a3, a2] => [d, D] - [d, D] => 0 @ ( a*b*E + a*b*F + b*c*E + b*c*F + b*f*E + c*A*E + c*A*F + f*A*E )
_images/2c97104039bbf53390f7a0f3a66d5b84032463282eaa2a378933b04440fa91ff.png
8 => [a4, a3, a4] - [a4, a3, a4] => [E, e] - [E, e] => 0 @ ( a*b*d + b*c*d + b*d*f + b*f*C + c*d*A + d*f*A + f*A*B + f*A*C )
_images/b5ce2d9027de0c1108295b65aace3e944efd26a2c2aa63f0b2cc13a2281b3a1e.png
( a*b*C - c*A*B ) @ ( e*F + D*E + D*F )
( c*d*e*F - f*C*D*E ) @ ( b + A )
( a*b*d*e*F - f*A*B*D*E ) @ ( 1 )
( a*A - a*A ) @ ( d*e*F + e*B*F + e*C*F + B*D*E + B*D*F + C*D*E + C*D*F )
( c*C - c*C ) @ ( b*e*F + b*D*E + b*D*F + e*A*F + A*D*E + A*D*F )
( f*F - f*F ) @ ( b*d*e + b*e*C + b*C*D + d*e*A + e*A*B + e*A*C + A*B*D + A*C*D )
( b*B - b*B ) @ ( a*e*F + a*D*E + a*D*F + c*e*F + c*D*E + c*D*F + f*D*E )
( d*D - d*D ) @ ( a*b*E + a*b*F + b*c*E + b*c*F + b*f*E + c*A*E + c*A*F + f*A*E )
( e*E - e*E ) @ ( a*b*d + b*c*d + b*d*f + b*f*C + c*d*A + d*f*A + f*A*B + f*A*C )