{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "cae010ce-7df5-4e2e-b89c-16ef35618b26",
   "metadata": {},
   "source": [
    "# Reaction Rates\n",
    "\n",
    "In this notebook, we will teach how to assign custom rates based on the meta-species states. \n",
    "Let us start with the basic definitions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "98a8ca7b-c908-494b-86d8-c2fac76dcd7f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<mobspy.modules.meta_class.Reactions at 0x7f8af8b7ec40>"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from mobspy import *\n",
    "\n",
    "Age, Mortal, Infectable, Virus = BaseSpecies()\n",
    "Replicator = New(Age)\n",
    "\n",
    "Age.young >> Age.old [1]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7329cac4-8f9f-45a0-aa10-19021f9000ff",
   "metadata": {},
   "source": [
    "For any meta-reaction if a number is returned MobsPy will automatically construct the reaction rate using mass-action kinetics if the model is deterministic and propensity expressions if the model is stochastic. The number returned will be the rate constant used to construct this reaction.\n",
    "\n",
    "We now define a replication reaction with a higher rate for old individuals. To archive this, one can pass a function as a rate instead of a number. One can also query for the characteristics of the reactants inside the function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "da60cebb-cd22-4ccc-8e43-5639175caf60",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<mobspy.modules.meta_class.Reactions at 0x7f8af8bb7640>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Replicator >> 2*Replicator.young [lambda r1: 2 if r1.old else 1]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "455b3040-4ec6-41e8-bf20-b70ac57c87e0",
   "metadata": {},
   "source": [
    "If a function is passed as a rate MobsPy will pass the meta-species states to the arguments of this function. In the replicator reaction above, the first argument of the function r1 will receive the first meta-species and if it is in the young state it will return the rate 2.\n",
    "\n",
    "To continue we will define three tipes of Cells - C1, C2, and C3 - and two types of viruses - V1, V2. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "55a45c34-4078-4121-bec6-a684fde13cad",
   "metadata": {},
   "outputs": [],
   "source": [
    "Cell = Infectable*Age*Mortal\n",
    "C1, C2, C3 = New(Cell)\n",
    "V1, V2 = New(Virus)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f08fa35c-a4ff-4aee-ae80-a3ed613ccd17",
   "metadata": {},
   "source": [
    "We now wish to define infection reaction with rates that depend on the type of Cell and Virus used. In this case the user has two options and we will list both here. \n",
    "The first one uses the method .is_a(meta species) which returns true if the reactant is or inherits from the given meta-species passed as an argument."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "208e7573-f40e-472c-9a2f-92562fa50cac",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The reactants C1_dot_not_infect_dot_old and V2 yield a rate constant of: 1.2100000000000002\n",
      "The reactants C1_dot_not_infect_dot_young and V2 yield a rate constant of: 1.2100000000000002\n",
      "The reactants C1_dot_not_infect_dot_old and V1 yield a rate constant of: 0.8800000000000001\n",
      "The reactants C1_dot_not_infect_dot_young and V1 yield a rate constant of: 0.8800000000000001\n",
      "The reactants C2_dot_not_infect_dot_old and V2 yield a rate constant of: 1.32\n",
      "The reactants C2_dot_not_infect_dot_young and V2 yield a rate constant of: 1.32\n",
      "The reactants C2_dot_not_infect_dot_old and V1 yield a rate constant of: 0.96\n",
      "The reactants C2_dot_not_infect_dot_young and V1 yield a rate constant of: 0.96\n",
      "The reactants C3_dot_not_infect_dot_old and V2 yield a rate constant of: 1.32\n",
      "The reactants C3_dot_not_infect_dot_young and V2 yield a rate constant of: 1.32\n",
      "The reactants C3_dot_not_infect_dot_old and V1 yield a rate constant of: 0.96\n",
      "The reactants C3_dot_not_infect_dot_young and V1 yield a rate constant of: 0.96\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Compiling model\n"
     ]
    }
   ],
   "source": [
    "infection_dict = {'C1':{'V1':1.1 , 'V2':1.3 , 'V3':1.7}, \n",
    "                  'C2':{'V1':1.4 , 'V2':1.1 , 'V3':1.9},\n",
    "                  'C3':{'V1':1.9 , 'V2':1.2 , 'V3':1.6}}    \n",
    "    \n",
    "def infection_rate_1(r1, r2): \n",
    "    factor = 1\n",
    "    if r1.is_a(C1):\n",
    "        factor = 1.1*factor\n",
    "    else:\n",
    "        factor = 1.2*factor\n",
    "    \n",
    "    if r2.is_a(V1):\n",
    "        factor = 0.8*factor\n",
    "    elif r2.is_a(V2):\n",
    "        factor = 1.1*factor\n",
    "    else:\n",
    "        factor = 1.2*factor\n",
    "    \n",
    "    print(f'The reactants {r1} and {r2} yield a rate constant of: {factor}')\n",
    "    return factor\n",
    "    \n",
    "\n",
    "Infectable.not_infect + Virus >> Infectable.infected [infection_rate_1]\n",
    "\n",
    "S_test = Simulation(C1 | C2 | C3 | V1 | V2)\n",
    "S_test.compile(verbose=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eaf8faf1-2846-461e-b853-5f85e91db0a2",
   "metadata": {},
   "source": [
    "Note that 'dot' replaces '.' in the provided names as sbml files do not accept '.' in the species names.\n",
    "\n",
    "Another method is to use the string form of the reactant states. As you might have seen from the provious code, the reactants take the form of Name_dot_characteristic1_dot_characteristic2. This is due to sbml syntax incompatibility with dots in the "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "5302997a-8195-46f1-abc9-49537adf5fd4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Reset reactions to remove previously added reaction\n",
    "#Infectable.reset_reactions()\n",
    "#Virus.reset_reactions()\n",
    "\n",
    "#infection_dict = {'C1':{'V1':1.1 , 'V2':1.3 , 'V3':1.7}, \n",
    "#                  'C2':{'V1':1.4 , 'V2':1.1 , 'V3':1.9},\n",
    "#                  'C3':{'V1':1.9 , 'V2':1.2 , 'V3':1.6}}\n",
    "\n",
    "\n",
    "# def infection_rate_2(r1, r2): \n",
    "#    rate = infection_dict[r1.get_name()][r2.get_name()]\n",
    "#    print(f'Reactants {r1} and {r2} yielded a rate of {rate}')\n",
    "#    return rate\n",
    "    \n",
    "\n",
    "#Infectable.not_infect + Virus >> Infectable.infected [infection_rate_2]\n",
    "\n",
    "#S_test = Simulation(C1 | C2 | C3 | V1 | V2)\n",
    "#S_test.compile(verbose=False)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "413474b5-eb34-411b-9947-3bcad377bdc6",
   "metadata": {},
   "source": [
    "If one wishes to assign rates other than mass action kinetics, one can pass a function that returns a string containing the rate expression to be used. For instance, in the model bellow we use the hill function as an activatior for the production of a meta-species P."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "c3ae8073-28da-4a54-a9a5-9b0c8619bb32",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Compiling model\n",
      "Starting Simulator\n",
      "Running simulation in parallel\n",
      "Simulation is Over\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 16624 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "A, P = BaseSpecies()\n",
    "\n",
    "Zero >> A [1]\n",
    "A >> A + P [lambda r1: f'{r1}/(1 + (10/{r1})^4)']\n",
    "\n",
    "S = Simulation(A | P)\n",
    "S.duration = 10\n",
    "S.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a2e2e53d-d6e0-493a-845f-61a7cc6ee1f0",
   "metadata": {},
   "source": [
    "As a final note, for custom string rates, MobsPy, like COPASI, only accepts meta-species which are part of the reactants of a reaction for the construction of the rate expression. If there is a species which is not a reactant in the rate expression the model will produce a compilation error."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2034da00-29b5-42ef-84ba-c951bebb83f6",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
