The source files for all examples can be found in /examples.

Market Clearing

This example uses ConvexFlows to solve a market clearing problem

using ConvexFlows
using Random, LinearAlgebra, SparseArrays
using Plots, LogExpFunctions

const CF = ConvexFlows
ConvexFlows

Generating the problem data

Random.seed!(1)
nb = 3
ng = 2
budgets = ones(nb)
3-element Vector{Float64}:
 1.0
 1.0
 1.0

Construct the problem

First, we build a graph with 3 buyers and 2 goods. Buyer $b$ gets utility $u(x) = \sqrt{b + gx} - \sqrt{b}$ from $x$ units of a good $g$.

# Edges
u(x, b, g) = sqrt(b + g*x) - sqrt(b)

edges = Edge[]
for b in 1:nb, g in 1:ng
    # ub = 1.0 since only one unit of each good
    push!(edges, Edge((nb + g, b); h=x->u(x, b, g), ub=1e3))
end

Objective function

Here, we use a custom objective function and must define the associated methods.

struct MarketClearingObjective{T} <: Objective
    budget::Vector{T}
    nb::Int
    ng::Int
    ϵ::T
end
function MarketClearingObjective(budget::Vector{T}, nb::Int, ng::Int; tol=1e-8) where T
    @assert length(budget) == nb
    return MarketClearingObjective{T}(budget, nb, ng, tol)
end
Base.length(obj::MarketClearingObjective) = obj.nb + obj.ng

function CF.U(obj::MarketClearingObjective{T}, y) where T
    any(y[obj.nb+1] .< -1) && return -Inf
    return sum(obj.budget .* log.(y[1:obj.nb])) - obj.ϵ/2*sum(abs2, y[obj.nb+1:end])
end

function CF.Ubar(obj::MarketClearingObjective{T}, ν) where T
    return sum(log.(obj.budget ./ ν[1:obj.nb]) .- 1) + sum(ν[obj.nb+1:end])
end

function CF.∇Ubar!(g, obj::MarketClearingObjective{T}, ν) where T
    g[1:obj.nb] .= -obj.budget ./ ν[1:obj.nb]
    g[obj.nb+1:end] .= 1.0
    return nothing
end

function CF.Ubar(obj::MarketClearingObjective{T}, ν) where T ystar_goods = max.(-ν[obj.nb+1:end]/obj.ϵ, -1)

return sum(log.(1 ./ ν[1:obj.nb]) .- 1) +
    (-obj.ϵ/2*sum(abs2, ystar_goods) - dot(ν[obj.nb+1:end], ystar_goods))

end

function CF.∇Ubar!(g, obj::MarketClearingObjective{T}, ν) where T g[1:obj.nb] .= -1 ./ ν[1:obj.nb] g[obj.nb+1:end] .= -max.(-ν[obj.nb+1:end]/obj.ϵ, -1) return nothing end

obj = MarketClearingObjective(budgets, nb, ng)
Main.MarketClearingObjective{Float64}([1.0, 1.0, 1.0], 3, 2, 1.0e-8)

Solve the problem

Note that we could opt to use the LBFGS-B solver instead.

prob = problem(obj=obj, edges=edges)
result = solve!(prob; options=BFGSOptions(max_iters=200, print_iter=50))
--- Result ---
Status:      OPTIMAL
f(x)       :   -3.193
∇f(x)      :   9.267e-07
num iters  :   27
solve time : 3.516s

Display results

# Allocation of each good
println("Final utilities: ", prob.y[1:nb])
for b in 1:nb, g in 1:ng
    println("Buyer $b gets $(-prob.xs[2(b-1) + g][1]) units of good $g for utility $(prob.xs[2(b-1) + g][2])")
end
Final utilities: [0.43451609684578835, 0.33445348238242634, 0.28235640348843116]
Buyer 1 gets 0.38447580735309633 units of good 1 for utility 0.1766375004023526
Buyer 1 gets 0.2911291453278942 units of good 2 for utility 0.25787848803284175
Buyer 2 gets 0.33682218007289694 units of good 1 for utility 0.11445323587722878
Buyer 2 gets 0.3353271500199204 units of good 2 for utility 0.22000010176016027
Buyer 3 gets 0.27870111028377975 units of good 1 for utility 0.07866758861639922
Buyer 3 gets 0.37354375814158475 units of good 2 for utility 0.2036887194034811

This page was generated using Literate.jl.