{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "7ee10bd7-40c0-49aa-b66a-664c8fa5bbf5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Der eigentliche LP/(M)IP-Solver \"Gurobi\"\n",
    "import gurobipy as grb\n",
    "\n",
    "# Eine Menge nützlicher Routinen zu Iteratoren (erlaubt z.B. Iteration über alle Kombinationen)\n",
    "import itertools\n",
    "\n",
    "# Graphen\n",
    "import networkx as nx\n",
    "from networkx.classes.graphviews import subgraph_view\n",
    "\n",
    "# Generation von zufälligen Zahlen für Instanzen/Punktmengen\n",
    "import random\n",
    "\n",
    "# Fürs Wurzelziehen\n",
    "import math\n",
    "\n",
    "# Fürs Zeichnen (hier von Graphen, kann aber auch Daten visualisieren)\n",
    "import matplotlib\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "98e49156-6a70-4eb4-9f45-e1f9cf1d85aa",
   "metadata": {},
   "source": [
    "## Hilfsroutinen\n",
    "Zur Generierung von Instanzen und Erzeugung/Sortierung der Kantenmenge."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "16d94ecd-1448-4172-9bed-9f7d0cfd21c2",
   "metadata": {},
   "outputs": [],
   "source": [
    "def random_points(n, w=10_000, h=10_000):\n",
    "    \"\"\"\n",
    "    n zufällige Punkte mit ganzzahligen Koordinaten in einem w * h-Rechteck.\n",
    "    :param n: Anzahl der Punkte\n",
    "    :param w: Breite des Rechtecks.\n",
    "    :param h: Höhe des Rechtecks.\n",
    "    :return: Eine Liste von Punkten als (x,y)-Tupel.\n",
    "    \"\"\"\n",
    "    return [(random.randint(0,w), random.randint(0,h)) for _ in range(n)]\n",
    "\n",
    "def squared_distance(p1, p2):\n",
    "    \"\"\"\n",
    "    Berechne die (quadrierte) euklidische Distanz zwischen Punkten p1 und p2.\n",
    "    \"\"\"\n",
    "    return (p1[0]-p2[0])**2 + (p1[1]-p2[1])**2\n",
    "\n",
    "def all_edges(points):\n",
    "    \"\"\"\n",
    "    Erzeuge eine Liste aller Kanten zwischen den\n",
    "    gegebenen Punkten und sortiere sie (aufsteigend) nach Länge.\n",
    "    \"\"\"\n",
    "    edges = [(v,w) for v, w in itertools.combinations(points, 2)]\n",
    "    edges.sort(key=lambda p: squared_distance(*p))  # *p ist hier wie p[0], p[1]\n",
    "    return edges\n",
    "\n",
    "def filter_edges(edges, max_sq_length):\n",
    "    return [e for e in edges if squared_distance(*e) <= max_sq_length]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a8dcce0a-3b70-4862-b53e-da059cc5ac29",
   "metadata": {},
   "source": [
    "## Zeichnen von Lösungen"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "f436c90b-a091-49e4-8c80-aa6bdd929024",
   "metadata": {},
   "outputs": [],
   "source": [
    "def draw_edges(edges):\n",
    "    \"\"\"\n",
    "    Malt eine gegebene Liste von Kanten als Graph.\n",
    "    Die längste Kante wird dabei hervorgehoben (rot, dicker) dargestellt.\n",
    "    \"\"\"\n",
    "    points = set([e[0] for e in edges] + [e[1] for e in edges])\n",
    "    draw_graph = nx.empty_graph()\n",
    "    draw_graph.add_nodes_from(points)\n",
    "    draw_graph.add_edges_from(edges)\n",
    "    g_edges = draw_graph.edges()\n",
    "    max_length = max((squared_distance(*e) for e in g_edges))\n",
    "    color = [('red' if squared_distance(*e) == max_length else 'black') for e in g_edges]\n",
    "    width = [(1.0 if squared_distance(*e) == max_length else 0.5) for e in g_edges]\n",
    "    plt.clf()\n",
    "    fig, ax = plt.gcf(), plt.gca()\n",
    "    fig.set_size_inches(7,7)\n",
    "    ax.set_aspect(1.0)\n",
    "    nx.draw_networkx(draw_graph, pos={p: p for p in points}, node_size=8,\n",
    "                     with_labels=False, edgelist=g_edges, edge_color=color, width=width, ax=ax)\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ea3eb541-29f5-41ad-b1da-b5bc3b3b8fe9",
   "metadata": {},
   "source": [
    "## Greedy-Heuristik\n",
    "Genau wie bei SAT."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "ce48e10e-4a97-4396-864a-3e7efaa4a8fa",
   "metadata": {},
   "outputs": [],
   "source": [
    "class GreedyDBST:\n",
    "    \"\"\"\n",
    "    Löse Degree-Constrained Bottleneck Spanning Tree mit einer Greedy-Heuristik.\n",
    "    Geht durch die (aufsteigend nach Länge sortierte) Liste der möglichen Kanten,\n",
    "    und fügt eine Kante ein, wenn das vom Grad her noch geht und die Endpunkte\n",
    "    noch nicht in derselben Zusammenhangskomponente sind (im Prinzip wie Kruskal).\n",
    "    \"\"\"\n",
    "    def __init__(self, points, degree):\n",
    "        self.points = points\n",
    "        self.all_edges = all_edges(points)\n",
    "        self._component_of = {v: v for v in points}\n",
    "        self.degree = degree\n",
    "        \n",
    "    def __component_root(self, v):\n",
    "        cof = self._component_of[v]\n",
    "        if cof != v:\n",
    "            cof = self.__component_root(cof)\n",
    "            self._component_of[v] = cof\n",
    "        return cof\n",
    "        \n",
    "    def __merge_if_not_same_component(self, v, w):\n",
    "        cv = self.__component_root(v)\n",
    "        cw = self.__component_root(w)\n",
    "        if cv != cw:\n",
    "            self._component_of[cw] = cv\n",
    "            return True\n",
    "        return False\n",
    "    \n",
    "    def solve(self):\n",
    "        edges = []\n",
    "        degree = {v: 0 for v in self.points}\n",
    "        n = len(self.points)\n",
    "        m = 0\n",
    "        for v,w in self.all_edges:\n",
    "            if degree[v] < self.degree and degree[w] < self.degree:\n",
    "                if self.__merge_if_not_same_component(v,w):\n",
    "                    edges.append((v,w))\n",
    "                    degree[v] += 1\n",
    "                    degree[w] += 1\n",
    "                    m += 1\n",
    "                    if m == n-1:\n",
    "                        self.max_sq_length = squared_distance(v,w)\n",
    "                        print(f\"Bottleneck bei Greedy: {math.sqrt(self.max_sq_length)}\")\n",
    "                        break\n",
    "        return edges"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "84d4ae29-bade-4b66-b9cd-15a39971b340",
   "metadata": {},
   "source": [
    "## Eigentlicher Solver"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "e55fe5d3-2098-4e71-a393-bab221a4bc6d",
   "metadata": {},
   "outputs": [],
   "source": [
    "class DBSTSolverIP:\n",
    "    def __make_vars(self):\n",
    "        # Erzeuge binäre Variablen (vtype=grb.GRB.BINARY) für die Kanten\n",
    "        self.bnvars = {e: self.model_bottleneck.addVar(lb=0, ub=1, vtype=grb.GRB.BINARY)\n",
    "                       for e in self.all_edges}\n",
    "        # Erzeuge eine nicht ganzzahlige Variable (vtype=grb.GRB.CONTINUOUS) fürs Bottleneck\n",
    "        self.l = self.model_bottleneck.addVar(lb=0,\n",
    "                                              ub=squared_distance(*self.all_edges[-1]),\n",
    "                                              vtype=grb.GRB.CONTINUOUS)\n",
    "        \n",
    "    def __add_degree_bounds(self, model, varmap):\n",
    "        for v in self.points:\n",
    "            edgevars = 0\n",
    "            for e in self.edges_of[v]:\n",
    "                if e in varmap:\n",
    "                    edgevars += varmap[e]\n",
    "            model.addConstr(edgevars >= 1)\n",
    "            model.addConstr(edgevars <= self.degree)\n",
    "            \n",
    "    def __add_total_edges(self, model, varmap):\n",
    "        model.addConstr(sum(varmap.values()) == len(self.points)-1)\n",
    "\n",
    "    def __make_edges(self):\n",
    "        edges_of = {p: [] for p in self.points}\n",
    "        for e in self.all_edges:\n",
    "            edges_of[e[0]].append(e)\n",
    "            edges_of[e[1]].append(e)\n",
    "        return edges_of\n",
    "    \n",
    "    def __add_bottleneck_constraints(self):\n",
    "        for e, x_e in self.bnvars.items():\n",
    "            self.model_bottleneck.addConstr(self.l >= squared_distance(*e) * x_e)\n",
    "    \n",
    "    def __get_integral_solution(self, model, varmap):\n",
    "        \"\"\"\n",
    "        Bestimmt den Graph, der durch die aktuelle ganzzahlige Zwischenlösung gebildet wird.\n",
    "        \"\"\"\n",
    "        variables = [x_e for e, x_e in varmap.items()]\n",
    "        values = model.cbGetSolution(variables)\n",
    "        graph = nx.empty_graph()\n",
    "        graph.add_nodes_from(self.points)\n",
    "        for i, (e, x_e) in enumerate(varmap.items()):\n",
    "            # x_e = v in der aktuellen Lösung\n",
    "            v = values[i]\n",
    "            if v >= 0.5:  # die Werte sind nicht unbedingt immer exakt genau 0 oder 1 (Numerik)\n",
    "                graph.add_edge(e[0], e[1])\n",
    "        return graph\n",
    "    \n",
    "    def __forbid_component(self, model, varmap, component):\n",
    "        \"\"\"\n",
    "        Verbiete die Komponente component, indem erzwungen wird,\n",
    "        dass wenigstens eine Kante über den Rand der Komponente gewählt werden muss.\n",
    "        \"\"\"\n",
    "        crossing_edges = 0\n",
    "        for v in component:\n",
    "            for e in self.edges_of[v]:\n",
    "                if e in varmap:\n",
    "                    target = e[0] if e[0] != v else e[1]\n",
    "                    if target not in component:\n",
    "                        crossing_edges += varmap[e]\n",
    "        # Das eigentliche Constraint wird statt mit addConstr über cbLazy eingefügt.\n",
    "        model.cbLazy(crossing_edges >= 1)\n",
    "    \n",
    "    def __callback_integral(self, model, varmap):\n",
    "        # Hier müssen wir überprüfen, ob die Lösung zusammenhängend ist.\n",
    "        # Falls das nicht der Fall ist, müssen wir zusätzliche Bedingungen hinzufügen,\n",
    "        # die die aktuelle Lösung verbieten.\n",
    "        graph = self.__get_integral_solution(model, varmap)\n",
    "        for component in nx.connected_components(graph):\n",
    "            if len(component) == len(self.points):\n",
    "                # Die Komponente enthält alle Knoten,\n",
    "                # der Graph ist also zusammenhängend\n",
    "                return\n",
    "            self.__forbid_component(model, varmap, component)\n",
    "\n",
    "    def __callback_fractional(self, model, varmap):\n",
    "        # hier müssen wir streng genommen nichts tun;\n",
    "        # es gibt allerdings Dinge die wir tun können,\n",
    "        # die die Suche beschleunigen können.\n",
    "        # Die aktuelle Lösung erhalten wir über die Methode\n",
    "        # model.cbGetNodeRel(Liste der Variablen).\n",
    "        # Sie kann nicht-ganzzahlige Werte für die Variablen enthalten,\n",
    "        # die eigentlich ganzzahlig sein sollten.\n",
    "        pass\n",
    "\n",
    "    def callback(self, where, model, varmap):\n",
    "        if where == grb.GRB.Callback.MIPSOL:\n",
    "            # wir haben eine ganzzahlige Zwischenlösung\n",
    "            self.__callback_integral(model, varmap)\n",
    "        elif where == grb.GRB.Callback.MIPNODE and \\\n",
    "             model.cbGet(grb.GRB.Callback.MIPNODE_STATUS) == grb.GRB.OPTIMAL:\n",
    "            # wir haben eine nicht-ganzzahlige Zwischenlösung\n",
    "            self.__callback_fractional(model, varmap)\n",
    "\n",
    "    def __init__(self, points, edges, degree):\n",
    "        self.points = points\n",
    "        self.all_edges = edges\n",
    "        self.degree = degree\n",
    "        self.edges_of = self.__make_edges()\n",
    "        self.model_bottleneck = grb.Model()  # Das IP-Modell für das min Bottleneck\n",
    "        self.model_minsum = grb.Model()\n",
    "        self.remaining_edges = None\n",
    "        self.msvars = None\n",
    "        self.__make_vars()\n",
    "        self.__add_degree_bounds(self.model_bottleneck, self.bnvars)\n",
    "        self.__add_total_edges(self.model_bottleneck, self.bnvars)\n",
    "        self.__add_bottleneck_constraints()\n",
    "        # Wir müssen vorher ankündigen, dass wir Lazy Constraints nutzen.\n",
    "        # Sonst macht der Solver möglicherweise Optimierungen, die nur zulässig sind,\n",
    "        # wenn er alle Constraints vorher kennt, und wir bekommen eine Exception.\n",
    "        self.model_bottleneck.Params.lazyConstraints = 1\n",
    "        # Setze die Zielfunktion\n",
    "        self.model_bottleneck.setObjective(self.l, grb.GRB.MINIMIZE)\n",
    "    \n",
    "    def __init_minsum_model(self):\n",
    "        # Erzeuge binäre Variablen (vtype=grb.GRB.BINARY) für die Kanten\n",
    "        self.msvars = {e: self.model_minsum.addVar(lb=0, ub=1, vtype=grb.GRB.BINARY)\n",
    "                       for e in self.remaining_edges}\n",
    "        self.__add_degree_bounds(self.model_minsum, self.msvars)\n",
    "        self.__add_total_edges(self.model_minsum, self.msvars)\n",
    "        self.model_minsum.Params.lazyConstraints = 1\n",
    "        obj = sum((math.sqrt(squared_distance(*e)) * x_e for e, x_e in self.msvars.items()))\n",
    "        self.model_minsum.setObjective(obj, grb.GRB.MINIMIZE)\n",
    "    \n",
    "    def __solve_bottleneck(self):\n",
    "        # Finde optimales Bottleneck\n",
    "        cb_bn = lambda model, where: self.callback(where, model, self.bnvars)\n",
    "        self.model_bottleneck.optimize(cb_bn)\n",
    "        if self.model_bottleneck.status != grb.GRB.OPTIMAL:\n",
    "            raise RuntimeError(\"Unerwarteter Status nach Optimierung!\")\n",
    "        sqlen = int(round(self.model_bottleneck.objVal))\n",
    "        print(f\"Optimales Bottleneck: {math.sqrt(sqlen)}\")\n",
    "        self.remaining_edges = filter_edges(self.all_edges, sqlen)\n",
    "        self.__init_minsum_model()\n",
    "        \n",
    "    def __solve_minsum(self):\n",
    "        # Finde optimalen Baum\n",
    "        cb_ms = lambda model, where: self.callback(where, model, self.msvars)\n",
    "        self.model_minsum.optimize(cb_ms)\n",
    "        if self.model_bottleneck.status != grb.GRB.OPTIMAL:\n",
    "            raise RuntimeError(\"Unerwarteter Status nach Optimierung!\")\n",
    "        # Gib alle Kanten mit Wert >= 0.5 zurück\n",
    "        # (es gibt numerische Gründe für >= 0.5 statt == 1)\n",
    "        return [e for e, x_e in self.msvars.items() if x_e.x >= 0.5]\n",
    "    \n",
    "    def solve(self):\n",
    "        self.__solve_bottleneck()\n",
    "        return self.__solve_minsum()\n",
    "\n",
    "        \n",
    "def solve(points, degree):\n",
    "    greedy = GreedyDBST(points, degree)\n",
    "    greedy_sol = greedy.solve()\n",
    "    remaining_edges = filter_edges(greedy.all_edges, greedy.max_sq_length)\n",
    "    ip = DBSTSolverIP(points, remaining_edges, degree)\n",
    "    return ip.solve()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "781aa3bb-c937-4167-ac46-3d4baeb64386",
   "metadata": {},
   "outputs": [],
   "source": [
    "points = random_points(100)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "59292449-026c-409d-a002-0ede0433a892",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Bottleneck bei Greedy: 1918.5955801054063\n",
      "Changed value of parameter lazyConstraints to 1\n",
      "   Prev: 0  Min: 0  Max: 1  Default: 0\n",
      "Gurobi Optimizer version 9.1.1 build v9.1.1rc0 (mac64)\n",
      "Thread count: 4 physical cores, 8 logical processors, using up to 8 threads\n",
      "Optimize a model with 670 rows, 470 columns and 3283 nonzeros\n",
      "Model fingerprint: 0xfa4262eb\n",
      "Variable types: 1 continuous, 469 integer (469 binary)\n",
      "Coefficient statistics:\n",
      "  Matrix range     [1e+00, 4e+06]\n",
      "  Objective range  [1e+00, 1e+00]\n",
      "  Bounds range     [1e+00, 4e+06]\n",
      "  RHS range        [1e+00, 1e+02]\n",
      "Presolve removed 479 rows and 3 columns\n",
      "Presolve time: 0.00s\n",
      "Presolved: 191 rows, 467 columns, 2315 nonzeros\n",
      "Variable types: 0 continuous, 467 integer (467 binary)\n",
      "\n",
      "Root relaxation: objective 3.681009e+06, 81 iterations, 0.00 seconds\n",
      "\n",
      "    Nodes    |    Current Node    |     Objective Bounds      |     Work\n",
      " Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time\n",
      "\n",
      "     0     0 3681009.00    0    4          - 3681009.00      -     -    0s\n",
      "     0     0 3681009.00    0   10          - 3681009.00      -     -    0s\n",
      "     0     0 3681009.00    0    6          - 3681009.00      -     -    0s\n",
      "     0     2 3681009.00    0    9          - 3681009.00      -     -    0s\n",
      "*   95    16               7    3681009.0000 3681009.00  0.00%   3.7    0s\n",
      "\n",
      "Cutting planes:\n",
      "  Lazy constraints: 212\n",
      "\n",
      "Explored 160 nodes (792 simplex iterations) in 0.21 seconds\n",
      "Thread count was 8 (of 8 available processors)\n",
      "\n",
      "Solution count 1: 3.68101e+06 \n",
      "\n",
      "Optimal solution found (tolerance 1.00e-04)\n",
      "Best objective 3.681009000000e+06, best bound 3.681009000000e+06, gap 0.0000%\n",
      "\n",
      "User-callback calls 513, time in user-callback 0.10 sec\n",
      "Optimales Bottleneck: 1918.5955801054063\n",
      "Changed value of parameter lazyConstraints to 1\n",
      "   Prev: 0  Min: 0  Max: 1  Default: 0\n",
      "Gurobi Optimizer version 9.1.1 build v9.1.1rc0 (mac64)\n",
      "Thread count: 4 physical cores, 8 logical processors, using up to 8 threads\n",
      "Optimize a model with 201 rows, 469 columns and 2345 nonzeros\n",
      "Model fingerprint: 0x70c3c96d\n",
      "Variable types: 0 continuous, 469 integer (469 binary)\n",
      "Coefficient statistics:\n",
      "  Matrix range     [1e+00, 1e+00]\n",
      "  Objective range  [7e+01, 2e+03]\n",
      "  Bounds range     [1e+00, 1e+00]\n",
      "  RHS range        [1e+00, 1e+02]\n",
      "Presolve removed 10 rows and 2 columns\n",
      "Presolve time: 0.00s\n",
      "Presolved: 191 rows, 467 columns, 2315 nonzeros\n",
      "Variable types: 0 continuous, 467 integer (467 binary)\n",
      "\n",
      "Root relaxation: objective 5.972984e+04, 43 iterations, 0.00 seconds\n",
      "\n",
      "    Nodes    |    Current Node    |     Objective Bounds      |     Work\n",
      " Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time\n",
      "\n",
      "     0     0 61746.6735    0    4          - 61746.6735      -     -    0s\n",
      "     0     0 62518.5506    0   31          - 62518.5506      -     -    0s\n",
      "     0     0 63093.9798    0   18          - 63093.9798      -     -    0s\n",
      "     0     0 63101.1671    0   24          - 63101.1671      -     -    0s\n",
      "     0     0 63163.7378    0   24          - 63163.7378      -     -    0s\n",
      "     0     0 63229.2871    0   22          - 63229.2871      -     -    0s\n",
      "     0     0 63416.3201    0   29          - 63416.3201      -     -    0s\n",
      "     0     0 63477.7205    0   24          - 63477.7205      -     -    0s\n",
      "     0     0 63477.7205    0   24          - 63477.7205      -     -    0s\n",
      "     0     2 63477.7205    0   24          - 63477.7205      -     -    0s\n",
      " 13989 11783 66129.3988   36   22          - 65705.9009      -   7.5    5s\n",
      " 32481 28680 66830.5284   50   24          - 65818.3099      -   7.1   10s\n",
      "*47132 34865             176    68988.462661 65877.2869  4.51%   6.9   14s\n",
      "*47304 32206             163    68665.783639 65878.7705  4.06%   6.9   14s\n",
      " 47724 32436 66672.7919   44   36 68665.7836 65879.3386  4.06%   6.9   15s\n",
      "H52126 34290                    68519.316465 65924.1302  3.79%   6.9   18s\n",
      " 55615 37343 68002.5391  112   17 68519.3165 65954.6783  3.74%   6.8   20s\n",
      "*61582 41868             145    68504.459351 65997.3165  3.66%   6.8   21s\n",
      "*64851 43648             162    68460.083559 66014.3888  3.57%   6.8   23s\n",
      "*66873 40569             162    68235.596512 66023.3597  3.24%   6.8   23s\n",
      " 69484 42547 68118.4221   85   10 68235.5965 66036.0005  3.22%   6.8   25s\n",
      " 80057 50350 67721.8180   59   26 68235.5965 66077.6498  3.16%   6.9   30s\n",
      "*84611 51830             153    68175.720226 66093.4232  3.05%   6.9   31s\n",
      "H85401 48076                    68024.408069 66095.3386  2.84%   6.9   32s\n",
      "H85403 43207                    67863.666742 66095.3386  2.61%   6.9   32s\n",
      " 89897 46152 66396.3244   48   30 67863.6667 66110.6463  2.58%   7.1   35s\n",
      " 101144 53573 66674.3675   43   33 67863.6667 66151.7900  2.52%   7.2   40s\n",
      " 110719 59620 66641.0387   38   28 67863.6667 66182.7708  2.48%   7.4   45s\n",
      " 120936 66190 67846.4741   70   30 67863.6667 66207.1812  2.44%   7.5   50s\n",
      " 130741 72428 66884.7414   44   33 67863.6667 66227.6028  2.41%   7.6   55s\n",
      " 139537 77646 66725.4563   49   29 67863.6667 66245.4110  2.38%   7.6   60s\n",
      " 149414 84093 66286.9973   42   20 67863.6667 66263.1922  2.36%   7.7   65s\n",
      " 159746 90710 66859.3623   48   29 67863.6667 66281.2011  2.33%   7.7   70s\n",
      " 170741 97553 67802.2334   68   16 67863.6667 66297.6467  2.31%   7.8   75s\n",
      " 179778 103229 67474.5691   53   23 67863.6667 66310.6496  2.29%   7.8   80s\n",
      " 187823 108012     cutoff   61      67863.6667 66323.1588  2.27%   7.9   85s\n",
      " 198472 114680 67837.5683   74   18 67863.6667 66338.2120  2.25%   7.9   90s\n",
      " 207922 120352 67180.5998   62   34 67863.6667 66348.5893  2.23%   7.9   95s\n",
      " 218108 126631 67788.3817   56   24 67863.6667 66360.4202  2.22%   8.0  100s\n",
      " 228110 132784 67425.9132   57   22 67863.6667 66372.4288  2.20%   8.0  105s\n",
      " 237391 138090 67842.7904   62   28 67863.6667 66382.3153  2.18%   8.0  110s\n",
      " 247774 144560     cutoff   56      67863.6667 66392.0133  2.17%   8.0  115s\n",
      " 258668 150698 66860.2231   46   37 67863.6667 66402.2850  2.15%   8.0  120s\n",
      " 268963 156934 66858.6619   49   24 67863.6667 66411.0660  2.14%   8.1  125s\n",
      " 279521 163328 67780.4331   71   27 67863.6667 66420.3515  2.13%   8.1  130s\n",
      " 289048 169074 67837.7662   92   10 67863.6667 66427.5097  2.12%   8.1  135s\n",
      " 298862 174454 66719.2552   46   25 67863.6667 66436.3046  2.10%   8.1  140s\n",
      " 309177 180282 66870.6044   51   30 67863.6667 66445.1786  2.09%   8.1  145s\n",
      " 319370 186203 67192.0818   47   25 67863.6667 66453.0699  2.08%   8.1  150s\n",
      " 328341 191606 66948.6978   52   30 67863.6667 66459.5273  2.07%   8.1  155s\n",
      " 337895 197335 67252.2607   51   27 67863.6667 66466.8054  2.06%   8.1  160s\n",
      " 345957 202051 67796.8851   61   22 67863.6667 66472.1832  2.05%   8.2  165s\n",
      " 354162 206418 67119.2330   53   26 67863.6667 66477.3105  2.04%   8.2  170s\n",
      " 364558 212426     cutoff   76      67863.6667 66484.5359  2.03%   8.2  175s\n",
      " 373702 217764 67003.8116   42   37 67863.6667 66490.1937  2.02%   8.2  180s\n",
      " 383966 223351 67862.3428   59   29 67863.6667 66496.0227  2.02%   8.2  185s\n",
      " 393657 228974 66641.1384   42   33 67863.6667 66501.9187  2.01%   8.2  190s\n",
      " 403303 234511 67798.8709   61   21 67863.6667 66507.1843  2.00%   8.2  195s\n",
      " 412405 240051     cutoff   53      67863.6667 66511.9182  1.99%   8.2  200s\n",
      " 423565 246087 67304.7863   53   26 67863.6667 66518.9137  1.98%   8.2  205s\n",
      " 431540 250764 67730.8589   61   16 67863.6667 66522.8526  1.98%   8.2  210s\n",
      " 441903 256546 67734.5477   58   28 67863.6667 66528.7026  1.97%   8.3  215s\n",
      " 451784 262173 66779.8146   47   22 67863.6667 66533.7323  1.96%   8.3  220s\n",
      " 461370 267737 66941.8361   48   22 67863.6667 66539.0286  1.95%   8.3  225s\n",
      " 469062 272112 67785.3625   58   28 67863.6667 66542.3430  1.95%   8.3  230s\n",
      " 477718 276589     cutoff   60      67863.6667 66546.6307  1.94%   8.3  235s\n",
      " 488051 282223 67313.5073   47   24 67863.6667 66551.8927  1.93%   8.3  240s\n",
      " 497544 287387 66921.2946   48   33 67863.6667 66556.4581  1.93%   8.3  245s\n",
      " 508203 293158 67796.4045  101    8 67863.6667 66561.3618  1.92%   8.3  250s\n",
      " 518393 298716 67248.8757   54   24 67863.6667 66565.9799  1.91%   8.3  255s\n",
      " 529108 304600 67615.4199   80   19 67863.6667 66570.6083  1.91%   8.3  260s\n",
      "H535464 297079                    67784.689665 66573.3974  1.79%   8.3  262s\n",
      " 538815 299063 67695.9477   59   28 67784.6897 66574.8928  1.78%   8.3  265s\n",
      " 550140 304731 67667.0679   73   16 67784.6897 66579.7281  1.78%   8.3  270s\n",
      " 559806 309622     cutoff   59      67784.6897 66583.8284  1.77%   8.3  275s\n",
      " 568609 314341 67496.4582   64   16 67784.6897 66587.6917  1.77%   8.3  280s\n",
      " 579343 319661 67165.2797   44   22 67784.6897 66591.9865  1.76%   8.3  285s\n",
      " 586782 323612 67606.9428   50   24 67784.6897 66594.6446  1.76%   8.3  290s\n",
      " 596539 328515 66776.9128   37   37 67784.6897 66598.6566  1.75%   8.3  295s\n",
      " 605886 333228 67494.9097   50   34 67784.6897 66602.0400  1.74%   8.4  300s\n",
      " 614208 337523 67098.5197   57   34 67784.6897 66605.3128  1.74%   8.4  305s\n",
      " 624352 342496 67765.1763   64   25 67784.6897 66609.5331  1.73%   8.4  310s\n",
      " 632726 346275 66996.8894   45   24 67784.6897 66612.3531  1.73%   8.4  315s\n",
      " 641769 351496 67432.0755   53   28 67784.6897 66615.8808  1.72%   8.4  320s\n",
      " 650441 355747 67421.2660   57   27 67784.6897 66619.0160  1.72%   8.4  325s\n",
      " 659373 360028 67597.3593   54   29 67784.6897 66622.2971  1.71%   8.4  330s\n",
      " 669447 364873 66645.5133   49   32 67784.6897 66625.7582  1.71%   8.4  335s\n",
      " 678667 369480 67397.4325   69   17 67784.6897 66629.1211  1.70%   8.4  340s\n",
      " 688461 374183     cutoff   69      67784.6897 66632.5108  1.70%   8.4  345s\n",
      " 698414 379143 67452.6379   49   16 67784.6897 66635.7282  1.70%   8.4  350s\n",
      " 707177 383254 67392.0473   58   30 67784.6897 66638.6710  1.69%   8.4  355s\n",
      " 717031 388218 67627.5640   57   21 67784.6897 66641.7969  1.69%   8.4  360s\n",
      " 726812 392990 67127.1787   44   29 67784.6897 66644.7668  1.68%   8.4  365s\n",
      " 734957 396990 66884.4227   51   27 67784.6897 66647.3006  1.68%   8.4  370s\n",
      " 745094 401944 66861.8459   43   26 67784.6897 66650.4592  1.67%   8.4  375s\n",
      " 754690 406460 67644.8363   50   28 67784.6897 66653.2871  1.67%   8.4  380s\n",
      " 763596 410586 66875.1284   53   30 67784.6897 66655.8379  1.67%   8.4  385s\n",
      " 771708 414403 67778.0212   77    - 67784.6897 66657.9596  1.66%   8.4  390s\n",
      " 781389 419259 67485.2281   57   22 67784.6897 66661.0186  1.66%   8.4  395s\n",
      " 790444 423901 66860.8818   43   24 67784.6897 66663.3930  1.65%   8.4  400s\n",
      " 800128 428516 67462.7277   65   25 67784.6897 66666.4175  1.65%   8.4  405s\n",
      " 809103 432946 67417.2201   65   30 67784.6897 66668.8959  1.65%   8.4  410s\n",
      " 819067 437572 67317.8637   48   30 67784.6897 66671.5111  1.64%   8.4  415s\n",
      " 828144 441808 67465.8577   48   28 67784.6897 66674.1792  1.64%   8.4  420s\n",
      " 837387 446188     cutoff   57      67784.6897 66676.7831  1.63%   8.4  425s\n",
      " 847111 450722 67049.1391   65   31 67784.6897 66679.3620  1.63%   8.4  430s\n",
      " 856121 454368 67568.8117   59   37 67784.6897 66681.6587  1.63%   8.4  435s\n",
      " 863981 458543 66794.3141   44   24 67784.6897 66683.6780  1.62%   8.4  440s\n",
      " 871516 462498 67240.8740   50   35 67784.6897 66685.6293  1.62%   8.4  445s\n",
      " 880488 466848 66954.9609   49   34 67784.6897 66688.0161  1.62%   8.4  450s\n",
      " 889393 470976     cutoff   74      67784.6897 66690.2413  1.61%   8.4  455s\n",
      " 898165 475313 67555.6473   54   20 67784.6897 66692.6947  1.61%   8.4  460s\n",
      " 906586 479062 67534.8714   48   28 67784.6897 66694.7366  1.61%   8.4  465s\n",
      " 915749 483082     cutoff   69      67784.6897 66697.0031  1.60%   8.4  470s\n",
      " 926078 488205 67070.9066   49   24 67784.6897 66699.5702  1.60%   8.4  475s\n",
      " 935253 492198 67725.6254   66   20 67784.6897 66701.7236  1.60%   8.4  480s\n",
      " 945325 497051     cutoff   60      67784.6897 66704.1504  1.59%   8.4  485s\n",
      " 954390 501068     cutoff   61      67784.6897 66706.3393  1.59%   8.4  490s\n",
      " 962248 504946 67614.3996   55   21 67784.6897 66708.2850  1.59%   8.4  495s\n",
      " 971715 509358 66957.9452   54   40 67784.6897 66710.2605  1.59%   8.4  500s\n",
      " 980848 513606 66926.3920   50   33 67784.6897 66712.1928  1.58%   8.5  505s\n",
      " 990919 518203 66930.8350   64   18 67784.6897 66714.4064  1.58%   8.5  510s\n",
      " 999655 522451 67583.7692   54   30 67784.6897 66716.3361  1.58%   8.5  515s\n",
      " 1009960 527254 66779.5980   53   22 67784.6897 66718.7894  1.57%   8.5  520s\n",
      " 1018714 531257 67604.9369   49   28 67784.6897 66720.6244  1.57%   8.5  525s\n",
      " 1029256 536217 67575.8578   67   23 67784.6897 66722.9874  1.57%   8.5  530s\n",
      " 1038070 540212 67484.0551   49   25 67784.6897 66725.1376  1.56%   8.5  535s\n",
      " 1046963 544279 67557.8589   50   27 67784.6897 66727.0296  1.56%   8.5  540s\n",
      " 1056617 548652 67705.4197   50   28 67784.6897 66729.1094  1.56%   8.5  545s\n",
      " 1065680 553031 67442.4064   46   33 67784.6897 66730.9509  1.55%   8.5  550s\n",
      " 1075906 556911 67617.4918   57   17 67784.6897 66732.9121  1.55%   8.5  555s\n",
      " 1083686 560542 67501.4524   44   22 67784.6897 66734.6312  1.55%   8.5  560s\n",
      " 1092550 564611 66972.3803   46   26 67784.6897 66736.4920  1.55%   8.5  565s\n",
      " 1101138 568463 66879.7430   49   42 67784.6897 66738.3822  1.54%   8.5  570s\n",
      " 1111415 573316 67757.5137   51   31 67784.6897 66740.5307  1.54%   8.5  575s\n",
      " 1121516 577410 67420.7498   57   36 67784.6897 66742.5390  1.54%   8.5  580s\n",
      " 1129342 581389 67103.7967   52   30 67784.6897 66744.1504  1.54%   8.5  585s\n",
      " 1139868 586028 infeasible   81      67784.6897 66746.1716  1.53%   8.5  590s\n",
      " 1148934 590174 67319.3366   56   25 67784.6897 66748.1318  1.53%   8.5  595s\n",
      " 1158192 594272     cutoff   58      67784.6897 66750.0772  1.53%   8.5  600s\n",
      " 1168133 598734 67513.6080   53   38 67784.6897 66751.7291  1.52%   8.5  605s\n",
      " 1177001 602595 66990.5419   45   30 67784.6897 66753.5064  1.52%   8.5  610s\n",
      " 1187346 607029 67154.9855   49   28 67784.6897 66755.5167  1.52%   8.5  615s\n",
      " 1195292 610720 67130.1296   44   25 67784.6897 66756.9031  1.52%   8.5  620s\n",
      " 1203615 614689 67354.3427   49   21 67784.6897 66758.4882  1.51%   8.5  625s\n",
      " 1213052 618807 67417.4784   47   24 67784.6897 66760.1294  1.51%   8.5  630s\n",
      " 1222255 622795 67751.0029   48   23 67784.6897 66761.9021  1.51%   8.5  635s\n",
      " 1231486 626723 67480.8947   42   27 67784.6897 66763.5594  1.51%   8.5  640s\n",
      " 1241709 631329     cutoff   58      67784.6897 66765.4754  1.50%   8.5  645s\n",
      " 1250954 635429 67023.0770   52   22 67784.6897 66767.0901  1.50%   8.5  650s\n",
      " 1260949 639600 67598.1456   56   20 67784.6897 66768.9355  1.50%   8.5  655s\n",
      " 1270337 643781 67534.4962   54   25 67784.6897 66770.5326  1.50%   8.5  660s\n",
      " 1279183 647704 67473.1291   52   24 67784.6897 66772.1859  1.49%   8.5  665s\n",
      " 1287942 651570     cutoff   50      67784.6897 66773.7986  1.49%   8.5  670s\n",
      "\n",
      "Cutting planes:\n",
      "  Gomory: 8\n",
      "  MIR: 27\n",
      "  Flow cover: 27\n",
      "  Zero half: 62\n",
      "  Lazy constraints: 2825\n",
      "\n",
      "Explored 1289942 nodes (10940722 simplex iterations) in 671.10 seconds\n",
      "Thread count was 8 (of 8 available processors)\n",
      "\n",
      "Solution count 10: 67784.7 67863.7 68024.4 ... 68988.5\n",
      "\n",
      "Solve interrupted\n",
      "Best objective 6.778468966455e+04, best bound 6.677416308379e+04, gap 1.4908%\n",
      "\n",
      "User-callback calls 2603210, time in user-callback 10.39 sec\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 504x504 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "draw_edges(solve(points, 3))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b5180deb-4cc3-41f2-a440-e04f787b93df",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.7.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}