#!/usr/bin/python # -*- coding: utf-8 -*- """Try using hill-climbing to solve a geometric constraint problem. """ from __future__ import print_function import math import numpy import flagellate import pentagon def square_constraints(points): """Return a number telling how far the points are from forming a square of side 5. """ p1, p2, p3, p4 = points[0:2], points[2:4], points[4:6], points[6:] side1 = p2 - p1 side2 = p3 - p2 side3 = p4 - p3 side4 = p1 - p4 return (side1.dot(side2)**2 + side2.dot(side3)**2 + side3.dot(side4)**2 + side4.dot(side1)**2 # not sure why this last one is necessary + ((side1**2).sum() - 5**2)**2 + ((side2**2).sum() - (side3**2).sum())**2 ) def draw(points): # Convert vector to complex list form used by pentagon.py so we # can draw it and see that it’s not very fucking square: xs, ys = 8 * points[::2], 8 * points[1::2] xmax, xmin = int(math.ceil(xs.max())), int(math.floor(xs.min())) ymax, ymin = int(math.ceil(ys.max())), int(math.floor(ys.min())) complexes = xs - xmin + 1j * (ys - ymin) size = xmax - xmin + 1j * (ymax - ymin) for line in pentagon.draw(pentagon.eofill(complexes, size)): print(line) if __name__ == '__main__': # Validate square_constraints with a known solution: print(square_constraints(numpy.array([0, 0, 3, 4, 7, 1, 4, -3]))) # And a known non-solution: print(square_constraints(numpy.array([0, 0, 3, 4, 7, 1, 4, -3.1]))) step = lambda: 10 * (numpy.random.rand(8) - 0.5) for i, (x, q) in enumerate(flagellate.climb(square_constraints, step)): print(i, x, q) if not i % 100: draw(x) if q < 1e-6 or i > 1e9: break draw(x)