diff --git a/docs/examples/patchtst-fm.ipynb b/docs/examples/patchtst-fm.ipynb new file mode 100644 index 0000000..e5b9a05 --- /dev/null +++ b/docs/examples/patchtst-fm.ipynb @@ -0,0 +1,443 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "87cd2049", + "metadata": {}, + "source": [ + "# Using PatchTST-FM\n", + "\n", + "This is an example for using PatchTST-FM with the `timecopilot` library." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "45ab9d16", + "metadata": {}, + "outputs": [], + "source": [ + "import nest_asyncio\n", + "\n", + "nest_asyncio.apply()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "8a43f8f9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " See https://github.com/google-research/timesfm/blob/master/README.md for updated APIs.\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "from timecopilot import TimeCopilot, TimeCopilotForecaster\n", + "from timecopilot.models.foundation.patchtst_fm import PatchTSTFM" + ] + }, + { + "cell_type": "markdown", + "id": "57cfe7d0", + "metadata": {}, + "source": [ + "## Setup the model" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "4ddf583e", + "metadata": {}, + "outputs": [], + "source": [ + "model = PatchTSTFM()" + ] + }, + { + "cell_type": "markdown", + "id": "1e4c38f7", + "metadata": {}, + "source": [ + "## Create a TimeCopilot instance with PatchTST-FM" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cba895b1", + "metadata": {}, + "outputs": [], + "source": [ + "tc = TimeCopilot(\n", + " llm=\"openai:gpt-4o\",\n", + " forecasters=[\n", + " model\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "90b8ad9f", + "metadata": {}, + "source": [ + "## Forecasting \n", + "\n", + "Once setup is complete, you can generate forecasts with patchTST-FM." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "e6bd16a2", + "metadata": {}, + "outputs": [], + "source": [ + "df = pd.read_csv(\"https://timecopilot.s3.amazonaws.com/public/data/air_passengers.csv\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "5af63b5a", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:p-63174:t-8427712640:_client.py:_send_single_request:HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n", + "0it [00:00, ?it/s]INFO:p-63174:t-8427712640:modeling_patchtst_fm.py:forward:Context Len: 8192 | Forecast Len: 24 \n", + "100%|██████████| 1/1 [00:00<00:00, 2.24it/s]\n", + "1it [00:01, 1.84s/it]\n", + "1it [00:00, 253.82it/s]\n", + "INFO:p-63174:t-8427712640:_client.py:_send_single_request:HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n", + " 0%| | 0/1 [00:00\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
unique_iddsPatchTST-FM
0AirPassengers1961-01-01455.369843
1AirPassengers1961-02-01426.025330
2AirPassengers1961-03-01480.801575
3AirPassengers1961-04-01489.973755
4AirPassengers1961-05-01507.733276
5AirPassengers1961-06-01591.214844
6AirPassengers1961-07-01677.264404
7AirPassengers1961-08-01672.098755
8AirPassengers1961-09-01576.013306
9AirPassengers1961-10-01489.467194
10AirPassengers1961-11-01410.380615
11AirPassengers1961-12-01458.822571
12AirPassengers1962-01-01495.677673
13AirPassengers1962-02-01462.333984
14AirPassengers1962-03-01526.376099
15AirPassengers1962-04-01539.923218
16AirPassengers1962-05-01578.381042
17AirPassengers1962-06-01670.164917
18AirPassengers1962-07-01765.694946
19AirPassengers1962-08-01778.758301
20AirPassengers1962-09-01655.340515
21AirPassengers1962-10-01560.360107
22AirPassengers1962-11-01487.918152
23AirPassengers1962-12-01535.687012
\n", + "" + ], + "text/plain": [ + " unique_id ds PatchTST-FM\n", + "0 AirPassengers 1961-01-01 455.369843\n", + "1 AirPassengers 1961-02-01 426.025330\n", + "2 AirPassengers 1961-03-01 480.801575\n", + "3 AirPassengers 1961-04-01 489.973755\n", + "4 AirPassengers 1961-05-01 507.733276\n", + "5 AirPassengers 1961-06-01 591.214844\n", + "6 AirPassengers 1961-07-01 677.264404\n", + "7 AirPassengers 1961-08-01 672.098755\n", + "8 AirPassengers 1961-09-01 576.013306\n", + "9 AirPassengers 1961-10-01 489.467194\n", + "10 AirPassengers 1961-11-01 410.380615\n", + "11 AirPassengers 1961-12-01 458.822571\n", + "12 AirPassengers 1962-01-01 495.677673\n", + "13 AirPassengers 1962-02-01 462.333984\n", + "14 AirPassengers 1962-03-01 526.376099\n", + "15 AirPassengers 1962-04-01 539.923218\n", + "16 AirPassengers 1962-05-01 578.381042\n", + "17 AirPassengers 1962-06-01 670.164917\n", + "18 AirPassengers 1962-07-01 765.694946\n", + "19 AirPassengers 1962-08-01 778.758301\n", + "20 AirPassengers 1962-09-01 655.340515\n", + "21 AirPassengers 1962-10-01 560.360107\n", + "22 AirPassengers 1962-11-01 487.918152\n", + "23 AirPassengers 1962-12-01 535.687012" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "result.fcst_df" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "a0bdb36f", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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7IiIiAgkJSVVOMb//d//4YMPPlAhnVTLTZ8+XQVz+pBgTVp8SuXbHXfccc2WmDWha9euql3nsmXLcO+99yIqKgrbtm3DZ599htdff90gx2AFHhERERERERERERFROfH5hTiXmw+JA3reYP6dTogLK/CobpDqPWlXKW0phwwZgieffFJVtklgJT+/8cYb+O2338paS7q5uanwTFetJgHe6dOn1W2kKu22225D06ZNVbtLmU9X3ptvvomBAweibdu2eOGFF7Br1y7k5eXptU5pX/m///0P99xzDxo0aICbb74Z7733HuLj46v9GNx9993q99CdVqxYccVtJGyU3083B3DUqFFl1YKGwAo8IiIiIiIiIiIiIqKrVN+1d3aAm41+H6N3dHFU58czc1FcUgLLWqwGItPkaGmpKuGMdezKWrVqlQqrCgsLUVxcrIIxaYcpYd68efNw6tQpZGRkqKo3CdlycnJU68urOXLkiGpZKeHc9XTo0KFs29fXV50nJCQgMDBQrzVLADh79mxs2rQJe/fuxaJFi/DWW2+pajhp+3k9UvH3448/lv2sawsqpA2ntBC9fG3lSeWdhI5SlSgBnlQaGhIr8IiIiIiIiIiIiIiIqtE+U7RwtIedpQUyNcW4kFtQg6sjcyFVadLG0hinqrSTlCo7Cd7OnDmjZrt99913SExMxJgxY1TQJu0iZWadtIkUBQUF122/qQ8bG5sKj5eQ8LAyZDbenXfeiffffx9hYWHw8/NT2zcyd+5c9fvqTuVJJaHM7NOdZP7f1Y4rj82MGTNUoCkVgIbECjwiIiIiIiIiIiIionJ2p2Wr897u+rXPFDaWFmjj5IAjmTk4mpmDYEe7GlwhkeFJSCVhVXkS2EmgJnPqZBae0LXP1JE2mhqNpsJlUv0m99u6dWuFSraaZmtri2bNmiE7W/sevh4fHx91qg5poymtM59//nlVcWhIDPCIiIiIiIiIiIiIiEolFhTidI52BlfPSlTgiQ4u2gAvNCsX4xp61NAKiWqPBHrSUvOTTz7BLbfcgp07d6o2leXJbDxpP7lx40Z07NhRtdWUy6ZOnaoCLmktKZdfuHBBtce86667DLK2VatW4ZdffsHEiRPRsmVLNbtv5cqVWL16NRYvXozaMHLkSFWl6OrqavB9s4UmEREREREREREREVGpPaXVd22c7OGp5/w7nQ6lc/BCM3NqZG1EtU2Ctw8//BDvvPMO2rdvj59++knNwyuvT58+ap7chAkT4O3tjXfffVddvnDhQtxxxx145JFH0Lp1azzwwAN6Vcbpq23btiosfPrpp9GpUyf06tVLVQd+/fXXmDx5MmqDtP1s0KCBqvwz+L5LJJKs52ToopubG9LT02skJSUiIiIiIiIiIiIi8/C/0zH49mISpvs3wFstAyp132OZORh+4DTcra0Q1q99leaQkfl+/i9z0CIiIhAcHAx7e/saPRaZp8q8RliBR0RERERERERERERUandaljrvXcn2maKVkz1sLCyQVqRBdF5BDayOiOoLBnhERERERERERERERABSCosQlq2df9fL3anS97eztERrJ21VzbHMXIOvj4jqDwZ4REREREREREREREQA9pZW37VwtIO3rU2V9tHBxUGdh2YxwCOiqmOAR0RERERERERERESk2mdmV7l9pk6Ii2PZPDwioqpigEdEREREREREREREVG7+XZ9qBHi6CjxpoVlSUmKwtZH54PNOhnhtGDXACwoKgoWFxRWnWbNmqevz8vLUtpeXF5ydnTF+/HjEx8dX2EdUVBRGjx4NR0dH+Pj44Nlnn0VRUZGRfiMiIiIiIiIiIiIiMkfphUU4Xtr2sjoVeG2cHGBlASQXFiE2v9CAKyRTZ2Ojbbuak8PqS7q6goICdW5lZYUbsYYR7d+/HxqNpuzn48eP46abbsKdd96pfn7qqafwzz//4Pfff4ebmxseffRR3H777di5c6e6Xu4r4V2jRo2wa9cuxMbGYsqUKepN8tZbbxnt9yIiIiIiIiIiIiIi87I3PRtSG9PUwQ4N7ao2/044WFmipaM9wrLzVBWen72tQddJpktCGXd3dyQkJKifpfBIipaIRHFxMRITE9Xrwtra2rQDPG9v7wo/v/3222jWrBkGDhyI9PR0fPPNN/j5558xZMgQdf3ixYvRpk0b7NmzB7169cL69etx8uRJ/Pvvv2jYsCE6deqE119/Hc8//zxeffVV2NryDyMRERERERERERER6d8+s7e7U7X31cHFURvgZeVgpLebAVZH5kIKjoQuxCMqz9LSEoGBgXoFu0YN8C4vG/zxxx8xe/ZstfCDBw+isLAQw4YNK7tN69at1S+2e/duFeDJeUhIiArvdEaMGIGZM2fixIkT6Ny581WPlZ+fr046GRkZNfzbEREREREREREREZEp252Wrc57VaN9pk6IiwN+jQNCM7UtOan+kHzD19dXjfySjIOoPCk8kxBPHyYT4K1YsQJpaWm477771M9xcXHqF5Fy0/IkrJPrdLcpH97prtdddy3z5s3Da6+9VgO/BRERERERERERERGZm6wiDUKzcqo9/06ng7ODOj+WyVlo9bmdpj5zzoiuRb+YrxZIu8ybb74Zfn5+NX6sOXPmqBadulN0dHSNH5OIiIiIiIiIiIiITNO+9GxoSoDG9rYIMMDMunYuDpAGefEFRYjPZxUWEZlpgHfhwgU1x+7++++v0CdW2mpKVV558fHxZT1k5Vx+vvx63XXXYmdnB1dX1wonIiIiIiIiIiIiIqqfDDn/TjhZWaG5o53aZhUeEZltgLd48WLVD3b06NFll3Xt2hU2NjbYuHFj2WXh4eGIiopC79691c9yHhoaWmEY5IYNG1Qg17Zt21r+LYiIiIiIiIiIiIjIvAO86rfP1Ono4qjOQ7M4B4+IKs/oM/CKi4tVgDd16lRYW/+3HDc3N8yYMQOzZ8+Gp6enCuUee+wxFdr16tVL3Wb48OEqqJs8eTLeffddNffuxRdfxKxZs1SVHRERERERERERERHR9WRrNDhSWiXXx4ABXoiLA/6IT0VoJgM8IjLDAE9aZ0pV3fTp06+4bv78+bC0tMT48eORn5+PESNG4PPPPy+7XgZArlq1CjNnzlTBnpOTkwoC586dW8u/BRERERERERERERGZo4PpOSgqAfzsbBBogPl3OiHO2go8ttAkoqqwKCkpKUE9l5GRoSr+0tPTOQ+PiIiIiIiIiIiIqB5553ws5l+Ix/iGHvisbROD7TezSIMW20PV9vG+7dHA1uj1NPUSP/8nc8W/GEREREREREREREQmYkdqJh4Pi4KvnQ06uzqii6sTurg6oom9LSwsLIy9vDqpJubfCRdrKzR1sMP53Hwcz8rBIE+GR0SkPwZ4RERERERERERERCbi25gkXMovVKeDGdJ6MUld7mljhU4ujirU6+zqhJ5uTiogourJ1RTjkHqcJcBzMvj+O7g4qADvWGYuAzwiqhQGeEREREREREREREQmoKi4BNtTM9X2C8GNkFRYpMKl45m5SCnUYFNKpjqJhrbW2NOrLRysLI28avN2KCMbBSUl8LG1VtVyhhbi4ogVCWmcg0dElcYAj4iIiIiIiIiIiMgEHM7MQaamGB7WVnisSUNYlbbMLCguxomsPBzOyFaB3uqkdMQXFOFAejb6e7oYe9lmbVe59pk10aK0g7ODOpcKPCKiyuDXM4iIiIiIiIiIiIhMwJaUDHUuoZwuvBO2lpaqdeb0AG982rYJRjVwqxA+UdXtSNU+hv09aiYIDXHRBnhReQVIKyyqkWMQUd3EAI+IiIiIiIiIiIjIBGwtbY856AZhUh8PZ3W+kwFetWQVaXAwI1tt9y99TA3N3cYagfa2avt4FqvwiEh/DPCIiIiIiIiIiIiIjCy9dN6dGHCDtph93bVh0+GMHGRrNLWyvrpod1oWikqgArYmNTD/TqdDaRXeUbbRJKJKYIBHREREREREREREZGQ70rJQDKCFox0CSiu2rkUCJ387GxSWlGB/uraCjKrePnNADbXP1Ong4qjOQzO1AS0RkT4Y4BERERERERERERGZSPvMgTeovhMWFhboW9rycVdpCEWVty1V+5j396yZ9pk6Ic66CjwGeESkPwZ4REREREREREREREZUUlKCzboAT89qsL7u2ttxDl7VJOQXIiw7T233K30sa0oXV0dYAIjILUB8fmGNHouI6g4GeERERERERERERERGFJlbgOi8AthYWKBP6Xy7G+lTWoF3JDMHWUWcg1eVlqWivbMDvGyta/RYbjbW6ji6uXtERPpggEdERERERERERERkRFtLWzl2c3OEk7WVXvdpbG+rZuFpSoB9nINXadt17TNLg9Ca1rs0mN3FAI+I9MQAj4iIiIiIiIiIiMgE5t8N8nCt1P10c/DYRrPyLUu3lT7mA/RsWVpdvd2d1Dkr8IhIXwzwiIiIiIiIiIiIiIyksLgEO0qrwQZ6Vi5M6lta1bUzlaFQZcgsuov5hbC1sECP0mCtpvVyd1Zz8M7k5COxgHPwiOjGGOARERERERERERERGcnhjGxkaorhaWOFEBftnDR96eblHcvMQQbn4OltW2lg2lVallrp17K0ujxsrNHGyV5t705jy1MiujEGeERERERERERERERGsqVsFpsLrCykRkt/fva2CHawRTGAvWzNWOn5d7XVPlOHc/CIqDIY4BEREREREREREREZef5dZdtn6vR1196Pc/D0oykpKWs5WtsBXp/SmYWcg0dE+mCAR0RERERERERERGQEaYVFOJyRo7YH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", + "text/plain": [ + "
" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tcf = TimeCopilotForecaster(\n", + " models=[\n", + " model\n", + " ]\n", + ")\n", + "tcf.plot(df, result.fcst_df)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "timecopilot", + "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.11.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/mkdocs.yml b/mkdocs.yml index 0c84b22..f4df3da 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -30,6 +30,7 @@ nav: - examples/chronos-family.ipynb - examples/cryptocurrency-quickstart.ipynb - examples/sktime.ipynb + - examples/patchtst-fm.ipynb - Experiments: - experiments/gift-eval.md - experiments/fev.md diff --git a/pyproject.toml b/pyproject.toml index 6f672c9..4f29d8f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -88,7 +88,7 @@ dependencies = [ "tabpfn-time-series==1.0.3 ; python_full_version < '3.13'", "tensorboard>=2.20.0", "timecopilot-chronos-forecasting>=0.2.0", - "timecopilot-granite-tsfm>=0.1.1", + "timecopilot-granite-tsfm>=0.1.2", "timecopilot-timesfm>=0.2.1", "timecopilot-tirex>=0.1.0 ; python_full_version >= '3.11'", "timecopilot-toto>=0.1.5", diff --git a/tests/models/conftest.py b/tests/models/conftest.py index 7d6633e..ab97e62 100644 --- a/tests/models/conftest.py +++ b/tests/models/conftest.py @@ -6,6 +6,7 @@ from timecopilot.models.foundation.chronos import Chronos from timecopilot.models.foundation.flowstate import FlowState from timecopilot.models.foundation.moirai import Moirai +from timecopilot.models.foundation.patchtst_fm import PatchTSTFM from timecopilot.models.foundation.timesfm import TimesFM from timecopilot.models.foundation.toto import Toto from timecopilot.models.ml import AutoLGBM @@ -69,6 +70,9 @@ def disable_mps_session(monkeypatch): repo_id="ibm-granite/granite-timeseries-flowstate-r1", alias="FlowState-Granite", ), + PatchTSTFM( + context_length=2_048, + ), Toto(context_length=256, batch_size=2), Moirai( context_length=256, diff --git a/tests/models/test_models.py b/tests/models/test_models.py index bf9cb46..daa7ade 100644 --- a/tests/models/test_models.py +++ b/tests/models/test_models.py @@ -194,7 +194,7 @@ def test_using_quantiles(model): qs = [round(i * 0.1, 1) for i in range(1, 10)] df = generate_series(n_series=3, freq="D") if model.alias in ["AutoLGBM", "AutoNHITS", "AutoTFT"]: - # AutoLGBM does not support quantiles yet + # These models do not support quantiles yet with pytest.raises(ValueError) as excinfo: model.forecast( df=df, @@ -239,8 +239,13 @@ def test_using_quantiles(model): def test_using_level(model): level = [0, 20, 40, 60, 80] # corresponds to qs [0.1, 0.2, ..., 0.9] df = generate_series(n_series=2, freq="D") - if model.alias in ["AutoLGBM", "AutoNHITS", "AutoTFT"]: - # AutoLGBM does not support quantiles yet + if model.alias in [ + "AutoLGBM", + "AutoNHITS", + "AutoTFT", + "PatchTST-FM", + ]: + # These models do not support levels yet with pytest.raises(ValueError) as excinfo: model.forecast( df=df, diff --git a/timecopilot/models/foundation/flowstate.py b/timecopilot/models/foundation/flowstate.py index 5cb2b87..81f007c 100644 --- a/timecopilot/models/foundation/flowstate.py +++ b/timecopilot/models/foundation/flowstate.py @@ -3,16 +3,15 @@ import numpy as np import pandas as pd import torch -from gluonts.transform import LastValueImputation from tqdm import tqdm from tsfm_public import FlowStateForPrediction from tsfm_public.models.flowstate.utils.utils import get_fixed_factor -from ..utils.forecaster import Forecaster, QuantileConverter +from ..utils.forecaster import Forecaster, QuantileConverter, _DataProcessor from .utils import TimeSeriesDataset -class FlowState(Forecaster): +class FlowState(Forecaster, _DataProcessor): """ FlowState is the first time-scale adjustable Time Series Foundation Model (TSFM), open-sourced by IBM Research. Combining a State Space Model (SSM) Encoder with a @@ -99,49 +98,6 @@ def _get_model(self) -> FlowStateForPrediction: del model torch.cuda.empty_cache() - def _left_pad_and_stack_1D(self, tensors: list[torch.Tensor]) -> torch.Tensor: - max_len = max(len(c) for c in tensors) - padded = [] - for c in tensors: - assert isinstance(c, torch.Tensor) - assert c.ndim == 1 - padding = torch.full( - size=(max_len - len(c),), - fill_value=torch.nan, - device=c.device, - dtype=c.dtype, - ) - padded.append(torch.concat((padding, c), dim=-1)) - return torch.stack(padded) - - def _prepare_and_validate_context( - self, - context: list[torch.Tensor] | torch.Tensor, - ) -> torch.Tensor: - if isinstance(context, list): - context = self._left_pad_and_stack_1D(context) - assert isinstance(context, torch.Tensor) - if context.ndim == 1: - context = context.unsqueeze(0) - assert context.ndim == 2 - return context - - def _maybe_impute_missing(self, batch: torch.Tensor) -> torch.Tensor: - if torch.isnan(batch).any(): - batch = batch.float().numpy() - imputed_rows = [] - for i in range(batch.shape[0]): - row = batch[i] - imputed_row = LastValueImputation()(row) - imputed_rows.append(imputed_row) - batch = np.vstack(imputed_rows) - batch = torch.tensor( - batch, - dtype=self.dtype, - device=self.device, - ) - return batch - def _predict_batch( self, model: FlowStateForPrediction, diff --git a/timecopilot/models/foundation/patchtst_fm.py b/timecopilot/models/foundation/patchtst_fm.py new file mode 100644 index 0000000..744b39f --- /dev/null +++ b/timecopilot/models/foundation/patchtst_fm.py @@ -0,0 +1,275 @@ +from contextlib import contextmanager + +import numpy as np +import pandas as pd +import torch +from tqdm import tqdm +from tsfm_public import PatchTSTFMForPrediction + +from ..utils.forecaster import Forecaster, QuantileConverter, _DataProcessor +from .utils import TimeSeriesDataset + +# default to the median quantile +# PatchTST-FM supports quantiles from 0.01 to 0.99 +DEFAULT_QUANTILES = [0.5] + + +class PatchTSTFM(Forecaster, _DataProcessor): + """ + PatchTST-FM is a Time Series Foundation Model (TSFM) from IBM Research based on a + standard patch Transformer. This generic architecture achieves state-of-the-art + zero-shot forecasting performance with a straightforward training protocol. The + work provides a transparent, reproducible baseline with comprehensive ablations + on model scaling, data composition, and training techniques. + + See the [official repo](https://github.com/ibm-granite/granite-tsfm) and + [paper](https://arxiv.org/abs/2602.06909) for more details. + """ + + # NOTE: may want to adjust default context_length, default on granite_tsfm is 8192 + def __init__( + self, + repo_id: str = "ibm-research/patchtst-fm-r1", + # scale_factor: float | None = None, + context_length: int = 8192, # default from granite-tsfm + batch_size: int = 2_048, + alias: str = "PatchTST-FM", + ): + """ + Initialize PatchTSTFM time series foundation model. + + Args: + repo_id (str, optional): The Hugging Face Hub model ID or local path to + load the PatchTST-FM model from. Supported models: + + - `ibm-research/patchtst-fm-r1` + + context_length (int, optional): Maximum context length (input window size) + for the model. Controls how much history is used for each forecast. + Defaults to 8,192. The model supports flexible context lengths. + batch_size (int, optional): Batch size for inference. Defaults to 2,048. + Adjust based on available memory and model size. Larger batch sizes + can improve throughput but require more GPU memory. + alias (str, optional): Name to use for the model in output DataFrames and + logs. Defaults to "PatchTST-FM". + + Notes: + **Academic Reference:** + + - Paper: [Revisiting the Generic Transformer: Deconstructing a + Strong Baseline for Time Series Foundation Models]( + https://arxiv.org/abs/2602.06909) + + **Resources:** + + - GitHub: [ibm-granite/granite-tsfm](https://github.com/ibm-granite/granite-tsfm) + - HuggingFace Models: [ibm-research/patchtst-fm-r1](https://huggingface.co/ibm-research/patchtst-fm-r1) + + **Technical Details:** + + - The model is loaded onto the best available device (GPU if + available, otherwise CPU). + + **Supported Models:** + + - `ibm-research/patchtst-fm-r1` (default) + """ + self.repo_id = repo_id + # self.scale_factor = scale_factor + self.context_length = context_length + self.batch_size = batch_size + # NOTE: 'mps' may not be 100% reliable, initial tests with the + # patchtst-fm gift_eval notebook resulted in predictions of 0 across + # the board. for now use mps when available, change if it becomes an issue. + self.device = "cuda" if torch.cuda.is_available() else "cpu" + # self.device = ( + # "cuda" + # if torch.cuda.is_available() + # else ("mps" if torch.mps.is_available() else "cpu") + # ) + self.alias = alias + self.dtype = torch.float32 + + @contextmanager + def _get_model(self) -> PatchTSTFMForPrediction: + model = PatchTSTFMForPrediction.from_pretrained(self.repo_id).to(self.device) + try: + model.eval() + yield model + finally: + del model + if self.device.startswith("cuda"): + torch.cuda.empty_cache() + elif self.device.startswith("mps"): + torch.mps.empty_cache() + + def _predict_batch( + self, + model: PatchTSTFMForPrediction, + batch: list[torch.Tensor] | torch.Tensor, + h: int, + quantiles: list[float] | None, + # scale_factor: float, + ) -> tuple[np.ndarray, np.ndarray | None]: + context = self._prepare_and_validate_context(batch) + if context.shape[1] > self.context_length: + context = context[..., -self.context_length :] + context = self._maybe_impute_missing(context) + # context is (batch, context_length) + + # input data is grouped by id + # input shape: (id_group/batch, data) + # output shape: (batch/id, quantiles, h) + quantile_levels = DEFAULT_QUANTILES if quantiles is None else quantiles + + fcst = model( + context, + prediction_length=h, + quantile_levels=quantile_levels, + # scale_factor=scale_factor, + # batch_first=False, + ).quantile_predictions + fcst = fcst.squeeze(-1).transpose(-1, -2) # now shape is (batch, h, quantiles) + + # may not be the ideal solution, but this should be more adaptable + # when quantiles can vary. + # there is no guarantee that 0.5 will be in the list of quantiles. + fcst_mean = fcst.mean(dim=-1).squeeze() if fcst.ndim >= 3 else fcst.squeeze() + # fcst_mean = fcst[..., quantile_levels.index(0.5)].squeeze() + fcst_mean_np = fcst_mean.detach().cpu().numpy() + fcst_quantiles_np = ( + fcst.detach().cpu().numpy() if quantiles is not None else None + ) + return fcst_mean_np, fcst_quantiles_np + + def _predict( + self, + model: PatchTSTFMForPrediction, + dataset: TimeSeriesDataset, + h: int, + quantiles: list[float] | None, + # scale_factor: float, + ) -> tuple[np.ndarray, np.ndarray | None]: + fcsts = [ + self._predict_batch( + model, + batch, + h, + quantiles, + # scale_factor, + ) + for batch in tqdm(dataset) + ] # list of tuples + fcsts_mean_tp, fcsts_quantiles_tp = zip(*fcsts, strict=False) + # handle single item forecast output + fcsts_mean_np = fcsts_mean_tp[0] + if fcsts_mean_tp[0].shape != tuple(): + fcsts_mean_np = np.concatenate(fcsts_mean_tp) + if quantiles is not None: + fcsts_quantiles_np = np.concatenate(fcsts_quantiles_tp) + else: + fcsts_quantiles_np = None + return fcsts_mean_np, fcsts_quantiles_np + + def forecast( + self, + df: pd.DataFrame, + h: int, + freq: str | None = None, + level: list[int | float] | None = None, + quantiles: list[float] | None = None, + ) -> pd.DataFrame: + """Generate forecasts for time series data using the model. + + This method produces point forecasts and, optionally, prediction + intervals or quantile forecasts. The input DataFrame can contain one + or multiple time series in stacked (long) format. + + Args: + df (pd.DataFrame): + DataFrame containing the time series to forecast. It must + include as columns: + + - "unique_id": an ID column to distinguish multiple series. + - "ds": a time column indicating timestamps or periods. + - "y": a target column with the observed values. + + h (int): + Forecast horizon specifying how many future steps to predict. + freq (str, optional): + Frequency of the time series (e.g. "D" for daily, "M" for + monthly). See [Pandas frequency aliases](https://pandas.pydata.org/ + pandas-docs/stable/user_guide/timeseries.html#offset-aliases) for + valid values. If not provided, the frequency will be inferred + from the data. + level (list[int | float], optional): + Confidence levels for prediction intervals, expressed as + percentages (e.g. [80, 95]). If provided, the returned + DataFrame will include lower and upper interval columns for + each specified level. + quantiles (list[float], optional): + List of quantiles to forecast, expressed as floats between 0 + and 1. Should not be used simultaneously with `level`. When + provided, the output DataFrame will contain additional columns + named in the format "model-q-{percentile}", where {percentile} + = 100 × quantile value. + + Returns: + pd.DataFrame: + DataFrame containing forecast results. Includes: + + - point forecasts for each timestamp and series. + - prediction intervals if `level` is specified. + - quantile forecasts if `quantiles` is specified. + + For multi-series data, the output retains the same unique + identifiers as the input DataFrame. + """ + freq = self._maybe_infer_freq(df, freq) + # When support for levels is added remove PatchTST-FM + # from the list of models that throw this exception in + # tests/models/test_models:test_using_level() + if level is not None: + raise ValueError("Level is not supported for patchtst-fm yet.") + qc = QuantileConverter(level=level, quantiles=quantiles) + dataset = TimeSeriesDataset.from_df( + df, + batch_size=self.batch_size, + dtype=self.dtype, + ) + fcst_df = dataset.make_future_dataframe(h=h, freq=freq) + # scale_factor = self.scale_factor or get_fixed_factor(freq) + with self._get_model() as model: + cfg = model.config + supported_quantiles = cfg.quantile_levels + if qc.quantiles is not None and not set(qc.quantiles).issubset( + supported_quantiles + ): + raise ValueError( + "PatchTSTFM only supports the default quantiles, " + f"supported quantiles are {supported_quantiles}, " + f"quantiles provided are {qc.quantiles}, " + "please use the default quantiles or default level." + ) + + fcsts_mean_np, fcsts_quantiles_np = self._predict( + model, + dataset, + h, + quantiles=qc.quantiles, + # scale_factor=scale_factor, + ) + + fcst_df[self.alias] = fcsts_mean_np.reshape(-1) + + # should only enter when quantiles are used + if qc.quantiles is not None and fcsts_quantiles_np is not None: + for i, q in enumerate(qc.quantiles): + fcst_df[f"{self.alias}-q-{int(q * 100)}"] = fcsts_quantiles_np[ + ..., i + ].reshape(-1) + fcst_df = qc.maybe_convert_quantiles_to_level( + fcst_df, + models=[self.alias], + ) + return fcst_df diff --git a/timecopilot/models/foundation/sundial.py b/timecopilot/models/foundation/sundial.py index 3ee6a72..daf0baf 100644 --- a/timecopilot/models/foundation/sundial.py +++ b/timecopilot/models/foundation/sundial.py @@ -1,21 +1,21 @@ import sys from contextlib import contextmanager +from typing import TYPE_CHECKING -if sys.version_info >= (3, 13): +if sys.version_info >= (3, 13) and not TYPE_CHECKING: raise ImportError("Sundial requires Python < 3.13") import numpy as np import pandas as pd import torch -from gluonts.transform import LastValueImputation from tqdm import tqdm from transformers import AutoModelForCausalLM -from ..utils.forecaster import Forecaster, QuantileConverter +from ..utils.forecaster import Forecaster, QuantileConverter, _DataProcessor from .utils import TimeSeriesDataset -class Sundial(Forecaster): +class Sundial(Forecaster, _DataProcessor): """ Sundial is a family of generative time series foundation models, pre-trained on TimeBench (10^12 time points). It uses the TimeFlow Loss to @@ -91,50 +91,6 @@ def _get_model(self) -> AutoModelForCausalLM: del model torch.cuda.empty_cache() - def _left_pad_and_stack_1D(self, tensors: list[torch.Tensor]) -> torch.Tensor: - max_len = max(len(c) for c in tensors) - padded = [] - for c in tensors: - assert isinstance(c, torch.Tensor) - assert c.ndim == 1 - padding = torch.full( - size=(max_len - len(c),), - fill_value=torch.nan, - device=c.device, - dtype=c.dtype, - ) - padded.append(torch.concat((padding, c), dim=-1)) - return torch.stack(padded) - - def _prepare_and_validate_context( - self, - context: list[torch.Tensor] | torch.Tensor, - ) -> torch.Tensor: - if isinstance(context, list): - context = self._left_pad_and_stack_1D(context) - assert isinstance(context, torch.Tensor) - if context.ndim == 1: - context = context.unsqueeze(0) - assert context.ndim == 2 - - return context - - def _maybe_impute_missing(self, batch: torch.Tensor) -> torch.Tensor: - if torch.isnan(batch).any(): - batch = batch.float().numpy() - imputed_rows = [] - for i in range(batch.shape[0]): - row = batch[i] - imputed_row = LastValueImputation()(row) - imputed_rows.append(imputed_row) - batch = np.vstack(imputed_rows) - batch = torch.tensor( - batch, - dtype=self.dtype, - device=self.device, - ) - return batch - def _predict_batch( self, model: AutoModelForCausalLM, diff --git a/timecopilot/models/utils/forecaster.py b/timecopilot/models/utils/forecaster.py index 42322bb..276e785 100644 --- a/timecopilot/models/utils/forecaster.py +++ b/timecopilot/models/utils/forecaster.py @@ -2,6 +2,7 @@ import numpy as np import pandas as pd import plotly.graph_objects +import torch import utilsforecast.processing as ufp from gluonts.time_feature.seasonality import ( DEFAULT_SEASONALITIES, @@ -9,6 +10,7 @@ from gluonts.time_feature.seasonality import ( get_seasonality as _get_seasonality, ) +from gluonts.transform import LastValueImputation from prophet import Prophet as ProphetBase from scipy import stats from tqdm import tqdm @@ -614,3 +616,54 @@ def maybe_convert_quantiles_to_level( df = ufp.assign_columns(df, hi_tgt, df[hi_src]) out_cols.extend([lo_tgt, hi_tgt]) return df[out_cols] + + +class _DataProcessor: + def __init__(self, dtype: torch.dtype, device: torch.device) -> None: + self.dtype = dtype + self.device = device + + def _left_pad_and_stack_1D(self, tensors: list[torch.Tensor]) -> torch.Tensor: + max_len = max(len(c) for c in tensors) + padded = [] + for c in tensors: + assert isinstance(c, torch.Tensor) + assert c.ndim == 1 + padding = torch.full( + size=(max_len - len(c),), + fill_value=torch.nan, + device=c.device, + dtype=c.dtype, + ) + padded.append(torch.concat((padding, c), dim=-1)) + return torch.stack(padded) + + def _prepare_and_validate_context( + self, + context: list[torch.Tensor] | torch.Tensor, + ) -> torch.Tensor: + if isinstance(context, list): + context = self._left_pad_and_stack_1D(context) + assert isinstance(context, torch.Tensor) + if context.ndim == 1: + context = context.unsqueeze(0) + assert context.ndim == 2 + return context + + def _maybe_impute_missing( + self, batch: torch.Tensor, dtype=torch.float32 + ) -> torch.Tensor: + if torch.isnan(batch).any(): + batch = batch.to(dtype=dtype).detach().cpu().numpy() + imputed_rows = [] + for i in range(batch.shape[0]): + row = batch[i] + imputed_row = LastValueImputation()(row) + imputed_rows.append(imputed_row) + batch = np.vstack(imputed_rows) + batch = torch.tensor( + batch, + dtype=self.dtype, + device=self.device, + ) + return batch diff --git a/uv.lock b/uv.lock index 81ebecd..06773e7 100644 --- a/uv.lock +++ b/uv.lock @@ -6934,7 +6934,7 @@ requires-dist = [ {name = "statsforecast", specifier = ">=2.0.2"}, {name = "tensorboard", specifier = ">=2.20.0"}, {name = "timecopilot-chronos-forecasting", specifier = ">=0.2.0"}, - {name = "timecopilot-granite-tsfm", specifier = ">=0.1.1"}, + {name = "timecopilot-granite-tsfm", specifier = ">=0.1.2"}, {name = "timecopilot-timesfm", specifier = ">=0.2.1"}, {name = "timecopilot-toto", specifier = ">=0.1.5"}, {name = "torchmetrics", specifier = ">=1.8.2"}, @@ -6999,15 +6999,16 @@ dependencies = [ {marker = "python_full_version >= '3.13'", name = "scikit-learn", source = {registry = "https://pypi.org/simple"}, version = "1.7.2"}, {name = "datasets"}, {name = "deprecated"}, + {name = "einops"}, {name = "torch"}, {name = "urllib3"}, ] name = "timecopilot-granite-tsfm" -sdist = {hash = "sha256:2de37c562a3d680cc339ae9501d0955c8116c0cc2529b59d420b098a8961b429", size = 16593234, upload-time = "2025-10-08T16:33:46.143Z", url = "https://files.pythonhosted.org/packages/01/03/485fe233a13d63f53be40681a5fc84fef553ef4ce7ebcea4e8d6db553991/timecopilot_granite_tsfm-0.1.1.tar.gz"} +sdist = {hash = "sha256:bb415be3863468acec46d86086f53d944ea7a6717b1b14a1eb7038e3d5fffd79", size = 16607701, upload-time = "2026-02-27T19:20:57.794Z", url = "https://files.pythonhosted.org/packages/df/0d/ac0ee5d93c197e150928a3bf0f65217c8e5d8cb49432ea22bf29732be8e4/timecopilot_granite_tsfm-0.1.2.tar.gz"} source = {registry = "https://pypi.org/simple"} -version = "0.1.1" +version = "0.1.2" wheels = [ - {hash = "sha256:8967dc197373f5c1a7f72ae2f4669b21c4778d4e367c19c9e0c7f9033e0fbd85", size = 3814636, upload-time = "2025-10-08T16:33:42.611Z", url = "https://files.pythonhosted.org/packages/02/1a/66fe9f1e140c7fac0d8b9507696e739546cc89e87ff90bbd156cfa4a36d7/timecopilot_granite_tsfm-0.1.1-py3-none-any.whl"}, + {hash = "sha256:3daaa365bbe1b5183df2d132f0fca873fb7f1da5f5d881731a71d1c32e5cd526", size = 3825899, upload-time = "2026-02-27T19:20:54.214Z", url = "https://files.pythonhosted.org/packages/42/f4/0625d8f5720041318c6baca0da7a7ee73060cf62ac8673d968a63da19008/timecopilot_granite_tsfm-0.1.2-py3-none-any.whl"}, ] [[package]]