{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n\n# Composite-Algorithms and Pipelines\n\nSometimes a pipeline or algorithms requires a list of parameters or nested objects.\nAs we can not support parameters which names are not known when the class is defined, such cases need to be handled\nvia composite fields.\n\nA composite field is a parameter expecting a value of the shape `[(name1, sub_para1), (name2, sub_para2), ...]`.\nThe sub-paras can themselves be tpcp objects.\n\nAs it is difficult at runtime to know, if a parameter is expected to be a composite field, you need to actively\nspecify all fields that should be considered composite fields during class definition using the `_composite_params`\nattribute:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import dataclasses\nimport traceback\nfrom typing import List, Optional, Tuple\n\nfrom tpcp import CloneFactory, Pipeline\nfrom tpcp.exceptions import ValidationError\n\n\n@dataclasses.dataclass\nclass Workflow(Pipeline):\n    _composite_params = (\"pipelines\",)\n\n    pipelines: Optional[List[Tuple[str, Pipeline]]] = None\n\n    def __init__(self, pipelines=None):\n        self.pipelines = pipelines"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "That's it!\nNow tpcp knows, that `pipelines` should be a composite field and will actually complain, if we try to assign\nsomething invalid.\nComposite fields are allowed to either have the value None, or be a list of tuples as explained above\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "instance = Workflow()\ninstance.pipelines  # Our default value of None"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "instance.pipelines = \"something invalid\"\ntry:\n    print(instance.get_params())\nexcept ValidationError as e:\n    traceback.print_exc()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "While you could set the individual sub-params in a composite field to whatever you want, the real value of explicit\ncomposite fields are the use of tpcp-objects\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "@dataclasses.dataclass\nclass MyPipeline(Pipeline):\n    param: float = 4\n    param2: int = 10\n\n\nworkflow_instance = Workflow(pipelines=[(\"pipe1\", MyPipeline()), (\"pipe2\", MyPipeline(param2=5))])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We can now use `get_params` to get a deep inspection of the nested objects:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "workflow_instance.get_params(deep=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Or we can set params using the following syntax:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "workflow_instance = workflow_instance.set_params(pipelines__pipe1__param=2, pipelines__pipe2=MyPipeline(param2=4))\nworkflow_instance.get_params(deep=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Note that it is not possible to set parameters for keys that don't exist yet!\nIn such a case, you would manually recreate the full list.\n\n"
      ]
    }
  ],
  "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.15"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}