Source code for bmtk.builder.builder_utils
import numpy as np
import hashlib
import logging
import uuid
try:
from mpi4py import MPI
comm = MPI.COMM_WORLD
mpi_rank = comm.Get_rank()
mpi_size = comm.Get_size()
barrier = comm.barrier
except ImportError:
comm = None
mpi_rank = 0
mpi_size = 1
barrier = lambda: None
MPI_fail_params_nonuniform = True # throw exception unless params across MPI ranks are the same.
logger = logging.getLogger(__name__)
[docs]def add_hdf5_attrs(hdf5_handle):
# TODO: move this as a utility function
hdf5_handle['/'].attrs['magic'] = np.uint32(0x0A7A)
hdf5_handle['/'].attrs['version'] = [np.uint32(0), np.uint32(1)]
[docs]def list_to_hash(str_list):
str_list = str_list.copy()
str_list.sort()
combined_keys = ':'.join(str_list).encode('utf-8')
return hashlib.md5(combined_keys).hexdigest()
[docs]def check_properties_across_ranks(properties, graph_type='node'):
"""Checks that a properties table is consistant across all MPI ranks. Mainly used by add_nodes() and add_edges()
method due to bug where using random generator without rng_seed was causing issues building the network properties
consistantly using multiple cores.
Will throw an Exception or a warning message (if MPI_fail_params_nonuniform is false)
:param properties: A dictionary
:param graph_type: 'node' or 'edge', used in error message. default 'node'.
"""
if mpi_size < 2:
return
# Check that model_properties have the same number of items and the keys match
n_args = len(properties)
ranked_args = comm.allgather(n_args)
if len(set(ranked_args)) > 1:
err_msg = '{} properties are not the same across all ranks.'.format(graph_type)
if not MPI_fail_params_nonuniform:
logger.warning(err_msg)
else:
raise IndexError(err_msg)
if n_args == 0:
return
# create a string/id that will be uniform across all ranks, even if the dict on one rank returns keys out-of-order.
prop_keys = list(properties.keys())
prop_keys.sort()
combined_keys = ':'.join(prop_keys).encode('utf-8')
hash_id = hashlib.md5(combined_keys).hexdigest()
ranked_keys = comm.allgather(hash_id)
if len(set(ranked_keys)) > 1:
err_msg = '{} properties are not the same across all ranks.'.format(graph_type)
if not MPI_fail_params_nonuniform:
logger.warning(err_msg)
else:
raise IndexError(err_msg)
# For each item in model_properties dictionary try to check that values are the same
for pkey in prop_keys:
# Don't use Dict.items() method since it is possible the ret order is different across ranks.
pval = properties[pkey]
try:
if isinstance(pval, bytes):
phash = hashlib.md5(pval).hexdigest()
elif isinstance(pval, str):
phash = hashlib.md5(pval.encode('utf-8')).hexdigest()
elif isinstance(pval, (int, float, bool)):
phash = pval
elif isinstance(pval, (list, tuple)):
joined_keys = ':'.join([str(p) for p in pval]).encode('utf-8')
phash = hashlib.md5(joined_keys).hexdigest()
elif isinstance(pval, np.ndarray):
phash = hashlib.md5(pval.data.tobytes()).hexdigest()
else:
continue
except TypeError as te:
# If the hashing fails assume there is no MPI data issue and continue with the next property.
continue
ranked_vals = comm.allgather(phash)
if len(set(ranked_vals)) > 1:
err_msg = '{} property "{}" varies across ranks, please make sure parameter value is uniform across all' \
'ranks or set bmtk.builder.MPI_fail_params_nonuniform to False'.format(graph_type, pkey)
if not MPI_fail_params_nonuniform:
logger.warning(err_msg)
else:
raise TypeError(err_msg)
__build_time_uuid = None
[docs]def build_time_uuid():
"""Produces a unique identifier that will be unique for each time a network building script is executed.
"""
global __build_time_uuid
if __build_time_uuid is not None:
return __build_time_uuid
elif comm is not None and mpi_size > 1:
barrier()
try:
if mpi_rank == 0:
bcast_data = str(uuid.uuid4().hex)
else:
bcast_data = None
bcast_data = comm.bcast(bcast_data, root=0)
__build_time_uuid = bcast_data
except Exception as e:
__build_time_uuid = 'tmp'
else:
__build_time_uuid = str(uuid.uuid4().hex)
# __build_time_uuid = str(uuid.uuid4().hex)
return __build_time_uuid