Skip to content

Commit 2eeca0f

Browse files
authored
Merge pull request #409 from mimiframework/mcs-reshape-dist
- Added support for distributions that produce a matrix of values
2 parents d553b62 + 85e3ead commit 2eeca0f

17 files changed

+312
-50
lines changed

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ os:
33
- linux
44
- osx
55
julia:
6-
- 1.0
76
- 1.1
87
- nightly
98
matrix:

REQUIRE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
julia 1.0
1+
julia 1.1
22
Compose
33
CSVFiles
44
DataFrames

appveyor.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
environment:
22
matrix:
3-
- julia_version: 1.0
43
- julia_version: 1.1
54
- julia_version: nightly
65

src/mcs/defmcs.jl

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,6 @@ macro defmcs(expr)
108108
push!(_corrs, esc(expr))
109109

110110
# e.g., ext_var5[2010:2050, :] *= name2
111-
# A bug in Macrotools prevents this shorter expression from working:
112-
# elseif @capture(elt, ((extvar_ = rvname_Symbol) |
113-
# (extvar_ += rvname_Symbol) |
114-
# (extvar_ *= rvname_Symbol) |
115-
# (extvar_ = distname_(distargs__)) |
116-
# (extvar_ += distname_(distargs__)) |
117-
# (extvar_ *= distname_(distargs__))))
118111
elseif (@capture(elt, extvar_ = rvname_Symbol) ||
119112
@capture(elt, extvar_ += rvname_Symbol) ||
120113
@capture(elt, extvar_ *= rvname_Symbol) ||

src/mcs/mcs.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ include("montecarlo.jl")
1515
export
1616
@defmcs, generate_trials!, run_mcs, save_trial_inputs, save_trial_results, set_models!,
1717
EmpiricalDistribution, RandomVariable, TransformSpec, CorrelationSpec, MonteCarloSimulation,
18-
RANDOM, LHS, INNER, OUTER
18+
RANDOM, LHS, INNER, OUTER, ReshapedDistribution

src/mcs/mcs_types.jl

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ end
4242

4343
Base.eltype(ss::SampleStore) = eltype(ss.values)
4444

45-
distribution(ss::SampleStore) = ss.dist
46-
4745
#
4846
# TBD: maybe have a different SampleStore subtype for values drawn from a dist
4947
# versus those loaded from a file, which would be treated as immutable?
@@ -62,6 +60,28 @@ function Statistics.quantile(ss::SampleStore{T}, probs::AbstractArray) where T
6260
end
6361

6462

63+
""" ReshapedDistribution
64+
A pseudo-distribution that returns a reshaped array of values from the
65+
stored distribution and dimensions.
66+
67+
Example:
68+
rd = ReshapedDistribution([5, 5], Dirichlet(25,1))
69+
"""
70+
struct ReshapedDistribution
71+
dims::Vector{Int}
72+
dist::Distribution
73+
end
74+
75+
# function Base.rand(rd::ReshapedDistribution, draws::Int=1)
76+
# values = rand(rd.dist, draws)
77+
# dims = (draws == 1 ? rd.dims : [rd.dims..., draws])
78+
# return reshape(values, dims...)
79+
# end
80+
81+
function Base.rand(rd::ReshapedDistribution, draws::Int=1)
82+
return [reshape(rand(rd.dist), rd.dims...) for i in 1:draws]
83+
end
84+
6585
struct TransformSpec
6686
paramname::Symbol
6787
op::Symbol

src/mcs/montecarlo.jl

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ using IterableTables
33
using TableTraits
44
using Random
55
using ProgressMeter
6+
using Serialization
67

78
function Base.show(io::IO, mcs::MonteCarloSimulation)
89
println("MonteCarloSimulation")
@@ -83,11 +84,11 @@ function _store_trial_results(mcs::MonteCarloSimulation, trialnum::Int)
8384
end
8485

8586
"""
86-
save_trial_results(mcs::MonteCarloSimulation, output_dir::String)
87+
save_trial_results(mcs::MonteCarloSimulation, output_dir)
8788
8889
Save the stored MCS results to files in the directory `output_dir`
8990
"""
90-
function save_trial_results(mcs::MonteCarloSimulation, output_dir::AbstractString)
91+
function save_trial_results(mcs::MonteCarloSimulation, output_dir)
9192
multiple_results = (length(mcs.results) > 1)
9293

9394
for (i, results) in enumerate(mcs.results)
@@ -106,8 +107,44 @@ function save_trial_results(mcs::MonteCarloSimulation, output_dir::AbstractStrin
106107
end
107108
end
108109

109-
function save_trial_inputs(mcs::MonteCarloSimulation, filename::String)
110-
mkpath(dirname(filename), mode=0o770) # ensure that the specified path exists
110+
function _dummy_writer(mcs, pname, dir)
111+
@info "_dummy_writer(pname=$pname, dir=$dir)"
112+
end
113+
114+
115+
"""
116+
_serialize(mcs::MonteCarloSimulation, pname, dir)
117+
118+
This is the default function used for writing non-bit-type (array-valued)
119+
random vars, e.g., those produced by ReshapedDistribution.
120+
121+
This default method uses Serialization.serialize to write out the array.
122+
No application-defined types are written, so the file should be robust.
123+
"""
124+
function _serialize(mcs::MonteCarloSimulation, pname, dir)
125+
rv = mcs.rvdict[pname]
126+
if rv isa RandomVariable{Mimi.SampleStore{T}} where T
127+
name = first(split(string(pname), "!")) # e.g., :alpha!1 --> "alpha"
128+
path = joinpath(dir, "$name.dat")
129+
serialize(path, mcs.rvdict[pname].dist.values)
130+
else
131+
error("Tried to _serialize $pname, which isn't a RandomVariable{SampleStore{T}}")
132+
end
133+
end
134+
135+
function save_trial_inputs(mcs::MonteCarloSimulation, filename, non_bit_writer=_serialize)
136+
dir = dirname(filename)
137+
mkpath(dir, mode=0o770) # ensure that the specified path exists
138+
139+
# If ReshapedDistribution was used, a single trial value for a field
140+
# will be an Array of values. CSV format doesn't handle these well,
141+
# so we serialize the arrays into a file using the field's name.
142+
non_bits = [name for (name, T) in zip(column_names(mcs), column_types(mcs)) if ! isbitstype(T)]
143+
for pname in non_bits
144+
non_bit_writer(mcs, pname, dir)
145+
end
146+
147+
# TBD: avoid writing array values to CSV files...
111148
save(filename, mcs)
112149
return nothing
113150
end
@@ -190,7 +227,7 @@ function _copy_mcs_params(mcs::MonteCarloSimulation)
190227

191228
for (i, m) in enumerate(mcs.models)
192229
md = m.mi.md
193-
param_vec[i] = Dict{Symbol, ModelParameter}(trans.paramname => copy(external_param(md, trans.paramname)) for trans in mcs.translist)
230+
param_vec[i] = Dict{Symbol, ModelParameter}(trans.paramname => deepcopy(external_param(md, trans.paramname)) for trans in mcs.translist)
194231
end
195232

196233
return param_vec
@@ -225,7 +262,13 @@ function _param_indices(param::ArrayModelParameter{T}, md::ModelDef, trans::Tran
225262
num_pdims = length(pdims)
226263

227264
tdims = trans.dims
228-
num_dims = length(tdims)
265+
num_dims = length(tdims)
266+
267+
# special case for handling reshaped data where a single draw returns a matrix of values
268+
if num_dims == 0
269+
indices = repeat([Colon()], num_pdims)
270+
return indices
271+
end
229272

230273
if num_pdims != num_dims
231274
pname = trans.paramname
@@ -256,7 +299,8 @@ function _perturb_param!(param::ScalarModelParameter{T}, md::ModelDef, trans::Tr
256299
end
257300
end
258301

259-
function _perturb_param!(param::ArrayModelParameter{T}, md::ModelDef, trans::TransformSpec, rvalue::Number) where T
302+
function _perturb_param!(param::ArrayModelParameter{T}, md::ModelDef,
303+
trans::TransformSpec, rvalue::Union{Number, Array{<: Number, N}}) where {T, N}
260304
op = trans.op
261305
pvalue = value(param)
262306
indices = _param_indices(param, md, trans)
@@ -539,6 +583,9 @@ IterableTables.getiterator(mcs::MonteCarloSimulation) = MCSIterator{mcs.nt_type}
539583
column_names(mcs::MonteCarloSimulation) = fieldnames(mcs.nt_type)
540584
column_types(mcs::MonteCarloSimulation) = [eltype(fld) for fld in values(mcs.rvdict)]
541585

586+
# TBD: strip the "!1" off the end of each field name?
587+
# column_names(iter::MCSIterator) = Tuple([first(split(string(name), "!")) for name in fieldnames(iter.mcs.nt_type)])
588+
542589
column_names(iter::MCSIterator) = column_names(iter.mcs)
543590
column_types(iter::MCSIterator) = IterableTables.column_types(iter.mcs)
544591

test/mcs/runtests.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ using Test
99
@info("test_defmcs.jl")
1010
include("test_defmcs.jl")
1111

12+
@info("test_reshaping.jl")
13+
include("test_reshaping.jl")
1214
end

test/mcs/test-model/emissions.jl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
@defcomp emissions begin
2+
regions = Index() # The regions index must be specified for each component
3+
4+
E = Variable(index=[time, regions]) # Total greenhouse gas emissions
5+
E_Global = Variable(index=[time]) # Global emissions (sum of regional emissions)
6+
sigma = Parameter(index=[time, regions]) # Emissions output ratio
7+
YGROSS = Parameter(index=[time, regions]) # Gross output - Note that YGROSS is now a parameter
8+
9+
# function init(p, v, d)
10+
# end
11+
12+
function run_timestep(p, v, d, t)
13+
# Define an equation for E
14+
for r in d.regions
15+
v.E[t,r] = p.YGROSS[t,r] * p.sigma[t,r]
16+
end
17+
18+
# Define an equation for E_Global
19+
for r in d.regions
20+
v.E_Global[t] = sum(v.E[t,:])
21+
end
22+
end
23+
24+
end
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
@defcomp grosseconomy begin
2+
regions = Index() #Note that a regional index is defined here
3+
4+
YGROSS = Variable(index=[time, regions]) #Gross output
5+
K = Variable(index=[time, regions]) #Capital
6+
l = Parameter(index=[time, regions]) #Labor
7+
tfp = Parameter(index=[time, regions]) #Total factor productivity
8+
s = Parameter(index=[time, regions]) #Savings rate
9+
depk = Parameter(index=[regions]) #Depreciation rate on capital - Note that it only has a region index
10+
k0 = Parameter(index=[regions]) #Initial level of capital
11+
share = Parameter() #Capital share
12+
13+
tester = Parameter(index=[time, regions]) # test for reshaped Dirichlet distribution
14+
15+
function run_timestep(p, v, d, t)
16+
# Note that the regional dimension is defined in d and parameters and variables are indexed by 'r'
17+
18+
# Define an equation for K
19+
for r in d.regions
20+
if is_first(t)
21+
v.K[t,r] = p.k0[r]
22+
else
23+
v.K[t,r] = (1 - p.depk[r])^5 * v.K[t-1,r] + v.YGROSS[t-1,r] * p.s[t-1,r] * 5
24+
end
25+
end
26+
27+
# Define an equation for YGROSS
28+
for r in d.regions
29+
v.YGROSS[t,r] = p.tfp[t,r] * v.K[t,r]^p.share * p.l[t,r]^(1-p.share)
30+
end
31+
end
32+
end

0 commit comments

Comments
 (0)