-
Notifications
You must be signed in to change notification settings - Fork 53
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
[MRG] Update legacy json and param conversion function for new json format #772
Changes from 9 commits
94a062a
4effcc7
f91b699
945b879
4231bca
65c7ce0
ba1a96f
0bf246c
b4d565d
16689fb
9509178
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -662,27 +662,50 @@ def compare_dictionaries(d1, d2): | |||||
return d1 | ||||||
|
||||||
|
||||||
def convert_to_hdf5(params_fname, out_fname, include_drives=True, | ||||||
overwrite=True, write_output=False): | ||||||
"""Converts json or param format to hdf5 | ||||||
def convert_to_json(params_fname, | ||||||
out_fname, | ||||||
network_connectivity='jones_2009_model', | ||||||
include_drives=True, | ||||||
overwrite=True): | ||||||
"""Converts legacy json or param format to hierarchical json format | ||||||
|
||||||
Parameters | ||||||
---------- | ||||||
params_fname : str or Path | ||||||
Path to file | ||||||
out_fname: str | ||||||
Path to output | ||||||
network_connectivity: str or None, default:' jones_2009_model' | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Made this argument name change. Thanks! |
||||||
Options: ['jones_2009_model', 'law_2021_model', 'calcium_model', None] | ||||||
Neocortical network model to use. Models are defined in | ||||||
network_models. If None, the base Network object with no defined | ||||||
network connectivity will be used. | ||||||
include_drives: bool, default=True | ||||||
Include drives from params file | ||||||
overwrite: bool, default=True | ||||||
Overwrite file | ||||||
write_output: bool, default=False | ||||||
Write out simulations | ||||||
Returns | ||||||
------- | ||||||
None | ||||||
""" | ||||||
from .network import Network | ||||||
from .network_models import jones_2009_model, law_2021_model, calcium_model | ||||||
|
||||||
# Assign network model callables | ||||||
model_functions = {'jones_2009_model': jones_2009_model, | ||||||
'law_2021_model': law_2021_model, | ||||||
'calcium_model': calcium_model | ||||||
} | ||||||
if network_connectivity: | ||||||
try: | ||||||
network_model = model_functions[network_connectivity] | ||||||
except KeyError: | ||||||
raise KeyError(f'Invalid network connectivity: ' | ||||||
f'"{network_connectivity}"; ' | ||||||
f'Valid options: {list(model_functions.keys())}') | ||||||
else: | ||||||
network_model = Network | ||||||
|
||||||
# Validate inputs | ||||||
_validate_type(params_fname, (str, Path), 'params_fname') | ||||||
_validate_type(out_fname, (str, Path), 'out_fname') | ||||||
|
@@ -692,17 +715,18 @@ def convert_to_hdf5(params_fname, out_fname, include_drives=True, | |||||
params_suffix = params_fname.suffix.lower().split('.')[-1] | ||||||
|
||||||
# Add suffix if not supplied | ||||||
if out_fname.suffix != '.hdf5': | ||||||
out_fname = out_fname.with_suffix('.hdf5') | ||||||
|
||||||
net = Network(params=read_params(params_fname), | ||||||
add_drives_from_params=include_drives, | ||||||
legacy_mode=True if params_suffix == 'param' else False, | ||||||
) | ||||||
net.write(fname=out_fname, | ||||||
overwrite=overwrite, | ||||||
write_output=write_output, | ||||||
) | ||||||
if out_fname.suffix != '.json': | ||||||
out_fname = out_fname.with_suffix('.json') | ||||||
|
||||||
net = network_model(params=read_params(params_fname), | ||||||
add_drives_from_params=include_drives, | ||||||
legacy_mode=(True if params_suffix == 'param' | ||||||
else False), | ||||||
) | ||||||
|
||||||
net.write_configuration(fname=out_fname, | ||||||
overwrite=overwrite, | ||||||
) | ||||||
return | ||||||
|
||||||
|
||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,12 @@ | |
|
||
import pytest | ||
|
||
from hnn_core import read_params, Params, jones_2009_model, convert_to_hdf5 | ||
from hnn_core import (read_params, Params, convert_to_json, | ||
Network) | ||
from hnn_core.hnn_io import read_network_configuration | ||
from hnn_core.network_models import (jones_2009_model, law_2021_model, | ||
calcium_model) | ||
|
||
|
||
hnn_core_root = Path(__file__).parents[1] | ||
|
||
|
@@ -74,29 +79,140 @@ def test_base_params(): | |
assert params == params_base | ||
|
||
|
||
def test_convert_to_hdf5_bad_type(): | ||
"""Tests type validation in convert_to_hdf5 function""" | ||
good_path = hnn_core_root | ||
path_str = str(good_path) | ||
bad_path = 5 | ||
|
||
# Valid path and string, but not actual files | ||
with pytest.raises( | ||
ValueError, | ||
match="Unrecognized extension, expected one of" | ||
): | ||
convert_to_hdf5(good_path, path_str) | ||
|
||
# Bad params_fname | ||
with pytest.raises( | ||
TypeError, | ||
match="params_fname must be an instance of str or Path" | ||
): | ||
convert_to_hdf5(bad_path, good_path) | ||
|
||
# Bad out_fname | ||
with pytest.raises( | ||
TypeError, | ||
match="out_fname must be an instance of str or Path" | ||
): | ||
convert_to_hdf5(good_path, bad_path) | ||
class TestConvertToJson: | ||
"""Tests convert_to_json function""" | ||
|
||
path_default = Path(hnn_core_root, 'param', 'default.json') | ||
|
||
def test_default_network_connectivity(self, tmp_path): | ||
"""Tests conversion with default parameters""" | ||
|
||
net_params = jones_2009_model(params=read_params(self.path_default), | ||
add_drives_from_params=True) | ||
|
||
# Write json and check if constructed network is equal | ||
outpath = Path(tmp_path, 'default.json') | ||
convert_to_json(self.path_default, | ||
outpath | ||
) | ||
net_json = read_network_configuration(outpath) | ||
assert net_json == net_params | ||
|
||
# Write json without drives | ||
outpath_no_drives = Path(tmp_path, 'default_no_drives.json') | ||
convert_to_json(self.path_default, | ||
outpath_no_drives, | ||
include_drives=False | ||
) | ||
net_json_no_drives = read_network_configuration(outpath_no_drives) | ||
assert net_json_no_drives != net_json | ||
assert bool(net_json_no_drives.external_drives) is False | ||
|
||
# Check that writing with no extension will add one | ||
outpath_no_ext = Path(tmp_path, 'default_no_ext') | ||
convert_to_json(self.path_default, | ||
outpath_no_ext | ||
) | ||
assert outpath_no_ext.with_suffix('.json').exists() | ||
|
||
def test_law_network_connectivity(self, tmp_path): | ||
"""Tests conversion with Law 2021 network connectivity model""" | ||
|
||
net_params = law_2021_model(read_params(self.path_default), | ||
add_drives_from_params=True, | ||
) | ||
|
||
# Write json and check if constructed network is equal | ||
outpath = Path(tmp_path, 'default.json') | ||
convert_to_json(self.path_default, | ||
outpath, | ||
network_connectivity='law_2021_model') | ||
net_json = read_network_configuration(outpath) | ||
assert net_json == net_params | ||
|
||
def test_calcium_network_connectivity(self, tmp_path): | ||
"""Tests conversion with calcium network connectivity model""" | ||
|
||
net_params = calcium_model(read_params(self.path_default), | ||
add_drives_from_params=True, | ||
) | ||
|
||
# Write json and check if constructed network is equal | ||
outpath = Path(tmp_path, 'default.json') | ||
convert_to_json(self.path_default, | ||
outpath, | ||
network_connectivity='calcium_model') | ||
net_json = read_network_configuration(outpath) | ||
assert net_json == net_params | ||
|
||
def test_no_network_connectivity(self, tmp_path): | ||
"""Tests conversion with no network connectivity model""" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good edge case to check! |
||
|
||
net_params = Network(read_params(self.path_default), | ||
add_drives_from_params=True, | ||
) | ||
|
||
# Write json and check if constructed network is equal | ||
outpath = Path(tmp_path, 'default.json') | ||
convert_to_json(self.path_default, | ||
outpath, | ||
network_connectivity=None) | ||
net_json = read_network_configuration(outpath) | ||
assert net_json == net_params | ||
ntolley marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def test_convert_to_json_legacy(self, tmp_path): | ||
"""Tests conversion of a param legacy file to json""" | ||
|
||
# Download params | ||
param_url = ('https://raw.githubusercontent.com/hnnsolver/' | ||
'hnn-core/test_data/default.param') | ||
params_base_fname = Path(hnn_core_root, 'param', 'default.param') | ||
if not op.exists(params_base_fname): | ||
urlretrieve(param_url, params_base_fname) | ||
net_params = jones_2009_model(read_params(params_base_fname), | ||
add_drives_from_params=True, | ||
legacy_mode=True | ||
) | ||
|
||
# Write json and check if constructed network is equal | ||
outpath = Path(tmp_path, 'default.json') | ||
convert_to_json(params_base_fname, outpath) | ||
net_json = read_network_configuration(outpath) | ||
assert net_json == net_params | ||
|
||
def test_convert_to_json_bad_type(self): | ||
"""Tests type validation in convert_to_json function""" | ||
|
||
good_path = hnn_core_root | ||
path_str = str(good_path) | ||
bad_path = 5 | ||
bad_network_conn = 'bad_model' | ||
|
||
# Valid path and string, but not actual files | ||
with pytest.raises( | ||
ValueError, | ||
match="Unrecognized extension, expected one of" | ||
): | ||
convert_to_json(good_path, path_str) | ||
|
||
# Bad params_fname | ||
with pytest.raises( | ||
TypeError, | ||
match="params_fname must be an instance of str or Path" | ||
): | ||
convert_to_json(bad_path, good_path) | ||
|
||
# Bad out_fname | ||
with pytest.raises( | ||
TypeError, | ||
match="out_fname must be an instance of str or Path" | ||
): | ||
convert_to_json(good_path, bad_path) | ||
|
||
# Bad network_connectivity | ||
with pytest.raises( | ||
KeyError, | ||
match="Invalid network connectivity:" | ||
): | ||
convert_to_json(good_path, good_path, | ||
network_connectivity=bad_network_conn) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry I missed the discussion. Do you guys have a specification of the hierarchical json format? how will you store connectivity etc? Or is it an intermediate format before going to hdf5?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The format is Network object converted to a dictionary format. So the connectivities are just the same hierarchical dictionary format as the Network Object. Pretty much everything is preserved except numpy arrays need to be converted to lists when written to json and converted back upon read-in.
This is not an intermediate format before going to hdf5. All the network connections, drives, cell parameters can be saved and loaded with this format.
What is not included with this format is the ability to save outputs (the optional recordings during simulation saved to the Network). [Is there a better term to use than "outputs"?] We plan to use the hdf5 format for the saving of network outputs. For that hdf5 file, a version of this json will be saved as a single dataset in the hdf5 (A string json binarized), that way the network configurations are paired with the outputs.