{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Experiment Tracking: Coloring a Single Run\n\nThis example demostrates how to use opt-sugar in combination with mlflow\nfor single objective optimization experiment tracking\n\n<img src=\"https://mybinder.org/badge_logo.svg\" target=\"https://mybinder.org/v2/gh/juandados/opt-sugar/main?labpath=doc%2Fsource%2Fauto_examples%2Fplot_coloring.ipynb\">\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# sphinx_gallery_thumbnail_path = '_static/coloring.png'\nimport datetime\nfrom urllib.parse import urlparse\nfrom itertools import product\nfrom collections import defaultdict\nimport logging\n\nimport gurobipy as gp\nimport mlflow\nfrom mlflow.exceptions import MlflowException\n\n# import sys; sys.path.append('/Users/Juan.ChaconLeon/opt/opt-sugar/src')  # when running locally\nfrom opt_sugar import opt_flow\nfrom opt_sugar import low_sugar\n\n# The following function helper is very handy to visualize our colored graphs.\nfrom utils.coloring import get_graph_to_show\n\n# The generate_graph_data generates random graphs given a graph size and an edge probability.\nfrom utils.coloring import generate_graph_data\n\n# TODO: reformat this example similar to\n#  https://github.com/scikit-learn/scikit-learn/blob/main/examples/calibration/plot_calibration_multiclass.py"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## The Optimizations Model Builder\n\nThe following class is the builder for the coloring problem.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "class ColoringModelBuilder:\n\n    def __init__(self, data):\n        self.data = data\n        self.degree = None\n        self.variables = None\n\n    def build_variables(self, base_model):\n        degrees = defaultdict(int)\n        for v1, v2 in self.data[\"edges\"]:\n            degrees[v1] += 1\n            degrees[v2] += 1\n        self.degree = max(degrees.items(), key=lambda x: x[1])[1]\n        color_keys = list(product(self.data[\"nodes\"], range(self.degree)))\n        color = base_model.addVars(color_keys, vtype=\"B\", name=\"color\")\n        max_color = base_model.addVar(lb=0, ub=self.degree, vtype=\"C\", name=\"max_color\")\n        self.variables = {\"color\": color, \"max_color\": max_color}\n\n    def build_constraints(self, base_model):\n        color = self.variables[\"color\"]\n        for v1, c in color:\n            # if color[v1, c] == 1 -> color[v2, c] == 0 for all v2 such that (v1, v2) or\n            # belongs to E\n            for v2 in self.data[\"nodes\"]:\n                if (v2, v1) in self.data[\"edges\"] or (v1, v2) in self.data[\"edges\"]:\n                    base_model.addConstr(\n                        color[v2, c] <= 1 - color[v1, c], name=f\"color_{c}_{v1}_{v2}\"\n                    )\n\n        for v in self.data[\"nodes\"]:\n            base_model.addConstr(\n                gp.quicksum(color[v, c] for c in range(self.degree)) == 1,\n                name=f\"every_node_has_color_{v}\",\n            )\n\n        max_color = self.variables[\"max_color\"]\n        for v, c in color:\n            base_model.addConstr(\n                c * color[v, c] <= max_color, name=f\"max_color_{v}_{c}\"\n            )\n\n    def build_objective(self, base_model):\n        max_color = self.variables[\"max_color\"]\n        base_model.setObjective(max_color, gp.GRB.MINIMIZE)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Tracking an Optimization Experiment\n\nAdd description here.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "logging.getLogger(\"mlflow\").setLevel(logging.CRITICAL)  # Can be set DEBUG\n\ntry:\n    experiment_name = f\"opt_exp_{datetime.datetime.now().strftime('%Y_%m_%d')}\"\n    experiment_id = mlflow.create_experiment(name=experiment_name)\nexcept MlflowException:\n    experiment_id = mlflow.get_experiment_by_name(name=experiment_name).experiment_id\n\nwith mlflow.start_run(experiment_id=experiment_id):\n    def build(data):\n        # Create a new model\n        m = gp.Model(\"coloring\")\n        model_builder = ColoringModelBuilder(data)\n        model_builder.build_variables(m)\n        model_builder.build_constraints(m)\n        model_builder.build_objective(m)\n        # setting parameters\n        m.setParam('MIPFocus', 2)\n        return m\n\n    def callback(m):\n        objective = m.getObjective()\n        color_count = objective.getValue()\n        callback_result = {\"color_count\": color_count, \"MIPFocus\": m.getParamInfo('MIPFocus')[2], \"RunTime\": m.RunTime}\n        return callback_result\n\n    # Generating a graph instance\n    data = generate_graph_data(node_count=16, edge_probability=0.5)\n\n    # Building\n    opt_model = low_sugar.Model(build)\n    result = opt_model.optimize(data=data, callback=callback)\n    var_values = result[\"vars\"]\n    g = get_graph_to_show(data, var_values)\n    # g.show(name=\"vis.html\")\n\n    # Note: Above is replacement for opt_model.fit(data, fit_callback) and opt_model.predict(data)\n    mlflow.log_param(\"MIPFocus\", result[\"callback_result\"][\"MIPFocus\"])\n    mlflow.log_param(\"RunTime\", result[\"callback_result\"][\"RunTime\"])\n    mlflow.log_metric(\"color_count\", result[\"callback_result\"][\"color_count\"])\n\n    tracking_url_type_store = urlparse(mlflow.get_tracking_uri()).scheme\n    print(f\"tracking_url_type_store: {tracking_url_type_store}\")\n\n    # Register the model\n    if tracking_url_type_store != \"file\":\n        # There are other ways to use the Model Registry, which depends on the use case,\n        # please refer to the doc for more information:\n        # https://mlflow.org/docs/latest/model-registry.html#api-workflow\n        model_info = mlflow.sklearn.log_model(\n            opt_model, \"opt_model\", registered_model_name=\"OptModel\"\n        )\n    else:\n        model_info = mlflow.sklearn.log_model(opt_model, \"opt_model\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Load the Registered Model and Optimize with new Data\n\nAdd description here.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "logged_model_uri = model_info.model_uri\nprint(f\"logged_model_uri: {logged_model_uri}\")\n\n# Load model as a PyFuncModel.\nloaded_model = opt_flow.pyfunc.load_model(logged_model_uri)\n\n# Data generation\ndata = generate_graph_data(node_count=6, edge_probability=0.5)\nsolution = loaded_model.optimize(data)\nprint(f\"Optimized Coloring: {solution}\")\n\nvar_values = result[\"vars\"]\ng = get_graph_to_show(data, var_values)\n# g.show(name=\"vis.html\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "<img src=\"https://mybinder.org/badge_logo.svg\" target=\"https://mybinder.org/v2/gh/juandados/opt-sugar/main?labpath=doc%2Fsource%2Fauto_examples%2Fplot_coloring.ipynb\">\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.9.15"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}