I am trying to solve a problem which may seem quite simple, although I have been unable to find any solution in the documentation or discussion forums. I am not a mathematician, so please bare with me.

Can Mathematica group together constant parameters in a function, such that the "true" number of parameters will be shown? The simplest example would be to assume we have the following function: $$f(x)=k_1 \times k_2 \times x$$ where $k_1$ and $k_2$ are constant parameters, and $x$ is the variable. The function $f(x)$ really has only one parameter, which we can see by grouping together the two parameters such that:

$$k_3=k_1 \times k_2$$

to get the function

$$g(x)=k_3 \times x$$

Both functions $f(x)$ and $g(x)$ are equivalent. This example is very simple, and can be done manually. However, I am working on deriving complex expressions of enzyme kinetics, which can be quite large and have many parameters. The aim is to simplify the expressions, and group together the constant parameters to help reduce the expressions to more "practical" forms. To give another simple but better example from a real-world application, let:

$$ v(s)=\frac{k_1 \times k_{cat} \times e \times s}{k_1 \times s + k_2 + k_{cat}} $$

where $k_1$. $k_2$ and $k_{cat}$ are constants pertaining to reaction rates in different catalytic steps of some chemical reaction, and $e$ is the concentration of enzyme (also constant). $v(s)$ is rate of reaction where a chemical substrate $s$ is consumed.

By dividing the numerator and denominator with $k_1$, we get:

$$ v(s)=\frac{k_{cat} \times e \times s}{s + \frac{k_2 + k_{cat}}{k_1} } $$

Now we can group the parameters. Let:

$$ v_{max} = k_{cat} \times e$$

$$ k_m = \frac{k_2 + k_{cat}}{k_1} $$

The result is an example of the most common and simplest known enzyme kinetic expression known as Michaelis-Menten, which has the form:

$$ v(s)=\frac{v_{max} \times s}{k_m + s} $$

We have reduced the number of constant parameters from 4 down to 2, and created an equivalent expression for the reaction rate $v(s)$ which is more readable and understandable by a human. Can something like this be automated or semi-automated in Mathematica or Maxima for much larger expressions?

Any help would be greatly appreciated.

  • Why not merely FullSimply[] applied to your function? – David G. Stork Jan 12 '16 at 0:34
  • I am going to keep my clothes on, but my answer in this question attempts to answer this problem. – wxffles Jan 12 '16 at 2:07
  • Thank you for your answers and comments. David, the fullsimplify function does not group together constants, which is the central issue. wxffles, your answer in this thread is indeed relevant and quite good. I will need to tweak it a little, as I am only interested in grouping the constants, while leaving the function variables untouched. Moreover, the groupings do not necessarily have to occur more than once. I will post my tweaks to your code in a few days if no one else beats me to it. Thanks – konrad Jan 12 '16 at 3:00
  • Konrad, please see mathematica.stackexchange.com/help/merging-accounts - you have created two accounts – Verbeia Jan 12 '16 at 4:43

Here is a small package providing GroupConstants function, that automatically replaces groups of constants with auto-generated single constants. It provides only partial answer to this question since it does not perform any algebraic simplifications, only structural replacements for groups of constants.

It works by parsing given expression and replacing sub-expressions, that don't contain (FreeQ) symbols considered as variables, with auto generated parameters (C). When a Flat function is encountered, constant expressions, that are its arguments, are grouped together and replaced with single constant.

BeginPackage["ConstantsGrouping`"];
Unprotect["`*"];
ClearAll["`*"]


GroupConstants::usage = "\
GroupConstants[expr, vars] \
returns a List containing two elements. First element is given expr \
with subexpressions, not containing given vars, replaced by constants. \
Each constant replaces largest possible subexpression. \
Second element of returned list is an Association of constants, used in \
returned expression, to subexpressions they replaced.";

SecondPass::usage = "\
SecondPass \
is an option for GroupConstants that specifies whether used parser should \
perform second pass, on expression, to normalize constants enumeration.";


Begin["`Private`"];
ClearAll["`*"]


constantQ[expr_] := FreeQ[expr, $variablePattern]


(* Return unchanged expression excluded from parsing
   (by default numeric expressions). *)
parse[excluded_ /; MatchQ[excluded, $excludedForm]] := excluded

(* Replace expression considered as constant with a generated parameter
   and associate this parameter with said expression. *)
parse[const_?constantQ] :=
    With[{generatedConst = $generatedParameters[++$constantsCounter, const]},
        $constants[generatedConst] = const;
        generatedConst
    ]

(* For Flat functions group all their constant arguments and replace 
   whole groups with single constants. *)
parse[(h_ /; MemberQ[Attributes[h], Flat])[args__]] :=
    h @@ Replace[
        If[MemberQ[Attributes[h], Orderless],
            GatherBy
        (* else *),
            SplitBy
        ][{args}, constantQ]
        ,
        {
            const_?constantQ :> parse[h @@ const],
            nonConst_ :> h @@ parse /@ nonConst
        }
        ,
        {1}
    ]

(* For non-Flat functions simply map parser to their arguments. *)
parse[h_[args__]] := parse /@ h[args]

(* Return any other expression unchanged. *)
parse[expr_] := expr


listToAlternatives[l_List] := Alternatives @@ l
listToAlternatives[expr_] := expr


Options[GroupConstants] = {
    ExcludedForms -> {_?NumericQ},
    GeneratedParameters -> (C[#] &),
    SecondPass -> False
};
GroupConstants[expr_, vars_, OptionsPattern[]] :=
    Block[
        {
            $variablePattern = listToAlternatives[vars],
        $excludedForm = listToAlternatives@OptionValue[ExcludedForms],
            $generatedParameters = OptionValue[GeneratedParameters],
        $constantsCounter = 0,
            $constants = <||>
    },
    If[TrueQ[OptionValue[SecondPass]],
        With[{firstPass = parse[expr], $oldConstants = $constants},
            $constantsCounter = 0;
                {parse[firstPass], $constants /. $oldConstants}
            ]
        (* else *),
            {parse[expr], $constants}
        ]
    ]


End[];
Protect["`*"];
EndPackage[];

Two simple usage examples:

testExpr =
    (2 a b x Log[y] z + 3 c + d + 4 Pi x^2 + E^(2 x))/(e y^(5 f g) + 5 h i x)

(* Replace constant groups with default parameters, treat x and y as variables.*)
GroupConstants[testExpr, {x, y}]

(* Replace constant groups with ki symbols, treat only x as a variable. *)
GroupConstants[testExpr, x, GeneratedParameters -> (Symbol["k" <> ToString[#]]&)]

Examples of constants grouping


Going back to examples from question.

We can compare "normalized" versions of two functions. To make sure that constants have the same enumeration we allow parser to make second pass over given expression. For functions from question we get:

{expr1, $} =
    GroupConstants[(kCat e s)/(s + (k2 + kCat)/k1), s, SecondPass -> True]
{referenceExpr, $} = GroupConstants[(vMax s)/(kM + s), s, SecondPass -> True]
expr1 == referenceExpr
(* {(s C[2])/(s + C[1]), <|C[1] -> (k2 + kCat)/k1, C[2] -> e kCat|>} *)
(* {(s C[2])/(s + C[1]), <|C[1] -> kM, C[2] -> vMax|>} *)
(* True *)

Above works because, from point of view of position of variable s and constants, those expressions have the same structure.

Comparison as above will fail if expressions have different structure (even if mathematically equivalent):

{expr2, $} =
    GroupConstants[(k1 kCat e s)/(k1 s + k2 + kCat), s, SecondPass -> True]
expr2 == referenceExpr
(* {(s C[3])/(C[1] + s C[2]), <|C[1] -> k2 + kCat, C[2] -> k1, C[3] -> e k1 kCat|>} *)
(* (s C[3])/(C[1] + s C[2]) == (s C[2])/(s + C[1]) *)

Nonetheless I hope that GroupConstants might be useful part of semi-automated simplifications.

Your ability to automate or semi-automate is a bit limited because any code essentially requires knowledge of the expected final state. In this case you know what you want but to generalise to other enzyme kinetics maybe not. Having said that here is a way to reproduce your manual derivation. I am using Symbolize below which does not cut and paste well so will paste images rather than code:

Step 1. you divide top and bottom by k1

enter image description here

Step 2. reorganise the denominator

enter image description here

Step 3. make the substitutions

enter image description here

Step 4. combine it all

enter image description here

Generally speaking the sort of functions you are likely to need include Simplify, FullSimplify, Apart, Together, Expand, TrigExpand, PowerExpand and probably some others that don't immediately come to mind.

I would say that not only a universal approach of such replacements does not exists, but if it would, it would not give you advantage. The reason is that in the enzyme kinetics each such replacement has some physical sense. When you do one of them you simultaneously keep in mind, what this or that combination of rates means, and is it reasonable to choose such.

On the other hand situative replacements that one does depending upon the form of the expression at hand is of course possible and easy. One example is given above by Mike Honeychurch. Here I would like to add a simple approach to his one. Try the following.

Here is your reaction speed:

v = (k1*kc*e*s)/(k1*s + k2 + kc);

Introducing an intermediate rate k3=(k2+kc)/k1 makes the job. Than we substitute it back:

    (v /. k2 + kc -> k1*k3 // Simplify) /. k3 -> (k2 + kc)/k1

(*  (e kc s)/((k2 + kc)/k1 + s)  *)

This approach is not really better than that of Mike, it is just the question of a personal taste.

Have fun!

FullSimplify[(k e s)/(s + (k2 + kcat)/k1)]

$\frac{e\ k\ \text{k1} s}{\text{k1} s + \text{k2} + \text{kcat}}$

Of course you may need to change one of the variables, so you cannot multiply out the variables ahead of time.

I am not sure if Mathematica can do what you asked, but it can certainly help in defining the kinetic constants by picking out relevant parts of the rate law, and in providing a very important check that the rate-constant and kinetic-constant forms of the rate law agree.

The following may not provide an answer to your question at all.

If the rate law in kinetic-constant form is as follows:

derived rate law

Such a 'raw' form of the rate law might arise, for example, by application of the steady-state assumption to an enzyme reaction mechanism.

It is now required to convert this rate law to a more useful kinetic-constant form by the judicious definition of kinetic constants.

Let's define $k_{cat}$ as the velocity when substrate concentration is infinity, for unit enzyme concentration ($e_o$). (The usual case, it seems to me.)

kcat definition

$k_3$

(It is easier to 'drive' the substrate concentration to infinity by first taking the reciprocal of the rate law)

For more complex mechanisms, the definition of $k_{cat}$ will not be so simple (and then Mathematica becomes very, very useful).

Let's define Km (the Michealis Constant) as follows.

enter image description here

enter image description here

The important thing here is not the definition as such, but the easy at which Mathematica provides the expression by picking out parts of the rate law or coefficients. There are probably more logical ways to define Km.

Let's propose the following form of the Michaelis-Menten equation as the kinetic-constant form of the rate law:

enter image description here

Do the two forms of the rate law agree?

enter image description here

True

But of course

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.