|
| 1 | +using CSV |
| 2 | +using DataFrames |
| 3 | +using PyCall |
| 4 | + |
| 5 | +py""" |
| 6 | +import pickle |
| 7 | + |
| 8 | +def load_pickle(fpath): |
| 9 | + with open(fpath, "rb") as f: |
| 10 | + data = pickle.load(f) |
| 11 | + return data |
| 12 | +""" |
| 13 | + |
| 14 | +load_pickle = py"load_pickle" |
| 15 | + |
| 16 | +# these parameters must match those in the csv file name, and the folder from which they are downloaded |
| 17 | +type_dataset = "multi" |
| 18 | +county_size = 5 |
| 19 | +num_lakes_per_county = 50 |
| 20 | +budget_ratio = 0.5 |
| 21 | + |
| 22 | +dirname = "EBMC_generated/$(type_dataset)_dataset/" |
| 23 | +fname = "$(county_size)_$(num_lakes_per_county)_$(budget_ratio).csv" |
| 24 | +df_edge = DataFrame(CSV.File(dirname * fname)) |
| 25 | + |
| 26 | +info_data = load_pickle(dirname * "info_data.pickle") |
| 27 | + |
| 28 | +# === Unpack Experiment Settings === # |
| 29 | +# extract the value list for the (county_size, num_lakes_per_county, budget_ratio) key |
| 30 | +vals = info_data[(county_size, num_lakes_per_county, budget_ratio)] |
| 31 | + |
| 32 | +# values come from Python (0-based originally) so use 1-based Julia indices |
| 33 | +counties = vals[1] # likely a sequence of county ids |
| 34 | +num_lakes_per_county = Int(vals[2]) # ensure it's an Int |
| 35 | +infestation_status = vals[3] # likely a Python dict |
| 36 | +county_budget = vals[4] |
| 37 | + |
| 38 | +# determine infested lakes (any value > 0 in the nested dict) |
| 39 | +infested_lakes = String[] |
| 40 | +for (key, infestation_vals) in infestation_status |
| 41 | + if any(v -> v > 0, values(infestation_vals)) |
| 42 | + push!(infested_lakes, string(key)) |
| 43 | + end |
| 44 | +end |
| 45 | + |
| 46 | +# === lakes and lake->county mapping === # |
| 47 | +lakes = unique(vcat(df_edge[:, :dow_origin], df_edge[:, :dow_destination])) |
| 48 | +lake_county = Dict(lake => lake[1:2] for lake in lakes) # first two chars like Python's [:2] |
| 49 | + |
| 50 | +# === Compute Lake Weights === # |
| 51 | +w = Dict{String, Float64}(lake => 0.0 for lake in lakes) |
| 52 | +for row in eachrow(df_edge) |
| 53 | + if row[:bij] != 0 |
| 54 | + ori = string(row[:dow_origin]) |
| 55 | + dst = string(row[:dow_destination]) |
| 56 | + w[ori] += row[:bij] * row[:weight] |
| 57 | + w[dst] += row[:bij] * row[:weight] |
| 58 | + end |
| 59 | +end |
| 60 | + |
| 61 | +# === Set Model Parameters === # |
| 62 | +I = lakes |
| 63 | + |
| 64 | +I_c = Dict(county => [i for i in I if i[1:2] == county] for county in counties) |
| 65 | +I_c_complement = Dict(county => [i for i in I if !(i in I_c[county])] for county in counties) |
| 66 | + |
| 67 | +# build arc dictionaries n (weight) and t (bij) |
| 68 | +n = Dict{Tuple{Any,Any}, Float64}() |
| 69 | +t = Dict{Tuple{Any,Any}, Float64}() |
| 70 | +for row in eachrow(df_edge) |
| 71 | + arc = (row[:dow_origin], row[:dow_destination]) |
| 72 | + n[arc] = row[:weight] |
| 73 | + t[arc] = row[:bij] |
| 74 | +end |
| 75 | + |
| 76 | +arcs = collect(keys(n)) |
| 77 | + |
| 78 | +# arcs within, incoming to, and outgoing from each county |
| 79 | +arcs_c = Dict{Any, Vector{Tuple{Any,Any}}}() |
| 80 | +arcs_plus_c = Dict{Any, Vector{Tuple{Any,Any}}}() |
| 81 | +arcs_minus_c = Dict{Any, Vector{Tuple{Any,Any}}}() |
| 82 | + |
| 83 | +for county in counties |
| 84 | + arcs_c[county] = [arc for arc in arcs if (arc[1][1:2] == county) && (arc[2][1:2] == county)] |
| 85 | + arcs_plus_c[county] = [arc for arc in arcs if (arc[2][1:2] == county) && (arc[1][1:2] != county)] |
| 86 | + arcs_minus_c[county] = [arc for arc in arcs if (arc[1][1:2] == county) && (arc[2][1:2] != county)] |
| 87 | +end |
| 88 | + |
| 89 | +# === Define and Solve SELFISH Game using IPG.jl === # |
| 90 | + |
| 91 | +using IPG, SCIP |
| 92 | +using IPG.JuMP: Containers |
| 93 | + |
| 94 | +# define players |
| 95 | +players = [Player(name=county) for county in counties] |
| 96 | + |
| 97 | +# add variables |
| 98 | +x_c = Dict(p => @variable(p.X, [I_c[p.name]], Bin, base_name="x_$(p.name)_") for p in players) |
| 99 | +y_c = Dict(p => @variable(p.X, [arcs_minus_c[p.name]], Bin, base_name="y_$(p.name)_") for p in players) |
| 100 | + |
| 101 | +# concatenate x variables |
| 102 | +x = Containers.DenseAxisArray(vcat([x_c[p].data for p in players]...), vcat([x_c[p].axes[1] for p in players]...)) |
| 103 | + |
| 104 | +for p in players |
| 105 | + ### add constraints |
| 106 | + # TODO: x[arc[i]] may be a variable from another player; need to translate the index |
| 107 | + # before using in @constraint. This is a limitation of our implementation, that I am |
| 108 | + # currently handling with the internalize_expr method. Ideally, we would have the macro |
| 109 | + # overwritten so that the internalization is automatic. |
| 110 | + @constraint(p.X, [arc in arcs_minus_c[p.name]], y_c[p][arc] <= IPG.internalize_expr(p, x[arc[1]] + x[arc[2]])) |
| 111 | + @constraint(p.X, sum(x_c[p]) <= county_budget[p.name]) |
| 112 | + |
| 113 | + ### set payoff |
| 114 | + set_payoff!(p, sum(t[arc] * n[arc] * y_c[p][arc] for arc in arcs_minus_c[p.name])) |
| 115 | +end |
| 116 | + |
| 117 | +Σ, payoff_improvements = SGM(players, SCIP.Optimizer, max_iter=10, verbose=true) |
0 commit comments