Skip to content

Commit 31953a2

Browse files
committed
mv repo
0 parents  commit 31953a2

File tree

12 files changed

+791
-0
lines changed

12 files changed

+791
-0
lines changed

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2019 ajjacobs
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Project.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name = "Config"
2+
uuid = "9d05fc2c-525b-11e9-1a36-8f70a3e496dc"
3+
authors = ["AJ <[email protected]>"]
4+
version = "0.1.0"
5+
6+
[deps]
7+
BSON = "fbb218c0-5317-5bc6-957e-2ee96dd4b1f0"
8+
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
9+
HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f"
10+
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
11+
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
12+
Mmap = "a63ad114-7e13-5084-954f-fe012c677804"
13+
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
14+
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
15+
16+
[compat]
17+
julia = "1.0"

README.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Config.jl
2+
3+
A configuration manager for Julia.
4+
5+
## Quick Start
6+
7+
### Initialize a ConfigManager
8+
9+
Suppose you have a configuration file called "tdlambda.json", specifying
10+
a set of parameter settings for an experiment
11+
12+
```json
13+
{
14+
"save_path": "RandomWalk19/tdlambda",
15+
16+
"experiment": {
17+
"class": "MarkovRewardProcess",
18+
"episodes": 10
19+
},
20+
21+
"environment": {
22+
"class": "RandomWalk",
23+
"nstates": 19
24+
},
25+
26+
"agent": {
27+
"class": "TDLambda",
28+
"gamma": 1.0,
29+
"metastep": [0.025, 0.05, 0.075, 0.1],
30+
"lambda": [0.0, 0.4, 0.8, 0.9]
31+
}
32+
}
33+
```
34+
35+
Note: any config file *must* have the "save_path" parameter. This
36+
specifies the directory in `data/output` which the data will be saved to. In this
37+
example, data will be saved to `data/output/RandomWalk19/tdlambda`
38+
39+
Initialize a manager to manage all the details of this config file
40+
41+
```julia
42+
cfg = ConfigManager("tdlambda.json", @__DIR__)
43+
```
44+
45+
The second argument specifies where the data directory should be
46+
setup. In this case, a directory `data/` will be setup in the same directory
47+
as the experiment which `ConfigManager` was instantiated in.
48+
49+
### Parsing a config
50+
51+
Any lists of parameters in the lowest-level of the config can be swept over
52+
(in this case, `cfg["agent"]["metastep"]` and `cfg["agent"]["lambda"]`).
53+
The different parameter settings are linearized. In order to sweep all the
54+
parameters of this config, we can first check how many different parameters
55+
there are:
56+
57+
```julia
58+
N = total_combinations(cfg)
59+
```
60+
61+
Then, we need to parse each of the individual settings into a concrete
62+
parameterization:
63+
64+
```julia
65+
for idx=1:N
66+
parse!(cfg, idx)
67+
68+
run_some_experiment(cfg)
69+
end
70+
```
71+
72+
parse sets up the settings of a particular parameterization. After parsing the
73+
config, individual parameters can be accessed by indexing. For example, to this
74+
parameterization's "metastep" parameter, we can call `cfg["agent"]["metastep"]`.
75+
If you will be referencing certain nested parameters quite often, you can
76+
get the subconfiguration instead: `subcfg = get_subconfig(cfg, "agent")`.
77+
Then access parameters of the subconfig in the same way: `subcfg["metastep"]`.
78+
79+
Note that `parse!` has a third argument -- the run number -- which defaults to 1.
80+
To do multiple runs of an experiment, you can therefore do:
81+
82+
```julia
83+
for run=1:100
84+
for idx=1:N
85+
parse!(cfg, idx, run)
86+
87+
run_some_experiment(cfg)
88+
end
89+
end
90+
```
91+
92+
### Saving data
93+
94+
The `ConfigManager` also takes care of saving data to the right place.
95+
Just collect whatever data you want during your experiment in a `Dict()`
96+
and pass it to the ConfigManager.
97+
98+
```julia
99+
function experiment(cfg::ConfigManager)
100+
data = Dict()
101+
data["ValueError"] = Float64[]
102+
for i=1:1000
103+
push!(data["ValueError"], rand())
104+
end
105+
106+
save(cfg, data)
107+
end
108+
```
109+
110+
Then load the data later using `load(cfg)` (where cfg is a parse!'d config).
111+

src/Checklist.jl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Checklists which prevent already-ran
2+
# experiments from being re-ran. Useful when
3+
# ComputeCanada experiments timeout
4+
5+
function mark_config(self::ConfigManager)
6+
chk_lst = get_checklist(self)
7+
chk_lst[self["param_setting"]] = 1
8+
end
9+
10+
function check_config(self::ConfigManager)
11+
chk_lst = get_checklist(self)
12+
if chk_lst[self["param_setting"]] == 1
13+
println(string("Already ran param setting ", self["param_setting"], " ", self["run"]))
14+
exit(0)
15+
end
16+
end
17+
18+
function get_checklist(self::ConfigManager)
19+
filename = join(split(self[ "save_path"],"/"), "_")
20+
path = joinpath(_checklists(self), string(filename ,".bin"))
21+
22+
if !isfile(path)
23+
touch(path)
24+
end
25+
26+
f = open(path,"r+")
27+
return Mmap.mmap(f, Vector{UInt8}, self.total_combinations, (self["run"] - 1) * self.total_combinations * sizeof(UInt8))
28+
end
29+
30+
function unmarked_configs(self::ConfigManager)
31+
chkLst = get_checklist(self)
32+
indices = Int[]
33+
for i=1:total_combinations(self)
34+
if chkLst[i] != 1
35+
push!(indices, i)
36+
end
37+
end
38+
return indices
39+
end

src/Config.jl

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
module Config
2+
3+
using JSON
4+
using Dates
5+
using Mmap
6+
using Logging
7+
using HDF5
8+
using Statistics
9+
10+
# utilities
11+
include("Utils.jl")
12+
13+
# data managers
14+
include("Save.jl")
15+
16+
# Base Class
17+
include("Manager.jl")
18+
export ConfigManager, total_combinations, add!, parse!,
19+
get_sweep_params, param_setting_from_id, params_with, param_values,
20+
get_datafile, get_stepslog, get_expdir, get_logdir,
21+
log_config, get_subconfig, get_attrs,
22+
getindex, setindex!, haskey
23+
24+
# Saving/Loading/Parsing data
25+
include("Data.jl")
26+
export save, save!, load, update!, clear!,
27+
get_run_data, get_data, get_summary, get_best,
28+
sensitivity, get_max_runs,
29+
Summary
30+
31+
# Checklist to prevent overwriting results or redundantly re-running
32+
# already-ran experiments
33+
include("Checklist.jl")
34+
export dump_config, mark_config, check_config, unmarked_configs
35+
36+
37+
end # module

src/Data.jl

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
struct Summary
2+
μ
3+
σ
4+
n
5+
6+
Summary(data::Matrix{Float64}) = new(
7+
mean(data,dims=2),
8+
std(data,corrected=true,dims=2) / (size(data)[2]-1),
9+
size(data)[2]
10+
)
11+
12+
Summary(data::Vector{Float64}) = new(
13+
data,
14+
zeros(length(data)),
15+
1
16+
)
17+
end
18+
19+
# Saving/Loading data
20+
save!(self::ConfigManager, data::Dict) = save!(self.dataManager, get_datafile(self), data)
21+
save(self::ConfigManager, data::Dict) = save(self.dataManager, get_datafile(self), data)
22+
load(self::ConfigManager) = load(self.dataManager, get_datafile(self))
23+
load(self::ConfigManager, idx::Int) = load(parse!(self, idx))
24+
25+
# Parsing saved data
26+
27+
function get_run_data(self::ConfigManager, idx, run)
28+
parse!(self, idx, run)
29+
return load(self)
30+
end
31+
32+
function get_data(self::ConfigManager, idx)
33+
runs = get_max_runs(self,idx)
34+
all_data = Dict()
35+
for r in 1:runs
36+
data = get_run_data(self, idx, r)
37+
for k in keys(data)
38+
if haskey(all_data, k)
39+
all_data[k] = hcat(all_data[k], data[k])
40+
else
41+
all_data[k] = data[k]
42+
end
43+
end
44+
end
45+
return all_data
46+
end
47+
48+
function get_fields(self::ConfigManager)
49+
return keys(get_run_data(copy(self),1,1))
50+
end
51+
52+
function get_summary(self::ConfigManager, idx::Int, key::String)
53+
data = get_data(self,idx)
54+
return Summary(data[key])
55+
end
56+
57+
function get_best(self::ConfigManager, key; metric=mean, selector=argmin)
58+
return get_best(self, key, 1:self.total_combinations, metric=metric, selector=selector)
59+
end
60+
61+
function get_best(self::ConfigManager, key, indices; metric=mean, selector=argmin)
62+
data = []
63+
parsed_indices = []
64+
for idx in indices
65+
d = get_summary(self, idx, key).μ
66+
m = metric(d)
67+
if isfinite(m)
68+
push!(data, metric(d))
69+
push!(parsed_indices, idx)
70+
end
71+
end
72+
if length(data) == 0
73+
return nothing # all configs diverged
74+
end
75+
best = selector(data)
76+
return parsed_indices[best]
77+
end
78+
79+
function sensitivity(self::ConfigManager, key; metric=mean)
80+
best_idx = get_best(self,key)
81+
cfg = parse!(self, best_idx)
82+
83+
keys = get_sweep_params(self)
84+
d = Dict( k=> self[k] for k in keys )
85+
86+
# delete the key we're looking for sensitivity over
87+
delete!(d,key)
88+
89+
indices = configs_with(self, d)
90+
sens_data = []
91+
for idx in indices
92+
value = metric(get_summary(self,idx).μ)
93+
push!(sens_data, value)
94+
end
95+
return sens_data
96+
end
97+
98+
function get_max_runs(self::ConfigManager, idx::Int)
99+
cfg = parse!(self,idx,1)
100+
runs = readdir(get_expdir(self))
101+
return length(runs)
102+
end

0 commit comments

Comments
 (0)