{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Example demonstrating the use of the caching decorator with functional equivalance checking\n\nCaches the results of fitness evaluations in a pickle file\n(``example_fec_caching_cache.pkl``). To illustrate its practical use,\ncompare the runtime of this script when you first call it vs. the\nsecond time and when you comment out the decorator on\n`inner_objective`.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import functools\nimport multiprocessing as mp\nimport time\n\nimport numpy as np\n\nimport cgp"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We define the target function for this example.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "def f_target(x):\n    return x ** 2 + x + 1.0"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We then define the objective function for the evolutionary\nalgorithm: It consists of an inner objective which we wrap with the\ncaching decorator. This decorator specifies a pickle file that will\nbe used for caching results of fitness evaluations. In addition the\ndecorator accepts keyword arguments that specifiy the statistic of\nthe samples used for evaluation. The inner objective is then used by\nthe objective function to compute (or retrieve from cache) the\nfitness of the individual.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "@cgp.utils.disk_cache(\n    \"example_fec_caching_cache.pkl\",\n    compute_key=functools.partial(\n        cgp.utils.compute_key_from_numpy_evaluation_and_args,\n        _seed=12345,\n        _min_value=-10.0,\n        _max_value=10.0,\n        _batch_size=5,\n    ),\n    file_lock=mp.Lock(),\n)\ndef inner_objective(ind):\n    \"\"\"The caching decorator uses the return values generated from\n    providing random inputs to ind.to_numpy() to identify functionally\n    indentical individuals and avoid reevaluating them. Note that\n    caching only makes sense for deterministic objective functions, as\n    it assumes that identical phenotypes will always return the same\n    fitness values.\n\n    \"\"\"\n\n    f = ind.to_numpy()\n    x = np.linspace(-2.0, 2.0, 100)\n    y = f(x)\n    loss = (f_target(x) - y) ** 2\n\n    time.sleep(0.25)  # emulate long fitness evaluation\n\n    return np.mean(loss)\n\n\ndef objective(individual):\n    if not individual.fitness_is_None():\n        return individual\n\n    individual.fitness = -inner_objective(individual)\n\n    return individual"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Finally, we call the `evolve` method to perform the evolutionary search.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "pop = cgp.evolve(objective, max_generations=200, termination_fitness=0.0, print_progress=True)\n\n\nprint(f\"evolved function: {pop.champion.to_sympy()}\")"
      ]
    }
  ],
  "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.6"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}