Adapt to true garden shape and constraints
This commit is contained in:
parent
62b7a97c5b
commit
0837a30169
3 changed files with 106 additions and 47 deletions
|
@ -6,6 +6,7 @@ version = "0.1.0"
|
||||||
[deps]
|
[deps]
|
||||||
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
|
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
|
||||||
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
|
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
|
||||||
|
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
|
||||||
|
|
||||||
[compat]
|
[compat]
|
||||||
julia = "1"
|
julia = "1"
|
||||||
|
|
|
@ -1,68 +1,120 @@
|
||||||
module GardenOptim
|
module GardenOptim
|
||||||
|
|
||||||
using CSV
|
|
||||||
using Logging
|
using Logging
|
||||||
|
using CSV
|
||||||
|
using Tables
|
||||||
|
|
||||||
export update!
|
export loadplants, loadgarden, loadcosts, update!, randomgardenevolution!, outputgarden
|
||||||
|
|
||||||
function swap!(grid::Array{Int, 2}, i::Int, j::Int)
|
function loadplants()
|
||||||
t = grid[i]
|
plants = readlines("data/plants.txt")
|
||||||
grid[i] = grid[j]
|
@info "loaded $(length(plants)) plants"
|
||||||
grid[j] = t
|
plants
|
||||||
grid
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function neighbours(grid::Array{Int, 2}, idx)
|
function loadgarden(plants)
|
||||||
m = size(grid, 1)
|
garden = CSV.read("data/garden.csv")
|
||||||
j, i = divrem(idx - 1, m)
|
garden = coalesce.(garden, "")
|
||||||
i += 1
|
mask = convert(Matrix, garden .== "empty")
|
||||||
j += 1
|
garden = indexin(convert(Matrix, garden), plants)
|
||||||
neighbourindices = [(i, j-1), (i, j+1), (i-1, j), (i+1, j)]
|
garden = replace(garden, nothing=>0)
|
||||||
[grid[k, l] for (k, l) in neighbourindices if 0 < k <= m && 0 < l <= m]
|
@assert size(garden) == size(mask)
|
||||||
end
|
@info "loaded garden of size $(size(garden))"
|
||||||
|
garden, mask
|
||||||
function deltacost(grid::Array{Int, 2}, costs::Array{Float64, 2}, i::Int, j::Int)
|
|
||||||
cost = 0
|
|
||||||
for k in neighbours(grid, i)
|
|
||||||
cost += costs[k, grid[j]] - costs[k, grid[i]]
|
|
||||||
end
|
|
||||||
for k in neighbours(grid, j)
|
|
||||||
cost += costs[k, grid[i]] - costs[k, grid[j]]
|
|
||||||
end
|
|
||||||
cost
|
|
||||||
end
|
|
||||||
|
|
||||||
function update!(grid::Array{Int, 2}, costs::Array{Float64, 2}, beta::Float64 = 10.0)
|
|
||||||
N = length(grid)
|
|
||||||
i, j = 0, 0
|
|
||||||
while i == j
|
|
||||||
i, j = rand(1:N, 2)
|
|
||||||
end
|
|
||||||
d = deltacost(grid, costs, i, j)
|
|
||||||
@debug "cost difference $d"
|
|
||||||
if rand() < exp(- beta * d)
|
|
||||||
@debug "swapping indices $i and $j"
|
|
||||||
return swap!(grid, i, j)
|
|
||||||
end
|
|
||||||
grid
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function loadcosts()
|
function loadcosts()
|
||||||
df = CSV.read("data/costs.csv")
|
df = CSV.read("data/costs.csv")
|
||||||
df = coalesce.(df, 0) # replace missing values by 0
|
df = coalesce.(df, 0) # replace missing values by 0
|
||||||
costs = convert(Matrix, df[:, 2:end])
|
costs = convert(Matrix, df[:, 2:end])
|
||||||
@debug "cost matrix of size $(size(costs))"
|
@info "loaded cost matrix of size $(size(costs))"
|
||||||
# ensure the matrix is symmetric: keep the max of itself and its transpose
|
# ensure the matrix is symmetric: keep the max of itself and its transpose
|
||||||
costs = Float64.(max.(costs, permutedims(costs)))
|
costs = Float64.(max.(costs, permutedims(costs)))
|
||||||
end
|
end
|
||||||
|
|
||||||
function randomgridevolution(costs::Array{Float64, 2}, gardensize::Int = 50, steps::Int = 10000)
|
function randomindex(mask::Matrix{Bool})
|
||||||
m = size(costs, 1)
|
while true
|
||||||
grid = rand(1:m, gardensize, gardensize)
|
i = rand(1:length(mask))
|
||||||
for i = 1:steps
|
if mask[i]
|
||||||
update!(grid, costs, 10.0)
|
return i
|
||||||
end
|
end
|
||||||
grid
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function swap!(garden::Matrix{Int}, i::Int, j::Int)
|
||||||
|
t = garden[i]
|
||||||
|
garden[i] = garden[j]
|
||||||
|
garden[j] = t
|
||||||
|
garden
|
||||||
|
end
|
||||||
|
|
||||||
|
function neighbours(garden::Matrix{Int}, idx)
|
||||||
|
m, n = size(garden)
|
||||||
|
j, i = divrem(idx - 1, m)
|
||||||
|
i += 1
|
||||||
|
j += 1
|
||||||
|
neighbourindices = [(i, j-1), (i, j+1), (i-1, j), (i+1, j)]
|
||||||
|
# cells filled with 0 are not part of the garden
|
||||||
|
[
|
||||||
|
garden[k, l] for (k, l) in neighbourindices
|
||||||
|
if 0 < k <= m && 0 < l <= n && garden[k, l] != 0
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
function deltacost(garden::Matrix{Int}, costs::Matrix{Float64}, i::Int, j::Int)
|
||||||
|
cost = 0
|
||||||
|
for k in neighbours(garden, i)
|
||||||
|
cost += costs[k, garden[j]] - costs[k, garden[i]]
|
||||||
|
end
|
||||||
|
for k in neighbours(garden, j)
|
||||||
|
cost += costs[k, garden[i]] - costs[k, garden[j]]
|
||||||
|
end
|
||||||
|
cost
|
||||||
|
end
|
||||||
|
|
||||||
|
function update!(
|
||||||
|
garden::Matrix{Int},
|
||||||
|
mask::Matrix{Bool},
|
||||||
|
costs::Matrix{Float64},
|
||||||
|
beta::Float64 = 10.0
|
||||||
|
)
|
||||||
|
N = length(garden)
|
||||||
|
i = randomindex(mask)
|
||||||
|
j = randomindex(mask)
|
||||||
|
while i == j
|
||||||
|
j = randomindex(mask)
|
||||||
|
end
|
||||||
|
d = deltacost(garden, costs, i, j)
|
||||||
|
@debug "cost difference $d"
|
||||||
|
if rand() < exp(- beta * d)
|
||||||
|
@debug "swapping indices $i and $j"
|
||||||
|
return swap!(garden, i, j)
|
||||||
|
end
|
||||||
|
garden
|
||||||
|
end
|
||||||
|
|
||||||
|
function randomfillgarden!(garden::Matrix{Int}, mask::Matrix{Bool}, plantcount::Int)
|
||||||
|
garden[mask] = rand(1:plantcount, sum(mask))
|
||||||
|
garden
|
||||||
|
end
|
||||||
|
|
||||||
|
function randomgardenevolution!(
|
||||||
|
garden::Matrix{Int},
|
||||||
|
mask::Matrix{Bool},
|
||||||
|
costs::Matrix{Float64};
|
||||||
|
steps::Int = 10000
|
||||||
|
)
|
||||||
|
m = size(costs, 1)
|
||||||
|
garden = randomfillgarden!(garden, mask, m)
|
||||||
|
for i = 1:steps
|
||||||
|
update!(garden, mask, costs, 10.0)
|
||||||
|
end
|
||||||
|
garden
|
||||||
|
end
|
||||||
|
|
||||||
|
function outputgarden(garden::Matrix{Int}, plants::Vector{String})
|
||||||
|
output = vcat([""], plants)[garden .+ 1]
|
||||||
|
CSV.write("output.csv", Tables.table(output), writeheader=false)
|
||||||
end
|
end
|
||||||
|
|
||||||
end # module
|
end # module
|
||||||
|
|
|
@ -2,6 +2,10 @@ using GardenOptim
|
||||||
using Test
|
using Test
|
||||||
|
|
||||||
@testset "GardenOptim.jl" begin
|
@testset "GardenOptim.jl" begin
|
||||||
|
@testset "randomindex" begin
|
||||||
|
mask = rand(Bool, 5, 5)
|
||||||
|
@test mask[GardenOptim.randomindex(mask)]
|
||||||
|
end
|
||||||
@testset "swap" begin
|
@testset "swap" begin
|
||||||
grid = ones(Int, 5, 5)
|
grid = ones(Int, 5, 5)
|
||||||
grid[2] = 5
|
grid[2] = 5
|
||||||
|
@ -18,6 +22,8 @@ using Test
|
||||||
@test length(GardenOptim.neighbours(grid, 8)) == 4
|
@test length(GardenOptim.neighbours(grid, 8)) == 4
|
||||||
@test length(GardenOptim.neighbours(grid, 25)) == 2
|
@test length(GardenOptim.neighbours(grid, 25)) == 2
|
||||||
@test GardenOptim.neighbours(grid, 1) == [1, 1]
|
@test GardenOptim.neighbours(grid, 1) == [1, 1]
|
||||||
|
grid[3] = 0
|
||||||
|
@test length(GardenOptim.neighbours(grid, 4)) == 2
|
||||||
end
|
end
|
||||||
@testset "deltacost" begin
|
@testset "deltacost" begin
|
||||||
grid = ones(Int, 5, 5)
|
grid = ones(Int, 5, 5)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue