Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add info/benchmarking mode to SimulationSet #265

Merged
merged 4 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/ridepy/extras/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ def sort_params(params: dict) -> dict:

def create_params_json(*, params: dict, sort=True) -> str:
"""
Create a dictionary containing simulation parameters to pretty JSON.
Convert a dictionary containing simulation parameters to pretty JSON.
Parameter dictionaries may contain anything that is supported
by `.ParamsJSONEncoder` and `.ParamsJSONDecoder`, e.g. `RequestGenerator`,
`TransportSpace`s and dispatchers. For additional detail, see :ref:`Executing Simulations`.
Expand Down Expand Up @@ -251,3 +251,15 @@ def read_events_json(jsonl_path: Path) -> list[dict]:
"""
with jsonl_path.open("r", encoding="utf-8") as f:
return list(map(json.loads, f.readlines()))


def create_info_json(info: dict) -> str:
"""
Convert a dictionary containing simulation info to pretty JSON.

Parameters
----------
info
dictionary containing the info to save
"""
return json.dumps(info, indent=4)
37 changes: 35 additions & 2 deletions src/ridepy/extras/simulation_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
read_params_json,
ParamsJSONEncoder,
ParamsJSONDecoder,
create_info_json,
)

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -214,9 +215,12 @@ def perform_single_simulation(
data_dir: Path,
jsonl_chunksize: int = 1000,
debug: bool = False,
param_path_suffix: str = "_params.json",
event_path_suffix: str = ".jsonl",
param_path_suffix: str = "_params.json",
info_path_suffix: str = "_info.json",
dry_run: bool = False,
info: bool = False,
info_contents: Optional[dict[str, Any]] = None,
) -> str:
"""
Execute a single simulation run based on a parameter dictionary
Expand Down Expand Up @@ -252,6 +256,10 @@ def perform_single_simulation(
Simulation events will be stored under "data_dir/<simulation_id><event_path_suffix>"
dry_run
If True, do not actually simulate. Just pretend to and return the corresponding ID.
info
Info/benchmark mode. If true, record the time it took to run the simulation run in a separate info file.
info_contents
Additional contents to write to the info file.

Returns
-------
Expand All @@ -264,6 +272,7 @@ def perform_single_simulation(
sim_id = make_sim_id(params_json)
event_path = data_dir / f"{sim_id}{event_path_suffix}"
param_path = data_dir / f"{sim_id}{param_path_suffix}"
info_path = data_dir / f"{sim_id}{info_path_suffix}"

if (
param_path.exists()
Expand Down Expand Up @@ -341,9 +350,22 @@ def perform_single_simulation(
else:
raise ValueError("Must *either* specify `n_reqs` *or* `t_cutoff`")

simulation_start_time = time()

while chunk := list(it.islice(simulation, jsonl_chunksize)):
save_events_json(jsonl_path=event_path, events=chunk)

simulation_end_time = time()

if info:
simulation_duration = simulation_end_time - simulation_start_time
with info_path.open("w") as f:
info = {
"simulation_duration": simulation_duration,
"jsonl_chunksize": jsonl_chunksize,
} | (info_contents or {})
f.write(create_info_json(info))

with open(str(param_path), "w") as f:
f.write(params_json)
tock = time()
Expand All @@ -363,6 +385,7 @@ def simulate_parameter_combinations(
event_path_suffix: str = ".jsonl",
param_path_suffix: str = "_params.json",
dry_run: bool = False,
info: bool = False,
):
"""
Run simulations for different parameter combinations using multiprocessing.
Expand All @@ -388,6 +411,8 @@ def simulate_parameter_combinations(
Simulation events will be stored under "data_dir/<simulation_id><event_path_suffix>"
dry_run
If True, do not actually simulate. Just pretend to and return the corresponding IDs.
info
Info/benchmark mode. If true, record the time it took to run each simulation run.

Returns
-------
Expand All @@ -404,6 +429,11 @@ def simulate_parameter_combinations(
param_path_suffix=param_path_suffix,
event_path_suffix=event_path_suffix,
dry_run=dry_run,
info=info,
info_contents={
"process_chunksize": process_chunksize,
"max_workers": max_workers,
},
),
param_combinations,
chunksize=process_chunksize,
Expand Down Expand Up @@ -745,7 +775,7 @@ def param_combinations() -> Iterator[dict[str, dict[str, Any]]]:
def __next__(self):
return next(self._param_combinations)

def run(self, dry_run=False):
def run(self, dry_run: bool = False, info: bool = False):
"""
Run the simulations configured through `base_params`, `zip_params` and `product_params` using
multiprocessing. The parameters and resulting output events are written to disk
Expand All @@ -760,6 +790,8 @@ def run(self, dry_run=False):
----------
dry_run
If True, do not actually simulate.
info
Info/benchmark mode. If true, record the time it took to run each simulation run.
"""

self._simulation_ids = simulate_parameter_combinations(
Expand All @@ -772,6 +804,7 @@ def run(self, dry_run=False):
event_path_suffix=self._event_path_suffix,
param_path_suffix=self._param_path_suffix,
dry_run=dry_run,
info=info,
)

def __len__(self) -> int:
Expand Down
2 changes: 1 addition & 1 deletion src/ridepy/util/spaces_cython/spaces.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ cdef class Graph(TransportSpace):
if make_attribute_distance is None:
weights = None
else:
weights = [G[u][v][make_attribute_distance] for u, v in G.edges()]
weights = [distance for u, v, distance in G.edges.data(data=make_attribute_distance)]

return cls(
vertices=list(G.nodes()),
Expand Down