{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Process Model simulation\n",
    "In this example, we present a simple process model and simulate its execution to obtain performance metrics.\n",
    "## Initialization\n",
    "As usual, we start the notebook by initializing the Modelverse connection.\n",
    "Additionally, we will be using matplotlib to draw a figure at the end, so include it now already"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "sys.path.append(\"../wrappers/\")\n",
    "from modelverse import *\n",
    "init()\n",
    "login(None, None)\n",
    "\n",
    "%matplotlib inline\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Load libraries\n",
    "Load all the necessary formalisms and activities first.\n",
    "They might already exist in the Modelverse, but it doesn't matter that much now: just overwrite.\n",
    "Also loads the DEVS library for process modelling building blocks."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "model_add(\"~/formalisms/DEVS/DEVS_MM\", \"formalisms/SimpleClassDiagrams\", open(\"../models/ClassicDEVS/metamodels/DEVS.mvc\", 'r').read())\n",
    "model_add(\"~/formalisms/PM/PM_Extended_MM\", \"formalisms/SimpleClassDiagrams\", open(\"../models/FTGPM/metamodels/control_patterns.mvc\", 'r').read())\n",
    "model_add(\"~/formalisms/Experiment/Experiment_MM\", \"formalisms/SimpleClassDiagrams\", open(\"../models/PythonPDEVS_Experiment/metamodels/experiment.mvc\", 'r').read())\n",
    "model_add(\"~/models/DEVS/PM_library\", \"~/formalisms/DEVS/DEVS_MM\", open(\"../models/ClassicDEVS/models/process_patterns_library.mvc\", 'r').read())\n",
    "model_add(\"~/formalisms/String\", \"formalisms/SimpleClassDiagrams\", open(\"../models/String/metamodels/String.mvc\", 'r').read())\n",
    "\n",
    "def traceability_pm_devs(model):\n",
    "    instantiate(model, \"Association\", (\"PM/ProcessNode\", \"DEVS/DEVSInstance\"), ID=\"Trace\")\n",
    "\n",
    "transformation_add_MT({\"PM\": \"~/formalisms/PM/PM_Extended_MM\"}, {\"DEVS\": \"~/formalisms/DEVS/DEVS_MM\"}, \"~/formalisms/PM/to_DEVS\", open(\"../models/FTGPM/transformations/patterns_to_DEVS.mvc\", 'r').read(), traceability_pm_devs)\n",
    "transformation_add_AL({\"model1\": \"~/formalisms/DEVS/DEVS_MM\", \"model2\": \"~/formalisms/DEVS/DEVS_MM\"}, {\"result\": \"~/formalisms/DEVS/DEVS_MM\"}, \"~/formalisms/DEVS/merge\", open(\"../models/ClassicDEVS/transformations/merge.alc\", 'r').read())\n",
    "transformation_add_AL({\"DEVS\": \"~/formalisms/DEVS/DEVS_MM\", \"Experiment\": \"~/formalisms/Experiment/Experiment_MM\"}, {\"result\": \"~/formalisms/String\"}, \"~/formalisms/DEVS/simulate\", open(\"../models/ClassicDEVS/transformations/simulate.alc\", 'r').read())\n",
    "\n",
    "model_add(\"~/models/PM/to_DEVS\", \"formalisms/ProcessModel\", open(\"../models/FTGPM/PM_patterns_simulation_relative.mvc\", 'r').read())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Specify Input\n",
    "Next we specify the input models and the experiment that we wish to perform.\n",
    "The input Process Model is the one that will be simulated, and can be tuned in a variety of ways, such as through the use of different parameters.\n",
    "The experiment can be further tweaked to alter the range of available resources or to change the number of samples."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "model_add(\"~/models/PM/example_PM\", \"~/formalisms/PM/PM_Extended_MM\", \"\"\"\n",
    "    Initial init {\n",
    "        name = \"initial\"\n",
    "    }\n",
    "    Activity req {\n",
    "        name = \"define_requirements\"\n",
    "        distribution = \"lambda iteration: max(0.0, random.gauss(10.0 * (1-math.exp(-(iteration+1) / 0.7)), (10.0 * (1-math.exp(-(iteration+1) / 0.7)))*0.15625))\"\n",
    "    }\n",
    "    SimpleMerge merge {\n",
    "        name = \"merge_0\"\n",
    "    }\n",
    "    Activity model {\n",
    "        name = \"model_system\"\n",
    "        distribution = \"lambda iteration: max(0.0, random.gauss(100.0 * (1-math.exp(-(iteration+1) / 0.7)), (100.0 * (1-math.exp(-(iteration+1) / 0.7)))*0.15625))\"\n",
    "    }\n",
    "    ParallelSplit split {\n",
    "        name = \"split_0\"\n",
    "    }\n",
    "    MultiInstance simulate {\n",
    "        name = \"simulate\"\n",
    "        nr_instances = 10\n",
    "        distribution = \"lambda iteration: max(0.0, random.gauss(20.0 * (1-math.exp(-(iteration+1) / 0.7)), (20.0 * (1-math.exp(-(iteration+1) / 0.7)))*0.15625))\"\n",
    "    }\n",
    "    Activity check {\n",
    "        name = \"check\"\n",
    "        distribution = \"lambda iteration: max(0.0, random.gauss(30.0 * (1-math.exp(-(iteration+1) / 0.7)), (30.0 * (1-math.exp(-(iteration+1) / 0.7)))*0.15625))\"\n",
    "    }\n",
    "    Synchronization sync {\n",
    "        name = \"sync_0\"\n",
    "    }\n",
    "    Activity evaluate {\n",
    "        name = \"evaluate\"\n",
    "        distribution = \"lambda iteration: max(0.0, random.gauss(0.1 * (1-math.exp(-(iteration+1) / 0.7)), (0.1 * (1-math.exp(-(iteration+1) / 0.7)))*0.15625))\"\n",
    "    }\n",
    "    ExclusiveChoice choice {\n",
    "        name = \"xor_0\"\n",
    "        distribution = \"lambda iteration: random.random() > {0: 0.99, 1: 0.9, 2: 0.8, 3: 0.5, 4: 0.2, 5: 0.1, 6: 0.02}.get(iteration, 0.0)\"\n",
    "    }\n",
    "    Finish finish {\n",
    "        name = \"finish\"\n",
    "    }\n",
    "\n",
    "    Next (init, req) {}\n",
    "    Next (req, merge) {}\n",
    "    Next (merge, model) {}\n",
    "    Next (model, split) {}\n",
    "    Next (split, simulate) {}\n",
    "    Next (split, check) {}\n",
    "    Next (simulate, sync) {}\n",
    "    Next (check, sync) {}\n",
    "    Next (sync, evaluate) {}\n",
    "    Next (evaluate, choice) {}\n",
    "    DecisionTrue (choice, finish) {}\n",
    "    DecisionFalse (choice, merge) {}\n",
    "    \"\"\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "model_add(\"~/models/Experiment/example_experiment\", \"~/formalisms/Experiment/Experiment_MM\", \"\"\"\n",
    "    Experiment {\n",
    "        code = \\\"\\\"\\\"\n",
    "    import random\n",
    "    import math\n",
    "\n",
    "    from pypdevs.simulator import Simulator\n",
    "\n",
    "    def simulate(nresources, seed):\n",
    "        random.seed(seed)\n",
    "        model = Root('root', [nresources])\n",
    "        sim = Simulator(model)\n",
    "        sim.setClassicDEVS()\n",
    "        sim.simulate()\n",
    "        return model.submodels['finish'].state\n",
    "\n",
    "    def main():\n",
    "        results = ''\n",
    "        for i in range(1, 14):\n",
    "            results = results + str(i)\n",
    "            for v in range(2, 10):\n",
    "                results = results + ' ' + str(simulate(i, v))\n",
    "            results = results + '\\\\n'\n",
    "        return results\n",
    "        \\\"\\\"\\\"\n",
    "    }\n",
    "    \"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Enact process\n",
    "Finally we enact the process, thereby generating the *~/results* model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "process_execute(\"~/models/PM/to_DEVS\", {\"PM\": \"~/models/PM/example_PM\", \"DEVS library\": \"~/models/DEVS/PM_library\", \"Experiment\": \"~/models/Experiment/example_experiment\", \"result\": \"~/result\"}, {})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally plot the simulation results, by reading out the value of the string model.\n",
    "Using this information, we split it up and then forward it to matplotlib for plotting as a boxplot."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 8064 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "results = read_attrs(\"~/result\", all_instances(\"~/result\", \"String\").pop())[\"value\"]\n",
    "data = results.strip().split(\"\\n\")\n",
    "data = [i.split() for i in data]\n",
    "data = [[float(j) for j in i[1:]] for i in data]\n",
    "plt.boxplot(data);"
   ]
  }
 ],
 "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.5.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
