<?xml version="1.0" encoding="UTF-8"?><!-- generator="wordpress/2.0.12-alpha" -->
<rss version="2.0" 
	xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
	<title>Comments on: The right placement</title>
	<link>http://www.lshift.net/blog/2007/01/11/the-right-placement</link>
	<description>What happens at LShift</description>
	<pubDate>Fri, 21 Nov 2008 19:46:10 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.0.12-alpha</generator>

	<item>
		<title>by: Paul Crowley</title>
		<link>http://www.lshift.net/blog/2007/01/11/the-right-placement#comment-30913</link>
		<pubDate>Tue, 16 Jan 2007 10:49:54 +0000</pubDate>
		<guid>http://www.lshift.net/blog/2007/01/11/the-right-placement#comment-30913</guid>
					<description>&lt;p&gt;Now I have three solutions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the fast but not-quite-right solution I started with&lt;/li&gt;
&lt;li&gt;the quadratic programming solution discussed in the comments&lt;/li&gt;
&lt;li&gt;and a new linear-time algorithm&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think the new algorithm produces the same optimal solution defined by the quadratic programming algorithm, but my current proof of it is so ugly that it's too much work to set it out here and I don't trust it.  I think I'm on the tail of a more elegant proof now - I'll make a new blog entry if I find it.  In any case, this algorithm is much faster than the CVXOPT-based solution and produces results that look indistinguishable, without the artifacts of the first algorithm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fast algorithm:&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;    def place_labels(xmin, xmax, lwidth, xpos):
        offsets = [x - i * lwidth for i, x in enumerate(xpos)]
        max_offset = xmax - lwidth * (len(xpos) -1)
        return [i * lwidth + max(xmin, min(max_offset,
            (max(offsets[:i+1]) + min(offsets[i:])) / 2.0))
            for i in range(len(xpos))]
&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;CVXOPT-based algorithm:&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;    import sys
    sys.path.append('/home/paul/path/lib/python')

    import cvxopt.base
    import cvxopt.solvers
    cvxopt.solvers.options['show_progress'] = False
    cvxopt.solvers.options['maxiters'] = 5000
    #cvxopt.solvers.options['feastol'] = 1E-15

    def place_labels(xmin, xmax, lwidth, xpos):
        n = len(xpos)
        res = cvxopt.solvers.qp(
            cvxopt.base.spmatrix(1.0, range(n), range(n)),
            -cvxopt.base.matrix(xpos),
            cvxopt.base.spmatrix(-1, range(n), range(n), (n+1, n))
                + cvxopt.base.spmatrix(1, range(1, n+1), range(n), (n+1, n)),
            cvxopt.base.matrix([0.0 - xmin] + [-lwidth for i in range(len(xpos)-1)] + [xmax]))
        if res['status'] != 'optimal':
            raise Exception("Label optimizer failed: " + repr(res))
        return [x for x in res['x']]
&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;New fast and perhaps optimal algorithm:&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;    class PointGroup(object):
        def __init__(self, kind, n, x):
            self.kind = kind
            self.n = n
            self.x = x

    def place_labels(xmin, xmax, lwidth, xpos):
        groups = [PointGroup('l', 0, xmin)] + [PointGroup('m', 1, x) for x in xpos] + [PointGroup('r', 0, xmax + lwidth)]
        res = []
        for g in groups:
            res.append(g)
            while len(res) &gt;= 2 and res[-2].x + lwidth * res[-2].n &gt;= res[-1].x:
                left = res[-2]; right = res[-1]
                del res[-2:]
                if left.kind == 'l' and right.kind == 'm':
                    res.append(PointGroup('l', left.n +right.n, left.x))
                elif left.kind == 'm' and right.kind == 'r':
                    res.append(PointGroup('r', left.n + right.n, right.x - lwidth * left.n))
                elif left.kind == 'm' and right.kind == 'm':
                    res.append(PointGroup('m', left.n + right.n, 
                        (left.x * left.n + (right.x - left.n * lwidth) * right.n) / float(left.n + right.n)))
                else:
                    raise Exception("Bonk!")
        return [g.x + lwidth * i for g in res for i in range(g.n)]

&lt;/pre&gt;
</description>
		<content:encoded><![CDATA[<p>Now I have three solutions:</p>
<ul>
<li>the fast but not-quite-right solution I started with</li>
<li>the quadratic programming solution discussed in the comments</li>
<li>and a new linear-time algorithm</li>
</ul>
<p>I think the new algorithm produces the same optimal solution defined by the quadratic programming algorithm, but my current proof of it is so ugly that it&#8217;s too much work to set it out here and I don&#8217;t trust it.  I think I&#8217;m on the tail of a more elegant proof now - I&#8217;ll make a new blog entry if I find it.  In any case, this algorithm is much faster than the CVXOPT-based solution and produces results that look indistinguishable, without the artifacts of the first algorithm.</p>
<p><strong>Fast algorithm:</strong></p>
<pre>    def place_labels(xmin, xmax, lwidth, xpos):
        offsets = [x - i * lwidth for i, x in enumerate(xpos)]
        max_offset = xmax - lwidth * (len(xpos) -1)
        return [i * lwidth + max(xmin, min(max_offset,
            (max(offsets[:i+1]) + min(offsets[i:])) / 2.0))
            for i in range(len(xpos))]
</pre>
<p><strong>CVXOPT-based algorithm:</strong></p>
<pre>    import sys
    sys.path.append('/home/paul/path/lib/python')

    import cvxopt.base
    import cvxopt.solvers
    cvxopt.solvers.options['show_progress'] = False
    cvxopt.solvers.options['maxiters'] = 5000
    #cvxopt.solvers.options['feastol'] = 1E-15

    def place_labels(xmin, xmax, lwidth, xpos):
        n = len(xpos)
        res = cvxopt.solvers.qp(
            cvxopt.base.spmatrix(1.0, range(n), range(n)),
            -cvxopt.base.matrix(xpos),
            cvxopt.base.spmatrix(-1, range(n), range(n), (n+1, n))
                + cvxopt.base.spmatrix(1, range(1, n+1), range(n), (n+1, n)),
            cvxopt.base.matrix([0.0 - xmin] + [-lwidth for i in range(len(xpos)-1)] + [xmax]))
        if res['status'] != 'optimal':
            raise Exception("Label optimizer failed: " + repr(res))
        return [x for x in res['x']]
</pre>
<p><strong>New fast and perhaps optimal algorithm:</strong></p>
<pre>    class PointGroup(object):
        def __init__(self, kind, n, x):
            self.kind = kind
            self.n = n
            self.x = x

    def place_labels(xmin, xmax, lwidth, xpos):
        groups = [PointGroup('l', 0, xmin)] + [PointGroup('m', 1, x) for x in xpos] + [PointGroup('r', 0, xmax + lwidth)]
        res = []
        for g in groups:
            res.append(g)
            while len(res) >= 2 and res[-2].x + lwidth * res[-2].n >= res[-1].x:
                left = res[-2]; right = res[-1]
                del res[-2:]
                if left.kind == 'l' and right.kind == 'm':
                    res.append(PointGroup('l', left.n +right.n, left.x))
                elif left.kind == 'm' and right.kind == 'r':
                    res.append(PointGroup('r', left.n + right.n, right.x - lwidth * left.n))
                elif left.kind == 'm' and right.kind == 'm':
                    res.append(PointGroup('m', left.n + right.n,
                        (left.x * left.n + (right.x - left.n * lwidth) * right.n) / float(left.n + right.n)))
                else:
                    raise Exception("Bonk!")
        return [g.x + lwidth * i for g in res for i in range(g.n)]
</pre>
]]></content:encoded>
				</item>
	<item>
		<title>by: Joachim Dahl</title>
		<link>http://www.lshift.net/blog/2007/01/11/the-right-placement#comment-30646</link>
		<pubDate>Sun, 14 Jan 2007 18:59:07 +0000</pubDate>
		<guid>http://www.lshift.net/blog/2007/01/11/the-right-placement#comment-30646</guid>
					<description>&lt;p&gt;You could also write your own KKT solver (§8.7 in the CVXOPT documentation). Essentially you would solve 
the positive definite system&lt;/p&gt;

&lt;p&gt;(I + G'&lt;em&gt;diag(d)&lt;/em&gt;G)dx = r&lt;/p&gt;

&lt;p&gt;where 'd' is a given positive scaling vector.  That's a 
tridiagonal system, that can be solved very efficiently; 
the next release of CVXOPT will include the banded
solvers in LAPACK suited for something like this.&lt;/p&gt;

&lt;p&gt;Joachim&lt;/p&gt;
</description>
		<content:encoded><![CDATA[<p>You could also write your own KKT solver (§8.7 in the CVXOPT documentation). Essentially you would solve<br />
the positive definite system</p>
<p>(I + G&#8217;<em>diag(d)</em>G)dx = r</p>
<p>where &#8216;d&#8217; is a given positive scaling vector.  That&#8217;s a<br />
tridiagonal system, that can be solved very efficiently;<br />
the next release of CVXOPT will include the banded<br />
solvers in LAPACK suited for something like this.</p>
<p>Joachim</p>
]]></content:encoded>
				</item>
	<item>
		<title>by: Joachim Dahl</title>
		<link>http://www.lshift.net/blog/2007/01/11/the-right-placement#comment-30633</link>
		<pubDate>Sun, 14 Jan 2007 15:49:43 +0000</pubDate>
		<guid>http://www.lshift.net/blog/2007/01/11/the-right-placement#comment-30633</guid>
					<description>&lt;p&gt;Hi Paul,&lt;/p&gt;

&lt;p&gt;Specifying this problem as sparse will be much faster for
anything but toy-problems:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;
P = spmatrix(1.0, range(n), range(n))
q = matrix(xpos)
G = spmatrix(-1,range(n),range(n),(n+1,n)) + \
    spmatrix( 1,range(1,n+1),range(n),(n+1,n))
h = matrix([0.0 - xmin] + [-lwidth for i in range(len(q)-1)] + [xmax]);
res = solvers.qp(P, q, G, h)
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Best
Joachim&lt;/p&gt;
</description>
		<content:encoded><![CDATA[<p>Hi Paul,</p>
<p>Specifying this problem as sparse will be much faster for<br />
anything but toy-problems:</p>
<p><code><br />
P = spmatrix(1.0, range(n), range(n))<br />
q = matrix(xpos)<br />
G = spmatrix(-1,range(n),range(n),(n+1,n)) + \<br />
    spmatrix( 1,range(1,n+1),range(n),(n+1,n))<br />
h = matrix([0.0 - xmin] + [-lwidth for i in range(len(q)-1)] + [xmax]);<br />
res = solvers.qp(P, q, G, h)<br />
</code></p>
<p>Best<br />
Joachim</p>
]]></content:encoded>
				</item>
	<item>
		<title>by: Paul Crowley</title>
		<link>http://www.lshift.net/blog/2007/01/11/the-right-placement#comment-30630</link>
		<pubDate>Sun, 14 Jan 2007 14:24:25 +0000</pubDate>
		<guid>http://www.lshift.net/blog/2007/01/11/the-right-placement#comment-30630</guid>
					<description>&lt;p&gt;&lt;code&gt;
        res = cvxopt.solvers.qp(
            cvxopt.base.matrix([[1.0*int(i == j) for j in range(len(xpos))] for i in range(len(xpos))]),
            cvxopt.base.matrix([[-x for x in xpos]]),
            cvxopt.base.matrix([[0.0 for j in range(i)] + [-1.0, 1.0] + [0.0 for j in range(len(xpos)-i-1)] for i in range(len(xpos))]),
            cvxopt.base.matrix([0.0 - xmin] + [-lwidth for i in range(len(xpos)-1)] + [xmax]))
&lt;/code&gt;&lt;/p&gt;
</description>
		<content:encoded><![CDATA[<p><code><br />
        res = cvxopt.solvers.qp(<br />
            cvxopt.base.matrix([[1.0*int(i == j) for j in range(len(xpos))] for i in range(len(xpos))]),<br />
            cvxopt.base.matrix([[-x for x in xpos]]),<br />
            cvxopt.base.matrix([[0.0 for j in range(i)] + [-1.0, 1.0] + [0.0 for j in range(len(xpos)-i-1)] for i in range(len(xpos))]),<br />
            cvxopt.base.matrix([0.0 - xmin] + [-lwidth for i in range(len(xpos)-1)] + [xmax]))<br />
</code></p>
]]></content:encoded>
				</item>
	<item>
		<title>by: Paul Crowley</title>
		<link>http://www.lshift.net/blog/2007/01/11/the-right-placement#comment-30008</link>
		<pubDate>Fri, 12 Jan 2007 16:56:40 +0000</pubDate>
		<guid>http://www.lshift.net/blog/2007/01/11/the-right-placement#comment-30008</guid>
					<description>&lt;p&gt;Yes, that seems a plausible approach.  It occurred to me to cast this as a quadratic programming problem, but I hadn't realised that such a convenient library for it existed - thanks for the pointer.&lt;/p&gt;
</description>
		<content:encoded><![CDATA[<p>Yes, that seems a plausible approach.  It occurred to me to cast this as a quadratic programming problem, but I hadn&#8217;t realised that such a convenient library for it existed - thanks for the pointer.</p>
]]></content:encoded>
				</item>
	<item>
		<title>by: Jan Van lent</title>
		<link>http://www.lshift.net/blog/2007/01/11/the-right-placement#comment-29878</link>
		<pubDate>Thu, 11 Jan 2007 19:41:12 +0000</pubDate>
		<guid>http://www.lshift.net/blog/2007/01/11/the-right-placement#comment-29878</guid>
					<description>&lt;p&gt;Should have put code tags around formulas and code in the previous comments.&lt;/p&gt;
</description>
		<content:encoded><![CDATA[<p>Should have put code tags around formulas and code in the previous comments.</p>
]]></content:encoded>
				</item>
	<item>
		<title>by: Jan Van lent</title>
		<link>http://www.lshift.net/blog/2007/01/11/the-right-placement#comment-29877</link>
		<pubDate>Thu, 11 Jan 2007 19:39:18 +0000</pubDate>
		<guid>http://www.lshift.net/blog/2007/01/11/the-right-placement#comment-29877</guid>
					<description>&lt;p&gt;Untested python code for cvxopt:&lt;/p&gt;

&lt;p&gt;d = 0.1
a = arange(n) + uniform(-d, d, n)&lt;/p&gt;

&lt;p&gt;x = variable(n, 'x')
c&lt;em&gt;x&lt;/em&gt;low = ( x&lt;em&gt;min &#60;= x )
c&lt;/em&gt;x&lt;em&gt;up = ( x &#60;= x&lt;/em&gt;max )
c_d = [ x[i+1] - x[i] &#62;= d for i in range(n-1) ]&lt;/p&gt;

&lt;p&gt;t = variable(n, 't')
c&lt;em&gt;t&lt;/em&gt;low = ( -t &#60;= a - x )
c&lt;em&gt;t&lt;/em&gt;up = ( a - x &#60;= t )
lp&lt;em&gt;1 = op(t, [ c&lt;/em&gt;x&lt;em&gt;low, c&lt;/em&gt;x&lt;em&gt;up ] + cd + [ c&lt;/em&gt;t&lt;em&gt;low, c&lt;/em&gt;t_up ])&lt;/p&gt;

&lt;p&gt;t&lt;em&gt;inf = variable(1, 't&lt;/em&gt;inf')
c&lt;em&gt;t&lt;/em&gt;inf&lt;em&gt;low = ( -t&lt;/em&gt;inf &#60;= a - x )
c&lt;em&gt;t&lt;/em&gt;inf&lt;em&gt;up = ( a - x &#60;= t&lt;/em&gt;inf )
lp&lt;em&gt;inf = op(t, [ c&lt;/em&gt;x&lt;em&gt;low, c&lt;/em&gt;x&lt;em&gt;up ] + cd + [ c&lt;/em&gt;t&lt;em&gt;inf&lt;/em&gt;low, c&lt;em&gt;t&lt;/em&gt;inf_up ])&lt;/p&gt;

&lt;p&gt;lp_1.solve()
print x.value(), t.value()&lt;/p&gt;
</description>
		<content:encoded><![CDATA[<p>Untested python code for cvxopt:</p>
<p>d = 0.1<br />
a = arange(n) + uniform(-d, d, n)</p>
<p>x = variable(n, &#8216;x&#8217;)<br />
c<em>x</em>low = ( x<em>min &lt;= x )<br />
c</em>x<em>up = ( x &lt;= x</em>max )<br />
c_d = [ x[i+1] - x[i] &gt;= d for i in range(n-1) ]</p>
<p>t = variable(n, &#8216;t&#8217;)<br />
c<em>t</em>low = ( -t &lt;= a - x )<br />
c<em>t</em>up = ( a - x &lt;= t )<br />
lp<em>1 = op(t, [ c</em>x<em>low, c</em>x<em>up ] + cd + [ c</em>t<em>low, c</em>t_up ])</p>
<p>t<em>inf = variable(1, &#8216;t</em>inf&#8217;)<br />
c<em>t</em>inf<em>low = ( -t</em>inf &lt;= a - x )<br />
c<em>t</em>inf<em>up = ( a - x &lt;= t</em>inf )<br />
lp<em>inf = op(t, [ c</em>x<em>low, c</em>x<em>up ] + cd + [ c</em>t<em>inf</em>low, c<em>t</em>inf_up ])</p>
<p>lp_1.solve()<br />
print x.value(), t.value()</p>
]]></content:encoded>
				</item>
	<item>
		<title>by: Jan Van lent</title>
		<link>http://www.lshift.net/blog/2007/01/11/the-right-placement#comment-29876</link>
		<pubDate>Thu, 11 Jan 2007 19:38:02 +0000</pubDate>
		<guid>http://www.lshift.net/blog/2007/01/11/the-right-placement#comment-29876</guid>
					<description>&lt;p&gt;Probably overkill, but you can try to formulate this as a linear programming problem.&lt;/p&gt;

&lt;p&gt;l&lt;em&gt;1 norm
min \sum&lt;/em&gt;i t&lt;em&gt;i
s.t.
x&lt;/em&gt;min &#60;= x&lt;em&gt;i &#60;= x&lt;/em&gt;max
x&lt;em&gt;{i+1} - x&lt;/em&gt;i &#62;= d
-t&lt;em&gt;i &#60;= a&lt;/em&gt;i - x&lt;em&gt;i &#60;= t&lt;/em&gt;i&lt;/p&gt;

&lt;p&gt;l&lt;em&gt;\infty norm
min t
s.t.
x&lt;/em&gt;min &#60;= x&lt;em&gt;i &#60;= x&lt;/em&gt;max
x&lt;em&gt;{i+1} - x&lt;/em&gt;i &#62;= d
-t &#60;= a&lt;em&gt;i - x&lt;/em&gt;i &#60;= t&lt;/p&gt;

&lt;p&gt;The l_2 norm would give a quadratic programming problem.&lt;/p&gt;

&lt;p&gt;You can solve linear programming (and other) problems in python using for example cvxopt (http://www.ee.ucla.edu/~vandenbe/cvxopt/).&lt;/p&gt;
</description>
		<content:encoded><![CDATA[<p>Probably overkill, but you can try to formulate this as a linear programming problem.</p>
<p>l<em>1 norm<br />
min \sum</em>i t<em>i<br />
s.t.<br />
x</em>min &lt;= x<em>i &lt;= x</em>max<br />
x<em>{i+1} - x</em>i &gt;= d<br />
-t<em>i &lt;= a</em>i - x<em>i &lt;= t</em>i</p>
<p>l<em>\infty norm<br />
min t<br />
s.t.<br />
x</em>min &lt;= x<em>i &lt;= x</em>max<br />
x<em>{i+1} - x</em>i &gt;= d<br />
-t &lt;= a<em>i - x</em>i &lt;= t</p>
<p>The l_2 norm would give a quadratic programming problem.</p>
<p>You can solve linear programming (and other) problems in python using for example cvxopt (http://www.ee.ucla.edu/~vandenbe/cvxopt/).</p>
]]></content:encoded>
				</item>
</channel>
</rss>
