sign_vector_conditions.unique_existence

Existence and uniqueness of equilibria

Let us consider the following matrices to describe a chemical reaction network:

sage: S = matrix([[1, 0, -1, 0], [0, 1, 0, -1]])
sage: S
[ 1  0 -1  0]
[ 0  1  0 -1]
sage: St = matrix([[1, 0, -1, 1], [0, 1, -1, 0]])
sage: St
[ 1  0 -1  1]
[ 0  1 -1  0]

To check whether a unique equilibrium exists, we apply condition_uniqueness_minors():

sage: from sign_vector_conditions import *
sage: condition_uniqueness_minors(S, St)
True

This means that the chemical reaction network has at most one equilibrium. Next, we verify whether an equilibrium exists. First, we check the face condition. For this purpose, we compute the cocircuits of the oriented matroids corresponding to the matrices:

sage: from sign_vectors.oriented_matroids import *
sage: cc1 = cocircuits_from_matrix(S, dual=True)
sage: cc1
{(+0+0), (0-0-), (-0-0), (0+0+)}
sage: cc2 = cocircuits_from_matrix(St, dual=True)
sage: cc2
{(+++0), (0+++), (+00-), (-00+), (0---), (---0)}

Here, we are only interested in the positive cocircuits:

sage: cc1p = [X for X in cc1 if X > 0]
sage: cc1p
[(+0+0), (0+0+)]
sage: cc2p = [X for X in cc2 if X > 0]
sage: cc2p
[(+++0), (0+++)]

Since every sign vector in cc2p has a smaller element in cc1p, the face condition is satisfied. There is also a function in the package that can be used directly to check whether this condition is fulfilled:

sage: condition_faces(S, St)
True

We need to check a third condition to verify surjectivity. For this purpose, we consider again the oriented matroid determined by S:

sage: covectors_from_matrix(S, dual=False)
{(0000), (+--+), (++--), (-0+0), (0-0+), (--++), (0+0-), (-++-), (+0-0)}

Since there are no nonnegative covectors, the chemical reaction network has at least one equilibrium. The package offers a function to check this condition condition:

sage: condition_nondegenerate(S, St)
True

Hence, the chemical reaction network has a unique equilibrium.

Let us consider another example. We swap the two matrices from before:

sage: S, St = St, S

Because of symmetry, there is at most one equilibrium:

sage: condition_uniqueness_sign_vectors(S, St)
True

Now, we check the face condition:

sage: cc1 = cocircuits_from_matrix(S, dual=True)
sage: cc1
{(+++0), (0+++), (+00-), (-00+), (0---), (---0)}
sage: cc2 = cocircuits_from_matrix(St, dual=True)
sage: cc2
{(+0+0), (0-0-), (-0-0), (0+0+)}

Again, we are only interested in the positive cocircuits:

sage: cc1p = [X for X in cc1 if X > 0]
sage: cc1p
[(+++0), (0+++)]
sage: cc2p = [X for X in cc2 if X > 0]
sage: cc2p
[(+0+0), (0+0+)]

Therefore, the condition does not hold. We also apply the corresponding function from the package:

sage: condition_faces(S, St)
False

Consequently, there exists no unique equilibrium.

Now, we consider Example 20 from [MHR19]. Here, we have a parameter a > 0. Depending on this parameter, the chemical reaction network has a unique equilibrium:

sage: var('a')
a
sage: W = matrix(3, 6, [0, 0, 1, 1, -1, 0, 1, -1, 0, 0, 0, -1, 0, 0, 1, -1, 0, 0])
sage: W
[ 0  0  1  1 -1  0]
[ 1 -1  0  0  0 -1]
[ 0  0  1 -1  0  0]
sage: Wt = matrix(3, 6, [1, 1, 0, 0, -1, a, 1, -1, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0])
sage: Wt
[ 1  1  0  0 -1  a]
[ 1 -1  0  0  0  0]
[ 0  0  1 -1  0  0]
sage: S = W.right_kernel_matrix()
sage: S
[1 1 0 0 0 0]
[0 0 1 1 2 0]
[1 0 0 0 0 1]
sage: from elementary_vectors.functions import kernel_matrix_using_elementary_vectors
sage: St = kernel_matrix_using_elementary_vectors(Wt) # prevents division by ``a``
sage: St
[-1 -1  0  0 -2  0]
[ 0  0  1  1  0  0]
[ 0  0  0  0  a  1]

The first two conditions depend on the sign vectors of the corresponding oriented matroids. Consequently, the choice of the positive parameter a does not affect the result:

sage: assume(a > 0)
sage: condition_uniqueness_sign_vectors(S, St)
True

Hence, there exists at most one equilibrium. Also the face condition is satisfied:

sage: condition_faces(S, St)
True

For specific values of a, the pair of subspaces determined by kernels of the matrices is nondegenerate. This is the case for \(a \in (0, 1) \cup (1, 2)\):

sage: condition_nondegenerate(S, St(a=1/2))
True
sage: condition_nondegenerate(S, St(a=3/2))
True

On the other hand, this condition does not hold if \(a \in {1} \cup [2, \infty)\):

sage: condition_nondegenerate(S, St(a=1))
False

To certify the result, we call:

sage: condition_degenerate(S, St(a=1), certify=True)
(True, (1, 1, 0, 0, -1, 1))

Hence, the positive support of the vector v = (1, 1, 0, 0, -1, 1) of St can be covered by a sign vector (++000+) corresponding to ker(S). Further, v does not satisfy the support condition.

sage: condition_nondegenerate(S, St(a=2)) False sage: condition_nondegenerate(S, St(a=3)) False

Functions

condition_degenerate(stoichiometric_matrix, ...)

Return whether a pair of subspaces given by matrices is degenerate.

condition_faces(stoichiometric_matrix, ...)

Condition on positive sign vectors for existence and uniqueness of equilibria

condition_nondegenerate(...)

Return whether a pair of subspaces given by matrices is nondegenerate.

sign_vector_conditions.unique_existence.condition_degenerate(stoichiometric_matrix, kinetic_order_matrix, certify: bool = False) bool

Return whether a pair of subspaces given by matrices is degenerate.

This condition is about whether all positive equal components of a vector in St can be covered by covectors corresponding to the kernel of S.

INPUT:

  • stoichiometric_matrix – a matrix

  • kinetic_order_matrix – a matrix

  • certify – a boolean (default: False)

OUTPUT: a boolean

If certify is true, a list is returned to certify the result. (see the examples)

EXAMPLES:

sage: from sign_vector_conditions.unique_existence import *

Next, we certify our results. In the first examples, the subspaces are trivially nondegenerate since there are no nonnegative covectors in the kernel of S:

sage: S = matrix([[1, 0, -1, 0], [0, 1, 0, -1]])
sage: St = matrix([[1, 0, 0, 1], [0, 1, 0, 1]])
sage: condition_degenerate(S, St, certify=True)
(False, 'no nonnegative covectors')

Here, we have a pair of degenerate subspaces:

sage: S = matrix([[1, 1, 0]])
sage: St = matrix([[0, 0, 1]])
sage: condition_degenerate(S, St, certify=True)
(True, (1, 1, 0))

The resulting vector lies in the row space of St. The nonnegative covector (++0) in the kernel of S covers the first two equal components.

In the following, we have another example for nondegenerate subspaces:

sage: S = matrix([[1, 0, 0, 1, -1], [0, 1, 0, 1, -1], [0, 0, 1, 0, 1]])
sage: S
[ 1  0  0  1 -1]
[ 0  1  0  1 -1]
[ 0  0  1  0  1]
sage: St = matrix([[1, 0, 0, 1, -1], [0, 1, 0, 1, -1], [0, 0, 1, 0, -1]])
sage: St
[ 1  0  0  1 -1]
[ 0  1  0  1 -1]
[ 0  0  1  0 -1]
sage: condition_degenerate(S, St, certify=True)
(False, ([[[0, 2, 3]], [[1, 2, 3]]], [[[2, 4]]], []))

The certificate tells us that there is no vector in the row space of St with positive support on the components 0, 2, 3 and 1, 2, 3. Positive equal components can partially be covered by a covector (00+0+) which corresponds to [[2, 4]]. However, it is impossible to fully cover the positive support.

In the next example, there exists a partial cover:

sage: S = matrix([[1, -1, 0, 0], [0, 0, 1, 1]])
sage: St = matrix([[1, 0, 0, 1], [0, 1, 0, 1]])
sage: condition_degenerate(S, St, certify=True)
(False, ([], [[[2, 3]]], [[[[2, 3]], [(--++)]]]))

In fact, a vector in St with equal positive components on [2, 3] corresponding to (--++) can be fully covered by covectors. However, this vector would not satisfy the support condition.

sign_vector_conditions.unique_existence.condition_faces(stoichiometric_matrix, kinetic_order_matrix) bool

Condition on positive sign vectors for existence and uniqueness of equilibria

INPUT:

  • stoichiometric_matrix – a matrix

  • kinetic_order_matrix – a matrix

OUTPUT: TODO Return whether every positive sign vector X corresponding to the rows of St has a positive sign vector Y corresponding to the rows of S such that Y <= X.

Return a boolean.

EXAMPLES:

sage: from sign_vector_conditions.unique_existence import condition_faces
sage: S = matrix([[1, 0, -1, 0], [0, 1, 0, -1]])
sage: S
[ 1  0 -1  0]
[ 0  1  0 -1]
sage: St = matrix([[1, 0, -1, 1], [0, 1, -1, 0]])
sage: St
[ 1  0 -1  1]
[ 0  1 -1  0]
sage: condition_faces(S, St)
True
sign_vector_conditions.unique_existence.condition_nondegenerate(stoichiometric_matrix, kinetic_order_matrix) bool

Return whether a pair of subspaces given by matrices is nondegenerate.

INPUT:

  • stoichiometric_matrix – a matrix

  • kinetic_order_matrix – a matrix

OUTPUT: a boolean