Skip to content

Commit b1b0bf6

Browse files
committed
Merges worlds output in single dataframe by default; Adds priority queuing; Adds multiple runs per batch; Adds optional data source entry to yaml; Adds min/max to Generator samplers; Adds SVG export to Streamlit Dashboard.
1 parent cbaeaeb commit b1b0bf6

19 files changed

Lines changed: 445 additions & 269 deletions

.vscode/launch.json

Lines changed: 46 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,48 @@
11
{
2-
// Use IntelliSense to learn about possible attributes.
3-
// Hover to view descriptions of existing attributes.
4-
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5-
"version": "0.2.0",
6-
"configurations": [
7-
{
8-
"name": "Debug with Streamlit",
9-
"type": "debugpy",
10-
"request": "launch",
11-
"module": "streamlit",
12-
"args": [
13-
"run",
14-
"datasim.py",
15-
"--server.port",
16-
"2000",
17-
"--",
18-
"-v",
19-
"world=examples.icu.icu.ICU"
20-
],
21-
"cwd": "${workspaceFolder}",
22-
"console": "integratedTerminal"
23-
},
24-
{
25-
"name": "Debug without Streamlit",
26-
"type": "debugpy",
27-
"request": "launch",
28-
"program": "datasim.py",
29-
"args": [
30-
"-v",
31-
"world=examples.icu.icu.ICU",
32-
"-o=output",
33-
"-c"
34-
],
35-
"cwd": "${workspaceFolder}",
36-
"console": "integratedTerminal"
37-
},
38-
{
39-
"name": "Run CI tests",
40-
"type": "debugpy",
41-
"request": "launch",
42-
"program": "ci-checks.py",
43-
"cwd": "${workspaceFolder}",
44-
"console": "integratedTerminal"
45-
}
46-
]
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": "Debug with Streamlit",
9+
"type": "debugpy",
10+
"request": "launch",
11+
"module": "streamlit",
12+
"args": [
13+
"run",
14+
"datasim.py",
15+
"--server.port",
16+
"2000",
17+
"--",
18+
"-v",
19+
"world=examples.icu.icu.ICU"
20+
],
21+
"cwd": "${workspaceFolder}",
22+
"console": "integratedTerminal"
23+
},
24+
{
25+
"name": "Debug without Streamlit",
26+
"type": "debugpy",
27+
"request": "launch",
28+
"program": "datasim.py",
29+
"args": [
30+
"-v",
31+
"world=examples.icu.icu.ICU",
32+
"-o=output",
33+
"--clear-output",
34+
"-c"
35+
],
36+
"cwd": "${workspaceFolder}",
37+
"console": "integratedTerminal"
38+
},
39+
{
40+
"name": "Run CI tests",
41+
"type": "debugpy",
42+
"request": "launch",
43+
"program": "ci-checks.py",
44+
"cwd": "${workspaceFolder}",
45+
"console": "integratedTerminal"
46+
}
47+
]
4748
}

.vscode/tasks.json

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,34 @@
11
{
2-
// See https://go.microsoft.com/fwlink/?LinkId=733558
3-
// for the documentation about the tasks.json format
4-
"version": "2.0.0",
5-
"tasks": [
6-
{
7-
"label": "Run without dashboard",
8-
"type": "shell",
9-
"command": "python",
10-
"args": [
11-
"sim_main.py",
12-
"world=examples.icu.icu.ICU",
13-
],
14-
"problemMatcher": [],
15-
"group": {
16-
"kind": "build",
17-
"isDefault": true
18-
}
19-
},
20-
{
21-
"label": "Run with Streamlit",
22-
"type": "shell",
23-
"command": "streamlit",
24-
"args": [
25-
"run",
26-
"sim_main.py",
27-
"streamlit",
28-
"world=examples.icu.icu.ICU",
29-
],
30-
"problemMatcher": [],
31-
"group": "build"
32-
}
33-
]
2+
// See https://go.microsoft.com/fwlink/?LinkId=733558
3+
// for the documentation about the tasks.json format
4+
"version": "2.0.0",
5+
"tasks": [
6+
{
7+
"label": "Run without dashboard",
8+
"type": "shell",
9+
"command": "python",
10+
"args": [
11+
"datasim.py",
12+
"world=examples.icu.icu.ICU",
13+
],
14+
"problemMatcher": [],
15+
"group": {
16+
"kind": "build",
17+
"isDefault": true
18+
}
19+
},
20+
{
21+
"label": "Run with Streamlit",
22+
"type": "shell",
23+
"command": "streamlit",
24+
"args": [
25+
"run",
26+
"datasim.py",
27+
"streamlit",
28+
"world=examples.icu.icu.ICU",
29+
],
30+
"problemMatcher": [],
31+
"group": "build"
32+
}
33+
]
3434
}

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ See sample code, the basic setup is:
3636
-d / --debug: print debug output
3737
-v / --verbose: print all verbose output
3838
-o=<path> / --out-path=<path>: save output in the specified directory
39+
-s / --split-worlds: split output into one file per run
40+
--clear-output: first delete anything in the specified output directory (use with caution!)
3941
-c / --csv: save csv output instead of Pickle
4042

4143
Example: streamlit run datasim.py -v world=examples.icu.icu.ICU

datasim.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
-d / --debug: print debug output
2525
-v / --verbose: print all verbose output
2626
-o=<path> / --out-path=<path>: save output in the specified directory
27+
-s / --split-worlds: split output into one file per run
28+
--clear-output: first delete anything in the specified output directory (use with caution!)
2729
-c / --csv: save csv output instead of Pickle"""
2830
)
2931
exit()
@@ -50,14 +52,22 @@
5052

5153
output_csv = "-c" in argv or "--csv" in argv
5254

55+
split_worlds = "-s" in argv or "--split-worlds" in argv
56+
57+
clear_output = "--clear-output" in argv
58+
5359

5460
if "streamlit" in modules:
5561
import streamlit as st
5662

5763
if "runner" not in st.session_state:
5864
st.session_state.update_time = 1.0
5965
st.session_state.runner = Runner(
60-
world_class_object, auto_output_path=output_path, auto_output_csv=output_csv
66+
world_class_object,
67+
auto_output_path=output_path,
68+
clear_auto_output_path=clear_output,
69+
auto_output_csv=output_csv,
70+
split_worlds=split_worlds,
6171
)
6272

6373
any_active = st.session_state.runner.simulate(stop_server=False)
@@ -74,5 +84,6 @@
7484
world_class_object,
7585
headless=True,
7686
auto_output_path=output_path,
87+
clear_auto_output_path=clear_output,
7788
auto_output_csv=output_csv,
7889
).simulate()

datasim/dataset.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -435,11 +435,14 @@ def remove_source(self, source: DataSource):
435435

436436
def _update(self) -> bool:
437437
changed = False
438+
any_output = False
438439
for source in self.sources:
439440
if not self._gathered:
440441
log(f"- Updating: {source.options.name}...", LogLevel.verbose)
441442
source._update_trace()
442443
changed = True
444+
if source.options.plot_type != PlotType.none:
445+
any_output = True
443446
if source.options.plot_type not in (PlotType.none, PlotType.export_only):
444447
self.output._add_source(
445448
self.world.index,
@@ -448,16 +451,17 @@ def _update(self) -> bool:
448451
source.options.secondary_y,
449452
)
450453

451-
self.output.dataframes[self.world.index][self.id] = DataFrame()
452-
if self.id not in self.output.dataframe_names[self.world.index]:
453-
self.output.dataframe_names[self.world.index][self.id] = (
454-
f"{self.world.title} - {self.id} - {self.world.variation.replace(":", ".")}"
455-
if self.world.variation
456-
else ""
457-
)
454+
if any_output:
455+
self.output.dataframes[self.world.index][self.id] = DataFrame()
456+
if self.id not in self.output.dataframe_names[self.world.index]:
457+
self.output.dataframe_names[self.world.index][self.id] = (
458+
f"{self.world.title} - {self.id} - {self.world.variation.replace(":", ".")}"
459+
if self.world.variation and self.world.runner.split_worlds
460+
else f"{self.world.title} - {self.id}"
461+
)
458462

459463
for source in self.sources:
460-
if source.dataset:
464+
if source.options.plot_type != PlotType.none and source.dataset is not None:
461465
self.output._update_source(source)
462466

463467
dataframe = source._data_frame.copy()

datasim/entity.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,7 @@ def add_output(
8989
and plot_options.plot_type != PlotType.export_only
9090
):
9191
plot_options.plot_type = PlotType.none
92-
elif plot_options.plot_type == PlotType.none:
93-
plot_options.plot_type = PlotType.pie
94-
92+
9593
if data_id == "":
9694
data_id = str(self)
9795

datasim/generator.py

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,18 @@
99
class Sampler:
1010
property: str
1111

12-
def __init__(self, property: str):
12+
def __init__(
13+
self,
14+
property: str,
15+
min: float | None = None,
16+
max: float | None = None,
17+
scale: float = 1.0,
18+
):
1319
self.property = property
20+
self.min = min
21+
self.max = max
22+
self.scale = scale
23+
self.first = True
1424

1525
@staticmethod
1626
def _from_yaml(property: str, params: Dict) -> "Sampler":
@@ -23,6 +33,9 @@ def _from_yaml(property: str, params: Dict) -> "Sampler":
2333
params["value"],
2434
params.get("sample", "independent") == "accumulate",
2535
params.get("start", None),
36+
params.get("min", None),
37+
params.get("max", None),
38+
params.get("scale", 1.0),
2639
)
2740

2841
if "distribution" in params:
@@ -31,6 +44,10 @@ def _from_yaml(property: str, params: Dict) -> "Sampler":
3144
params["distribution"],
3245
params.get("parameters", {}),
3346
params.get("sample", "independent"),
47+
params.get("start", None),
48+
params.get("min", None),
49+
params.get("max", None),
50+
params.get("scale", 1.0),
3451
)
3552

3653
return StaticSampler(property, None)
@@ -45,21 +62,33 @@ class StaticSampler(Sampler):
4562
step: Value
4663

4764
def __init__(
48-
self, property: str, value: Value, accumulate: bool = False, start: Value = None
65+
self,
66+
property: str,
67+
value: Value,
68+
accumulate: bool = False,
69+
start: Value = None,
70+
min: float | None = None,
71+
max: float | None = None,
72+
scale: float = 1.0,
4973
):
50-
super().__init__(property)
74+
super().__init__(property, min, max, scale)
5175
self.accumulate = accumulate
5276
self.value = start if start else 0.0 if isinstance(value, float) else 0
5377
self.step = value
5478

5579
def next(self) -> Value:
56-
if (
57-
self.accumulate
58-
and isinstance(self.value, int | float)
59-
and isinstance(self.step, int | float)
60-
):
61-
self.value += self.step
62-
return self.value
80+
if self.first:
81+
self.first = False
82+
else:
83+
if (
84+
self.accumulate
85+
and isinstance(self.value, int | float)
86+
and isinstance(self.step, int | float)
87+
):
88+
self.value += self.step
89+
return (
90+
self.value if not isinstance(self.value, float) else self.scale * self.value
91+
)
6392

6493

6594
class DistributionSampler(Sampler):
@@ -76,20 +105,34 @@ def __init__(
76105
parameters: Dict,
77106
accumulation: Literal["independent", "accumulate"],
78107
start: Optional[float] = None,
108+
min: float | None = None,
109+
max: float | None = None,
110+
scale=1.0,
79111
):
80-
super().__init__(property)
81-
self.value = start if start else 0.0
112+
super().__init__(property, min, max, scale)
82113
self.accumulate = accumulation == "accumulate"
83114
self.rng = np.random.default_rng()
84115
self.np_function = getattr(self.rng, np_generator)
85116
self.parameters = parameters
117+
self.value = start if start else self.sample()
86118

87-
def next(self) -> Value:
88-
sample = self.np_function(**self.parameters)
89-
if not self.accumulate:
90-
return sample
119+
def sample(self) -> float:
120+
value = self.np_function(**self.parameters)
121+
if self.min and value < self.min:
122+
return self.min
123+
if self.max and value > self.max:
124+
return self.max
125+
return self.scale * value
91126

92-
self.value += sample
127+
def next(self) -> Value:
128+
if self.first:
129+
self.first = False
130+
else:
131+
sample = self.sample()
132+
if not self.accumulate:
133+
return sample
134+
135+
self.value += sample
93136
return self.value
94137

95138

0 commit comments

Comments
 (0)