{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "bed4637d-8e0c-42b4-9331-8a39ca2381a6",
   "metadata": {},
   "source": [
    "Copyright (c) 2025 Mitsuru Ohno  \n",
    "Use of this source code is governed by a BSD-3-style  \n",
    "license that can be found in the LICENSE file.  "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "911d53c0",
   "metadata": {},
   "source": [
    "## 当ノートブックのワークフロー  \n",
    "複数のデータフレームを横断的に処理する事例。3成分で相互平衡になっている。  \n",
    "1. 未知の速度定数を含む、csvに書き込んだ反応式を読み込む。  \n",
    "2. 化学種の濃度の経時変化の実験データを読み込む。初期濃度を変化させた複数の条件での実験結果を用い、全体を最適化できる速度定数を求めることを目標とする。  \n",
    "2. 化学種ごとの微分型の反応速度式を作成する。速度定数が未知の素反応にはシンボリックな変数が割り振られる、  \n",
    "3. 作成した微分方程式を、数値解析可能な形式にする  \n",
    "4.  scipy.optimize.minimizeを使い、化学種ごとの濃度の経時変化が、実験値と近づくように、未知の速度定数を求める。本ノートブックでは、0に近い速度定数を持つ素反応も含まれることを想定し、速度定数を対数処理してから最適化するモード（def run_fitの引数をuse_log_fit=Trueにする）を用いた。  \n",
    "5. 経時変化を図示する  \n",
    "\n",
    "残念ながら、df2の結果を再現できていない。複数のデータフレームを横断的に処理する実行例として掲載する。  "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f793b762-0cca-45f9-ad15-a0c092ac36e6",
   "metadata": {},
   "source": [
    "## 反応式を記載したcsvファイルを指定する  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "92b6caf1-fbe4-47c8-9bf7-15dc223b3b28",
   "metadata": {},
   "outputs": [],
   "source": [
    "file_path_rxn = './sample_data/ref5/sample_rxn_ref5.csv'  # CSVファイルのパスを指定"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95324112-077e-4f27-9529-2a86aaa179f2",
   "metadata": {},
   "source": [
    "## 反応速度式をscipy.integrate.solve_ivpで処理できる連立微分方程式にする  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "75e2680b-f29a-4109-8707-97deb1943716",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "from rxnfit import RxnODEbuild, SolverConfig, RxnODEsolver\n",
    "from rxnfit.expdata_reader import expdata_read\n",
    "from rxnfit.expdata_fit_sci import ExpDataFitSci\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e182c934",
   "metadata": {},
   "source": [
    "# 反応速度式の作成"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "aeb55f6f-83d3-4689-9921-c0f9c3c7216f",
   "metadata": {},
   "outputs": [],
   "source": [
    "builded_rxnode = RxnODEbuild(file_path_rxn)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "dc5a5d88-0b5d-4f67-bd1c-81209bae4d0a",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "number of species: 3\n",
      "unique species: ['oXy', 'pXy', 'mXy']\n",
      "rate constant: {'k1': k1, 'k2': k2, 'k3': k3, 'k4': k4, 'k5': k5, 'k6': k6}\n",
      "\n",
      "=== debug info ===\n",
      "Successfully created function for oXy with args: ['t', 'oXy', 'pXy', 'mXy']\n",
      "Successfully created function for pXy with args: ['t', 'oXy', 'pXy', 'mXy']\n",
      "Successfully created function for mXy with args: ['t', 'oXy', 'pXy', 'mXy']\n",
      "order of args: ['t', 'oXy', 'pXy', 'mXy']\n",
      "system of ODE: {'oXy': '-1/1*k1*oXy(t)+1/1*k2*pXy(t)-1/1*k3*oXy(t)+1/1*k4*mXy(t)', 'pXy': '1/1*k1*oXy(t)-1/1*k2*pXy(t)-1/1*k5*pXy(t)+1/1*k6*mXy(t)', 'mXy': '1/1*k3*oXy(t)-1/1*k4*mXy(t)+1/1*k5*pXy(t)-1/1*k6*mXy(t)'}\n"
     ]
    }
   ],
   "source": [
    "builded_rxnode.get_ode_info(debug_info=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "e98f2a67",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Successfully created function for oXy with args: ['t', 'oXy', 'pXy', 'mXy']\n",
      "Successfully created function for pXy with args: ['t', 'oXy', 'pXy', 'mXy']\n",
      "Successfully created function for mXy with args: ['t', 'oXy', 'pXy', 'mXy']\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[Eq(Derivative(oXy(t), t), -k1*oXy(t) + k2*pXy(t) - k3*oXy(t) + k4*mXy(t)),\n",
       " Eq(Derivative(pXy(t), t), k1*oXy(t) - k2*pXy(t) - k5*pXy(t) + k6*mXy(t)),\n",
       " Eq(Derivative(mXy(t), t), k3*oXy(t) - k4*mXy(t) + k5*pXy(t) - k6*mXy(t))]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 作成した微分方程式\n",
    "builded_rxnode.get_ode_system()[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "1cfb1b8a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'k1': k1, 'k2': k2, 'k3': k3, 'k4': k4, 'k5': k5, 'k6': k6}\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[sympy.core.symbol.Symbol,\n",
       " sympy.core.symbol.Symbol,\n",
       " sympy.core.symbol.Symbol,\n",
       " sympy.core.symbol.Symbol,\n",
       " sympy.core.symbol.Symbol,\n",
       " sympy.core.symbol.Symbol]"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 速度定数の確認\n",
    "print(builded_rxnode.rate_consts_dict)\n",
    "\n",
    "check_type = [v for v in builded_rxnode.rate_consts_dict.values()]\n",
    "[type(e) for e in check_type]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a0f9e382",
   "metadata": {},
   "source": [
    "## 経時変化の実験データを読み込み　　\n",
    "### データフレーム化  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "6d74af85",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[([array([ 0. ,  0.5,  1. ,  2. ,  3. ,  7. , 20. , 25. , 44. ]),\n",
       "   array([ 0. ,  0.5,  1. ,  2. ,  3. ,  7. , 20. , 25. , 44. ]),\n",
       "   array([ 0. ,  0.5,  1. ,  2. ,  3. ,  7. , 20. , 25. , 44. ])],\n",
       "  [array([0.999, 0.95 , 0.846, 0.738, 0.632, 0.434, 0.19 , 0.175, 0.187]),\n",
       "   array([0.   , 0.048, 0.148, 0.241, 0.328, 0.478, 0.632, 0.627, 0.602]),\n",
       "   array([0.001, 0.002, 0.006, 0.021, 0.04 , 0.088, 0.178, 0.198, 0.211])]),\n",
       " ([array([ 0,  1,  2,  3,  5,  8, 12, 22, 46]),\n",
       "   array([ 0,  1,  2,  3,  5,  8, 12, 22, 46]),\n",
       "   array([ 0,  1,  2,  3,  5,  8, 12, 22, 46])],\n",
       "  [array([0.005, 0.006, 0.026, 0.02 , 0.046, 0.089, 0.114, 0.144, 0.174]),\n",
       "   array([0.986, 0.973, 0.933, 0.918, 0.86 , 0.768, 0.709, 0.647, 0.609]),\n",
       "   array([0.009, 0.021, 0.041, 0.062, 0.094, 0.143, 0.177, 0.209, 0.217])]),\n",
       " ([array([ 0. ,  1. ,  1.5,  2.5,  4. ,  8. , 20. , 28. ]),\n",
       "   array([ 0. ,  1. ,  1.5,  2.5,  4. ,  8. , 20. , 28. ]),\n",
       "   array([ 0. ,  1. ,  1.5,  2.5,  4. ,  8. , 20. , 28. ])],\n",
       "  [array([0.001, 0.009, 0.027, 0.028, 0.038, 0.103, 0.151, 0.172]),\n",
       "   array([0.005, 0.307, 0.389, 0.476, 0.558, 0.628, 0.628, 0.612]),\n",
       "   array([0.993, 0.684, 0.584, 0.496, 0.404, 0.269, 0.221, 0.215])]),\n",
       " ([array([ 0.5,  1. ,  2. ,  3. ,  5. ,  7. , 23. ]),\n",
       "   array([ 0.5,  1. ,  2. ,  3. ,  5. ,  7. , 23. ]),\n",
       "   array([ 0.5,  1. ,  2. ,  3. ,  5. ,  7. , 23. ])],\n",
       "  [array([0.477, 0.441, 0.372, 0.331, 0.278, 0.25 , 0.177]),\n",
       "   array([0.09 , 0.156, 0.295, 0.401, 0.505, 0.547, 0.617]),\n",
       "   array([0.434, 0.402, 0.333, 0.288, 0.217, 0.203, 0.206])])]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df1 = pd.read_csv(f'./sample_data/ref5/ref5_df1.csv')\n",
    "df2 = pd.read_csv(f'./sample_data/ref5/ref5_df2.csv')\n",
    "df3 = pd.read_csv(f'./sample_data/ref5/ref5_df3.csv')\n",
    "df4 = pd.read_csv(f'./sample_data/ref5/ref5_df4.csv')\n",
    "\n",
    "# plot_datasets で名前指定する場合、df.attrs['name'] を設定（省略可）\n",
    "df1.attrs['name'] = 'df1'\n",
    "df2.attrs['name'] = 'df2'\n",
    "df3.attrs['name'] = 'df3'\n",
    "df4.attrs['name'] = 'df4'\n",
    "\n",
    "expdata_read([df1, df2, df3, df4])  # 複数データフレームのリストを渡すとまとめて読み込み可能"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6ef7e98f",
   "metadata": {},
   "source": [
    "### フィッティング  \n",
    "ExpDataFitSci でシンボリックな速度定数をフィッティング。  \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "a303dde2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Successfully created function for oXy with args: ['t', 'oXy', 'pXy', 'mXy']\n",
      "Successfully created function for pXy with args: ['t', 'oXy', 'pXy', 'mXy']\n",
      "Successfully created function for mXy with args: ['t', 'oXy', 'pXy', 'mXy']\n",
      "Optimization success: True\n",
      "Fitted rate constants:\n",
      "  k1 = 1e-06\n",
      "  k2 = 1e-06\n",
      "  k3 = 0.1\n",
      "  k4 = 0.1\n",
      "  k5 = 0.1\n",
      "  k6 = 0.1\n",
      "Residual sum of squares: 1e+15  R²: -1.50606e+14\n"
     ]
    }
   ],
   "source": [
    "builded_rxnode = RxnODEbuild(file_path_rxn)\n",
    "t_range = (float(df1.iloc[:, 0].min()), float(df1.iloc[:, 0].max()))\n",
    "fit_sci = ExpDataFitSci(builded_rxnode, [df1, df2, df3, df4], t_range)\n",
    "result, param_info, fit_metrics = fit_sci.run_fit(\n",
    "    p0=[1e-6, 1e-6, 1e-1, 1e-1, 1e-1, 1e-1], # 未知の速度定数の初期値  \n",
    "    use_log_fit=True, # k1, k2が0に近いと予想されるため、対数変換した値でフィットする  \n",
    "    lower_bound=1e-10,\n",
    ")  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "4627b275",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\mt_oh\\DataScience\\venvs\\rxnfit314_sphinx\\Lib\\site-packages\\IPython\\core\\interactiveshell.py:3747: UserWarning: Integration failed for dataset 2.\n",
      "  exec(code_obj, self.user_global_ns, self.user_ns)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  Failed DataFrame (dataset 2):\n",
      "   hr    oXy    mXy    pXy\n",
      "0   0  0.005  0.986  0.009\n",
      "1   1  0.006  0.973  0.021\n",
      "2   2  0.026  0.933  0.041\n",
      "3   3  0.020  0.918  0.062\n",
      "4   5  0.046  0.860  0.094\n",
      "\n",
      "=== Time-course plot ===\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 107368 characters]",
      "text/plain": [
       "<Figure size 1200x800 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "=== Concentration at the final time point ===\n",
      "df1:\n",
      "  oXy: 0.339460\n",
      "  mXy: 0.333333\n",
      "  pXy: 0.327208\n",
      "df3:\n",
      "  oXy: 0.326911\n",
      "  mXy: 0.332999\n",
      "  pXy: 0.339089\n",
      "df4:\n",
      "  oXy: 0.333944\n",
      "  mXy: 0.333666\n",
      "  pXy: 0.333389\n"
     ]
    }
   ],
   "source": [
    "# 経時変化を図示（plot_fitted_solution を使用）\n",
    "fit_sci.plot_fitted_solution(\n",
    "    expdata_df=[df1, df2, df3, df4], \n",
    "    plot_datasets=[\"df1\", \"df2\", \"df3\", \"df4\"], \n",
    "    species=['oXy', 'mXy', 'pXy'], \n",
    "    subplot_layout=(2,2)\n",
    ")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.14.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
