From 29558b283c7700ddaa83cc6ce606e3ca904c4a0e Mon Sep 17 00:00:00 2001 From: mitchphillipson Date: Thu, 30 Nov 2023 09:23:46 -0600 Subject: [PATCH 01/17] Add integer indexing in GamsSet --- src/GamsSet.jl | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/GamsSet.jl b/src/GamsSet.jl index 10d2ce8..588f1d5 100644 --- a/src/GamsSet.jl +++ b/src/GamsSet.jl @@ -115,20 +115,17 @@ function Base.in(x::GamsElement,r::GamsSet) end -function Base.getindex(X::GamsSet,i::Symbol) - #for elm in X.elements - # if elm.name == i - # return elm - # end - #end +function Base.getindex(X::GamsSet,i::Int) + return X.elements[i] +end + +function Base.getindex(X::GamsSet,i::Symbol) try - return X.elements[X.index[i]] + return X[X.index[i]] catch error("$i is not a member of this set") end - - end function Base.getindex(X::GamsSet,i::GamsSet) From 65969caac5167ddaa3578bcd6d7a283e17b24641 Mon Sep 17 00:00:00 2001 From: mitchphillipson Date: Thu, 30 Nov 2023 09:24:23 -0600 Subject: [PATCH 02/17] Start progress on SparseDense parameters --- src/new_parameter.jl | 61 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/new_parameter.jl diff --git a/src/new_parameter.jl b/src/new_parameter.jl new file mode 100644 index 0000000..04e8d7f --- /dev/null +++ b/src/new_parameter.jl @@ -0,0 +1,61 @@ +struct new_parameter{T<:Number,N} <: AbstractArray{T,N} + universe::GamsUniverse + domain::NTuple{N,Symbol} + data::Dict{NTuple{N,Any},T} + description::String + new_parameter(universe::GamsUniverse,domain::Tuple{Vararg{Symbol}},description::String) = new{Float64,length(domain)}(GU,domain,Dict{Any,Float64}(),description) +end + + +function Base.length(P::new_parameter) + return prod(length(P.universe[i]) for i∈P.domain) +end + +function domain(P::new_parameter) + return P.domain +end + +function dimension(M::new_parameter) + return length(domain(M)) +end + +function Base.size(P::new_parameter) + return Tuple(length(P.universe[i]) for i∈P.domain) +end + +function Base.axes(P::new_parameter) + return Tuple([i for i∈P.universe[d]] for d∈domain(P)) +end + +#Base.CartesianIndices(P::new_parameter) = CartesianIndices(size(P)) +Base.eachindex(P::new_parameter) = CartesianIndices(size(P)) + +#function Base.getindex(P::new_parameter{T,N},I::Vararg{Any,N}) where {T,N} + +# get(P.data,I,zero(T)) + +#end + + +Base.setindex!(P::new_parameter{T,N}, value, I::Vararg{Any,N}) where {T,N} = (P.data[I] = value) + + + + +function Base.summary(io::IO,P::new_parameter) + d = domain(P) + print(io,"Description: $(P.description)\nDomain: $(d)\n\n") + return +end + + +function Base.show(io::IO, ::MIME"text/plain", P::new_parameter) + summary(io,P) + #println(io,":") + if length(P.data) > 0 + print(io,P.data) + end + return +end + +Base.show(io::IO, x::new_parameter) = show(convert(IOContext, io), x) \ No newline at end of file From f188fef4ff0714966aaaa96d699a0ca5e49aa376 Mon Sep 17 00:00:00 2001 From: mitchphillipson Date: Thu, 30 Nov 2023 09:24:38 -0600 Subject: [PATCH 03/17] Initialize GamsMask --- src/masking.jl | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 src/masking.jl diff --git a/src/masking.jl b/src/masking.jl new file mode 100644 index 0000000..857370e --- /dev/null +++ b/src/masking.jl @@ -0,0 +1,98 @@ +struct GamsMask{N} + universe::GamsUniverse + domain::NTuple{N,Symbol}#Vector{Symbol} + data::Dict{NTuple{N,Symbol},Bool} + description::String + GamsMask(GU,domain::Vararg{Symbol,N};description = "") where {N} = new{N}(GU,domain,Dict{NTuple{N,Symbol},Bool}(),description) +end + +function domain(P::GamsMask) + return P.domain +end + +function dimension(M::GamsMask) + return length(domain(M)) +end + +function Base.length(M::GamsMask) + return prod(length(P.universe[i]) for i∈P.domain) +end + + +function Base.size(P::GamsMask) + return Tuple(length(P.universe[i]) for i∈P.domain) +end + +function Base.axes(P::GamsMask) + return Tuple([i for i∈P.universe[d]] for d∈domain(P)) +end + +function _convert_idx(x::Symbol,GU::GamsUniverse,d::Symbol) + @assert (x==d || x∈GU[d]) "Symbol $x is neither a set nor an element of the set $d." + if x == d + return [i for i∈GU[d]] + elseif x∈GU[d] + return [x] + end +end + +function _convert_idx(x::Vector,GU::GamsUniverse,d::Symbol) + @assert (all(i∈GU[d] for i∈x)) "At least one element of $x is not in set $d" + return x +end + +function _convert_idx(x::GamsSet,GU::GamsUniverse,d::Symbol) + @assert x==GU[d] "The set\n\n$x\ndoes not match the domain set $d" + return [i for i∈x] +end + +function _convert_idx(x,GU::GamsUniverse,d::Symbol) + @assert x∈ GU[d] "$x is out of bounds in set $d" + return [x] +end + + +function Base.getindex(P::GamsMask{N},idx::CartesianIndex{N}) where {N} + + GU = P.universe + I = map((x,d) -> GU[d][x].name ,Tuple(idx),domain(P)) + return P[I...] +end + +function Base.getindex(P::GamsMask{N},idx::Vararg{Any,N}) where {N} + GU = P.universe + idx = map((x,d)->_convert_idx(x,GU,d),idx,domain(P)) + X = collect(Iterators.product(idx...)) + + if length(X) == 1 + return get(P.data,X[1],0) + else + return get.(Ref(P.data),X,0) + end +end + + + +Base.setindex!(P::GamsMask{N}, value, I::Vararg{Any,N}) where {N} = (P.data[I] = value) + + + + + +function Base.summary(io::IO,P::GamsMask) + d = domain(P) + print(io,"Description: $(P.description)\nDomain: $(d)\n\n") + return +end + + +function Base.show(io::IO, ::MIME"text/plain", P::GamsMask) + summary(io,P) + #println(io,":") + if length(P.data) > 0 + print(io,P.data) + end + return +end + +Base.show(io::IO, x::GamsMask) = show(convert(IOContext, io), x) \ No newline at end of file From fe615b4b0db7aebd6e37185e954e7cd8a4da7016 Mon Sep 17 00:00:00 2001 From: mitchphillipson Date: Wed, 17 Jan 2024 07:10:44 -0600 Subject: [PATCH 04/17] Update gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 29126e4..8af1832 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ docs/site/ # committed for packages, but should be committed for applications that require a static # environment. Manifest.toml + + +testing_ground/ \ No newline at end of file From 772d72d393aaf94caf1ff6419269ac827ae1c5ba Mon Sep 17 00:00:00 2001 From: mitchphillipson Date: Wed, 17 Jan 2024 07:11:24 -0600 Subject: [PATCH 05/17] Adding a show method to masking --- src/masking.jl | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/masking.jl b/src/masking.jl index 857370e..9878b41 100644 --- a/src/masking.jl +++ b/src/masking.jl @@ -18,6 +18,7 @@ function Base.length(M::GamsMask) return prod(length(P.universe[i]) for i∈P.domain) end +Base.iterate(sa::GamsMask, args...) = iterate(values(sa.data), args...) function Base.size(P::GamsMask) return Tuple(length(P.universe[i]) for i∈P.domain) @@ -95,4 +96,37 @@ function Base.show(io::IO, ::MIME"text/plain", P::GamsMask) return end -Base.show(io::IO, x::GamsMask) = show(convert(IOContext, io), x) \ No newline at end of file +Base.show(io::IO, x::GamsMask) = show(convert(IOContext, io), x) + +function Base.show(io::IOContext, x::GamsMask) + summary(io,x) + if isempty(x) + return show(io, MIME("text/plain"), x) + end + limit = get(io, :limit, false)::Bool + half_screen_rows = limit ? div(displaysize(io)[1] - 8, 2) : typemax(Int) + if !haskey(io, :compact) + io = IOContext(io, :compact => true) + end + + key_strings = [ + (join(key, ", "), value) for + (i, (key, value)) in enumerate(x.data) if + i < half_screen_rows || i > length(x) - half_screen_rows + ] + + sort!(key_strings; by = x -> x[1]) + pad = maximum(length(x[1]) for x in key_strings) + + for (i, (key, value)) in enumerate(key_strings) + print(io, " [", rpad(key, pad), "] = ", value) + if i != length(key_strings) + println(io) + if i == half_screen_rows + println(io, " ", " "^pad, " \u22ee") + end + end + end + + return +end \ No newline at end of file From f1c419862332fdfd39741836776b22c21eae4c52 Mon Sep 17 00:00:00 2001 From: mitchphillipson Date: Wed, 17 Jan 2024 07:12:04 -0600 Subject: [PATCH 06/17] Adding a show method to a new_parameter --- src/new_parameter.jl | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/new_parameter.jl b/src/new_parameter.jl index 04e8d7f..1eda5d4 100644 --- a/src/new_parameter.jl +++ b/src/new_parameter.jl @@ -11,6 +11,8 @@ function Base.length(P::new_parameter) return prod(length(P.universe[i]) for i∈P.domain) end +Base.iterate(sa::new_parameter, args...) = iterate(values(sa.data), args...) + function domain(P::new_parameter) return P.domain end @@ -58,4 +60,37 @@ function Base.show(io::IO, ::MIME"text/plain", P::new_parameter) return end -Base.show(io::IO, x::new_parameter) = show(convert(IOContext, io), x) \ No newline at end of file +Base.show(io::IO, x::new_parameter) = show(convert(IOContext, io), x) + +function Base.show(io::IOContext, x::new_parameter) + summary(io,x) + if isempty(x) + return show(io, MIME("text/plain"), x) + end + limit = get(io, :limit, false)::Bool + half_screen_rows = limit ? div(displaysize(io)[1] - 8, 2) : typemax(Int) + if !haskey(io, :compact) + io = IOContext(io, :compact => true) + end + + key_strings = [ + (join(key, ", "), value) for + (i, (key, value)) in enumerate(x.data) if + i < half_screen_rows || i > length(x) - half_screen_rows + ] + + sort!(key_strings; by = x -> x[1]) + pad = maximum(length(x[1]) for x in key_strings) + + for (i, (key, value)) in enumerate(key_strings) + print(io, " [", rpad(key, pad), "] = ", value) + if i != length(key_strings) + println(io) + if i == half_screen_rows + println(io, " ", " "^pad, " \u22ee") + end + end + end + + return +end \ No newline at end of file From 6d1b4e435312bf841579cce97845b1c977e507be Mon Sep 17 00:00:00 2001 From: mitchphillipson Date: Wed, 17 Jan 2024 07:29:34 -0600 Subject: [PATCH 07/17] Creating densesparse arrays --- src/GamsStructure.jl | 10 ++- src/GamsUniverse.jl | 4 +- src/dense_sparse.jl | 189 +++++++++++++++++++++++++++++++++++++++++++ src/io/load.jl | 3 +- src/io/unload.jl | 2 +- src/parameter.jl | 52 ++++++++++++ src/structs.jl | 30 +++++-- 7 files changed, 278 insertions(+), 12 deletions(-) create mode 100644 src/dense_sparse.jl create mode 100644 src/parameter.jl diff --git a/src/GamsStructure.jl b/src/GamsStructure.jl index bab7355..f216f93 100644 --- a/src/GamsStructure.jl +++ b/src/GamsStructure.jl @@ -15,9 +15,11 @@ export GamsElement export GamsSet, alias,@create_set!,@GamsDomainSets,GamsDomainSet, load_set,load_set!,@load_sets!,deactivate,activate -export GamsParameter,@create_parameters, +export Parameter,@create_parameters, load_parameter,load_parameter!,@load_parameters!,domain +export Mask + export GamsUniverse,add_set,add_parameter,unload,load_universe, load_universe!,sets,parameters @@ -30,11 +32,15 @@ export GamsUniverse,add_set,add_parameter,unload,load_universe, include("structs.jl") +include("dense_sparse.jl") +include("parameter.jl") include("GamsUniverse.jl") include("GamsSet.jl") -include("GamsParameter.jl") +#include("GamsParameter.jl") #include("GamsScalar.jl") + + include("io/unload.jl") include("io/load.jl") include("io/macros.jl") diff --git a/src/GamsUniverse.jl b/src/GamsUniverse.jl index 4cfb424..a8516c4 100644 --- a/src/GamsUniverse.jl +++ b/src/GamsUniverse.jl @@ -3,7 +3,7 @@ function add_set(GU::GamsUniverse,set_name::Symbol,set::GamsSet) GU.sets[set_name] = set end -function add_parameter(GU::GamsUniverse,parm_name::Symbol,parameter::GamsParameter) +function add_parameter(GU::GamsUniverse,parm_name::Symbol,parameter::Parameter) GU.parameters[parm_name] = parameter end @@ -59,6 +59,6 @@ function Base.show(io::IO, GU::GamsUniverse) end -function Base.setindex!(GU::GamsUniverse,parm::GamsParameter,key::Symbol) +function Base.setindex!(GU::GamsUniverse,parm::Parameter,key::Symbol) GU.parameters[key] = deepcopy(parm) end \ No newline at end of file diff --git a/src/dense_sparse.jl b/src/dense_sparse.jl new file mode 100644 index 0000000..d12d425 --- /dev/null +++ b/src/dense_sparse.jl @@ -0,0 +1,189 @@ + +dimension(P::DenseSparseArray{T,N}) where {T,N} = N + +function Base.size(P::DenseSparseArray) + U = universe(P) + return Tuple(length(U[i]) for i∈domain(P)) +end + + +#################### +### To Overwrite ### +#################### + +universe(P::DenseSparseArray) = nothing#GamsUniverse() +domain(P::DenseSparseArray{T,N}) where {T,N} = [] +data(P::DenseSparseArray{T,N}) where {T,N} = Dict{NTuple{N,Any},T}() +description(P::DenseSparseArray) = "" + +################ +### getindex ### +################ + +function _convert_idx(x::Symbol,GU::GamsUniverse,d::Symbol) + @assert (x==d || x∈GU[d]) "Symbol $x is neither a set nor an element of the set $d." + if x == d + return [[i] for i∈GU[d]] + elseif x∈GU[d] + return [[x]] + end +end + +function _convert_idx(x::Vector,GU::GamsUniverse,d::Symbol) + @assert (all(i∈GU[d] for i∈x)) "At least one element of $x is not in set $d" + return [[i] for i∈x] +end + +function _convert_idx(x::GamsSet,GU::GamsUniverse,d::Symbol) + @assert x==GU[d] "The set\n\n$x\ndoes not match the domain set $d" + return [[i] for i∈x] +end + + +function _convert_idx(x,GU::GamsUniverse,d::Symbol) + @assert x∈ GU[d] "$x is out of bounds in set $d" + return [x] +end + + +function Base.getindex(P::DenseSparseArray{T,N},idx::CartesianIndex{N}) where {T,N} + GU = universe(P) + I = map((x,d) -> GU[d][x].name ,Tuple(idx),domain(P)) + return P[I...] +end + + +""" + +if v = [:a,:b,:c,:d,:e] and p = (2,2,1) + +we expect output of + +[[:a,:b],[:c,:d],[:e]] +""" +function partition(v,p) + out = [] + steps = 1 + for i∈p + push!(out,[e for e∈v[steps:steps+i-1]]) + steps+=i + end + return out +end + +function dimension(x) + return 1 +end + +function Base.getindex(P::DenseSparseArray{T,N},idx::Vararg{Any}) where {T,N} + domain_match = partition(domain(P),dimension.(idx)) #Used for masking + + @assert sum(length.(domain_match)) == dimension(P) "Not enough inputs, or something. Get a better error message" + + GU = universe(P) + idx = map((x,d)->_convert_idx(x,GU,d...),idx,domain_match) + + X = Tuple.(Iterators.flatten.(Iterators.product(idx...))) + + + data_dict = data(P) + if length(X) == 1 + return get(data_dict,X[1],0) + else + return get.(Ref(data_dict),X,0) + end +end + + +################ +### setindex ### +################ + + +function Base.setindex!(P::DenseSparseArray{T,N}, value, idx::Vararg{Any,N}) where {T,N} + domain_match = partition(domain(P),dimension.(idx)) + @assert sum(length.(domain_match)) == dimension(P) "Not enough inputs, or something. Get a better error message" + + GU = universe(P) + idx = map((x,d)->_convert_idx(x,GU,d...),idx,domain_match) + + X = Tuple.(Iterators.flatten.(Iterators.product(idx...))) + + + + if length(X) == 1 + _setindex!(P,value,X[1]) + else + _setindex!.(Ref(P), value, X) + end + +end + +function _setindex!(P::DenseSparseArray{T,N}, value, idx) where {T,N} + d = data(P) + + if value == zero(T) + delete!(d, idx) + else + d[idx] = value + end + +end + + + +############ +### Show ### +############ + + +function Base.summary(io::IO,P::DenseSparseArray) + d = domain(P) + print(io,"Description: $(description(P))\nDomain: $(d)\n\n") + return +end + + +function Base.show(io::IO, ::MIME"text/plain", P::DenseSparseArray) + summary(io,P) + #println(io,":") + if length(data(P)) > 0 + print(io,data(P)) + end + return +end + +Base.show(io::IO, x::DenseSparseArray) = show(convert(IOContext, io), x) + +function Base.show(io::IOContext, x::DenseSparseArray) + summary(io,x) + if isempty(x) + return show(io, MIME("text/plain"), x) + end + limit = get(io, :limit, false)::Bool + half_screen_rows = limit ? div(displaysize(io)[1] - 8, 2) : typemax(Int) + if !haskey(io, :compact) + io = IOContext(io, :compact => true) + end + + key_strings = [ + (join(key, ", "), value) for + (i, (key, value)) in enumerate(data(x)) if + i < half_screen_rows || i > length(x) - half_screen_rows + ] + + sort!(key_strings; by = x -> x[1]) + pad = maximum(length(x[1]) for x in key_strings) + + for (i, (key, value)) in enumerate(key_strings) + print(io, " [", rpad(key, pad), "] = ", value) + if i != length(key_strings) + println(io) + if i == half_screen_rows + println(io, " ", " "^pad, " \u22ee") + end + end + end + + return +end \ No newline at end of file diff --git a/src/io/load.jl b/src/io/load.jl index 3e23442..2e25178 100644 --- a/src/io/load.jl +++ b/src/io/load.jl @@ -130,7 +130,8 @@ function load_parameter(GU::GamsUniverse, ) df = CSV.File("$path_to_parameter",stringtype=String,silencewarnings=true) - out = GamsParameter(GU,domain,description = description) + #out = GamsParameter(GU,domain,description = description) + out = Parameter(GU,domain,description = description) #If columns is set, load the data directly from the columns if !ismissing(columns) diff --git a/src/io/unload.jl b/src/io/unload.jl index 4280811..ec122a6 100644 --- a/src/io/unload.jl +++ b/src/io/unload.jl @@ -5,7 +5,7 @@ function unload(S::GamsSet,path,set_name) writedlm("$path/$set_name.csv",out,",") end -function unload(GU::GamsUniverse,P::GamsParameter,path,parm_name;raw_text=true) +function unload(GU::GamsUniverse,P::Parameter,path,parm_name;raw_text=true) if raw_text out = Vector{Vector{Any}}() diff --git a/src/parameter.jl b/src/parameter.jl new file mode 100644 index 0000000..7b5ff3b --- /dev/null +++ b/src/parameter.jl @@ -0,0 +1,52 @@ + + +domain(P::Parameter) = P.domain +universe(P::Parameter) = P.universe +data(P::Parameter) = P.data +description(P::Parameter) = P.description + + + +domain(P::Mask) = P.domain +universe(P::Mask) = P.universe +data(P::Mask) = P.data +description(P::Mask) = P.description + +function _convert_idx(x::Mask,GU::GamsUniverse,d::Vararg{Symbol}) + @assert domain(x) == d "The domain of the mask $(domain(x)) does not match the domain $d of the parameter in the position provided" + return keys(data(x)) +end + + +""" + @create_parameters(GU,block) + +Create many empty parameters + +``` +@create_parameters(GU,begin + :P, (:set_1,:set_2), "Description 1" + :X, (:set_1,), "Description 2" +end) +``` +""" +macro create_parameters(GU,block) + GU = esc(GU) + if !(isa(block,Expr) && block.head == :block) + error() + end + + code = quote end + for it in block.args + if isexpr(it,:tuple) + parm_name = it.args[1] + sets = it.args[2] + desc = "" + if length(it.args) >= 3 + desc = it.args[3] + end + push!(code.args,:($add_parameter($GU,$parm_name, Parameter($GU,$sets,description = $desc)))) + end + end + return code +end diff --git a/src/structs.jl b/src/structs.jl index 12d1fc2..2a5eb9a 100644 --- a/src/structs.jl +++ b/src/structs.jl @@ -46,15 +46,33 @@ or a mix of both P[:set_1,[:e_1]] """ -struct GamsParameter{N} +#struct GamsParameter{N} +# universe +# sets::Tuple{Vararg{Symbol,N}} +# value::Array{Float64,N} +# description::String +# GamsParameter(GU,sets::Tuple{Vararg{Symbol}},description::String) = new{length(sets)}(GU,sets,zeros(Float64,Tuple(length(GU[e].elements) for e∈sets)),description) +# GamsParameter(GU,sets::Tuple{Vararg{Symbol}};description = "") = new{length(sets)}(GU,sets,zeros(Float64,Tuple(length(GU[e].elements) for e∈sets)),description) +#end + + +abstract type DenseSparseArray{T,N} <: AbstractArray{T,N} end + +struct Parameter{T<:Number,N} <: DenseSparseArray{T,N} universe - sets::Tuple{Vararg{Symbol,N}} - value::Array{Float64,N} + domain::NTuple{N,Symbol} + data::Dict{NTuple{N,Any},T} description::String - GamsParameter(GU,sets::Tuple{Vararg{Symbol}},description::String) = new{length(sets)}(GU,sets,zeros(Float64,Tuple(length(GU[e].elements) for e∈sets)),description) - GamsParameter(GU,sets::Tuple{Vararg{Symbol}};description = "") = new{length(sets)}(GU,sets,zeros(Float64,Tuple(length(GU[e].elements) for e∈sets)),description) + Parameter(GU,domain::Tuple{Vararg{Symbol}};description::String="") = new{Float64,length(domain)}(GU,domain,Dict{Any,Float64}(),description) end +struct Mask{N} <: DenseSparseArray{Bool,N} + universe + domain::NTuple{N,Symbol} + data::Dict{NTuple{N,Any},Bool} + description::String + Mask(GU,domain::Vararg{Symbol,N};description::String = "") where {N}= new{N}(GU,domain,Dict{Any,Bool}(),description) +end #mutable struct GamsScalar # scalar::Number @@ -81,7 +99,7 @@ Print a universe to see it's members and their descriptions. """ struct GamsUniverse sets::Dict{Symbol,GamsSet} - parameters::Dict{Symbol,GamsParameter} + parameters::Dict{Symbol,Parameter} #scalars::Dict{Symbol,GamsScalar} GamsUniverse() = new(Dict(),Dict()) end From 07b7b30f5f264c6071a296cf22bd04606872f68a Mon Sep 17 00:00:00 2001 From: mitchphillipson Date: Wed, 17 Jan 2024 08:31:40 -0600 Subject: [PATCH 08/17] Macros for creating sets and parameters --- src/GamsStructure.jl | 7 ++-- src/macros.jl | 85 ++++++++++++++++++++++++++++++++++++++++++++ src/structs.jl | 1 + 3 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 src/macros.jl diff --git a/src/GamsStructure.jl b/src/GamsStructure.jl index f216f93..3f57924 100644 --- a/src/GamsStructure.jl +++ b/src/GamsStructure.jl @@ -13,9 +13,10 @@ export GamsElement export GamsSet, alias,@create_set!,@GamsDomainSets,GamsDomainSet, - load_set,load_set!,@load_sets!,deactivate,activate + load_set,load_set!,@load_sets!,deactivate,activate, + @set -export Parameter,@create_parameters, +export Parameter,@parameter,@parameters, load_parameter,load_parameter!,@load_parameters!,domain export Mask @@ -39,7 +40,7 @@ include("GamsSet.jl") #include("GamsParameter.jl") #include("GamsScalar.jl") - +include("macros.jl") include("io/unload.jl") include("io/load.jl") diff --git a/src/macros.jl b/src/macros.jl new file mode 100644 index 0000000..ea1bfea --- /dev/null +++ b/src/macros.jl @@ -0,0 +1,85 @@ +# This function is copied from JuMP +function _add_kw_args(call, kw_args) + for kw in kw_args + @assert Meta.isexpr(kw, :(=)) + push!(call.args, esc(Expr(:kw, kw.args...))) + end +end + +#Stolen from JuMP +function _plural_macro_code(model, block, macro_sym;entries_to_grab = 1) + if !Meta.isexpr(block, :block) + error( + "Invalid syntax for $(macro_sym)s. The second argument must be a " * + "`begin end` block. For example:\n" * + "```julia\n$(macro_sym)s(model, begin\n # ... lines here ...\nend)\n```.", + ) + end + @assert block.args[1] isa LineNumberNode + last_line = block.args[1] + code = quote end #Expr(:tuple) #I don't know why this works in JuMP, but not for me. Perhaps a Julia 1.10 thing + jump_macro = Expr(:., GamsStructure, QuoteNode(macro_sym)) #Change Main to module name here + for arg in block.args + if arg isa LineNumberNode + last_line = arg + elseif Meta.isexpr(arg, :tuple) # Line with commas. + macro_call = Expr(:macrocall, jump_macro, last_line, model) + # Because of the precedence of "=", Keyword arguments have to appear + # like: `x, (start = 10, lower_bound = 5)` + for i∈1:entries_to_grab + ex = popfirst!(arg.args) + push!(macro_call.args,ex) + end + for ex in arg.args + if Meta.isexpr(ex, :tuple) # embedded tuple + append!(macro_call.args, ex.args) + else + push!(macro_call.args, ex) + end + end + push!(code.args, esc(macro_call)) + else # Stand-alone symbol or expression. + macro_call = Expr(:macrocall, jump_macro, last_line, model, arg) + push!(code.args, esc(macro_call)) + end + end + return code +end + +macro parameter(GU, name, domain, kwargs...) + + #universe = esc(universe_sym) + constr_call = :(Parameter($(esc(GU)), $domain)) + _add_kw_args(constr_call, kwargs) + + return :($(esc(name)) = add_parameter($(esc(GU)), $(QuoteNode(name)), $constr_call)) +end + + +macro parameters(model, block) + return _plural_macro_code(model, block, Symbol("@parameter");entries_to_grab = 2) +end + + + +macro set(GU, set_name, description, block) + universe = esc(GU) + if !(isa(block,Expr) && block.head == :block) + error("Problem") + end + elements = [] + for it in block.args + if isexpr(it,:tuple) + elm = it.args[1] + desc = "" + if length(it.args)>=2 + desc = it.args[2] + end + push!(elements,GamsElement(elm,desc)) + end + end + + constr_call = :(GamsSet($elements,$(esc(description)))) + + return :($(esc(set_name)) = add_set($(esc(GU)), $(QuoteNode(set_name)), $constr_call)) +end \ No newline at end of file diff --git a/src/structs.jl b/src/structs.jl index 2a5eb9a..b917dc0 100644 --- a/src/structs.jl +++ b/src/structs.jl @@ -64,6 +64,7 @@ struct Parameter{T<:Number,N} <: DenseSparseArray{T,N} data::Dict{NTuple{N,Any},T} description::String Parameter(GU,domain::Tuple{Vararg{Symbol}};description::String="") = new{Float64,length(domain)}(GU,domain,Dict{Any,Float64}(),description) + Parameter(GU,domain::Symbol;description::String="") = new{Float64,1}(GU,tuple(domain),Dict{Any,Float64}(),description) end struct Mask{N} <: DenseSparseArray{Bool,N} From 48b75e3a272a864954aa68722d8c999c9b3d25aa Mon Sep 17 00:00:00 2001 From: mitchphillipson Date: Wed, 17 Jan 2024 09:01:22 -0600 Subject: [PATCH 09/17] Fix bug in displaying a universe --- src/GamsUniverse.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GamsUniverse.jl b/src/GamsUniverse.jl index a8516c4..affffb6 100644 --- a/src/GamsUniverse.jl +++ b/src/GamsUniverse.jl @@ -50,7 +50,7 @@ function Base.show(io::IO, GU::GamsUniverse) end out *= "\nParameters\n\n" for (key,parm) in GU.parameters - out *= "$key => $(parm.sets) => $(parm.description)\n" + out *= "$key => $(parm.domain) => $(parm.description)\n" end return print(io,out) From 973c092eec0c23ce4d8b573ec0fb3df93f8248df Mon Sep 17 00:00:00 2001 From: mitchphillipson Date: Wed, 17 Jan 2024 09:01:52 -0600 Subject: [PATCH 10/17] Add macro to extract variables from a universe --- src/GamsStructure.jl | 3 +++ src/macros.jl | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/GamsStructure.jl b/src/GamsStructure.jl index 3f57924..5c76ca0 100644 --- a/src/GamsStructure.jl +++ b/src/GamsStructure.jl @@ -25,6 +25,9 @@ export GamsUniverse,add_set,add_parameter,unload,load_universe, load_universe!,sets,parameters +export @extract_sets_as_vector,@extract + + #GamsScalar, # @GamsScalars #add_scalar,scalar, diff --git a/src/macros.jl b/src/macros.jl index ea1bfea..48011ca 100644 --- a/src/macros.jl +++ b/src/macros.jl @@ -82,4 +82,22 @@ macro set(GU, set_name, description, block) constr_call = :(GamsSet($elements,$(esc(description)))) return :($(esc(set_name)) = add_set($(esc(GU)), $(QuoteNode(set_name)), $constr_call)) +end + + +macro extract_sets_as_vector(GU, sets...) + code = quote end + for s∈sets + push!(code.args, :($(esc(s)) = [e for e∈$GU[$(QuoteNode(s))]])) + end + return code +end + + +macro extract(GU, vars...) + code = quote end + for s∈vars + push!(code.args, :($(esc(s)) = $(esc(GU))[$(QuoteNode(s))])) + end + return code end \ No newline at end of file From ff46cc11c0322d57e61ffb66126692a208f7aefe Mon Sep 17 00:00:00 2001 From: mitchphillipson Date: Wed, 17 Jan 2024 09:16:21 -0600 Subject: [PATCH 11/17] Add alias macro and change how the alias function works --- src/GamsStructure.jl | 2 +- src/GamsUniverse.jl | 17 +++++++++-------- src/macros.jl | 12 +++++++++++- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/GamsStructure.jl b/src/GamsStructure.jl index 5c76ca0..61f7e84 100644 --- a/src/GamsStructure.jl +++ b/src/GamsStructure.jl @@ -25,7 +25,7 @@ export GamsUniverse,add_set,add_parameter,unload,load_universe, load_universe!,sets,parameters -export @extract_sets_as_vector,@extract +export @extract_sets_as_vector,@extract, @alias #GamsScalar, diff --git a/src/GamsUniverse.jl b/src/GamsUniverse.jl index affffb6..95a2099 100644 --- a/src/GamsUniverse.jl +++ b/src/GamsUniverse.jl @@ -8,15 +8,16 @@ function add_parameter(GU::GamsUniverse,parm_name::Symbol,parameter::Parameter) end -function alias(GU::GamsUniverse,base_set::Symbol,aliases...) - for alias in aliases - new_set = deepcopy(GU[base_set]) - push!(new_set.aliases,base_set) - for al in new_set.aliases - push!(GU[al].aliases,alias) - end - add_set(GU,alias,new_set) +function alias(GU::GamsUniverse,base_set::Symbol,alias::Symbol) + #for alias in aliases + new_set = deepcopy(GU[base_set]) + push!(new_set.aliases,base_set) + for al in new_set.aliases + push!(GU[al].aliases,alias) end + add_set(GU,alias,new_set) + return new_set + end function sets(GU::GamsUniverse) diff --git a/src/macros.jl b/src/macros.jl index 48011ca..e784b2e 100644 --- a/src/macros.jl +++ b/src/macros.jl @@ -100,4 +100,14 @@ macro extract(GU, vars...) push!(code.args, :($(esc(s)) = $(esc(GU))[$(QuoteNode(s))])) end return code -end \ No newline at end of file +end + + + macro alias(GU, base_set, new_sets...) + code = quote end + GU = esc(GU) + for s∈new_sets + push!(code.args, :($(esc(s)) = alias($GU, $(QuoteNode(base_set)), $(QuoteNode(s))))) + end + return code + end \ No newline at end of file From bb5787151c9b8df7e649373d341e0c62f9e189df Mon Sep 17 00:00:00 2001 From: mitchphillipson Date: Wed, 17 Jan 2024 10:07:27 -0600 Subject: [PATCH 12/17] Changing sets to domain for parameters --- src/io/unload.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/io/unload.jl b/src/io/unload.jl index ec122a6..f512c9d 100644 --- a/src/io/unload.jl +++ b/src/io/unload.jl @@ -10,11 +10,11 @@ function unload(GU::GamsUniverse,P::Parameter,path,parm_name;raw_text=true) if raw_text out = Vector{Vector{Any}}() - tmp = [string(s) for s in P.sets] + tmp = [string(s) for s in P.domain] push!(tmp,"value") push!(out,tmp) - axes = [[e for e∈GU[s]] for s∈P.sets] + axes = [[e for e∈GU[s]] for s∈P.domain] for idx ∈ Iterators.product(axes...) tmp = Vector{Any}() append!(tmp,[idx...]) @@ -67,8 +67,8 @@ function unload(GU::GamsUniverse,path;to_unload = [],raw_text = true) for (key,parm) in GU.parameters if to_unload == [] || key∈to_unload unload(GU,parm,path,key;raw_text=raw_text) - cols = collect(1:length(parm.sets)) - info[:parm][key] = [parm.sets,parm.description,cols] + cols = collect(1:length(parm.domain)) + info[:parm][key] = [parm.domain,parm.description,cols] end end From 45d610beea7b616d48b0a4a4c8272964db5366b8 Mon Sep 17 00:00:00 2001 From: mitchphillipson Date: Wed, 17 Jan 2024 10:07:50 -0600 Subject: [PATCH 13/17] Updating tests --- test/Untitled-1.ipynb | 79 ------------------------------------------- test/activate.jl | 14 ++++---- test/load_unload.jl | 12 +++---- 3 files changed, 13 insertions(+), 92 deletions(-) delete mode 100644 test/Untitled-1.ipynb diff --git a/test/Untitled-1.ipynb b/test/Untitled-1.ipynb deleted file mode 100644 index bb912d0..0000000 --- a/test/Untitled-1.ipynb +++ /dev/null @@ -1,79 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "using GamsStructure\n", - "using MacroTools" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Description: \n", - "Domain: (:i, :j)\n", - "\n" - ] - }, - { - "data": { - "text/plain": [] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[1.0 2.0; 3.0 4.0" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "]" - ] - } - ], - "source": [ - "GU = GamsUniverse()\n", - "@load_sets!(GU,\"test_data\",begin\n", - " :i,\"\"\n", - " :j,\"\"\n", - "end)\n", - "\n", - "@load_parameters!(GU,\"test_data\",begin\n", - " :p, (:i,:j), description => \"Hi\"\n", - " :p_,(:i,:j), file_name=>\"p copy.csv\",value_name=>:data\n", - "end)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Julia 1.8.0", - "language": "julia", - "name": "julia-1.8" - }, - "language_info": { - "file_extension": ".jl", - "mimetype": "application/julia", - "name": "julia", - "version": "1.8.0" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/test/activate.jl b/test/activate.jl index d360514..eca57df 100644 --- a/test/activate.jl +++ b/test/activate.jl @@ -1,24 +1,24 @@ @testset "Loading and unloading a model" begin GU = GamsUniverse() - @create_set!(GU,:i,"Test Set",begin - a,"elm" - b,"elm2" + @set(GU, i,"Test Set",begin + a, "elm" + b, "elm2" end) alias(GU,:i,:j) - @create_parameters(GU,begin - :p, (:i,:j), "test parm" + @parameters(GU,begin + p, (:i,:j), (description = "test parm",) end) deactivate(GU,:i,:a) - @create_parameters(GU,begin - :q, (:i,:j), "test parm" + @parameters(GU,begin + q, (:i,:j), (description = "test parm",) end) @test GU[:p][:i,:j] == [0 0] diff --git a/test/load_unload.jl b/test/load_unload.jl index 1357019..5a749a3 100644 --- a/test/load_unload.jl +++ b/test/load_unload.jl @@ -1,21 +1,21 @@ @testset "Loading and unloading a model" begin GU = GamsUniverse() - @create_set!(GU,:i,"Test Set",begin + @set(GU, I,"Test Set",begin a,"elm" b,"elm2" end) - alias(GU,:i,:j) + @alias(GU, I, J) - @create_parameters(GU,begin - :p, (:i,:j), "test parm" + @parameters(GU,begin + p, (:I,:J), (description = "test parm",) end) cnt = 1 - for i∈GU[:i],j∈GU[:j] - GU[:p][[i],[j]] = cnt + for i∈I,j∈J + p[i,j] = cnt cnt+=1 end From d35394bd48b867983c70ee71ff70dfbb52de943f Mon Sep 17 00:00:00 2001 From: mitchphillipson Date: Wed, 17 Jan 2024 10:55:10 -0600 Subject: [PATCH 14/17] Removing old files --- src/GamsParameter.jl | 119 -------------------------------------- src/masking.jl | 132 ------------------------------------------- src/new_parameter.jl | 96 ------------------------------- 3 files changed, 347 deletions(-) delete mode 100644 src/GamsParameter.jl delete mode 100644 src/masking.jl delete mode 100644 src/new_parameter.jl diff --git a/src/GamsParameter.jl b/src/GamsParameter.jl deleted file mode 100644 index 46ffcba..0000000 --- a/src/GamsParameter.jl +++ /dev/null @@ -1,119 +0,0 @@ - - -""" - domain(P::GamsParameter) - -Return the domain of the paramter P in the form of a vector of -symbols. -""" -function domain(P::GamsParameter) - return P.sets -end - -""" - @create_parameters(GU,block) - -Create many empty parameters - -``` -@create_parameters(GU,begin - :P, (:set_1,:set_2), "Description 1" - :X, (:set_1,), "Description 2" -end) -``` -""" -macro create_parameters(GU,block) - GU = esc(GU) - if !(isa(block,Expr) && block.head == :block) - error() - end - - code = quote end - for it in block.args - if isexpr(it,:tuple) - parm_name = it.args[1] - sets = it.args[2] - desc = "" - if length(it.args) >= 3 - desc = it.args[3] - end - push!(code.args,:($add_parameter($GU,$parm_name, GamsParameter($GU,$sets,description = $desc)))) - end - end - return code -end - - -function __get_index(S::GamsSet,index) - - #return [S.index[i] for i∈index] - return get.(Ref(S.index),index,missing) -end - - -@inline _convert_idx(idx::Symbol,S::GamsSet,GU::GamsUniverse) = __get_index(S,GU[idx])#[S.index[i] for i∈GU[idx]] -@inline _convert_idx(idx::Vector{Symbol},S::GamsSet,GU::GamsUniverse) = length(idx)==1 ? S.index[idx[1]] : __get_index(S,idx)#[S.index[i] for i∈idx] -@inline _convert_idx(idx::GamsSet,S::GamsSet,GU::GamsUniverse) = __get_index(S,idx) #[S.index[i] for i∈idx] -@inline _convert_idx(idx::Colon,S::GamsSet,GU::GamsUniverse) = __get_index(S,S) #[S.index[i] for i∈S] -@inline _convert_idx(idx,S::GamsSet,GU::GamsUniverse) = idx - -""" -There are several choices for idx - - 1. : -> Return the entire parameter. This isn't a recommended usage, it's better to explicit about the set - 2. Symbol -> The symbol represents a set name. Return the parameter restricted to the elments of the specified set. - 3. Vector{Symbol} -> Return the elements of the paramter corresponding to the symbols in the vector - 4. Vector{Bool} -> For masking, you'll usually have a mask defined before using this option - 5. GamsSet -> Similar to 2, except giving an explicit GamsSet rather than its symbol. -""" -function Base.getindex(P::GamsParameter,idx...) - GU = P.universe - sets = [GU[s] for s∈domain(P)] - idx = map((x,S)->_convert_idx(x,S,GU),idx,sets) - return P.value[idx...] -end - - -function Base.iterate(iter::GamsParameter) - next = iterate(iter.value) - return next === nothing ? nothing : (next[1], next[2]) -end - -function Base.iterate(iter::GamsParameter, state) - next = iterate(iter.value, state) - return next === nothing ? nothing : (next[1], next[2]) -end - - -function Base.setindex!(P::GamsParameter,value,idx...) - GU = P.universe - sets = [GU[s] for s∈domain(P)] - idx = map((x,S)->_convert_idx(x,S,GU),idx,sets) - P.value[idx...] = value -end - -function Base.length(X::GamsParameter) - return length(X.value) -end - - -function Base.summary(io::IO,P::GamsParameter) - d = domain(P) - return print(io,"Description: $(P.description)\nDomain: $(d)\n\n") -end - - -function Base.show(io::IO, P::GamsParameter) - summary(io,P) - #println(io,":") - return Base.print_array(io,P.value) -end - - -#function Base.:*(P::GamsParameter,x) -# return P.value*x -#end - -#function Base.:*(x,P::GamsParameter) -# return P.value*x -#end \ No newline at end of file diff --git a/src/masking.jl b/src/masking.jl deleted file mode 100644 index 9878b41..0000000 --- a/src/masking.jl +++ /dev/null @@ -1,132 +0,0 @@ -struct GamsMask{N} - universe::GamsUniverse - domain::NTuple{N,Symbol}#Vector{Symbol} - data::Dict{NTuple{N,Symbol},Bool} - description::String - GamsMask(GU,domain::Vararg{Symbol,N};description = "") where {N} = new{N}(GU,domain,Dict{NTuple{N,Symbol},Bool}(),description) -end - -function domain(P::GamsMask) - return P.domain -end - -function dimension(M::GamsMask) - return length(domain(M)) -end - -function Base.length(M::GamsMask) - return prod(length(P.universe[i]) for i∈P.domain) -end - -Base.iterate(sa::GamsMask, args...) = iterate(values(sa.data), args...) - -function Base.size(P::GamsMask) - return Tuple(length(P.universe[i]) for i∈P.domain) -end - -function Base.axes(P::GamsMask) - return Tuple([i for i∈P.universe[d]] for d∈domain(P)) -end - -function _convert_idx(x::Symbol,GU::GamsUniverse,d::Symbol) - @assert (x==d || x∈GU[d]) "Symbol $x is neither a set nor an element of the set $d." - if x == d - return [i for i∈GU[d]] - elseif x∈GU[d] - return [x] - end -end - -function _convert_idx(x::Vector,GU::GamsUniverse,d::Symbol) - @assert (all(i∈GU[d] for i∈x)) "At least one element of $x is not in set $d" - return x -end - -function _convert_idx(x::GamsSet,GU::GamsUniverse,d::Symbol) - @assert x==GU[d] "The set\n\n$x\ndoes not match the domain set $d" - return [i for i∈x] -end - -function _convert_idx(x,GU::GamsUniverse,d::Symbol) - @assert x∈ GU[d] "$x is out of bounds in set $d" - return [x] -end - - -function Base.getindex(P::GamsMask{N},idx::CartesianIndex{N}) where {N} - - GU = P.universe - I = map((x,d) -> GU[d][x].name ,Tuple(idx),domain(P)) - return P[I...] -end - -function Base.getindex(P::GamsMask{N},idx::Vararg{Any,N}) where {N} - GU = P.universe - idx = map((x,d)->_convert_idx(x,GU,d),idx,domain(P)) - X = collect(Iterators.product(idx...)) - - if length(X) == 1 - return get(P.data,X[1],0) - else - return get.(Ref(P.data),X,0) - end -end - - - -Base.setindex!(P::GamsMask{N}, value, I::Vararg{Any,N}) where {N} = (P.data[I] = value) - - - - - -function Base.summary(io::IO,P::GamsMask) - d = domain(P) - print(io,"Description: $(P.description)\nDomain: $(d)\n\n") - return -end - - -function Base.show(io::IO, ::MIME"text/plain", P::GamsMask) - summary(io,P) - #println(io,":") - if length(P.data) > 0 - print(io,P.data) - end - return -end - -Base.show(io::IO, x::GamsMask) = show(convert(IOContext, io), x) - -function Base.show(io::IOContext, x::GamsMask) - summary(io,x) - if isempty(x) - return show(io, MIME("text/plain"), x) - end - limit = get(io, :limit, false)::Bool - half_screen_rows = limit ? div(displaysize(io)[1] - 8, 2) : typemax(Int) - if !haskey(io, :compact) - io = IOContext(io, :compact => true) - end - - key_strings = [ - (join(key, ", "), value) for - (i, (key, value)) in enumerate(x.data) if - i < half_screen_rows || i > length(x) - half_screen_rows - ] - - sort!(key_strings; by = x -> x[1]) - pad = maximum(length(x[1]) for x in key_strings) - - for (i, (key, value)) in enumerate(key_strings) - print(io, " [", rpad(key, pad), "] = ", value) - if i != length(key_strings) - println(io) - if i == half_screen_rows - println(io, " ", " "^pad, " \u22ee") - end - end - end - - return -end \ No newline at end of file diff --git a/src/new_parameter.jl b/src/new_parameter.jl deleted file mode 100644 index 1eda5d4..0000000 --- a/src/new_parameter.jl +++ /dev/null @@ -1,96 +0,0 @@ -struct new_parameter{T<:Number,N} <: AbstractArray{T,N} - universe::GamsUniverse - domain::NTuple{N,Symbol} - data::Dict{NTuple{N,Any},T} - description::String - new_parameter(universe::GamsUniverse,domain::Tuple{Vararg{Symbol}},description::String) = new{Float64,length(domain)}(GU,domain,Dict{Any,Float64}(),description) -end - - -function Base.length(P::new_parameter) - return prod(length(P.universe[i]) for i∈P.domain) -end - -Base.iterate(sa::new_parameter, args...) = iterate(values(sa.data), args...) - -function domain(P::new_parameter) - return P.domain -end - -function dimension(M::new_parameter) - return length(domain(M)) -end - -function Base.size(P::new_parameter) - return Tuple(length(P.universe[i]) for i∈P.domain) -end - -function Base.axes(P::new_parameter) - return Tuple([i for i∈P.universe[d]] for d∈domain(P)) -end - -#Base.CartesianIndices(P::new_parameter) = CartesianIndices(size(P)) -Base.eachindex(P::new_parameter) = CartesianIndices(size(P)) - -#function Base.getindex(P::new_parameter{T,N},I::Vararg{Any,N}) where {T,N} - -# get(P.data,I,zero(T)) - -#end - - -Base.setindex!(P::new_parameter{T,N}, value, I::Vararg{Any,N}) where {T,N} = (P.data[I] = value) - - - - -function Base.summary(io::IO,P::new_parameter) - d = domain(P) - print(io,"Description: $(P.description)\nDomain: $(d)\n\n") - return -end - - -function Base.show(io::IO, ::MIME"text/plain", P::new_parameter) - summary(io,P) - #println(io,":") - if length(P.data) > 0 - print(io,P.data) - end - return -end - -Base.show(io::IO, x::new_parameter) = show(convert(IOContext, io), x) - -function Base.show(io::IOContext, x::new_parameter) - summary(io,x) - if isempty(x) - return show(io, MIME("text/plain"), x) - end - limit = get(io, :limit, false)::Bool - half_screen_rows = limit ? div(displaysize(io)[1] - 8, 2) : typemax(Int) - if !haskey(io, :compact) - io = IOContext(io, :compact => true) - end - - key_strings = [ - (join(key, ", "), value) for - (i, (key, value)) in enumerate(x.data) if - i < half_screen_rows || i > length(x) - half_screen_rows - ] - - sort!(key_strings; by = x -> x[1]) - pad = maximum(length(x[1]) for x in key_strings) - - for (i, (key, value)) in enumerate(key_strings) - print(io, " [", rpad(key, pad), "] = ", value) - if i != length(key_strings) - println(io) - if i == half_screen_rows - println(io, " ", " "^pad, " \u22ee") - end - end - end - - return -end \ No newline at end of file From 9580f50ec4ffeef8808bedd4a97de91ebc82a035 Mon Sep 17 00:00:00 2001 From: mitchphillipson Date: Wed, 17 Jan 2024 10:55:19 -0600 Subject: [PATCH 15/17] Update ReadMe --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index b0a682d..a3963b7 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,18 @@ [Very terse documentation](https://mitchphillipson.github.io/GamsStructure.jl/dev/) +2024-01-17 + +To Do: + +1. Speed up indexing on parameters. Currently it is ~3-4x slower than NamedArrays +2. Documentation. Needs massive updating +3. Tests. Need more and more comprehensive +4. Lots of places call field names rather than with a function. Needs changing. + + +History: + To Do: 1. Deprecate and remove GamsScalars. From 7324d74f2f38823a0d1da3cb08d596cb446e914d Mon Sep 17 00:00:00 2001 From: mitchphillipson Date: Wed, 17 Jan 2024 10:55:34 -0600 Subject: [PATCH 16/17] Updating documentation --- docs/src/parameters.md | 10 +++--- docs/src/sets.md | 2 +- src/GamsStructure.jl | 1 - src/macros.jl | 74 ++++++++++++++++++++++++++++++++++++++++-- src/structs.jl | 50 ++++++++++++++-------------- 5 files changed, 102 insertions(+), 35 deletions(-) diff --git a/docs/src/parameters.md b/docs/src/parameters.md index 4f133bd..9c2aead 100644 --- a/docs/src/parameters.md +++ b/docs/src/parameters.md @@ -1,7 +1,9 @@ # Parameters ```@docs -GamsParameter +Parameter +@parameter +@parameters load_parameter(GU::GamsUniverse, path_to_parameter::String, domain::Tuple{Vararg{Symbol}}; @@ -17,7 +19,5 @@ load_parameter!(GU::GamsUniverse, columns::Union{Vector{Int},Missing} = missing, value_name = :value ) -@load_parameters!(GU,base_path,block) -@create_parameters(GU,block) -domain(P::GamsParameter) -``` \ No newline at end of file +``` + diff --git a/docs/src/sets.md b/docs/src/sets.md index 638bf52..1096e81 100644 --- a/docs/src/sets.md +++ b/docs/src/sets.md @@ -7,7 +7,7 @@ GamsSet(e::Tuple;description = "") GamsSet(x::Vector{Tuple{Symbol,String}};description = "") GamsSet(e::Vector{Symbol};description = "") GamsDomainSet(base_path::String,parm_name::Symbol,column::Int;description = "") -@create_set! +@set load_set load_set! @load_sets! diff --git a/src/GamsStructure.jl b/src/GamsStructure.jl index 61f7e84..81af823 100644 --- a/src/GamsStructure.jl +++ b/src/GamsStructure.jl @@ -11,7 +11,6 @@ using HDF5 export GamsElement - export GamsSet, alias,@create_set!,@GamsDomainSets,GamsDomainSet, load_set,load_set!,@load_sets!,deactivate,activate, @set diff --git a/src/macros.jl b/src/macros.jl index e784b2e..90e6be2 100644 --- a/src/macros.jl +++ b/src/macros.jl @@ -46,6 +46,19 @@ function _plural_macro_code(model, block, macro_sym;entries_to_grab = 1) return code end +""" + @parameter(GU, name, domain, kwargs...) + +Create a parameter in the universe. Also puts the name in the local +namespace. + +``` +@parameter(GU, p, (:I,:J), description = "this is p") +``` +This assumes both I and J are sets already in GU. The description +is an optional (but recommended) argument. + +""" macro parameter(GU, name, domain, kwargs...) #universe = esc(universe_sym) @@ -55,13 +68,38 @@ macro parameter(GU, name, domain, kwargs...) return :($(esc(name)) = add_parameter($(esc(GU)), $(QuoteNode(name)), $constr_call)) end +""" + @parameters(model, block) +Plural version of `@parameter`. + +``` +@parameters(GU, begin + p, (:i,:j), (description = "This is p",) + t, :i +``` +This create two parameters, p and t. p will have a description. +""" macro parameters(model, block) return _plural_macro_code(model, block, Symbol("@parameter");entries_to_grab = 2) end +""" + @set(GU, set_name, description, block) + +Macro to create a GamsSet. +``` +@set(GU,I,"example set",begin + element_1, "Description 1" + element_2, "Description 2" + element_3, "Description 3" +end) +``` +This will put the set I in the local name space as well. + +""" macro set(GU, set_name, description, block) universe = esc(GU) if !(isa(block,Expr) && block.head == :block) @@ -84,7 +122,17 @@ macro set(GU, set_name, description, block) return :($(esc(set_name)) = add_set($(esc(GU)), $(QuoteNode(set_name)), $constr_call)) end +""" + @extract_sets_as_vector(GU, sets...) + +Load the sets in the local namespace as vectors. +For example, if I and J are sets in GU, then +``` +@extract_sets_as_vector(GU, I, J) +``` +will make both I and J be vectors in the local namespace. +""" macro extract_sets_as_vector(GU, sets...) code = quote end for s∈sets @@ -94,6 +142,18 @@ macro extract_sets_as_vector(GU, sets...) end +""" + @extract(GU, vars...) + +Load the vars in the local namespace. This will preserve their type. + +You can load either sets or parameters by name. If I and J are sets +and p is a parameter in GU then +``` +@extract(GU, I, J, p) +``` +will put each in the local namespace. +""" macro extract(GU, vars...) code = quote end for s∈vars @@ -102,12 +162,22 @@ macro extract(GU, vars...) return code end +""" + @alias(GU, base_set, new_sets...) + +Add aliases of the base set with names given by new_sets. - macro alias(GU, base_set, new_sets...) +``` +@alias(GU, I, J, K) +``` +Then J and K will be deep copies of I in the local namespace and +added to GU as sets. +""" +macro alias(GU, base_set, new_sets...) code = quote end GU = esc(GU) for s∈new_sets push!(code.args, :($(esc(s)) = alias($GU, $(QuoteNode(base_set)), $(QuoteNode(s))))) end return code - end \ No newline at end of file +end \ No newline at end of file diff --git a/src/structs.jl b/src/structs.jl index b917dc0..beb41b6 100644 --- a/src/structs.jl +++ b/src/structs.jl @@ -17,6 +17,8 @@ end GamsSet(Elements::Vector{GamsElement}, description = "") Container to hold GamsElements. + +Best way to create a new set is using the [`@set`](@ref) macro. """ mutable struct GamsSet elements::Vector{GamsElement} @@ -32,19 +34,7 @@ end """ GamsParameter{N}(GU,sets::Tuple{Vararg{Symbol}},value::Array{Float64,N},description::String) -Container to hold parameters. - -Parameters can be indexed either by set name - -P[:set_1,:set_2] - -or by list of element names - -P[[:element_1,:element_2],[:e_1,:e_2]] - -or a mix of both -P[:set_1,[:e_1]] """ #struct GamsParameter{N} # universe @@ -58,6 +48,28 @@ P[:set_1,[:e_1]] abstract type DenseSparseArray{T,N} <: AbstractArray{T,N} end +""" + Parameter(GU,domain::Tuple{Vararg{Symbol}};description::String="") = new{Float64,length(domain)}(GU,domain,Dict{Any,Float64}(),description) + + +Container to hold parameters. Highly recommended to create using the [`@parameter`](@ref) macro. + +Parameters can be indexed either by set name + +P[:set_1,:set_2] + +or by list of element names + +P[[:element_1,:element_2],[:e_1,:e_2]] + +or a mix of both + +P[:set_1,:e_1] + +Order of precedence is set then element, so if you have an element with the same symbol +as the set name, there will be a conflict. You can either wrap the element name in a +vector or avoid this. +""" struct Parameter{T<:Number,N} <: DenseSparseArray{T,N} universe domain::NTuple{N,Symbol} @@ -75,12 +87,6 @@ struct Mask{N} <: DenseSparseArray{Bool,N} Mask(GU,domain::Vararg{Symbol,N};description::String = "") where {N}= new{N}(GU,domain,Dict{Any,Bool}(),description) end -#mutable struct GamsScalar -# scalar::Number -# description::String -# GamsScalar(scalar::Number;description = "") = new(scalar,description) -#end - """ GamsUniverse(sets::Dict{Symbol,GamsSet} @@ -117,14 +123,6 @@ end -#function set_scalar!(s::GamsScalar,scalar::Number) -# s.scalar = scalar -#end - -#function scalar(s::GamsScalar) -# return s.scalar -#end - From 418d7b823c143c5a23b2d3e971de9ead22030b4e Mon Sep 17 00:00:00 2001 From: mitchphillipson Date: Wed, 17 Jan 2024 12:36:04 -0600 Subject: [PATCH 17/17] Update documentation --- docs/make.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index 8c2b44b..f59ae94 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -19,7 +19,7 @@ makedocs( # assets = ["assets/extra_styles.css"], # sidebar_sitename = false, #), - strict = true, + #strict = true, pages = _PAGES )