简体   繁体   中英

Python OR-Tools Distance between points

I am using the or-tools constraint solver to find working positions of items in a room. I am using the CP solver instead of the knapsack solver because I need to add additional constraints.

I am representing each item as an x interval and y interval and adding a constraint like this:

model.AddNoOverlap2D(x_intervals, y_intervals)

This helps to place the objects such that they do not overlap, but I need to add another constraint to ensure there is some distance between the objects. This should be a basic 2D distance between the objects, but this formula requires sqrt and pow functions which do not seem usable by IntVar items.

I have considered using another "spacer object" that is centered on each primary object, and adding another NoOverlap2D constraint, but this would result in blocking out an entire square of space when the distance formula would be a more precise distance that doesn't waste as much space in the room.

Edit - The code below is now working for putting a distance constraint on items.

After the suggestion from Laurent I have tried to make many intermediate variables to handle this, but am stuck on multiplying or dividing the IntVars defining the intervals of the shape locations:

# Now lets make all possible combinations and make a distance between them
combos = list(combinations(range(len(rects_data)), 2))

print(combos) 

currentDistanceId = 0

distanceVariables = []

for listSet in combos:
    leftItem = all_vars[listSet[0]]
    rightItem = all_vars[listSet[1]]

    if leftItem.holderType == HolderType.Person and rightItem.holderType == HolderType.Person:
        print(f"Adding distances between {listSet[0]} and {listSet[1]} because both are people")
        
        currentDistanceId = currentDistanceId + 1

        # Add an intermediate variable to store the sum of x for the center of left object
        leftCenterXSum = model.NewIntVar(0, horizon.x*2, f"leftCenterXSum{currentDistanceId}")
        # Add constraint to it
        model.Add(leftCenterXSum == leftItem.x2 + leftItem.x1)
        # Add an intermediate variable to store the center of x of the left object
        leftCenterX = model.NewIntVar(0, horizon.x, f"leftCenterX{currentDistanceId}")
        # Add a constraint to divide it by 2 to make it te center
        model.AddDivisionEquality(leftCenterX, leftCenterXSum, 2)

        ## Repeat for x and y for left and right objects
        leftCenterYSum = model.NewIntVar(0, horizon.y*2, f"leftCenterYSum{currentDistanceId}")
        model.Add(leftCenterYSum == leftItem.y2 + leftItem.y1)
        leftCenterY = model.NewIntVar(0, horizon.y, f"leftCenterY{currentDistanceId}")
        model.AddDivisionEquality(leftCenterY, leftCenterYSum, 2)

        rightCenterXSum = model.NewIntVar(0, horizon.x*2, f"rightCenterXSum{currentDistanceId}")
        model.Add(rightCenterXSum == rightItem.x2 + rightItem.x1)
        rightCenterX = model.NewIntVar(0, horizon.x, f"rightCenterX{currentDistanceId}")
        model.AddDivisionEquality(rightCenterX, rightCenterXSum, 2)

        rightCenterYSum = model.NewIntVar(0, horizon.y*2, f"rightCenterYSum{currentDistanceId}")
        model.Add(rightCenterYSum == rightItem.y2 + rightItem.y1)
        rightCenterY = model.NewIntVar(0, horizon.y, f"rightCenterY{currentDistanceId}")
        model.AddDivisionEquality(rightCenterY, rightCenterYSum, 2)

        # Create variable for difference of x
        xDiff = model.NewIntVar(-horizon.x, horizon.x, f"xDiff{currentDistanceId}")

        # Create constraint for difference of x
        model.Add(xDiff == rightCenterX - leftCenterX)

        # Create variable for difference of y
        yDiff = model.NewIntVar(-horizon.y, horizon.y, f"yDiff{currentDistanceId}")

        # Create constraint for difference for y
        model.Add(yDiff == rightCenterY - leftCenterY)

        # Create variables for x and y squared
        xDiffSquared = model.NewIntVar(0, horizon.x**2, f"xDiffSquared{currentDistanceId}")
        yDiffSquared = model.NewIntVar(0, horizon.y**2, f"yDiffSquared{currentDistanceId}")

        # Add constraint to multiply them
        model.AddMultiplicationEquality(xDiffSquared, [xDiff, xDiff])
        model.AddMultiplicationEquality(yDiffSquared, [yDiff, yDiff])

        totalDistance = model.NewIntVar(0, horizon.x**2 + horizon.y**2, f"totalDistance{currentDistanceId}")

        model.Add(totalDistance == xDiffSquared + yDiffSquared)

        distanceVariables.append(totalDistance)

        model.Add( totalDistance >= distanceSquared )
    else:
        print(f"Skipping distances between {listSet[0]} and {listSet[1]} because one is furniture")

Instead of

Sqrt((x1-x2)^2 + (y1-y2)^2) >= d

Why don't you write

(x1-x2)^2 + (y1-y2)^2 >= d^2

Which is supported. You will need to write (in pseudo code)

IntVar t_x
Add(t_x == x1 - x2)
IntVar t_sx
AddMultiplicationEquality(t_sx, [t_x, t_x])

IntVar t_y
Add(t_y == y1 - y2)
IntVar t_sy
AddMultiplicationEquality(t_sy, [t_y, t_y])

Add(t_sx + t_sy >= d * d)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM