Skip to content

Commit cbd9f54

Browse files
committed
v0.2: sampling, YAML, mask [compat]
1 parent 545e151 commit cbd9f54

32 files changed

Lines changed: 3180 additions & 337 deletions

.github/workflows/CI.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
strategy:
1212
matrix:
1313
version:
14-
- '1.11.5'
14+
- '1.12.5'
1515
os:
1616
- ubuntu-latest
1717
- macOS-latest

.github/workflows/CompatHelper.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
runs-on: ${{ matrix.os }}
1010
strategy:
1111
matrix:
12-
julia-version: [1.10.4]
12+
julia-version: [1.12.5]
1313
julia-arch: [x64]
1414
os: [ubuntu-latest]
1515
steps:

.github/workflows/CompatHelperManual.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
if: "contains(github.event.head_commit.message, '[compat]')"
1010
strategy:
1111
matrix:
12-
julia-version: [1.10.4]
12+
julia-version: [1.12.5]
1313
julia-arch: [x64]
1414
os: [ubuntu-latest]
1515
steps:

.github/workflows/TagBot.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ jobs:
1212
- uses: JuliaRegistries/TagBot@v1
1313
with:
1414
token: ${{ secrets.GITHUB_TOKEN }}
15-
ssh: ${{ secrets.DOCUMENTER_KEY }}
15+
# ssh: ${{ secrets.DOCUMENTER_KEY }}

Project.toml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
11
name = "ParameterSpace"
22
uuid = "1cbe449d-ba56-45e5-b823-2aefb7650dd9"
3+
version = "0.2.0"
34
authors = ["islent <[email protected]>"]
4-
version = "0.1.3"
55

66
[deps]
77
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
8+
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
89
IterTools = "c8e1da08-722c-5040-9ed9-7db0dc04731e"
10+
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
911
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
1012
ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca"
13+
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
14+
Sobol = "ed01d8cd-4d21-5b2a-85b4-cc3bdc58bad4"
15+
TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
1116
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
17+
YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6"
1218

1319
[compat]
1420
DataFrames = "1"
21+
DocStringExtensions = "0.9.5"
1522
IterTools = "1"
23+
OrderedCollections = "1.8.1"
1624
ProgressMeter = "1"
17-
julia = "1.6.0"
25+
Sobol = "1"
26+
YAML = "0.4"
27+
julia = "1.6"

Readme.md

Lines changed: 215 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,269 @@
11
# ParameterSpace.jl
22

3-
General tuning tools for julia. Dive into the parameter space of functions or external programs.
3+
Parameter space exploration and tuning tools for Julia. Supports function tuning, program tuning, and various sampling strategies.
44

55
[![codecov](https://codecov.io/gh/JuliaAstroSim/ParameterSpace.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/JuliaAstroSim/ParameterSpace.jl)
66

7-
## Install
7+
## Installation
88

99
```julia
10-
]add ParameterSpace
10+
using Pkg
11+
Pkg.add("ParameterSpace")
1112
using ParameterSpace
1213
```
1314

14-
## Usage
15+
## Quick Start
1516

16-
Examples could be found in folder `examples`
17+
### Basic Usage with ParamSpace
1718

18-
### Tuning a function
19+
```julia
20+
using ParameterSpace
1921

20-
Let's take a simple function for example:
22+
# Define parameter dimensions
23+
pspace = ParamSpace([
24+
ParamDim("x", values=[1, 2, 3]),
25+
ParamDim("y", range=(0, 10, 2)) # 0, 2, 4, 6, 8, 10
26+
])
2127

22-
```julia
23-
@inline g(x::Real, y::Real) = x * y
28+
# Iterate over all combinations
29+
for params in pspace
30+
println("x=$(params.x), y=$(params.y)")
31+
end
32+
33+
# Analyze a function
34+
f(params) = params.x * params.y
35+
result = analyse_function(f, pspace)
2436
```
2537

26-
First construct the parameter space:
38+
### Sampling Strategies
39+
40+
For large parameter spaces, use sampling strategies:
2741

2842
```julia
29-
params = [Parameter("x", 1, 0:2),
30-
Parameter("y", 2, 0:2)]
31-
```
43+
# Random sampling
44+
result = analyse_function(f, pspace; strategy=RandomSampling(seed=42), n_samples=100)
3245

33-
which means there would be $3 \times 3 = 9$ combination of parameters in total, and `ParameterSpace` would help you run tests over all of them by calling the target function iterately:
46+
# Latin Hypercube Sampling (better coverage)
47+
result = analyse_function(f, pspace; strategy=LatinHypercubeSampling(), n_samples=100)
3448

35-
```julia
36-
tuning = analyse_function(g, params)
49+
# Sobol sequence (low discrepancy, deterministic)
50+
result = analyse_function(f, pspace; strategy=SobolSampling(), n_samples=100)
51+
52+
# Grid sampling (full enumeration)
53+
result = analyse_function(f, pspace; strategy=GridSampling())
3754
```
3855

39-
The returns are stored by `DataFrames` to give you enough freedom in data processing
56+
### Configuration Files
57+
58+
Define parameter spaces in YAML or TOML:
59+
60+
```yaml
61+
# params.yaml
62+
parameters:
63+
x:
64+
values: [1, 2, 3]
65+
default: 2
66+
y:
67+
range: [0, 10, 2]
68+
z:
69+
linspace: [0.0, 1.0, 11]
70+
```
4071
4172
```julia
42-
julia> tuning = analyse_function(g, params)
43-
9×3 DataFrame
44-
Row │ x y result
45-
│ Any Any Any
46-
─────┼──────────────────
47-
10 0 0
48-
21 0 0
49-
32 0 0
50-
40 1 0
51-
51 1 1
52-
62 1 2
53-
70 2 0
54-
81 2 2
55-
92 2 4
73+
pspace = load_yaml("params.yaml")
5674
```
5775

58-
If only tuning the second parameter `y` of function `g`, the other parameters should be set in order:
76+
## Detailed Usage
77+
78+
### ParamDim: Parameter Dimension
79+
80+
`ParamDim` defines a single parameter dimension with multiple construction methods:
81+
5982
```julia
60-
params = [Parameter("y", 2, 0:0.1:0.5)]
83+
# Direct values
84+
ParamDim("x", values=[1, 2, 3, 4, 5])
85+
86+
# Arithmetic sequence: start, stop, step
87+
ParamDim("y", range=(0, 10, 2)) # [0, 2, 4, 6, 8, 10]
88+
89+
# Linearly spaced: start, stop, n points
90+
ParamDim("z", linspace=(0.0, 1.0, 11)) # 11 points from 0 to 1
91+
92+
# Logarithmically spaced: 10^a to 10^b, n points
93+
ParamDim("w", logspace=(-1, 1, 5)) # [0.1, 1.0, 10.0]
94+
95+
# With default value and mask
96+
ParamDim("x", values=[1,2,3,4,5], default=3, mask=[true, false, true, true, false])
6197
```
6298

99+
### ParamSpace: Multi-dimensional Parameter Space
100+
63101
```julia
64-
julia> result = analyse_function(g, params, 1.0)
65-
6×2 DataFrame
66-
Row │ y result
67-
│ Any Any
68-
─────┼─────────────
69-
10.0 0.0
70-
20.1 0.1
71-
30.2 0.2
72-
40.3 0.3
73-
50.4 0.4
74-
60.5 0.5
102+
# From vector
103+
pspace = ParamSpace([
104+
ParamDim("x", values=[1, 2, 3]),
105+
ParamDim("y", values=[1, 2])
106+
])
107+
108+
# From dictionary
109+
pspace = ParamSpace(Dict(
110+
"x" => ParamDim("x", values=[1, 2, 3]),
111+
"y" => ParamDim("y", values=[1, 2])
112+
))
113+
114+
# Properties
115+
volume(pspace) # Total combinations: 6
116+
shape(pspace) # (3, 2)
117+
dim_names(pspace) # [:x, :y]
118+
119+
# State management for resumable iteration
120+
set_state!(pspace, 10) # Jump to state 10
121+
state_no(pspace) # Current state number
122+
reset!(pspace) # Reset to beginning
75123
```
76124

77-
### Tuning a program
125+
### Masking and Subspace Selection
78126

79-
It is assumed that all of the parameters are passed through parameter file. First you need to tell `ParameterSpace` how to run your program, by define a `Cmd`:
127+
Filter parameter values using masks:
80128

81129
```julia
82-
command = `julia E:/ParameterSpace.jl/examples/simple_program/print.jl`
130+
pspace = ParamSpace([
131+
ParamDim("x", values=[1, 2, 3, 4, 5]),
132+
ParamDim("y", values=[10, 20, 30])
133+
])
134+
135+
# Mask specific values
136+
set_mask!(pspace, :x, [true, false, true, false, true]) # Only 1, 3, 5
137+
138+
# Activate a subspace by conditions
139+
activate_subspace!(pspace, Dict("x" => [1, 3, 5])) # Only odd x values
140+
activate_subspace!(pspace, Dict("x" => [1, 2], "y" => 10)) # x in [1,2] and y=10
141+
142+
# Clear masks
143+
clear_mask!(pspace, :x)
144+
clear_all_masks!(pspace)
83145
```
84146

85-
It may cause issues if you do not run the program from an absolute path.
147+
### Coupled Parameters
86148

87-
Then write down the content of parameter file in formatted string:
149+
Synchronize parameters with coupled dimensions:
88150

89151
```julia
90-
content = "x = %d, y = %d"
152+
pspace = ParamSpace([
153+
ParamDim("x", values=[1, 2, 3])
154+
])
155+
156+
# Add coupled dimension: x_squared follows x
157+
add_coupled!(pspace, CoupledParamDim("x_squared", target=:x, values=[1, 4, 9]))
158+
159+
for params in pspace
160+
# params.x and params.x_squared are synchronized
161+
# x=1 → x_squared=1, x=2 → x_squared=4, x=3 → x_squared=9
162+
end
91163
```
92164

93-
Construct parameter space and use the tuning tool:
165+
### Analyzing Functions
94166

95167
```julia
96-
params = [Parameter("x", 1, 0:2),
97-
Parameter("y", 2, 0:2)]
168+
# New API: function receives NamedTuple
169+
f(params) = params.x * params.y + params.z
98170

99-
analyse_program(command, content, "param.txt", params)
100-
```
171+
pspace = ParamSpace([
172+
ParamDim("x", values=[1, 2]),
173+
ParamDim("y", values=[1, 2]),
174+
ParamDim("z", values=[0, 1])
175+
])
176+
177+
result = analyse_function(f, pspace; folder="output", filename="results.csv")
101178

102-
where `"param.txt"` defines the name of parameter file.
179+
# With parallel execution (multi-threaded)
180+
result = analyse_function(f, pspace; parallel=:threads)
103181

104-
Each set of parameters would be handled in a seperate sub-folder
182+
# With extra arguments and keyword arguments
183+
g(params, scale=1) = params.x * scale
184+
result = analyse_function(g, pspace; scale=2)
185+
```
105186

106-
There is no general way to pass data from a program to Julia, however it's easy and convenient to analyse the output files automatically if you could provide an anlysis function. The procedure has no difference from tuning a function, and the parameters of analysis function could be set with keyword `args::Union{Tuple,Array}`:
187+
### Analyzing External Programs
107188

108189
```julia
109-
function analyse(args...)
110-
...
111-
return ...
190+
# Define command to run
191+
command = `julia simulation.jl`
192+
193+
# Parameter file format (printf-style)
194+
content = "x = %d, y = %d"
195+
196+
# Analysis function to extract results
197+
function analyse()
198+
data = readlines("output.txt")
199+
return parse(Float64, data[1])
112200
end
113201

114-
analyse_program(command, content, "param.txt", params, analyse, args = [])
202+
pspace = ParamSpace([
203+
ParamDim("x", values=[1, 10, 100]),
204+
ParamDim("y", values=[1000, 10000])
205+
])
206+
207+
result = analyse_program(command, content, "param.txt", pspace, analyse;
208+
folder="output", filename="results.csv")
115209
```
116210

117-
more details in `examples/simple_program/`
211+
## Legacy API (Backward Compatibility)
212+
213+
The original `Parameter` type is still supported:
214+
215+
```julia
216+
# Legacy parameter definition
217+
params = [
218+
Parameter("x", 1, 0:2), # name, index, range
219+
Parameter("y", 2, 0:2)
220+
]
221+
222+
# Function receives arguments in order specified by Parameter.Index
223+
g(x, y) = x * y
224+
result = analyse_function(g, params)
225+
226+
# Partial tuning (fix some parameters)
227+
params = [Parameter("y", 2, 0:0.1:0.5)]
228+
result = analyse_function(g, params, 1.0) # x is fixed at 1.0
229+
```
230+
231+
## Sampling Strategies Comparison
232+
233+
| Strategy | Use Case | Pros | Cons |
234+
|----------|----------|------|------|
235+
| `GridSampling` | Small spaces, exhaustive search | Complete coverage | Exponential growth |
236+
| `RandomSampling` | Quick exploration | Simple, fast | Uneven coverage |
237+
| `LatinHypercubeSampling` | Medium-dimensional spaces | Good 1D projection | Not optimal for high dimensions |
238+
| `SobolSampling` | High-dimensional spaces | Low discrepancy, deterministic | Requires Sobol.jl |
239+
| `StratifiedGridSampling` | Large spaces, quick overview | Sparse but uniform | May miss important regions |
240+
241+
## API Reference
242+
243+
### Types
244+
- `ParamDim{T}`: Parameter dimension
245+
- `ParamSpace`: Multi-dimensional parameter space
246+
- `CoupledParamDim{T}`: Coupled parameter dimension
247+
- `Parameter{A}`: Legacy parameter type
248+
249+
### Sampling Strategies
250+
- `AbstractSamplingStrategy`: Abstract base type
251+
- `RandomSampling(seed=nothing)`: Random uniform sampling
252+
- `LatinHypercubeSampling(seed=nothing)`: Latin Hypercube Sampling
253+
- `SobolSampling()`: Sobol quasi-random sequence
254+
- `GridSampling()`: Complete grid traversal
255+
- `StratifiedGridSampling(resolution)`: Sparse grid sampling
256+
257+
### Functions
258+
- `analyse_function(f, pspace; strategy, n_samples, parallel, ...)`: Analyze function over parameter space
259+
- `analyse_program(cmd, content, paramfile, pspace, analyse; ...)`: Analyze external program
260+
- `sample(pspace, strategy; n)`: Sample points from parameter space
261+
- `load_yaml(filepath)`, `load_toml(filepath)`: Load from config files
262+
- `save_yaml(pspace, filepath)`, `save_toml(pspace, filepath)`: Save to config files
263+
264+
## Examples
265+
266+
See the `examples/` directory for more detailed examples:
267+
- `simple_function.jl`: Basic function tuning
268+
- `simple_program/`: External program tuning
269+

0 commit comments

Comments
 (0)