{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "cvt_1d.ipynb\n", "\n", "Discussion: This Jupyter notebook investigates Centroidal Voronoi Tessellations in 1D.\n", "\n", "Licensing: This code is distributed under the GNU LGPL license.\n", " \n", "Modified: 01 November 2016\n", "\n", "Author: John Burkardt, Lukas Bystricky" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using matplotlib backend: agg\n" ] } ], "source": [ "# Import necessary libraries and set plot option\n", "%matplotlib\n", "%matplotlib inline\n", "%config InlineBackend.figure_format = 'svg'\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import math\n", "import scipy.spatial as spatial" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# CVT's in 1D #\n", "\n", "In one dimension, many geometric calculations are easy to make exactly.\n", "We will take advantage of this fact to make a simple introduction to\n", "centroidal Voronoi tessellations.\n", "\n", "We will look at\n", "* how to compute the subregions and centroids of a Voronoi diagram in 1D;\n", "* how to write a function that takes one step of a 1D CVT iteration.\n", "* how to construct a CVT iteration;\n", "* how to compute and plot the energy of a Voronoi diagram;\n", "* how to plot the evolution of the positions of the generator points." ] }, { "cell_type": "markdown", "metadata": { "collapsed": false }, "source": [ "# Subregions and Centroids #\n", "\n", "Suppose we have a set of N points G spread over an interval.\n", "For simplicity, we'll assume the interval is [0,1], and we\n", "will assume that the points G are sorted.\n", "\n", "What is the Voronoi subregion associated with point G(i)?\n", "Assume for simplicity that G(i) is not the first or last \n", "point in the set. Then the local neighborhood looks like\n", "...G(i-1)..........G(i)..........G(i+1)\n", "and we can insert points A(i) and B(i), which are the left\n", "and right limits of the Voronoi subregion of G(i):\n", "...G(i-1)...A(i)...G(i)...B(i)...G(i+1)...\n", "so that all the points inside [A(i),B(i)] are closer to G(i)\n", "than to G(i-1), G(i+1), or any other G point.\n", "\n", "It's easy to see that A(i) must be midway between G(i-1) and G(i),\n", "and the B(i) is midway between G(i) and G(i+1), so\n", " A(i) = (G(i-1)+G(i))/2\n", " B(i) = (G(i)+G(i+1))/2\n", "and the centroid is the average of these two points:\n", " C(i) = (A(i)+B(i))/2\n", "\n", "Note, however, that if G(i) is the first element of G, then A(i) \n", "must be 0, and if G(i) is the last element of G, then B(i) must be 1." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ 0.1 0.275 0.45 0.6625 0.8875]\n" ] } ], "source": [ "# cvt_1d_step()\n", "#\n", "# Write a function:\n", "#\n", "# def cvt_1d_step ( g ):\n", "# ***\n", "# return c\n", "#\n", "# which accepts N 1D points G as input, and returns the \n", "# centroids C as output.\n", "#\n", "def cvt_1d_step ( g ):\n", " n = g.shape[0]\n", " a = np.zeros ( n )\n", " b = np.zeros ( n )\n", " a[0] = 0.0\n", " a[1:n] = ( g[0:n-1] + g[1:n] ) / 2.0\n", " b[0:n-1] = ( g[0:n-1] + g[1:n] ) / 2.0\n", " b[n-1] = 1.0\n", " c = ( a + b ) / 2.0\n", " return c\n", "#\n", "# Test the function.\n", "#\n", "g = np.array ( [0.1, 0.3, 0.4, 0.7, 0.85 ] )\n", "c = cvt_1d_step ( g )\n", "print ( c )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# A basic CVT iteration #\n", "\n", "Now we want to set up a very simple CVT iteration for the 1D problem\n", "We are given a starting set of N points G. We call cvt_1d_step()\n", "to get the centroids C. We replace G by C (careful. In Python\n", "you don't say G=C, but G=C.copy()!). You do this M times, and\n", "return the final \"improved\" values of G.\n", "\n", "Write a function:\n", "\n", "def cvt_1d ( g, m ):\n", " ***\n", " return g\n", " \n", "You should probably sort the points G, just in case the user\n", "didn't. This can be done by the statement \"g.sort()\"." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ 0.18197355 0.74829031 0.96554985 0.25288854 0.93519995 0.15256047\n", " 0.50021743 0.78243645 0.99311324 0.25395166]\n", "[ 0.05011037 0.15032031 0.25049889 0.35062864 0.45069685 0.55069685\n", " 0.65062864 0.75049889 0.85032031 0.95011037]\n" ] } ], "source": [ "# cvt_1d()\n", "\n", "def cvt_1d ( g, m ):\n", " g.sort ( )\n", " for step in range ( 0 , m ):\n", " c = cvt_1d_step ( g )\n", " g = c.copy ( )\n", " return g\n", "#\n", "# Test the function.\n", "#\n", "g = np.random.random ( 10 )\n", "print ( g )\n", "g = cvt_1d ( g, 200 )\n", "print ( g )" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "\n", "# Computing the Energy #\n", "\n", "Recall that, for the 1D case, given generators G, the energy of a point x\n", "is determined by finding g(i), the nearest generator, and then computing:\n", " e_point(x) = (x-g(i))^2\n", "\n", "The energy of the i-th subregion p(i), associatd with g(i), is\n", " e_subregion(i) = integral ( x in p(i) ) e_point(x)\n", "\n", "The total energy of a 1D Voronoi diagram is\n", " e_total(G) = sum ( all i ) ( e_subregion ( i ) )\n", "\n", "write a function \n", "\n", "def total_energy ( g ):\n", " ***\n", " return e\n", " \n", "which returns the total energy of a 1D Voronoi diagram. Given\n", "the points G, you have to compute the limits A and B, and then,\n", "for each i, evaluate \n", " integral ( a(i) <= x <= b(i) ) ( x - g(i) )^2\n", "and sum them up.\n", "\n", "Test your code by starting with a set of 5 points in G and compute the energy.\n", "Then, four times in a row, call cvt_1d_step, replace G by C and compute the new energy.\n", "You should see the energy decrease." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.00473958333333\n", "0.00345003255208\n", "0.00340912373861\n", "0.00339174457391\n" ] } ], "source": [ "def total_energy ( g ):\n", " n = g.shape[0]\n", " a = np.zeros ( n )\n", " b = np.zeros ( n )\n", " a[0] = 0.0\n", " a[1:n] = ( g[0:n-1] + g[1:n] ) / 2.0\n", " b[0:n-1] = ( g[0:n-1] + g[1:n] ) / 2.0\n", " b[n-1] = 1.0\n", "\n", " e = 0.0\n", " for i in range ( 0, n ):\n", " e_subregion = ( ( b[i] - g[i] ) ** 3 - ( a[i] - g[i] ) ** 3 ) / 3.0\n", " e = e + e_subregion\n", " return e\n", "#\n", "# Test the function.\n", "#\n", "g = np.array ( [0.1, 0.3, 0.4, 0.7, 0.85 ] )\n", "e = total_energy ( g )\n", "print ( e )\n", "\n", "c = cvt_1d_step ( g )\n", "g = c.copy ( )\n", "e = total_energy ( g )\n", "print ( e )\n", "\n", "c = cvt_1d_step ( g )\n", "g = c.copy ( )\n", "e = total_energy ( g )\n", "print ( e )\n", "\n", "c = cvt_1d_step ( g )\n", "g = c.copy ( )\n", "e = total_energy ( g )\n", "print ( e )" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Plot the energy\n", "#\n", "# Start with 20 random points in [0,1], and call cvt_1d_step() 20 times,\n", "# each time computing the energy and saving its value in a vector e_vector.\n", "# Then make a plot of e_vector to observe the behavior of the energy.\n", "#\n", "m = 20\n", "e_vector = np.zeros ( m + 1 )\n", "g = np.array ( [0.1, 0.3, 0.4, 0.7, 0.85 ] )\n", "e = total_energy ( g )\n", "e_vector[0] = e\n", "for i in range ( 0, m ):\n", " c = cvt_1d_step ( g )\n", " g = c.copy ( )\n", " e = total_energy ( g )\n", " e_vector[i+1] = e\n", "\n", "plt.plot ( e_vector )" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "# Watch the points move #\n", "\n", "It can be interesting, in the 1D case, to watch how all the points\n", "move from one step to the next, by making a plot.\n", "\n", "To do this, we have to make a table with M+1 rows (start + number of steps)\n", "and N columns (number of points). We save the starting value of G, and\n", "each improved value. Then we plot each column (column 0 will show how\n", "point g[0] moved, column 1 how g[1] moved and so on)\n", "\n", "Try this for the sample data from the previous exercise." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Plot the points as they move\n", "#\n", "# Start with 20 random points in [0,1], and call cvt_1d_step() 20 times,\n", "# each time computing the energy and saving its value in a vector e_vector.\n", "# Then make a plot of e_vector to observe the behavior of the energy.\n", "#\n", "m = 20\n", "n = 20\n", "x_vector = np.zeros ( [ m + 1, n ] )\n", "g = np.random.random ( n )\n", "g.sort()\n", "x_vector[0,:] = g.copy ( )\n", "for i in range ( 0, m ):\n", " c = cvt_1d_step ( g )\n", " g = c.copy ( )\n", " x_vector[i+1,:] = g.copy ( )\n", "\n", "for j in range ( 0, n ):\n", " plt.plot ( x_vector[:,j] )" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.8" } }, "nbformat": 4, "nbformat_minor": 0 }