【问题标题】:does cvxpy need starting conditions provided is zeros are not satisfying contraints?如果零不满足约束条件,cvxpy 是否需要起始条件?
【发布时间】:2022-01-21 15:13:18
【问题描述】:

我正在尝试修改 maxrect 库以解决不受方向限制的最大矩形。

查看代码我看到的约束是:

    """ :param coordinates:
        A list of of [x, y] pairs describing a closed, convex polygon.
    """

    coordinates = np.array(coordinates)
    x_range = np.max(coordinates, axis=0)[0]-np.min(coordinates, axis=0)[0]
    y_range = np.max(coordinates, axis=0)[1]-np.min(coordinates, axis=0)[1]

    scale = np.array([x_range, y_range])
    sc_coordinates = coordinates/scale

    poly = Polygon(sc_coordinates)
    inside_pt = (poly.representative_point().x,
                 poly.representative_point().y)

    A1, A2, B = pts_to_leq(sc_coordinates)

    bl = cvxpy.Variable(2)
    tr = cvxpy.Variable(2)
    br = cvxpy.Variable(2)
    tl = cvxpy.Variable(2)
    obj = cvxpy.Maximize(cvxpy.log(tr[0] - bl[0]) + cvxpy.log(tr[1] - bl[1]))
    constraints = [bl[0] == tl[0],
                   br[0] == tr[0],
                   tl[1] == tr[1],
                   bl[1] == br[1],
                   ]

    for i in range(len(B)):
        if inside_pt[0] * A1[i] + inside_pt[1] * A2[i] <= B[i]:
            constraints.append(bl[0] * A1[i] + bl[1] * A2[i] <= B[i])
            constraints.append(tr[0] * A1[i] + tr[1] * A2[i] <= B[i])
            constraints.append(br[0] * A1[i] + br[1] * A2[i] <= B[i])
            constraints.append(tl[0] * A1[i] + tl[1] * A2[i] <= B[i])

        else:
            constraints.append(bl[0] * A1[i] + bl[1] * A2[i] >= B[i])
            constraints.append(tr[0] * A1[i] + tr[1] * A2[i] >= B[i])
            constraints.append(br[0] * A1[i] + br[1] * A2[i] >= B[i])
            constraints.append(tl[0] * A1[i] + tl[1] * A2[i] >= B[i])

也就是说,他们将外接多边形中的每个点转换为 Ax + Ay = B 并检查矩形的角是否在其中,并最大化对角线。此外,还有 4 个约束可确保角的角度与参考框架对齐。

我在想我可以删除这 4 个约束。

但是,这允许矩形超出外接多边形的边界。这可能意味着我对上述约束的目的并不完全正确。也可能是现在允许这些点在参考框架内偏离它们的基数方向,所以我尝试添加不同的约束来保持矩形点的基数:

    if aligned:
        constraints.append(bl[0] == tl[0])
        constraints.append(br[0] == tr[0])
        constraints.append(tl[1] == tr[1])
        constraints.append(bl[1] == br[1])
    else:
        constraints.append(bl[0] < br[0])
        constraints.append(bl[0] < tr[0])
        constraints.append(tl[0] < br[0])
        constraints.append(tl[0] < tr[0])
        constraints.append(bl[1] < tl[1])
        constraints.append(bl[1] < tr[1])
        constraints.append(br[1] < tl[1])
        constraints.append(br[1] < tr[1])

有人可以帮我看看我缺少什么吗?

示例问题:

square = Polygon([(0,0), (0,2), (2,2), (2,0)], [
    [(0.5,0.5), (0.5,1.2), (1,1.2), (1,0.5)],
    [(1.1,1.1), (1.1,1.5), (1.5,1.5), (1.5,1.1)]
])
line = LineString((square.exterior.coords[0], square.exterior.coords[1]))
max_hull = find_maximal_convex_hull(line, square)
print('max hull', max_hull.wkt, max_hull.area)

# this line calls my cvypy script
pa = get_maximal_rectangle(max_hull.exterior.coords)
max_rect = Polygon([(pa[0][0],pa[0][1]), (pa[0][0],pa[1][1]), (pa[1][0],pa[1][1]), (pa[1][0],pa[0][1])])
print('max rectangle', pa)

plt.axis(xmin=-0.125,xmax=2.125,ymin=-0.125,ymax=2.125)

plt.plot(*square.exterior.xy, color='g')
[plt.plot(*i.xy, color='y') for i in square.interiors]

plt.plot(*max_hull.exterior.xy, color='b')
[plt.plot(*i.xy, color='r') for i in max_hull.interiors]

plt.plot(*max_rect.exterior.xy, color='r')

plt.show()

在上面的代码中,我得到了一个复杂的形状和一条边。我必须将其切割成(大致/几乎)最大的凸形,并且边缘必须与该形状相交。这项工作我已经完成了,它被分配给变量max_hull

现在,无论方向如何,我都想要形状中最大的矩形。在下图中,根据上面的代码绘制,我用绿色显示了外部正方形(左边缘是所需的边缘),它的孔用黄色显示,剩余的凸形,即给我的 cvypy 的外部多边形用蓝色显示, -- 它从可见的底线一直延伸到顶部 -- 并且从 cvxpy 返回的候选矩形为红色。

日志输出为:

<ipython-input-2-efd2419ff139>:272: ShapelyDeprecationWarning: Iteration over multi-part geometries is deprecated and will be removed in Shapely 2.0. Use the `geoms` property to access the constituent parts of a multi-part geometry.
  for base in split(S, ll):
455 unique polygons considered
max hull POLYGON ((0 0.95, 0 2, 2 2, 2 1.95, 0 0.95)) 1.0999999999999999
max rectangle (array([7.13062842e-09, 9.50000011e-01]), array([1.99999998, 1.99999999]))

为了在现代 cvypy 和 python 中使用 maxrect 库,请将 get_max_rectangle(在 init.py 中)solve 语句更改为 prob.solve() 和 return 语句。应用我的代码后,这是我正在使用的自定义函数:

import numpy as np
import cvxpy
from shapely.geometry import Polygon


def rect2poly(ll, ur):
    """
    Convert rectangle defined by lower left/upper right
    to a closed polygon representation.
    """
    x0, y0 = ll
    x1, y1 = ur

    return [
        [x0, y0],
        [x0, y1],
        [x1, y1],
        [x1, y0],
        [x0, y0]
    ]


def get_intersection(coords):
    """Given an input list of coordinates, find the intersection
    section of corner coordinates. Returns geojson of the
    interesection polygon.
    """
    ipoly = None
    for coord in coords:
        if ipoly is None:
            ipoly = Polygon(coord)
        else:
            tmp = Polygon(coord)
            ipoly = ipoly.intersection(tmp)

    # close the polygon loop by adding the first coordinate again
    first_x = ipoly.exterior.coords.xy[0][0]
    first_y = ipoly.exterior.coords.xy[1][0]
    ipoly.exterior.coords.xy[0].append(first_x)
    ipoly.exterior.coords.xy[1].append(first_y)

    inter_coords = zip(
        ipoly.exterior.coords.xy[0], ipoly.exterior.coords.xy[1])

    inter_gj = {"geometry":
                {"coordinates": [inter_coords],
                 "type": "Polygon"},
                "properties": {}, "type": "Feature"}

    return inter_gj, inter_coords


def two_pts_to_line(pt1, pt2):
    """
    Create a line from two points in form of
    a1(x) + a2(y) = b
    """
    pt1 = [float(p) for p in pt1]
    pt2 = [float(p) for p in pt2]
    try:
        slp = (pt2[1] - pt1[1]) / (pt2[0] - pt1[0])
    except ZeroDivisionError:
        slp = 1e5 * (pt2[1] - pt1[1])
    a1 = -slp
    a2 = 1.
    b = -slp * pt1[0] + pt1[1]

    return a1, a2, b


def pts_to_leq(coords):
    """
    Converts a set of points to form Ax = b, but since
    x is of length 2 this is like A1(x1) + A2(x2) = B.
    returns A1, A2, B
    """

    A1 = []
    A2 = []
    B = []
    for i in range(len(coords) - 1):
        pt1 = coords[i]
        pt2 = coords[i + 1]
        a1, a2, b = two_pts_to_line(pt1, pt2)
        A1.append(a1)
        A2.append(a2)
        B.append(b)
    return A1, A2, B


def get_maximal_rectangle(coordinates, aligned=False):
    """
    Find the largest, inscribed, axis-aligned rectangle.
    :param coordinates:
        A list of of [x, y] pairs describing a closed, convex polygon.
    """

    coordinates = np.array(coordinates)
    x_range = np.max(coordinates, axis=0)[0]-np.min(coordinates, axis=0)[0]
    y_range = np.max(coordinates, axis=0)[1]-np.min(coordinates, axis=0)[1]

    scale = np.array([x_range, y_range])
    sc_coordinates = coordinates/scale

    rep_point = Polygon(sc_coordinates).representative_point()
    inside_pt = (rep_point.x, rep_point.y)

    A1, A2, B = pts_to_leq(sc_coordinates)

    bl = cvxpy.Variable(2)
    tr = cvxpy.Variable(2)
    br = cvxpy.Variable(2)
    tl = cvxpy.Variable(2)
    obj = cvxpy.Maximize(cvxpy.log(tr[0] - bl[0]) + cvxpy.log(tr[1] - bl[1]))
    constraints = []
    
    if aligned:
        constraints.append(bl[0] == tl[0])
        constraints.append(br[0] == tr[0])
        constraints.append(tl[1] == tr[1])
        constraints.append(bl[1] == br[1])
    else:
        constraints.append(bl[0] < br[0])
        constraints.append(bl[0] < tr[0])
        constraints.append(tl[0] < br[0])
        constraints.append(tl[0] < tr[0])
        constraints.append(bl[1] < tl[1])
        constraints.append(bl[1] < tr[1])
        constraints.append(br[1] < tl[1])
        constraints.append(br[1] < tr[1])
        
    for i in range(len(B)):
        if inside_pt[0] * A1[i] + inside_pt[1] * A2[i] <= B[i]:
            constraints.append(bl[0] * A1[i] + bl[1] * A2[i] <= B[i])
            constraints.append(tr[0] * A1[i] + tr[1] * A2[i] <= B[i])
            constraints.append(br[0] * A1[i] + br[1] * A2[i] <= B[i])
            constraints.append(tl[0] * A1[i] + tl[1] * A2[i] <= B[i])
        else:
            constraints.append(bl[0] * A1[i] + bl[1] * A2[i] >= B[i])
            constraints.append(tr[0] * A1[i] + tr[1] * A2[i] >= B[i])
            constraints.append(br[0] * A1[i] + br[1] * A2[i] >= B[i])
            constraints.append(tl[0] * A1[i] + tl[1] * A2[i] >= B[i])

    prob = cvxpy.Problem(obj, constraints)
    #prob.solve(solver=cvxpy.CVXOPT, verbose=False, max_iters=1000, reltol=1e-9)
    #prob.solve(solver=cvxpy.SCS, verbose=True, use_indirect=False, max_iters=int(1e5))
    prob.solve()

    bottom_left = np.array(bl.value).T * scale
    top_right = np.array(tr.value).T * scale
    
    #return list(bottom_left[0]), list(top_right[0])
    return bottom_left, top_right

上面没有修改其他功能。

【问题讨论】:

    标签: python cvxpy


    【解决方案1】:

    在多边形中找到一个面积最大化的无约束矩形的问题是非凸的,所以你根本没有机会使用 cvxpy 来表达它。

    您删除的约束意味着您有一个轴对齐的矩形,没有它们,您只有一个包含在大多边形中的任意 4 边形,您正在最大化其两侧的乘积 - 不是直观的东西或您想要的东西。

    例如见https://docs.mosek.com/modeling-cookbook/powo.html#maximum-volume-cuboid

    【讨论】:

    • 我不是在寻找多边形的一般解决方案,而是在寻找凸多边形的解决方案,它在 O(n^4) 中具有幼稚的解决方案,而在更低的范围内具有更复杂的解决方案.但是在这种情况下的问题是,为什么剩余的约束和目标会导致解决方案超出 CVXpy 中这些相同约束的范围,我可以从那里得到它(我意识到它实际上可能不是最优的)。
    • 即使多边形是凸的,问题仍然是非凸的,并且在 cvxpy 中不可行。对不起。至于其余的,您必须发布一个完全可重现的示例。
    • 即使解决方案不是很好,它仍然应该返回一个符合剩余边界的候选者。感谢您对整个问题的指导,但现在请让我们专注于这个问题——这里有一个示例:外部多边形:多边形((0 0.95、0 2、2 2、2 1.95、0 0.95))我会发布一个显示结果的图像 array([7.13062842e-09, 9.50000011e-01]), array([1.99999998, 1.99999999]) 比它大
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-14
    • 2021-11-11
    • 2013-01-21
    • 1970-01-01
    • 2018-04-11
    相关资源
    最近更新 更多