The great fix of everything

This commit is contained in:
Alexander Munch-Hansen 2018-10-16 15:54:44 +02:00
parent bdbdfa624d
commit 05f0aa5551
2 changed files with 168 additions and 24 deletions

185
h2/mbc.py
View File

@ -1,8 +1,12 @@
import statistics
from math import inf
from math import inf, isnan, sqrt
from typing import Set, List, Tuple
import util
from scipy.optimize import linprog
import scipy
import random
from util import Side, Point, gen_point, display
from util import Side, Point, gen_point, display, display_line_only, gen_circular_point, gen_weird_point
def sidedness(slope: float, intersection: float, p3: Point, flipper: callable, eps=0.0000001) -> Side:
@ -20,12 +24,26 @@ def solve_1dlp(c: float, constraints: List[Tuple[float, float]]):
:param constraints: [(ai, bi), ...]
:return: x1
"""
try:
min_ = -10000
max_ = 10000
for constraint in constraints:
(a, b) = constraint
if a == 0:
assert(b >= 0)
if a > 0:
max_ = min(b/a, max_)
elif a < 0:
min_ = max(b/a, min_)
if c > 0:
return max(b/a for a, b in constraints if a < 0)
return min(b/a for a, b in constraints if a > 0)
except ValueError: # unbounded
return -inf if c > 0 else inf
return min_
else:
return max_
assert solve_1dlp(1, [(-1, -2)]) == 2
@ -34,6 +52,40 @@ assert solve_1dlp(1, [(-1, -3), (-1, -2)]) == 3
assert solve_1dlp(-1, [(1, 3), (1, 2)]) == 2
assert solve_1dlp(1, [(-1, 3), (-1, 2)]) == -2
def new_solve_1dlp(c, constraints, idx):
c1, c2 = c
((a1, a2), b) = constraints[idx]
q, p = b / a2, a1 /a2
interval = [-9999999, 9999999]
for new_idx, ((lel_a1, lel_a2), lel_b) in enumerate(constraints):
if not new_idx == idx:
bj, aj = (lel_b - lel_a2 * q), (lel_a1 - lel_a2 * p)
if aj < 0 and bj / aj > interval[0]:
interval[0] = bj / aj
elif aj > 0 and bj / aj < interval[1]:
interval[1] = bj/aj
c = -(c1 - c2 * p)
if c < 0:
return interval[0], q - (p * interval[0])
elif c >= 0:
return interval[1], q - (p * interval[1])
def new_solve_2dlp(c, constraints):
c1, c2 = c
x1 = -10000 if c1 > 0 else 10000
x2 = -10000 if c2 > 0 else 10000
for idx, ((a1, a2), b) in enumerate(constraints):
if not (a1*x1 + a2*x2 <= b):
x1,x2 = new_solve_1dlp(c, constraints[:idx+1], idx)
return x1,x2
def solve_2dlp(c: Tuple[float, float], constraints: List[Tuple[Tuple[float, float], float]]):
"""
@ -42,39 +94,125 @@ def solve_2dlp(c: Tuple[float, float], constraints: List[Tuple[Tuple[float, floa
:return: x1, x2
"""
c1, c2 = c
x1 = -inf if c1 > 0 else inf
x2 = -inf if c2 > 0 else inf
x1 = -10000 if c1 > 0 else 10000
x2 = -10000 if c2 > 0 else 10000
#random.shuffle(constraints)
for i, ((a1, a2), b) in enumerate(constraints[1:]):
if not a1*x1 + a2*x2 <= b:
x1 = solve_1dlp(c1 - c2*a1/a2,
[(ai1 - ai2*a1 / a2, bi - ai2*b / a2) for (ai1, ai2), bi in constraints[:i]])
x2 = (b - a1*x1) / a2
for idx, ((a1, a2), b) in enumerate(constraints[1:]):
#print("x1 and x2", x1, x2)
#print("{} + {} <= {}".format(a1*x1, a2*x2, b))
#print("pls",a1*x1 + a2*x2)
#print("yes"*10) if isnan(a1*x1+a2*x2) else print("no"*10)
if not (a1*x1 + a2*x2 <= b):
constraint_for_1d = []
new_obj = c[0] - ((c[1]*a1)/a2)
for constraint in constraints[:idx]:
(a_i1, a_i2), b_i = constraint
a_prime = a_i1 - ((a_i2*a1)/a2)
b_prime = b_i - ((a_i2*b)/a2)
constraint_for_1d.append((a_prime, b_prime))
#print("obj", new_obj)
#print("const", constraint_for_1d)
#print("lol",[cons[0] for cons in constraint_for_1d])
#res = linprog([new_obj], [[cons[0]] for cons in constraint_for_1d], [[cons[1]] for cons in constraint_for_1d], bounds=[(None, None)])
x1 = solve_1dlp(new_obj, constraint_for_1d)
#x1 = res.x
#print(res)
x2 = ((b/a2) - (a1/a2)*x1)
return x1, x2
def find_median(points):
num_candidates = min(5, len(points))
candidates = random.sample(points, num_candidates)
candidates.sort(key=lambda p: p.x)
median_i = num_candidates // 2
median = ((candidates[median_i - 1].x + candidates[median_i].x)/2,
(candidates[median_i - 1].y + candidates[median_i].y)/2)
return median[0]
def mbc_ch(points: Set[Point], flipper: callable) -> Set[Point]:
if len(points) <= 2:
if len(points) < 2:
return points
# Find the point with median x-coordinate, and partition the points on this point
med_x = find_median(points)
med_x = statistics.median(p.x for p in points)
#print(med_x)
# Find left and right points in regards to median
pl = {p for p in points if p.x < med_x}
pr = {p for p in points if p.x >= med_x}
#print("pl\n",pl)
#print("pr\n",pr)
c = [flipper(med_x), flipper(1)]
A = [[flipper(-p.x), flipper(-1)] for p in points]
b = [flipper(-p.y) for p in points]
# Find the bridge over the vertical line in pm
slope, intercept = solve_2dlp((flipper(med_x), flipper(1)), # confirmed correct
[((flipper(-p.x), flipper(-1)), flipper(-p.y)) for p in points]) # confirmed correct
#slope, intercept = solve_2dlp((flipper(med_x), flipper(1)),
# [((flipper(-p.x), flipper(-1)), flipper(-p.y)) for p in points]) # confirmed correct
slope, intercept = new_solve_2dlp((flipper(med_x), flipper(1)),
[((flipper(-p.x), flipper(-1)), flipper(-p.y)) for p in points])
#print("slope, intercept:",slope, intercept)
res = linprog(c, A, b, bounds=[[None, None], [None, None]], options={"tol": 0.01})
#print("res0, res1:",res.x[0], res.x[1])
#slope, intercept = res.x[0], res.x[1]
#display_line_only(points, slope, intercept, [])
# Find the two points which are on the line, should work
on = {p for p in points if sidedness(slope, intercept, p, flipper) == Side.ON}
left_point = min(on)
right_point = max(on)
#on = {p for p in points if sidedness(slope, intercept, p, flipper) == Side.ON}
#print("On Points:",on)
#left_point = min(on)
#right_point = max(on)
#dist_to_line = lambda p: abs(intercept + slope * p.x - p.y)/sqrt(1 + slope**2)
#left_point = min(pl, key = dist_to_line)
#right_point = min(pr, key=dist_to_line)
#display_line_only(points, slope, intercept, [left_point, right_point])
left_point = next(p for p in pl if sidedness(slope, intercept, p, flipper) == Side.ON)
right_point = next(p for p in pr if sidedness(slope, intercept, p, flipper) == Side.ON)
def find_med_point(points, med_x):
for p in points:
if med_x+0.001 >= p.x >= med_x-0.001:
return {p}
return {}
#print("med point:",find_med_point(points, med_x))
#display_line_only(points, slope, intercept, {left_point, right_point})
# Prune the points between the two line points
pl = {p for p in pl if p.x <= left_point.x}
@ -88,7 +226,12 @@ def mbc(points: Set[Point]) -> Set[Point]:
if __name__ == '__main__':
points = {gen_point(1, 10) for _ in range(20)}
# points = {gen_circular_point(1, 100, 50) for _ in range(200)}
points = {gen_weird_point(1, 100) for _ in range(200)}
#points = {Point(x=-33.11091053638924, y=38.88967778961347), Point(x=61.20269947424177, y=-78.96305419217254), Point(x=99.44053842147957, y=-89.11579172297581), Point(x=-92.40380889537532, y=84.33904351991652), Point(x=-90.63139185545595, y=-91.13793846505985)}
points = {Point(x=5.2, y=9.7), Point(x=5.3, y=4.9), Point(x=3.3, y=3.6), Point(x=9.2, y=4.8), Point(x=9.7, y=5.7), Point(x=5.6, y=8.7)}
upper_hull_points = mbc_ch(points, lambda x: x)
lower_hull_points = mbc_ch(points, lambda x: -x)

View File

@ -39,7 +39,6 @@ def gen_circular_point(lower : int = 0, upper: int = 10, radius: int = 5) -> Poi
r = radius * sqrt(random.uniform(lower, upper))
x = r * cos(a)
y = r * sin(a)
@ -49,7 +48,9 @@ def gen_weird_point(lower : int = 0, upper: int = 10) -> Point:
x = random.uniform(lower, upper)
y = x**2
return Point(x,y)
if x < 0:
return Point(random.uniform(x, -x), y)
return Point(random.uniform(-x, x), y)
def gen_triangular_point(left : Point, right : Point, top : Point):