[Fix] Removed the possibility to create an Obs from data on several replica (#258)

* [Fix] Removed the possibility to create an Obs from data on several replica

* [Fix] extended tests and corrected a small bug in the previous commit

---------

Co-authored-by: Simon Kuberski <simon.kuberski@cern.ch>
This commit is contained in:
s-kuberski 2025-02-25 16:58:44 +01:00 committed by GitHub
commit 17792418ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 111 additions and 61 deletions

View file

@ -529,7 +529,8 @@ def import_dobs_string(content, full_output=False, separator_insertion=True):
deltas.append(repdeltas)
idl.append(repidl)
res.append(Obs(deltas, obs_names, idl=idl))
obsmeans = [np.average(deltas[j]) for j in range(len(deltas))]
res.append(Obs([np.array(deltas[j]) - obsmeans[j] for j in range(len(obsmeans))], obs_names, idl=idl, means=obsmeans))
res[-1]._value = mean[i]
_check(len(e_names) == ne)

View file

@ -133,10 +133,11 @@ def create_json_string(ol, description='', indent=1):
names = []
idl = []
for key, value in obs.idl.items():
samples.append([np.nan] * len(value))
samples.append(np.array([np.nan] * len(value)))
names.append(key)
idl.append(value)
my_obs = Obs(samples, names, idl)
my_obs = Obs(samples, names, idl, means=[np.nan for n in names])
my_obs._value = np.nan
my_obs._covobs = obs._covobs
for name in obs._covobs:
my_obs.names.append(name)
@ -331,7 +332,8 @@ def _parse_json_dict(json_dict, verbose=True, full_output=False):
cd = _gen_covobsd_from_cdatad(o.get('cdata', {}))
if od:
ret = Obs([[ddi[0] + values[0] for ddi in di] for di in od['deltas']], od['names'], idl=od['idl'])
r_offsets = [np.average([ddi[0] for ddi in di]) for di in od['deltas']]
ret = Obs([np.array([ddi[0] for ddi in od['deltas'][i]]) - r_offsets[i] for i in range(len(od['deltas']))], od['names'], idl=od['idl'], means=[ro + values[0] for ro in r_offsets])
ret._value = values[0]
else:
ret = Obs([], [], means=[])
@ -356,7 +358,8 @@ def _parse_json_dict(json_dict, verbose=True, full_output=False):
taglist = o.get('tag', layout * [None])
for i in range(layout):
if od:
ret.append(Obs([list(di[:, i] + values[i]) for di in od['deltas']], od['names'], idl=od['idl']))
r_offsets = np.array([np.average(di[:, i]) for di in od['deltas']])
ret.append(Obs([od['deltas'][j][:, i] - r_offsets[j] for j in range(len(od['deltas']))], od['names'], idl=od['idl'], means=[ro + values[i] for ro in r_offsets]))
ret[-1]._value = values[i]
else:
ret.append(Obs([], [], means=[]))
@ -383,7 +386,8 @@ def _parse_json_dict(json_dict, verbose=True, full_output=False):
taglist = o.get('tag', N * [None])
for i in range(N):
if od:
ret.append(Obs([di[:, i] + values[i] for di in od['deltas']], od['names'], idl=od['idl']))
r_offsets = np.array([np.average(di[:, i]) for di in od['deltas']])
ret.append(Obs([od['deltas'][j][:, i] - r_offsets[j] for j in range(len(od['deltas']))], od['names'], idl=od['idl'], means=[ro + values[i] for ro in r_offsets]))
ret[-1]._value = values[i]
else:
ret.append(Obs([], [], means=[]))

View file

@ -82,6 +82,8 @@ class Obs:
raise ValueError('Names are not unique.')
if not all(isinstance(x, str) for x in names):
raise TypeError('All names have to be strings.')
if len(set([o.split('|')[0] for o in names])) > 1:
raise ValueError('Cannot initialize Obs based on multiple ensembles. Please average separate Obs from each ensemble.')
else:
if not isinstance(names[0], str):
raise TypeError('All names have to be strings.')
@ -1407,6 +1409,8 @@ def reweight(weight, obs, **kwargs):
raise ValueError('Error: Not possible to reweight an Obs that contains covobs!')
if not set(obs[i].names).issubset(weight.names):
raise ValueError('Error: Ensembles do not fit')
if len(obs[i].mc_names) > 1 or len(weight.mc_names) > 1:
raise ValueError('Error: Cannot reweight an Obs that contains multiple ensembles.')
for name in obs[i].names:
if not set(obs[i].idl[name]).issubset(weight.idl[name]):
raise ValueError('obs[%d] has to be defined on a subset of the configs in weight.idl[%s]!' % (i, name))
@ -1442,9 +1446,12 @@ def correlate(obs_a, obs_b):
-----
Keep in mind to only correlate primary observables which have not been reweighted
yet. The reweighting has to be applied after correlating the observables.
Currently only works if ensembles are identical (this is not strictly necessary).
Only works if a single ensemble is present in the Obs.
Currently only works if ensemble content is identical (this is not strictly necessary).
"""
if len(obs_a.mc_names) > 1 or len(obs_b.mc_names) > 1:
raise ValueError('Error: Cannot correlate Obs that contain multiple ensembles.')
if sorted(obs_a.names) != sorted(obs_b.names):
raise ValueError(f"Ensembles do not fit {set(sorted(obs_a.names)) ^ set(sorted(obs_b.names))}")
if len(obs_a.cov_names) or len(obs_b.cov_names):
@ -1755,7 +1762,11 @@ def import_bootstrap(boots, name, random_numbers):
def merge_obs(list_of_obs):
"""Combine all observables in list_of_obs into one new observable
"""Combine all observables in list_of_obs into one new observable.
This allows to merge Obs that have been computed on multiple replica
of the same ensemble.
If you like to merge Obs that are based on several ensembles, please
average them yourself.
Parameters
----------