zincware/ZnTrack

View on GitHub
examples/docs/03_dependencies.ipynb

Summary

Maintainability
Test Coverage
{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a781fb6d",
   "metadata": {},
   "source": [
    "# Dependencies\n",
    "\n",
    "For ZnTrack there are two different ways to set up dependencies:\n",
    "\n",
    "1. Node based dependencies\n",
    "2. File based dependencies\n",
    "\n",
    "## Node dependencies\n",
    "We will first look at Node based dependencies starting from a RandomNumber `Hello World` example.\n",
    "In our first stage we create a random number and then we add another Node that depends on this one.\n",
    "We can do this very easily by using `zn.deps`.\n",
    "\n",
    "This allows us to access all properties of the `dependency` attribute."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "2e8ff1cd-7967-4c0e-8f77-32c8a0ecd94b",
   "metadata": {},
   "outputs": [],
   "source": [
    "import zntrack\n",
    "from random import randrange\n",
    "from pathlib import Path"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "9d8da8c5-f05d-4832-804c-0ad78cdfb851",
   "metadata": {},
   "outputs": [],
   "source": [
    "zntrack.config.nb_name = \"03_dependencies.ipynb\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "a9842607-b54f-46bb-a708-269566dc0fbc",
   "metadata": {
    "nbsphinx": "hidden",
    "tags": []
   },
   "outputs": [],
   "source": [
    "from zntrack.utils import cwd_temp_dir\n",
    "\n",
    "temp_dir = cwd_temp_dir()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "71c2830e-c74a-4e49-84da-4cd9d8985423",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Initialized empty Git repository in /tmp/tmpazvjbvbl/.git/\n",
      "Initialized DVC repository.\n",
      "\n",
      "You can now commit the changes to git.\n",
      "\n",
      "+---------------------------------------------------------------------+\n",
      "|                                                                     |\n",
      "|        DVC has enabled anonymous aggregate usage analytics.         |\n",
      "|     Read the analytics documentation (and how to opt-out) here:     |\n",
      "|             <https://dvc.org/doc/user-guide/analytics>              |\n",
      "|                                                                     |\n",
      "+---------------------------------------------------------------------+\n",
      "\n",
      "What's next?\n",
      "------------\n",
      "- Check out the documentation: <https://dvc.org/doc>\n",
      "- Get help and share ideas: <https://dvc.org/chat>\n",
      "- Star us on GitHub: <https://github.com/iterative/dvc>\n"
     ]
    }
   ],
   "source": [
    "!git init\n",
    "\n",
    "!dvc init"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "410e335c-c62b-4bda-932d-db97c3a84b2c",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "class RandomNumber(zntrack.Node):\n",
    "    maximum = zntrack.zn.params()\n",
    "    number = zntrack.zn.outs()\n",
    "\n",
    "    def run(self):\n",
    "        self.number = float(randrange(self.maximum))\n",
    "\n",
    "\n",
    "class ComputePower(zntrack.Node):\n",
    "    random_number: RandomNumber = zntrack.zn.deps()\n",
    "    number = zntrack.zn.outs()\n",
    "    power = zntrack.zn.params()\n",
    "\n",
    "    def run(self):\n",
    "        self.number = self.random_number.number**self.power"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "e6f37308-a942-467d-94f6-1e6edfe7b317",
   "metadata": {},
   "source": [
    "We can now create the stages the usual way and look at the outcomes.\n",
    "This will create the following graph for us:\n",
    "\n",
    "\n",
    "[![](https://mermaid.ink/img/pako:eNpVjLEKwkAQRH8lTG0KLa-wMbWI2rkWa25jAtm7cO4RJOTfPQQLu2Hem1nQRi9w6MY4tz0nq64NBbJQ6u2NcIg6ZZNTnCUR7j-0K-jMwUc9Zn38o7ref9fYQCUpD778LxSqimC9qBBciV46zqMRKKxF5Wzx8g4tnKUsG-TJs0kz8DOxwnU8vmT9ACo6PUg?type=png)](https://mermaid.live/edit#pako:eNpVjLEKwkAQRH8lTG0KLa-wMbWI2rkWa25jAtm7cO4RJOTfPQQLu2Hem1nQRi9w6MY4tz0nq64NBbJQ6u2NcIg6ZZNTnCUR7j-0K-jMwUc9Zn38o7ref9fYQCUpD778LxSqimC9qBBciV46zqMRKKxF5Wzx8g4tnKUsG-TJs0kz8DOxwnU8vmT9ACo6PUg)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "00353190-29c3-4954-a9b5-aec358b35fa9",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Running DVC command: 'stage add --name RandomNumber --force ...'\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Creating 'dvc.yaml'\n",
      "Adding stage 'RandomNumber' in 'dvc.yaml'\n",
      "\n",
      "To track the changes with git, run:\n",
      "\n",
      "\tgit add nodes/RandomNumber/.gitignore dvc.yaml\n",
      "\n",
      "To enable auto staging, run:\n",
      "\n",
      "\tdvc config core.autostage true\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Jupyter support is an experimental feature! Please save your notebook before running this command!\n",
      "Submit issues to https://github.com/zincware/ZnTrack.\n",
      "[NbConvertApp] Converting notebook 03_dependencies.ipynb to script\n",
      "/data/fzills/miniconda3/envs/zntrack/lib/python3.10/site-packages/nbformat/__init__.py:93: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
      "  validate(nb)\n",
      "[NbConvertApp] Writing 6203 bytes to 03_dependencies.py\n",
      "Running DVC command: 'stage add --name ComputePower --force ...'\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Adding stage 'ComputePower' in 'dvc.yaml'\n",
      "\n",
      "To track the changes with git, run:\n",
      "\n",
      "\tgit add nodes/ComputePower/.gitignore dvc.yaml\n",
      "\n",
      "To enable auto staging, run:\n",
      "\n",
      "\tdvc config core.autostage true\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[NbConvertApp] Converting notebook 03_dependencies.ipynb to script\n",
      "/data/fzills/miniconda3/envs/zntrack/lib/python3.10/site-packages/nbformat/__init__.py:93: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
      "  validate(nb)\n",
      "[NbConvertApp] Writing 6203 bytes to 03_dependencies.py\n"
     ]
    }
   ],
   "source": [
    "with zntrack.Project() as project:\n",
    "    random_number = RandomNumber(maximum=16)\n",
    "    compute_power = ComputePower(random_number=random_number, power=2.0)\n",
    "project.run(repro=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "a1928386-539a-4698-81a1-e68e843e1415",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Running stage 'RandomNumber':                                         \n",
      "> zntrack run src.RandomNumber.RandomNumber --name RandomNumber\n",
      "Generating lock file 'dvc.lock'                                                 \n",
      "Updating lock file 'dvc.lock'\n",
      "\n",
      "Running stage 'ComputePower':\n",
      "> zntrack run src.ComputePower.ComputePower --name ComputePower\n",
      "Updating lock file 'dvc.lock'                                                   \n",
      "\n",
      "To track the changes with git, run:\n",
      "\n",
      "\tgit add dvc.lock\n",
      "\n",
      "To enable auto staging, run:\n",
      "\n",
      "\tdvc config core.autostage true\n",
      "Use `dvc push` to send your updates to remote storage.\n"
     ]
    }
   ],
   "source": [
    "!dvc repro"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "7d235eca-8834-4e31-ad4e-51f83a80a625",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3.0 ^ 2.0 = 9.0\n"
     ]
    }
   ],
   "source": [
    "random_number.load()\n",
    "compute_power.load()\n",
    "print(f\"{random_number.number} ^ {compute_power.power} = {compute_power.number}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c07bbdc0-cbaf-4b5e-afce-47c07dfbd0f1",
   "metadata": {},
   "source": [
    "## File dependencies\n",
    "The second approach for specifying dependencies in ZnTrack is to depend on files.\n",
    "This is useful when our pipeline requires output files from a previous stage, or when we want to track the changes in an input file.\n",
    "To create a file dependency, we first create a file from our random number.\n",
    "We then use the path to that file as our dependency.\n",
    "Setting a file dependency is simple and can be done by passing ``pathlib.Path`` or ``str`` to the ``dvc.deps`` method.\n",
    "Like other ``dvc.<...>`` attributes, it also supports lists:\n",
    "```py\n",
    "dependency: Path = dvc.deps([Path('some_file.txt'), 'some_other_file.txt'])\n",
    "```\n",
    "\n",
    "<div class=\"alert alert-info\">\n",
    "Info: Node working directory\n",
    "\n",
    "It is recommended to store files created by a node in the node's working directory (nwd), which is located at ``./nodes/<nodename>``.\n",
    "You can access the nwd using ``zntrack.nwd``. Here's an example:\n",
    "\n",
    "```python\n",
    "file: Path = dvc.outs(zntrack.nwd / \"random_number.txt\")\n",
    "```\n",
    "</div>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "8c062896-ca2c-4a8f-a15c-294ee3866855",
   "metadata": {},
   "outputs": [],
   "source": [
    "# zntrack: break\n",
    "\n",
    "\n",
    "class WriteToFile(zntrack.Node):\n",
    "    random_number: RandomNumber = zntrack.zn.deps()\n",
    "    file: Path = zntrack.dvc.outs(zntrack.nwd / \"random_number.txt\")\n",
    "\n",
    "    def run(self):\n",
    "        self.file.write_text(str(self.random_number.number))\n",
    "\n",
    "\n",
    "class PowerFromFile(zntrack.Node):\n",
    "    file: Path = zntrack.zn.deps()\n",
    "    number = zntrack.zn.outs()\n",
    "    power = zntrack.zn.params(2)\n",
    "\n",
    "    def run(self):\n",
    "        number = float(self.file.read_text())\n",
    "        self.number = number**self.power\n",
    "\n",
    "\n",
    "class ComparePowers(zntrack.Node):\n",
    "    power_deps = zntrack.zn.deps()\n",
    "\n",
    "    def run(self):\n",
    "        assert self.power_deps[0].number == self.power_deps[1].number"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "Let us create the stages and look at the graph."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "4b3d10e8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "NodeView((UUID('fcedbb2b-2f78-4e30-9d73-8663d88f83aa'), UUID('3b6008c4-a1bb-4fdf-9ce0-389c429fe4bf'), UUID('fc4b13b3-dd53-4347-8af1-3b8c6ca75b2a'), UUID('afdb6202-8db2-4f1d-a379-4af819846aee'), UUID('908d7dd4-405d-4da4-a40f-9cb323f5f0d2'), UUID('4d70c58b-8b22-44b6-aa97-43528094e209')))"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "project.nodes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "1eefb12a-65de-4c9b-a05a-b9827915b619",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Running DVC command: 'stage add --name RandomNumber --force ...'\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Modifying stage 'RandomNumber' in 'dvc.yaml'\n",
      "\n",
      "To track the changes with git, run:\n",
      "\n",
      "\tgit add dvc.yaml\n",
      "\n",
      "To enable auto staging, run:\n",
      "\n",
      "\tdvc config core.autostage true\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[NbConvertApp] Converting notebook 03_dependencies.ipynb to script\n",
      "/data/fzills/miniconda3/envs/zntrack/lib/python3.10/site-packages/nbformat/__init__.py:93: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
      "  validate(nb)\n",
      "[NbConvertApp] Writing 6203 bytes to 03_dependencies.py\n",
      "Running DVC command: 'stage add --name ComputePower --force ...'\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Modifying stage 'ComputePower' in 'dvc.yaml'\n",
      "\n",
      "To track the changes with git, run:\n",
      "\n",
      "\tgit add dvc.yaml\n",
      "\n",
      "To enable auto staging, run:\n",
      "\n",
      "\tdvc config core.autostage true\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[NbConvertApp] Converting notebook 03_dependencies.ipynb to script\n",
      "/data/fzills/miniconda3/envs/zntrack/lib/python3.10/site-packages/nbformat/__init__.py:93: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
      "  validate(nb)\n",
      "[NbConvertApp] Writing 6203 bytes to 03_dependencies.py\n",
      "Running DVC command: 'stage add --name WriteToFile --force ...'\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Adding stage 'WriteToFile' in 'dvc.yaml'\n",
      "\n",
      "To track the changes with git, run:\n",
      "\n",
      "\tgit add dvc.yaml nodes/WriteToFile/.gitignore\n",
      "\n",
      "To enable auto staging, run:\n",
      "\n",
      "\tdvc config core.autostage true\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[NbConvertApp] Converting notebook 03_dependencies.ipynb to script\n",
      "/data/fzills/miniconda3/envs/zntrack/lib/python3.10/site-packages/nbformat/__init__.py:93: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
      "  validate(nb)\n",
      "[NbConvertApp] Writing 6203 bytes to 03_dependencies.py\n",
      "Running DVC command: 'stage add --name PowerFromFile --force ...'\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Adding stage 'PowerFromFile' in 'dvc.yaml'\n",
      "\n",
      "To track the changes with git, run:\n",
      "\n",
      "\tgit add nodes/PowerFromFile/.gitignore dvc.yaml\n",
      "\n",
      "To enable auto staging, run:\n",
      "\n",
      "\tdvc config core.autostage true\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[NbConvertApp] Converting notebook 03_dependencies.ipynb to script\n",
      "/data/fzills/miniconda3/envs/zntrack/lib/python3.10/site-packages/nbformat/__init__.py:93: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
      "  validate(nb)\n",
      "[NbConvertApp] Writing 6203 bytes to 03_dependencies.py\n",
      "Running DVC command: 'stage add --name ComparePowers --force ...'\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Adding stage 'ComparePowers' in 'dvc.yaml'\n",
      "\n",
      "To track the changes with git, run:\n",
      "\n",
      "\tgit add dvc.yaml\n",
      "\n",
      "To enable auto staging, run:\n",
      "\n",
      "\tdvc config core.autostage true\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[NbConvertApp] Converting notebook 03_dependencies.ipynb to script\n",
      "/data/fzills/miniconda3/envs/zntrack/lib/python3.10/site-packages/nbformat/__init__.py:93: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
      "  validate(nb)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Stage 'RandomNumber' didn't change, skipping\n",
      "Running stage 'WriteToFile':\n",
      "> zntrack run src.WriteToFile.WriteToFile --name WriteToFile\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[NbConvertApp] Writing 6203 bytes to 03_dependencies.py\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Updating lock file 'dvc.lock'\n",
      "\n",
      "Running stage 'PowerFromFile':\n",
      "> zntrack run src.PowerFromFile.PowerFromFile --name PowerFromFile\n",
      "Updating lock file 'dvc.lock'\n",
      "\n",
      "Stage 'ComputePower' didn't change, skipping\n",
      "Running stage 'ComparePowers':\n",
      "> zntrack run src.ComparePowers.ComparePowers --name ComparePowers\n",
      "Updating lock file 'dvc.lock'\n",
      "\n",
      "To track the changes with git, run:\n",
      "\n",
      "\tgit add dvc.lock\n",
      "\n",
      "To enable auto staging, run:\n",
      "\n",
      "\tdvc config core.autostage true\n",
      "Use `dvc push` to send your updates to remote storage.\n"
     ]
    }
   ],
   "source": [
    "with zntrack.Project() as project:\n",
    "    random_number = RandomNumber(maximum=16)\n",
    "    compute_power = ComputePower(random_number=random_number, power=2.0)\n",
    "\n",
    "    write_to_file = WriteToFile(random_number=random_number)\n",
    "    power_from_file = PowerFromFile(file=write_to_file.file)\n",
    "    compare_powerts = ComparePowers(power_deps=[power_from_file, compute_power])\n",
    "project.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "883099de-6823-4000-9080-6f57b61a9bf9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                +--------------+                  \n",
      "                | RandomNumber |                  \n",
      "                +--------------+                  \n",
      "                **             ***                \n",
      "             ***                  ***             \n",
      "           **                        **           \n",
      " +-------------+                       **         \n",
      " | WriteToFile |                        *         \n",
      " +-------------+                        *         \n",
      "        *                               *         \n",
      "        *                               *         \n",
      "        *                               *         \n",
      "+---------------+               +--------------+  \n",
      "| PowerFromFile |               | ComputePower |  \n",
      "+---------------+               +--------------+  \n",
      "                **             ***                \n",
      "                  ***        **                   \n",
      "                     **    **                     \n",
      "                +---------------+                 \n",
      "                | ComparePowers |                 \n",
      "                +---------------+                 \n"
     ]
    }
   ],
   "source": [
    "!dvc dag"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# to verify we can also run the method manually\n",
    "compare_powerts.load()\n",
    "compare_powerts.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c0b8c97a-ab33-4ac4-9473-c203d88c0442",
   "metadata": {},
   "source": [
    "If we now look at our `dvc.yaml` we can see that for our Node dependencies we rely on the `nodes/<node_name>/outs.json` while for the file dependency it is directly connect to the passed file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "32e1e993-ff99-4eb6-97db-ffc3ce834ca5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "stages:\n",
       "  RandomNumber:\n",
       "    cmd: zntrack run src.RandomNumber.RandomNumber --name RandomNumber\n",
       "    params:\n",
       "    - RandomNumber\n",
       "    outs:\n",
       "    - nodes/RandomNumber/number.json\n",
       "  ComputePower:\n",
       "    cmd: zntrack run src.ComputePower.ComputePower --name ComputePower\n",
       "    deps:\n",
       "    - nodes/RandomNumber/number.json\n",
       "    params:\n",
       "    - ComputePower\n",
       "    outs:\n",
       "    - nodes/ComputePower/number.json\n",
       "  WriteToFile:\n",
       "    cmd: zntrack run src.WriteToFile.WriteToFile --name WriteToFile\n",
       "    deps:\n",
       "    - nodes/RandomNumber/number.json\n",
       "    outs:\n",
       "    - nodes/WriteToFile/random_number.txt\n",
       "  PowerFromFile:\n",
       "    cmd: zntrack run src.PowerFromFile.PowerFromFile --name PowerFromFile\n",
       "    deps:\n",
       "    - nodes/WriteToFile/random_number.txt\n",
       "    params:\n",
       "    - PowerFromFile\n",
       "    outs:\n",
       "    - nodes/PowerFromFile/number.json\n",
       "  ComparePowers:\n",
       "    cmd: zntrack run src.ComparePowers.ComparePowers --name ComparePowers\n",
       "    deps:\n",
       "    - nodes/ComputePower/number.json\n",
       "    - nodes/PowerFromFile/number.json\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Pretty, display\n",
    "\n",
    "display(Pretty(\"dvc.yaml\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## Node attributes as dependencies\n",
    "\n",
    "It is also possible to specify a Node attribute as a dependency. In this case you will be able to access the value of the attribute directly instead of using the Node class.\n",
    "This can be used for all `dvc.<option>` and `zn.<option>` as well as e.g. class properties.\n",
    "Note that the dvc dependencies will still be written for the full Node and won't be limited to the Node attribute.\n",
    "To be able to define a dependency of an attribute the `zntrack.getdeps` function is required."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "class ComputePowerFromNumber(zntrack.Node):\n",
    "    number: float = zntrack.zn.deps()  # this will be a float instead of RandomNumber\n",
    "\n",
    "    power: int = zntrack.zn.params()\n",
    "    result: float = zntrack.zn.outs()\n",
    "\n",
    "    def run(self):\n",
    "        self.result = self.number**self.power"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Running DVC command: 'stage add --name RandomNumber --force ...'\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Modifying stage 'RandomNumber' in 'dvc.yaml'\n",
      "\n",
      "To track the changes with git, run:\n",
      "\n",
      "\tgit add dvc.yaml\n",
      "\n",
      "To enable auto staging, run:\n",
      "\n",
      "\tdvc config core.autostage true\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[NbConvertApp] Converting notebook 03_dependencies.ipynb to script\n",
      "/data/fzills/miniconda3/envs/zntrack/lib/python3.10/site-packages/nbformat/__init__.py:93: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
      "  validate(nb)\n",
      "[NbConvertApp] Writing 6203 bytes to 03_dependencies.py\n",
      "Running DVC command: 'stage add --name ComputePowerFromNumber --force ...'\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Adding stage 'ComputePowerFromNumber' in 'dvc.yaml'\n",
      "\n",
      "To track the changes with git, run:\n",
      "\n",
      "\tgit add dvc.yaml nodes/ComputePowerFromNumber/.gitignore\n",
      "\n",
      "To enable auto staging, run:\n",
      "\n",
      "\tdvc config core.autostage true\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[NbConvertApp] Converting notebook 03_dependencies.ipynb to script\n",
      "/data/fzills/miniconda3/envs/zntrack/lib/python3.10/site-packages/nbformat/__init__.py:93: MissingIDFieldWarning: Code cell is missing an id field, this will become a hard error in future nbformat versions. You may want to use `normalize()` on your notebooks before validations (available since nbformat 5.1.4). Previous versions of nbformat are fixing this issue transparently, and will stop doing so in the future.\n",
      "  validate(nb)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Stage 'RandomNumber' didn't change, skipping\n",
      "Stage 'WriteToFile' didn't change, skipping\n",
      "Stage 'PowerFromFile' didn't change, skipping\n",
      "Stage 'ComputePower' didn't change, skipping\n",
      "Stage 'ComparePowers' didn't change, skipping\n",
      "Running stage 'ComputePowerFromNumber':\n",
      "> zntrack run src.ComputePowerFromNumber.ComputePowerFromNumber --name ComputePowerFromNumber\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[NbConvertApp] Writing 6203 bytes to 03_dependencies.py\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Updating lock file 'dvc.lock'\n",
      "\n",
      "To track the changes with git, run:\n",
      "\n",
      "\tgit add dvc.lock\n",
      "\n",
      "To enable auto staging, run:\n",
      "\n",
      "\tdvc config core.autostage true\n",
      "Use `dvc push` to send your updates to remote storage.\n"
     ]
    }
   ],
   "source": [
    "with zntrack.Project() as project:\n",
    "    random_number = RandomNumber(maximum=16)\n",
    "    compute_power = ComputePowerFromNumber(number=random_number.number, power=2.0)\n",
    "project.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "`getdeps(RandomNumber, \"number\")` can also be replaced by `getdeps(RandomNumber[\"nodename\"], \"number\")` or `getdeps(RandomNumber.load(name=\"nodename\"), \"number\")`.\n",
    "The first argument represents the Node and the second argument is the attribute, similar to `getattr()`. ZnTrack also provides a shorthand for this via `RandomNumber @ \"number\"` or `RandomNumber[\"nodename\"] @ \"number\"`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "compute_power.load()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3.0 ^ 2.0 = 9.0\n"
     ]
    }
   ],
   "source": [
    "print(f\"{compute_power.number} ^ {compute_power.power} = {compute_power.result}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "b26fb0f0-5f3e-40d0-b1fd-8d5844f051e8",
   "metadata": {
    "nbsphinx": "hidden",
    "tags": []
   },
   "outputs": [],
   "source": [
    "temp_dir.cleanup()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8b361c76",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "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.10.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}