1

I am currently attempting to calculate a specific type of set of locations within a grid. The problem at hand is related to a 2D packing optimization problem.

When packing items, I need to select a position (i,j) in the grid for the item. Due to the item size, several other positions in the grid could also be covered. Therefore I am in need of a set Qijc, which is the set of squares were placing an item c may cover position (i,j). For example if item c is sizes 2x2 the Qijc contains {(i,j),(i-1,j-1),(i-1,j),(i,j-1)}. If item c is in any of these positions.. position (i,j) is also covered.

I have the current working code. However, it brute forces the calculation and is extremely slow when hitting grid sizes of 150x400. I have been trying to optimize the code, and I believe that I am missing something simple. Any suggestions would be appreciated.

Currently, I store the information in a dictionary in order to have a "one-to-many" relationship. Another caveat to the problem is that I only store positions for item c, that would cover position (i,j) if the position is available in the network for item c.

In the given example, if item c = 3, and we are looking at position (4,3) the positions that would cover is (4,2) and (4,3), however, position (5,3) would also usually cover (4,3), however, is not stored as item c cannot go to this position. ( I hope this makes sense)

I believe that I have several redundant loops, and that it could be done in a much faster fashion, however, I struggle to wrap my head around it.

function validpositions(UnusuableSpace::Matrix{Int64}, nCargoes::Int64, SquaresL::Vector{Int64}, SquaresW::Vector{Int64})
    # This function should return all position that are valid in the layout for
    # cargo c in C. This should be returned as a tuple e.g. pos = [(1,1),(4,3),..,(18,1)].
    N = []
    for c in 1:nCargoesM
        pos = Vector{Tuple{Int64,Int64}}()

        for i in SquaresL[c]:size(UnusuableSpace)[1] # skip some of the rows. If the cargo has dimensions > 1.
            for j in 1:size(UnusuableSpace)[2]
                # Check if position includes a pillar / object
                if UnusuableSpace[i,j] != 1
                    # Check if these is space for the cargo in the position.
                    if (i-SquaresL[c]) >= 0 && (j+SquaresW[c]-1) <= size(UnusuableSpace)[2]
                        # Check if position i,j covers an are with pillars in due to cargo
                        # dimensions.
                        if sum(UnusuableSpace[i-SquaresL[c]+1:i,j:j+SquaresW[c]-1]) == 0
                            push!(pos,(i,j))
                        end
                    end
                end
            end

        end
        push!(N,pos)
    end
    # return all valid positions of the cargo c.
    return N
end # end valid position function





nCargoesM = 3
SquaresL = [1,2,3]
SquaresW = [1,2,2]


UnusableSpace = [
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 1 1 1 0 1 0 0 0
0 0 1 1 1 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0]

N = validpositions(UnusableSpace, nCargoesM, SquaresL, SquaresW)


function coveringSet(UnusableSpace::Matrix{Int64}, nCargoes::Int64, SquaresL::Vector{Int64}, SquaresW::Vector{Int64}, N)
    Qijc = Dict{}()
    for c in 1:nCargoesM
        for i in 1:size(UnusableSpace)[1]
            for j in 1:size(UnusableSpace)[2]
                tmpset = []
                for (k, l) in N[c]
                    # Get the points/nodes of the areas to check
                    vec1 = [i, j]
                    vec2 = [k-SquaresL[c]+1:k, l:l+SquaresW[c]-1]

                    tmp = [[x, y] for x in vec1[1], y in vec1[2]]
                    tpm = [[x, y] for x in vec2[1], y in vec2[2]]

                    # Check for overlapping
                    if sum([tmp in tpm for tmp = tmp]) > 0
                        push!(tmpset, [k, l])
                    end
                end
                push!(Qijc, [i, j, c] => tmpset)
            end
        end
    end
    return Qijc
end



Qijc = coveringSet(UnusableSpace, nCargoesM, SquaresL, SquaresW, N)


AliG
  • 73
  • 6
  • Do you need every solution or just a sufficiently good one? You might want to consider the problem as an optimization one, similar to a knapsack problem, and see if an optimizer like JuMP would speed your calculation. – Bill May 19 '22 at 01:06
  • Yeah, I need all combinations. I actually need it for an optimization model. However, I found a solution. – AliG May 30 '22 at 11:46

1 Answers1

0

I actually solved this problem myself after some time. You can precompute all the keys in the dictionary and thread the loop. Checking the positioning of the cargo and whether it is a spot that is allowable.

function coveringSet_new_threaded(UnusableSpace, nCargoesM::Int64, SquaresL::Vector{Int64}, SquaresW::Vector{Int64})
    # Create dictionary
    Qijc4 = Dict{}()

    for c in 1:nCargoesM
        for i in 1:size(UnusableSpace)[1]
            for j in 1:size(UnusableSpace)[2]
                push!(Qijc4, [i, j, c] => [])
            end
        end
    end

    # Finding the combinations of cargo size
    combinationsCargo = Vector{Tuple{Int64,Int64}}()
    for i in 1:length(SquaresL)
        push!(combinationsCargo, (SquaresL[i], SquaresW[i]))
    end

    # The unique combinations available
    CargoTypes = unique(combinationsCargo)


    Threads.@threads for c in 1:length(CargoTypes)
        typeindx = findall(x -> x == CargoTypes[c], combinationsCargo)
        # Index to control where to find information regarding squaresL and squaresW
        sizeidx = typeindx[1]

        #Threads.@threads for c in 1:nCargoesM
        for i in 1:size(UnusableSpace)[1]
            for j in 1:size(UnusableSpace)[2]
                tmpset = []

                # Generate the range of positions to check. (h,m) check if it also covers (i,j)
                # due to cargo size.
                for h in i:i+SquaresW[sizeidx]-1
                    for m in j-SquaresL[sizeidx]+1:j
                        # Check that the position is actually within the layout of the ship
                        if (h > 0 && m > 0 && h <= size(UnusableSpace)[1] && h <= size(UnusableSpace)[2])
                            # Check that the position is not unusable.
                            if UnusableSpace[h, m] != 1
                                # Check that the position given cargo size is withon the layout of the ship.
                                if (h - SquaresW[sizeidx]) >= 0 && (m + SquaresL[sizeidx] - 1) <= size(UnusableSpace)[2]
                                    # Check that the cargo, when in position (h,m) does not collide with any unusable space.
                                    if sum(UnusableSpace[h-SquaresW[sizeidx]+1:h, m:m+SquaresL[sizeidx]-1]) == 0
                                        # Only then is it a position for cargo c that covers (i,j).
                                        push!(tmpset, [h, m])
                                    end
                                end
                            end
                        end
                    end
                end
                # Push in set for that position. It may be empty.
                for k in typeindx
                    Qijc4[[i, j, k]] = tmpset
                end
            end
        end
    end

    return Qijc4
end
AliG
  • 73
  • 6