{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# GeoCert : 2-dimensional Toy Example\n",
    "In this example, we'll train a simple neural network and demonstrate the various capabilities of GeoCert.\n",
    "\n",
    "The network training code was largely copied from [Eric Wong's repository found here.](https://github.com/locuslab/convex_adversarial/blob/master/examples/2D.ipynb)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import stack\n",
    "import sys \n",
    "sys.path.append('..')\n",
    "sys.path.append('../mister_ed')\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.optim as optim\n",
    "from torch.autograd import Variable\n",
    "from torchvision import datasets, transforms\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn\n",
    "import matplotlib.patches as patches\n",
    "from scipy.spatial import HalfspaceIntersection\n",
    "\n",
    "seaborn.set(font_scale=2)\n",
    "seaborn.set_style(\"white\")\n",
    "\n",
    "import numpy as np \n",
    "%matplotlib inline\n",
    "seaborn.set(font_scale=2)\n",
    "seaborn.set_style(\"white\")\n",
    "from geocert import GeoCert\n",
    "from plnn import PLNN\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[2, 20, 50, 20, 2]\n",
      "0.7069634795188904 tensor(0.5833)\n",
      "0.5537288784980774 tensor(0.2500)\n",
      "0.4080643653869629 tensor(0.2500)\n",
      "0.3087824881076813 tensor(0.1667)\n",
      "0.1617790013551712 tensor(0.)\n",
      "0.04387936368584633 tensor(0.)\n",
      "0.014375497587025166 tensor(0.)\n",
      "0.006310502532869577 tensor(0.)\n",
      "0.0034499764442443848 tensor(0.)\n",
      "0.0021875500679016113 tensor(0.)\n",
      "0.0015173951396718621\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/matt/configs/env/lib/python3.6/site-packages/torch/tensor.py:339: UserWarning: non-inplace resize is deprecated\n",
      "  warnings.warn(\"non-inplace resize is deprecated\")\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 21772 characters]\n",
      "text/plain": [
       "<Figure size 576x576 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Training block -- train a simple 2d Network \n",
    "# --- define network \n",
    "plnn_obj = PLNN(layer_sizes=[2, 20, 50, 20, 2])\n",
    "net = plnn_obj.net\n",
    "\n",
    "# --- define random training points \n",
    "m = 12\n",
    "np.random.seed(3)\n",
    "x = [np.random.uniform(size=(2))]\n",
    "r = 0.16\n",
    "while(len(x) < m):\n",
    "    p = np.random.uniform(size=(2))\n",
    "    if min(np.abs(p-a).sum() for a in x) > 2*r:\n",
    "        x.append(p)\n",
    "epsilon = r/2\n",
    "X = torch.Tensor(np.array(x))\n",
    "torch.manual_seed(1)\n",
    "y = (torch.rand(m)+0.5).long()\n",
    "\n",
    "# --- train network \n",
    "opt = optim.Adam(net.parameters(), lr=1e-3)\n",
    "for i in range(1000):\n",
    "    out = net(Variable(X))\n",
    "    l = nn.CrossEntropyLoss()(out, Variable(y))\n",
    "    err = (out.max(1)[1].data != y).float().mean()\n",
    "    if i % 100 == 0:\n",
    "        print(l.item(), err)\n",
    "    opt.zero_grad()\n",
    "    (l).backward()\n",
    "    opt.step()\n",
    "print(l.item())\n",
    "\n",
    "# --- display network decision boundaries\n",
    "XX, YY = np.meshgrid(np.linspace(0, 1, 100), np.linspace(0, 1, 100))\n",
    "X0 = Variable(torch.Tensor(np.stack([np.ravel(XX), np.ravel(YY)]).T))\n",
    "y0 = net(X0)\n",
    "ZZ = (y0[:,0] - y0[:,1]).resize(100,100).data.numpy()\n",
    "\n",
    "fig, ax = plt.subplots(figsize=(8,8))\n",
    "ax.contourf(XX,YY,-ZZ, cmap=\"coolwarm\", levels=np.linspace(-1000,1000,3))\n",
    "ax.scatter(X.numpy()[:,0], X.numpy()[:,1], c=y.numpy(), cmap=\"coolwarm\", s=70)\n",
    "ax.axis(\"equal\")\n",
    "ax.axis([0,1,0,1])\n",
    "\n",
    "for a in x:\n",
    "    ax.add_patch(patches.Rectangle((a[0]-r/2, a[1]-r/2), r, r, fill=False))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Using GeoCert:\n",
    "GeoCert is run through the `GeoCert` object interface. Initialization requires knowledge of which neural network to certify, and optionally (useful for most applications) a bounds on the valid domain of the network. Individual points are verified using the `.run(...)` method. We can solve three types of problems:\n",
    "\n",
    "- `min_dist`: Computes the minimum distance to a point that is classified differently than the specified point\n",
    "- `decision_problem`: Returns yes/no whether or not an adversarial example exists within the specified radius of the input point \n",
    "- `count_regions`: Counts the number of linear regions within an $\\ell_p$ ball of the input point"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Initialize a GeoCert object to be reused\n",
    "geo = GeoCert(plnn_obj, hyperbox_bounds=(0.0, 1.0), verbose=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Minimum Distance : $\\ell_\\infty$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Academic license - for non-commercial use only\n"
     ]
    }
   ],
   "source": [
    "# Single example for computing L_inf min_dists\n",
    "test_point = torch.Tensor([0.420, 0.69])\n",
    "linf_output = geo.run(test_point, lp_norm='l_inf', problem_type='min_dist')\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "GeoCert Return Object\n",
      "\tProblem Type: min_dist\n",
      "\tStatus: SUCCESS\n",
      "\tRobustness: 0.0196\n"
     ]
    }
   ],
   "source": [
    "# Example printout from the return object\n",
    "print(linf_output)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original Logits    : [ 0.64337 -1.07056]\n",
      "Adversarial Logits : [-0.25114 -0.25115]\n"
     ]
    }
   ],
   "source": [
    "# Can collect the minimal distance adversarial example and examine its logits \n",
    "original_logits = net(test_point)\n",
    "adversarial_logits = net(torch.Tensor(linf_output.best_ex))\n",
    "print(\"Original Logits    :\", original_logits.detach().numpy())\n",
    "print(\"Adversarial Logits :\", adversarial_logits.detach().numpy())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.patches.Rectangle at 0x7f8a4006db00>"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 17204 characters]\n",
      "text/plain": [
       "<Figure size 576x576 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# And we can plot the ball around this to demonstrate...\n",
    "fig, ax = plt.subplots(figsize=(8,8))\n",
    "ax.contourf(XX,YY,-ZZ, cmap=\"coolwarm\", levels=np.linspace(-1000,1000,3))\n",
    "ax.axis(\"equal\")\n",
    "ax.axis([0,1,0,1])\n",
    "ax.scatter([test_point[0].item()], [test_point[1].item()], c=[0], cmap=\"coolwarm\", s=70)\n",
    "ax.add_patch(patches.Rectangle((test_point[0].item()-linf_output.best_dist, \n",
    "                                test_point[1].item()-linf_output.best_dist), \n",
    "                               linf_output.best_dist * 2, linf_output.best_dist * 2, fill=False))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 21060 characters]\n",
      "text/plain": [
       "<Figure size 576x576 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Now trying with several points to give more pretty pictures\n",
    "fig, ax = plt.subplots(figsize=(8,8))\n",
    "ax.contourf(XX,YY,-ZZ, cmap=\"coolwarm\", levels=np.linspace(-1000,1000,3))\n",
    "ax.axis(\"equal\")\n",
    "ax.axis([0,1,0,1])\n",
    "for i in range(10):\n",
    "    test_point = torch.rand(2)\n",
    "    linf_output = geo.run(test_point, lp_norm='l_inf', problem_type='min_dist')\n",
    "    ax.scatter([test_point[0].item()], [test_point[1].item()], c=[0], cmap=\"coolwarm\", s=70)\n",
    "    ax.add_patch(patches.Rectangle((test_point[0].item()-linf_output.best_dist, \n",
    "                                test_point[1].item()-linf_output.best_dist), \n",
    "                               linf_output.best_dist * 2, linf_output.best_dist * 2, fill=False))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Minimum Distance : $\\ell_2$\n",
    "We can do the same things with $\\ell_2$ instead of $\\ell_\\infty$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "GeoCert Return Object\n",
      "\tProblem Type: min_dist\n",
      "\tStatus: SUCCESS\n",
      "\tRobustness: 0.0254\n",
      "Original Logits    : [ 0.64337 -1.07056]\n",
      "Adversarial Logits : [-0.25343 -0.25343]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<matplotlib.patches.Circle at 0x7f8a3161f828>"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 17948 characters]\n",
      "text/plain": [
       "<Figure size 576x576 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Single example for computing L_2 min_dists\n",
    "test_point = torch.Tensor([0.420, 0.69])\n",
    "l2_output = geo.run(test_point, lp_norm='l_2', problem_type='min_dist')\n",
    "\n",
    "\n",
    "# Example printout from the return object\n",
    "print(l2_output)\n",
    "\n",
    "# Can collect the minimal distance adversarial example and examine its logits \n",
    "original_logits = net(test_point)\n",
    "adversarial_logits = net(torch.Tensor(l2_output.best_ex))\n",
    "print(\"Original Logits    :\", original_logits.detach().numpy())\n",
    "print(\"Adversarial Logits :\", adversarial_logits.detach().numpy())\n",
    "\n",
    "\n",
    "# And we can plot the ball around this to demonstrate...\n",
    "fig, ax = plt.subplots(figsize=(8,8))\n",
    "ax.contourf(XX,YY,-ZZ, cmap=\"coolwarm\", levels=np.linspace(-1000,1000,3))\n",
    "ax.axis(\"equal\")\n",
    "ax.axis([0,1,0,1])\n",
    "ax.scatter([test_point[0].item()], [test_point[1].item()], c=[0], cmap=\"coolwarm\", s=70)\n",
    "ax.add_patch(patches.Circle(test_point, l2_output.best_dist, fill=False))\n",
    "                              \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Decision Problem\n",
    "Sometimes you might want to ask \"Does an adversarial example exist within a ball of specified size?\" and just want a yes/no answer. This formulation is useful when considering robustness to a particular threat model and can be faster than the `min_dist` formulation, as if we find an (even suboptimal) adversarial example, we'll return early"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SAFE OUTPUT...\n",
      "GeoCert Return Object\n",
      "\tProblem Type: decision_problem\n",
      "\tStatus: FAILURE\n",
      "\tRadius 0.01\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Recall our original test point....\n",
    "test_point = torch.Tensor([0.420, 0.69])\n",
    "l2_output = geo.run(test_point, lp_norm='l_2', problem_type='min_dist')\n",
    "# which has best dist specified by \n",
    "decision_threshold = l2_output.best_dist\n",
    "\n",
    "# Now we can try two variants on the decision problem....\n",
    "safe_region_output = geo.run(test_point, lp_norm='l_2', problem_type='decision_problem', \n",
    "                             decision_radius=decision_threshold / 2.0)\n",
    "\n",
    "print(\"SAFE OUTPUT...\")\n",
    "print(safe_region_output)\n",
    "# The printout status 'FAILURE' means 'FAILURE' to find an adversarial example within the radius"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "UNSAFE OUTPUT...\n",
      "GeoCert Return Object\n",
      "\tProblem Type: decision_problem\n",
      "\tStatus: SUCCESS\n",
      "\tRadius 0.05\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# On the other hand, if there exists an adversarial example within the radius \n",
    "# we'll output 'SUCCESS' as the status \n",
    "\n",
    "unsafe_region_output = geo.run(test_point, lp_norm='l_2', problem_type='decision_problem', \n",
    "                             decision_radius=decision_threshold * 2.0)\n",
    "\n",
    "print(\"UNSAFE OUTPUT...\")\n",
    "print(unsafe_region_output)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Counting Linear Regions\n",
    "Finally, we can leverage GeoCert to count the number of linear regions (recall that ReLU nets are piecewise linear, so by 'linear regions', I mean the number of _pieces_) in a specified ball. Note that the domain restrictions are included by default here (so even if the ball extends past the \\[0, 1\\] hyperbox we've established upon initialization)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---Initial Polytope---\n",
      "(p0) Popped: 0.012007  | 0.400000\n",
      "(p0) Popped: 0.045557  | 0.400000\n",
      "(p0) Popped: 0.058370  | 0.400000\n",
      "(p0) Popped: 0.063863  | 0.400000\n",
      "(p0) Popped: 0.065156  | 0.400000\n",
      "(p0) Popped: 0.070869  | 0.400000\n",
      "(p0) Popped: 0.080528  | 0.400000\n",
      "(p0) Popped: 0.091434  | 0.400000\n",
      "(p0) Popped: 0.096517  | 0.400000\n",
      "(p0) Popped: 0.097760  | 0.400000\n",
      "(p0) Popped: 0.102931  | 0.400000\n",
      "(p0) Popped: 0.104261  | 0.400000\n",
      "(p0) Popped: 0.104383  | 0.400000\n",
      "(p0) Popped: 0.104611  | 0.400000\n",
      "(p0) Popped: 0.104912  | 0.400000\n",
      "(p0) Popped: 0.106060  | 0.400000\n",
      "(p0) Popped: 0.107298  | 0.400000\n",
      "(p0) Popped: 0.109353  | 0.400000\n",
      "(p0) Popped: 0.109859  | 0.400000\n",
      "(p0) Popped: 0.114480  | 0.400000\n",
      "(p0) Popped: 0.115043  | 0.400000\n",
      "(p0) Popped: 0.116181  | 0.400000\n",
      "(p0) Popped: 0.116725  | 0.400000\n",
      "(p0) Popped: 0.119623  | 0.400000\n",
      "(p0) Popped: 0.120030  | 0.400000\n",
      "(p0) Popped: 0.120218  | 0.400000\n",
      "(p0) Popped: 0.120921  | 0.400000\n",
      "(p0) Popped: 0.121544  | 0.400000\n",
      "(p0) Popped: 0.122606  | 0.400000\n",
      "(p0) Popped: 0.123032  | 0.400000\n",
      "(p0) Popped: 0.128376  | 0.400000\n",
      "(p0) Popped: 0.130774  | 0.400000\n",
      "(p0) Popped: 0.131021  | 0.400000\n",
      "(p0) Popped: 0.131051  | 0.400000\n",
      "(p0) Popped: 0.132643  | 0.400000\n",
      "(p0) Popped: 0.133651  | 0.400000\n",
      "(p0) Popped: 0.135571  | 0.400000\n",
      "(p0) Popped: 0.135748  | 0.400000\n",
      "(p0) Popped: 0.135765  | 0.400000\n",
      "(p0) Popped: 0.136717  | 0.400000\n",
      "(p0) Popped: 0.138179  | 0.400000\n",
      "(p0) Popped: 0.142244  | 0.400000\n",
      "(p0) Popped: 0.142244  | 0.400000\n",
      "(p0) Popped: 0.143706  | 0.400000\n",
      "(p0) Popped: 0.147253  | 0.400000\n",
      "(p0) Popped: 0.147288  | 0.400000\n",
      "(p0) Popped: 0.148587  | 0.400000\n",
      "(p0) Popped: 0.149578  | 0.400000\n",
      "(p0) Popped: 0.150940  | 0.400000\n",
      "(p0) Popped: 0.152556  | 0.400000\n",
      "(p0) Popped: 0.153491  | 0.400000\n",
      "(p0) Popped: 0.153789  | 0.400000\n",
      "(p0) Popped: 0.155237  | 0.400000\n",
      "(p0) Popped: 0.157799  | 0.400000\n",
      "(p0) Popped: 0.159666  | 0.400000\n",
      "(p0) Popped: 0.160725  | 0.400000\n",
      "(p0) Popped: 0.176983  | 0.400000\n",
      "(p0) Popped: 0.177393  | 0.400000\n",
      "(p0) Popped: 0.177796  | 0.400000\n",
      "(p0) Popped: 0.177805  | 0.400000\n",
      "(p0) Popped: 0.181366  | 0.400000\n",
      "(p0) Popped: 0.182046  | 0.400000\n",
      "(p0) Popped: 0.182916  | 0.400000\n",
      "(p0) Popped: 0.183897  | 0.400000\n",
      "(p0) Popped: 0.184205  | 0.400000\n",
      "(p0) Popped: 0.193784  | 0.400000\n",
      "(p0) Popped: 0.195790  | 0.400000\n",
      "(p0) Popped: 0.198814  | 0.400000\n",
      "(p0) Popped: 0.202482  | 0.400000\n",
      "(p0) Popped: 0.203879  | 0.400000\n",
      "(p0) Popped: 0.204616  | 0.400000\n",
      "(p0) Popped: 0.208781  | 0.400000\n",
      "(p0) Popped: 0.213237  | 0.400000\n",
      "(p0) Popped: 0.213237  | 0.400000\n",
      "(p0) Popped: 0.212681  | 0.400000\n",
      "(p0) Popped: 0.212459  | 0.400000\n",
      "(p0) Popped: 0.211796  | 0.400000\n",
      "(p0) Popped: 0.215419  | 0.400000\n",
      "(p0) Popped: 0.216221  | 0.400000\n",
      "(p0) Popped: 0.216712  | 0.400000\n",
      "(p0) Popped: 0.216739  | 0.400000\n",
      "(p0) Popped: 0.217438  | 0.400000\n",
      "(p0) Popped: 0.218214  | 0.400000\n",
      "(p0) Popped: 0.218417  | 0.400000\n",
      "(p0) Popped: 0.220132  | 0.400000\n",
      "(p0) Popped: 0.224072  | 0.400000\n",
      "(p0) Popped: 0.225599  | 0.400000\n",
      "(p0) Popped: 0.231849  | 0.400000\n",
      "(p0) Popped: 0.232338  | 0.400000\n",
      "(p0) Popped: 0.233343  | 0.400000\n",
      "(p0) Popped: 0.235296  | 0.400000\n",
      "(p0) Popped: 0.235315  | 0.400000\n",
      "(p0) Popped: 0.235732  | 0.400000\n",
      "(p0) Popped: 0.239465  | 0.400000\n",
      "(p0) Popped: 0.242934  | 0.400000\n",
      "(p0) Popped: 0.244479  | 0.400000\n",
      "(p0) Popped: 0.245660  | 0.400000\n",
      "(p0) Popped: 0.246398  | 0.400000\n",
      "(p0) Popped: 0.246398  | 0.400000\n",
      "(p0) Popped: 0.247357  | 0.400000\n",
      "(p0) Popped: 0.249824  | 0.400000\n",
      "(p0) Popped: 0.251283  | 0.400000\n",
      "(p0) Popped: 0.252859  | 0.400000\n",
      "(p0) Popped: 0.254439  | 0.400000\n",
      "(p0) Popped: 0.254559  | 0.400000\n",
      "(p0) Popped: 0.256319  | 0.400000\n",
      "(p0) Popped: 0.261437  | 0.400000\n",
      "(p0) Popped: 0.266070  | 0.400000\n",
      "(p0) Popped: 0.273053  | 0.400000\n",
      "(p0) Popped: 0.273522  | 0.400000\n",
      "(p0) Popped: 0.273658  | 0.400000\n",
      "(p0) Popped: 0.273870  | 0.400000\n",
      "(p0) Popped: 0.277446  | 0.400000\n",
      "(p0) Popped: 0.279972  | 0.400000\n",
      "(p0) Popped: 0.279972  | 0.400000\n",
      "(p0) Popped: 0.283476  | 0.400000\n",
      "(p0) Popped: 0.283502  | 0.400000\n",
      "(p0) Popped: 0.283761  | 0.400000\n",
      "(p0) Popped: 0.285763  | 0.400000\n",
      "(p0) Popped: 0.287374  | 0.400000\n",
      "(p0) Popped: 0.287865  | 0.400000\n",
      "(p0) Popped: 0.289647  | 0.400000\n",
      "(p0) Popped: 0.294508  | 0.400000\n",
      "(p0) Popped: 0.298420  | 0.400000\n",
      "(p0) Popped: 0.300335  | 0.400000\n",
      "(p0) Popped: 0.300386  | 0.400000\n",
      "(p0) Popped: 0.301076  | 0.400000\n",
      "(p0) Popped: 0.302207  | 0.400000\n",
      "(p0) Popped: 0.303187  | 0.400000\n",
      "(p0) Popped: 0.304290  | 0.400000\n",
      "(p0) Popped: 0.307808  | 0.400000\n",
      "(p0) Popped: 0.308485  | 0.400000\n",
      "(p0) Popped: 0.309388  | 0.400000\n",
      "(p0) Popped: 0.310266  | 0.400000\n",
      "(p0) Popped: 0.311724  | 0.400000\n",
      "(p0) Popped: 0.311806  | 0.400000\n",
      "(p0) Popped: 0.314072  | 0.400000\n",
      "(p0) Popped: 0.314167  | 0.400000\n",
      "(p0) Popped: 0.314477  | 0.400000\n",
      "(p0) Popped: 0.315568  | 0.400000\n",
      "(p0) Popped: 0.316710  | 0.400000\n",
      "(p0) Popped: 0.318150  | 0.400000\n",
      "(p0) Popped: 0.318353  | 0.400000\n",
      "(p0) Popped: 0.323110  | 0.400000\n",
      "(p0) Popped: 0.324688  | 0.400000\n",
      "(p0) Popped: 0.324829  | 0.400000\n",
      "(p0) Popped: 0.326158  | 0.400000\n",
      "(p0) Popped: 0.326257  | 0.400000\n",
      "(p0) Popped: 0.326982  | 0.400000\n",
      "(p0) Popped: 0.329661  | 0.400000\n",
      "(p0) Popped: 0.329984  | 0.400000\n",
      "(p0) Popped: 0.350872  | 0.400000\n",
      "(p0) Popped: 0.351503  | 0.400000\n",
      "(p0) Popped: 0.351871  | 0.400000\n",
      "(p0) Popped: 0.353231  | 0.400000\n",
      "(p0) Popped: 0.353452  | 0.400000\n",
      "(p0) Popped: 0.359286  | 0.400000\n",
      "(p0) Popped: 0.360264  | 0.400000\n",
      "(p0) Popped: 0.362171  | 0.400000\n",
      "(p0) Popped: 0.365888  | 0.400000\n",
      "(p0) Popped: 0.367972  | 0.400000\n",
      "(p0) Popped: 0.370863  | 0.400000\n",
      "(p0) Popped: 0.371760  | 0.400000\n",
      "(p0) Popped: 0.377698  | 0.400000\n",
      "(p0) Popped: 0.378183  | 0.400000\n",
      "(p0) Popped: 0.380902  | 0.400000\n",
      "(p0) Popped: 0.380968  | 0.400000\n",
      "(p0) Popped: 0.381195  | 0.400000\n",
      "(p0) Popped: 0.384490  | 0.400000\n",
      "(p0) Popped: 0.386369  | 0.400000\n",
      "(p0) Popped: 0.392249  | 0.400000\n",
      "(p0) Popped: 0.394500  | 0.400000\n",
      "(p0) Popped: 0.397889  | 0.400000\n",
      "COUNTED 174 LINEAR REGIONS\n"
     ]
    }
   ],
   "source": [
    "geo.verbose = True # This will take a while, so let's turn on printouts\n",
    "\n",
    "test_point = torch.Tensor([0.420, 0.69])\n",
    "region_count = geo.run(test_point, lp_norm='l_inf', problem_type='count_regions', \n",
    "                       decision_radius=0.4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "GeoCert Return Object\n",
      "\tProblem Type: count_regions\n",
      "\tStatus: SUCCESS\n",
      "\tRadius 0.40\n",
      "\tNum Linear Regions: 174\n"
     ]
    }
   ],
   "source": [
    "print(region_count)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "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.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
