from collections import defaultdict
import os
import toml
from . import synthetic
[docs]class Config(object):
"""
Store central configuration for iDQ.
"""
def __init__(self, **kwargs):
# load general parameters
self.tag = kwargs["general"]["tag"]
self.instrument = kwargs["general"]["instrument"]
self.classifiers = set(kwargs["general"]["classifiers"])
if "rootdir" in kwargs["general"]:
self.rootdir = kwargs["general"]["rootdir"]
else:
self.rootdir = os.getcwd()
# load sections
self.defaults = kwargs["defaults"]
self.samples = add_missing_kwargs(kwargs["samples"], **self.defaults)
self.segments = add_missing_kwargs(kwargs["segments"], **self.defaults)
self.condor = kwargs["condor"]
self.train = add_missing_kwargs(kwargs["train"], **self.defaults)
self.evaluate = add_missing_kwargs(kwargs["evaluate"], **self.defaults)
self.calibrate = add_missing_kwargs(kwargs["calibrate"], **self.defaults)
self.timeseries = add_missing_kwargs(kwargs["timeseries"], **self.defaults)
self.report = add_missing_kwargs(kwargs["report"], **self.defaults)
self.monitor = kwargs.get("monitor", {})
# inherit instrument config from general section
self.features = add_missing_kwargs(kwargs["features"], **self.defaults)
self.features["instrument"] = self.instrument
# load classifier config
self.classifier_map = {
c["name"]: add_missing_kwargs(c, **self.defaults)
for c in kwargs["classifier"]
}
# add column / bound defaults
for config in self.classifier_map.values():
# column defaults
for column in ("time", "significance", "frequency"):
if column in self.features:
config.setdefault(column, self.features[column])
# bound defaults
config.setdefault("bounds", config2bounds(self.features.get("bounds", {})))
@classmethod
def load(cls, path):
with open(path, "r") as f:
return cls(**toml.load(f))
def dump(self, path, rootdir=None):
if not rootdir:
rootdir = self.rootdir
config = {
"general": {
"rootdir": rootdir,
"tag": self.tag,
"instrument": self.instrument,
"classifiers": list(self.classifiers),
},
"samples": self.samples,
"features": self.features,
"condor": self.condor,
"segments": self.segments,
"train": self.train,
"evaluate": self.evaluate,
"calibrate": self.calibrate,
"timeseries": self.timeseries,
"report": self.report,
"monitor": self.monitor,
"classifier": list(self.classifier_map.values()),
"defaults": self.defaults,
}
with open(path, "w") as f:
f.write(toml.dumps(config))
[docs]def path2config(path, verbose=False):
"""
read in the config from a path
"""
if verbose:
print("reading config from: " + path)
if not os.path.exists(path):
raise OSError("file does not exist!: " + path)
return Config.load(path)
[docs]def config2bounds(bounds):
"""
a special parser for setting up bounds for trigger sets
"""
ans = {}
for key, bound in bounds.items():
min_val, max_val = bound
try:
min_val = int(min_val)
except ValueError:
min_val = float(min_val)
try:
max_val = int(max_val)
except ValueError:
max_val = float(max_val)
ans[key] = (min_val, max_val)
return ans
[docs]def add_missing_kwargs(kwargs, **new_kwargs):
"""
add kwargs to existing ones if they are not present.
"""
for key, val in new_kwargs.items():
if key not in kwargs:
kwargs[key] = val
return kwargs
[docs]def path2streams(path, segs, verbose=False):
"""
read in parameters from config and set up the appropriate data structures
"""
with open(path, "r") as f:
config = toml.load(f)
# iterate through sections (streams) and add their configs to the object
stream_names = set(stream["name"] for stream in config["stream"])
streams = []
channel2streams = defaultdict(list)
for stream_name, options in zip(stream_names, config["stream"]):
# iterate through required fields
if "rate" not in options:
raise KeyError('must specify "rate" for ' + stream_name)
rate = options["rate"]
if "frequency" not in options["dist"]:
raise KeyError('must specify "dist.frequency" section for ' + stream_name)
freq_opts = options["dist"]["frequency"]
freq_distrib = synthetic.DistribWrapper(freq_opts["type"], *freq_opts["args"])
if "snr" not in options["dist"]:
raise KeyError('must specify "dist.snr" section for ' + stream_name)
snr_opts = options["dist"]["snr"]
snr_distrib = synthetic.DistribWrapper(snr_opts["type"], *snr_opts["args"])
# instantiate the stream object
stream = synthetic.SyntheticTriggerStream(
stream_name, segs, rate, freq_distrib, snr_distrib
)
streams.append(stream)
# process channels and jitters if available
if "jitter" in config:
for jitters in config["jitter"]:
channel = jitters.pop("name")
channel2streams[channel].append((stream, jitters))
return streams, channel2streams