{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Example for evolutionary regression with genome reordering\n\nExample demonstrating the effect of genome reordering.\n\n## References\nGoldman B.W., Punch W.F. (2014): Analysis of Cartesian Genetic Programming\u2019s\nEvolutionary Mechanisms\nDOI: 10.1109/TEVC.2014.2324539\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# The docopt str is added explicitly to ensure compatibility with\n# sphinx-gallery.\ndocopt_str = \"\"\"\n   Usage:\n     example_reorder.py [--max-generations=<N>]\n\n   Options:\n     -h --help\n     --max-generations=<N>  Maximum number of generations [default: 300]\n\"\"\"\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport scipy.constants\nfrom docopt import docopt\n\nimport cgp\n\nargs = docopt(docopt_str)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We first define a target function.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "def f_target(x):\n    return x ** 2 + 1.0"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Then we define the objective function for the evolution. It uses\nthe mean-squared error between the output of the expression\nrepresented by a given individual and the target function evaluated\non a set of random points.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "def objective(individual):\n    if not individual.fitness_is_None():\n        return individual\n\n    n_function_evaluations = 1000\n\n    np.random.seed(1234)\n\n    f = individual.to_func()\n    loss = 0\n    for x in np.random.uniform(-4, 4, n_function_evaluations):\n        # the callable returned from `to_func` accepts and returns\n        # lists; accordingly we need to pack the argument and unpack\n        # the return value\n        y = f(x)\n        loss += (f_target(x) - y) ** 2\n\n    individual.fitness = -loss / n_function_evaluations\n\n    return individual"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Next, we set up the evolutionary search. We first define the\nparameters for the population, the genome of individuals, and two\nevolutionary algorithms without (default) and with genome reordering.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "population_params = {\"n_parents\": 1, \"seed\": 818821}\n\ngenome_params = {\n    \"n_inputs\": 1,\n    \"n_outputs\": 1,\n    \"n_columns\": 12,\n    \"n_rows\": 1,\n    \"levels_back\": None,\n    \"primitives\": (cgp.Add, cgp.Sub, cgp.Mul, cgp.ConstantFloat),\n}\n\nea_params = {\"n_offsprings\": 4, \"mutation_rate\": 0.03, \"n_processes\": 2}\nea_params_with_reorder = {\n    \"n_offsprings\": 4,\n    \"mutation_rate\": 0.03,\n    \"n_processes\": 2,\n    \"reorder_genome\": True,\n}\n\nevolve_params = {\"max_generations\": int(args[\"--max-generations\"]), \"termination_fitness\": 0.0}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We create two populations that will be evolved\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "pop = cgp.Population(**population_params, genome_params=genome_params)\npop_with_reorder = cgp.Population(**population_params, genome_params=genome_params)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "and two instances of the (mu + lambda) evolutionary algorithm\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "ea = cgp.ea.MuPlusLambda(**ea_params)\nea_with_reorder = cgp.ea.MuPlusLambda(**ea_params_with_reorder)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We define two callbacks for recording of fitness over generations\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "history = {}\nhistory[\"fitness_champion\"] = []\n\n\ndef recording_callback(pop):\n    history[\"fitness_champion\"].append(pop.champion.fitness)\n\n\nhistory_with_reorder = {}\nhistory_with_reorder[\"fitness_champion\"] = []\n\n\ndef recording_callback_with_reorder(pop):\n    history_with_reorder[\"fitness_champion\"].append(pop.champion.fitness)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "and finally perform the evolution of the two populations\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "cgp.evolve(\n    pop, objective, ea, **evolve_params, print_progress=True, callback=recording_callback,\n)\n\ncgp.evolve(\n    pop_with_reorder,\n    objective,\n    ea_with_reorder,\n    **evolve_params,\n    print_progress=True,\n    callback=recording_callback_with_reorder,\n)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "After finishing the evolution, we plot the evolution of the fittest individual\nwith and without genome reordering\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "width = 9.0\nfig = plt.figure(1, figsize=[width, width / scipy.constants.golden])\n\nplt.plot(history[\"fitness_champion\"], label=\"Champion\")\nplt.plot(history_with_reorder[\"fitness_champion\"], label=\"Champion with reorder\")\n\nplt.xlabel(\"Generation\")\nplt.ylabel(\"Fitness\")\nplt.legend([\"Champion\", \"Champion with reorder\"])\n\nplt.yscale(\"symlog\")\nplt.ylim(-1.0e2, 0.1)\nplt.axhline(0.0, color=\"0.7\")\n\nfig.savefig(\"example_reorder.pdf\", dpi=300)"
      ]
    }
  ],
  "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
}