pyerrors.input.json

  1import rapidjson as json
  2import gzip
  3import getpass
  4import socket
  5import datetime
  6import platform
  7import warnings
  8import re
  9import numpy as np
 10from ..obs import Obs
 11from ..covobs import Covobs
 12from ..correlators import Corr
 13from ..misc import _assert_equal_properties
 14from .. import version as pyerrorsversion
 15
 16
 17def create_json_string(ol, description='', indent=1):
 18    """Generate the string for the export of a list of Obs or structures containing Obs
 19    to a .json(.gz) file
 20
 21    Parameters
 22    ----------
 23    ol : list
 24        List of objects that will be exported. At the moment, these objects can be
 25        either of: Obs, list, numpy.ndarray, Corr.
 26        All Obs inside a structure have to be defined on the same set of configurations.
 27    description : str
 28        Optional string that describes the contents of the json file.
 29    indent : int
 30        Specify the indentation level of the json file. None or 0 is permissible and
 31        saves disk space.
 32
 33    Returns
 34    -------
 35    json_string : str
 36        String for export to .json(.gz) file
 37    """
 38
 39    def _gen_data_d_from_list(ol):
 40        dl = []
 41        No = len(ol)
 42        for name in ol[0].mc_names:
 43            ed = {}
 44            ed['id'] = name
 45            ed['replica'] = []
 46            for r_name in ol[0].e_content[name]:
 47                rd = {}
 48                rd['name'] = r_name
 49                rd['deltas'] = []
 50                offsets = [o.r_values[r_name] - o.value for o in ol]
 51                deltas = np.column_stack([ol[oi].deltas[r_name] + offsets[oi] for oi in range(No)])
 52                for i in range(len(ol[0].idl[r_name])):
 53                    rd['deltas'].append([ol[0].idl[r_name][i]])
 54                    rd['deltas'][-1] += deltas[i].tolist()
 55                ed['replica'].append(rd)
 56            dl.append(ed)
 57        return dl
 58
 59    def _gen_cdata_d_from_list(ol):
 60        dl = []
 61        for name in ol[0].cov_names:
 62            ed = {}
 63            ed['id'] = name
 64            ed['layout'] = str(ol[0].covobs[name].cov.shape).lstrip('(').rstrip(')').rstrip(',')
 65            ed['cov'] = list(np.ravel(ol[0].covobs[name].cov))
 66            ncov = ol[0].covobs[name].cov.shape[0]
 67            ed['grad'] = []
 68            for i in range(ncov):
 69                ed['grad'].append([])
 70                for o in ol:
 71                    ed['grad'][-1].append(o.covobs[name].grad[i][0])
 72            dl.append(ed)
 73        return dl
 74
 75    def write_Obs_to_dict(o):
 76        d = {}
 77        d['type'] = 'Obs'
 78        d['layout'] = '1'
 79        if o.tag:
 80            d['tag'] = [o.tag]
 81        if o.reweighted:
 82            d['reweighted'] = o.reweighted
 83        d['value'] = [o.value]
 84        data = _gen_data_d_from_list([o])
 85        if len(data) > 0:
 86            d['data'] = data
 87        cdata = _gen_cdata_d_from_list([o])
 88        if len(cdata) > 0:
 89            d['cdata'] = cdata
 90        return d
 91
 92    def write_List_to_dict(ol):
 93        _assert_equal_properties(ol)
 94        d = {}
 95        d['type'] = 'List'
 96        d['layout'] = '%d' % len(ol)
 97        taglist = [o.tag for o in ol]
 98        if np.any([tag is not None for tag in taglist]):
 99            d['tag'] = taglist
100        if ol[0].reweighted:
101            d['reweighted'] = ol[0].reweighted
102        d['value'] = [o.value for o in ol]
103        data = _gen_data_d_from_list(ol)
104        if len(data) > 0:
105            d['data'] = data
106        cdata = _gen_cdata_d_from_list(ol)
107        if len(cdata) > 0:
108            d['cdata'] = cdata
109        return d
110
111    def write_Array_to_dict(oa):
112        ol = np.ravel(oa)
113        _assert_equal_properties(ol)
114        d = {}
115        d['type'] = 'Array'
116        d['layout'] = str(oa.shape).lstrip('(').rstrip(')').rstrip(',')
117        taglist = [o.tag for o in ol]
118        if np.any([tag is not None for tag in taglist]):
119            d['tag'] = taglist
120        if ol[0].reweighted:
121            d['reweighted'] = ol[0].reweighted
122        d['value'] = [o.value for o in ol]
123        data = _gen_data_d_from_list(ol)
124        if len(data) > 0:
125            d['data'] = data
126        cdata = _gen_cdata_d_from_list(ol)
127        if len(cdata) > 0:
128            d['cdata'] = cdata
129        return d
130
131    def _nan_Obs_like(obs):
132        samples = []
133        names = []
134        idl = []
135        for key, value in obs.idl.items():
136            samples.append([np.nan] * len(value))
137            names.append(key)
138            idl.append(value)
139        my_obs = Obs(samples, names, idl)
140        my_obs._covobs = obs._covobs
141        for name in obs._covobs:
142            my_obs.names.append(name)
143        my_obs.reweighted = obs.reweighted
144        return my_obs
145
146    def write_Corr_to_dict(my_corr):
147        first_not_none = next(i for i, j in enumerate(my_corr.content) if np.all(j))
148        dummy_array = np.empty((my_corr.N, my_corr.N), dtype=object)
149        dummy_array[:] = _nan_Obs_like(my_corr.content[first_not_none].ravel()[0])
150        content = [o if o is not None else dummy_array for o in my_corr.content]
151        dat = write_Array_to_dict(np.array(content, dtype=object))
152        dat['type'] = 'Corr'
153        corr_meta_data = str(my_corr.tag)
154        if 'tag' in dat.keys():
155            dat['tag'].append(corr_meta_data)
156        else:
157            dat['tag'] = [corr_meta_data]
158        taglist = dat['tag']
159        dat['tag'] = {}  # tag is now a dictionary, that contains the previous taglist in the key "tag"
160        dat['tag']['tag'] = taglist
161        if my_corr.prange is not None:
162            dat['tag']['prange'] = my_corr.prange
163        return dat
164
165    if not isinstance(ol, list):
166        ol = [ol]
167
168    d = {}
169    d['program'] = 'pyerrors %s' % (pyerrorsversion.__version__)
170    d['version'] = '1.1'
171    d['who'] = getpass.getuser()
172    d['date'] = datetime.datetime.now().astimezone().strftime('%Y-%m-%d %H:%M:%S %z')
173    d['host'] = socket.gethostname() + ', ' + platform.platform()
174
175    if description:
176        d['description'] = description
177
178    d['obsdata'] = []
179    for io in ol:
180        if isinstance(io, Obs):
181            d['obsdata'].append(write_Obs_to_dict(io))
182        elif isinstance(io, list):
183            d['obsdata'].append(write_List_to_dict(io))
184        elif isinstance(io, np.ndarray):
185            d['obsdata'].append(write_Array_to_dict(io))
186        elif isinstance(io, Corr):
187            d['obsdata'].append(write_Corr_to_dict(io))
188        else:
189            raise Exception("Unkown datatype.")
190
191    def _jsonifier(obj):
192        if isinstance(obj, dict):
193            result = {}
194            for key in obj:
195                if key is True:
196                    result['true'] = obj[key]
197                elif key is False:
198                    result['false'] = obj[key]
199                elif key is None:
200                    result['null'] = obj[key]
201                elif isinstance(key, (int, float, np.floating, np.integer)):
202                    result[str(key)] = obj[key]
203                else:
204                    raise TypeError('keys must be str, int, float, bool or None')
205            return result
206        elif isinstance(obj, np.integer):
207            return int(obj)
208        elif isinstance(obj, np.floating):
209            return float(obj)
210        else:
211            raise ValueError('%r is not JSON serializable' % (obj,))
212
213    if indent:
214        return json.dumps(d, indent=indent, ensure_ascii=False, default=_jsonifier, write_mode=json.WM_SINGLE_LINE_ARRAY)
215    else:
216        return json.dumps(d, indent=indent, ensure_ascii=False, default=_jsonifier, write_mode=json.WM_COMPACT)
217
218
219def dump_to_json(ol, fname, description='', indent=1, gz=True):
220    """Export a list of Obs or structures containing Obs to a .json(.gz) file.
221    Dict keys that are not JSON-serializable such as floats are converted to strings.
222
223    Parameters
224    ----------
225    ol : list
226        List of objects that will be exported. At the moment, these objects can be
227        either of: Obs, list, numpy.ndarray, Corr.
228        All Obs inside a structure have to be defined on the same set of configurations.
229    fname : str
230        Filename of the output file.
231    description : str
232        Optional string that describes the contents of the json file.
233    indent : int
234        Specify the indentation level of the json file. None or 0 is permissible and
235        saves disk space.
236    gz : bool
237        If True, the output is a gzipped json. If False, the output is a json file.
238
239    Returns
240    -------
241    Null
242    """
243
244    jsonstring = create_json_string(ol, description, indent)
245
246    if not fname.endswith('.json') and not fname.endswith('.gz'):
247        fname += '.json'
248
249    if gz:
250        if not fname.endswith('.gz'):
251            fname += '.gz'
252
253        fp = gzip.open(fname, 'wb')
254        fp.write(jsonstring.encode('utf-8'))
255    else:
256        fp = open(fname, 'w', encoding='utf-8')
257        fp.write(jsonstring)
258    fp.close()
259
260
261def _parse_json_dict(json_dict, verbose=True, full_output=False):
262    """Reconstruct a list of Obs or structures containing Obs from a dict that
263    was built out of a json string.
264
265    The following structures are supported: Obs, list, numpy.ndarray, Corr
266    If the list contains only one element, it is unpacked from the list.
267
268    Parameters
269    ----------
270    json_string : str
271        json string containing the data.
272    verbose : bool
273        Print additional information that was written to the file.
274    full_output : bool
275        If True, a dict containing auxiliary information and the data is returned.
276        If False, only the data is returned.
277
278    Returns
279    -------
280    result : list[Obs]
281        reconstructed list of observables from the json string
282    or
283    result : Obs
284        only one observable if the list only has one entry
285    or
286    result : dict
287        if full_output=True
288    """
289
290    def _gen_obsd_from_datad(d):
291        retd = {}
292        if d:
293            retd['names'] = []
294            retd['idl'] = []
295            retd['deltas'] = []
296            for ens in d:
297                for rep in ens['replica']:
298                    rep_name = rep['name']
299                    if len(rep_name) > len(ens["id"]):
300                        if rep_name[len(ens["id"])] != "|":
301                            tmp_list = list(rep_name)
302                            tmp_list = tmp_list[:len(ens["id"])] + ["|"] + tmp_list[len(ens["id"]):]
303                            rep_name = ''.join(tmp_list)
304                    retd['names'].append(rep_name)
305                    retd['idl'].append([di[0] for di in rep['deltas']])
306                    retd['deltas'].append(np.array([di[1:] for di in rep['deltas']]))
307        return retd
308
309    def _gen_covobsd_from_cdatad(d):
310        retd = {}
311        for ens in d:
312            retl = []
313            name = ens['id']
314            layouts = ens.get('layout', '1').strip()
315            layout = [int(ls.strip()) for ls in layouts.split(',') if len(ls) > 0]
316            cov = np.reshape(ens['cov'], layout)
317            grad = ens['grad']
318            nobs = len(grad[0])
319            for i in range(nobs):
320                retl.append({'name': name, 'cov': cov, 'grad': [g[i] for g in grad]})
321            retd[name] = retl
322        return retd
323
324    def get_Obs_from_dict(o):
325        layouts = o.get('layout', '1').strip()
326        if layouts != '1':
327            raise Exception("layout is %s has to be 1 for type Obs." % (layouts), RuntimeWarning)
328
329        values = o['value']
330        od = _gen_obsd_from_datad(o.get('data', {}))
331        cd = _gen_covobsd_from_cdatad(o.get('cdata', {}))
332
333        if od:
334            ret = Obs([[ddi[0] + values[0] for ddi in di] for di in od['deltas']], od['names'], idl=od['idl'])
335            ret._value = values[0]
336        else:
337            ret = Obs([], [], means=[])
338            ret._value = values[0]
339        for name in cd:
340            co = cd[name][0]
341            ret._covobs[name] = Covobs(None, co['cov'], co['name'], grad=co['grad'])
342            ret.names.append(co['name'])
343
344        ret.reweighted = o.get('reweighted', False)
345        ret.tag = o.get('tag', [None])[0]
346        return ret
347
348    def get_List_from_dict(o):
349        layouts = o.get('layout', '1').strip()
350        layout = int(layouts)
351        values = o['value']
352        od = _gen_obsd_from_datad(o.get('data', {}))
353        cd = _gen_covobsd_from_cdatad(o.get('cdata', {}))
354
355        ret = []
356        taglist = o.get('tag', layout * [None])
357        for i in range(layout):
358            if od:
359                ret.append(Obs([list(di[:, i] + values[i]) for di in od['deltas']], od['names'], idl=od['idl']))
360                ret[-1]._value = values[i]
361            else:
362                ret.append(Obs([], [], means=[]))
363                ret[-1]._value = values[i]
364                print('Created Obs with means= ', values[i])
365            for name in cd:
366                co = cd[name][i]
367                ret[-1]._covobs[name] = Covobs(None, co['cov'], co['name'], grad=co['grad'])
368                ret[-1].names.append(co['name'])
369
370            ret[-1].reweighted = o.get('reweighted', False)
371            ret[-1].tag = taglist[i]
372        return ret
373
374    def get_Array_from_dict(o):
375        layouts = o.get('layout', '1').strip()
376        layout = [int(ls.strip()) for ls in layouts.split(',') if len(ls) > 0]
377        N = np.prod(layout)
378        values = o['value']
379        od = _gen_obsd_from_datad(o.get('data', {}))
380        cd = _gen_covobsd_from_cdatad(o.get('cdata', {}))
381
382        ret = []
383        taglist = o.get('tag', N * [None])
384        for i in range(N):
385            if od:
386                ret.append(Obs([di[:, i] + values[i] for di in od['deltas']], od['names'], idl=od['idl']))
387                ret[-1]._value = values[i]
388            else:
389                ret.append(Obs([], [], means=[]))
390                ret[-1]._value = values[i]
391            for name in cd:
392                co = cd[name][i]
393                ret[-1]._covobs[name] = Covobs(None, co['cov'], co['name'], grad=co['grad'])
394                ret[-1].names.append(co['name'])
395            ret[-1].reweighted = o.get('reweighted', False)
396            ret[-1].tag = taglist[i]
397        return np.reshape(ret, layout)
398
399    def get_Corr_from_dict(o):
400        if isinstance(o.get('tag'), list):  # supports the old way
401            taglist = o.get('tag')  # This had to be modified to get the taglist from the dictionary
402            temp_prange = None
403        elif isinstance(o.get('tag'), dict):
404            tagdic = o.get('tag')
405            taglist = tagdic['tag']
406            if 'prange' in tagdic:
407                temp_prange = tagdic['prange']
408            else:
409                temp_prange = None
410        else:
411            raise Exception("The tag is not a list or dict")
412
413        corr_tag = taglist[-1]
414        tmp_o = o
415        tmp_o['tag'] = taglist[:-1]
416        if len(tmp_o['tag']) == 0:
417            del tmp_o['tag']
418        dat = get_Array_from_dict(tmp_o)
419        my_corr = Corr([None if np.isnan(o.ravel()[0].value) else o for o in list(dat)])
420        if corr_tag != 'None':
421            my_corr.tag = corr_tag
422
423        my_corr.prange = temp_prange
424        return my_corr
425
426    prog = json_dict.get('program', '')
427    version = json_dict.get('version', '')
428    who = json_dict.get('who', '')
429    date = json_dict.get('date', '')
430    host = json_dict.get('host', '')
431    if prog and verbose:
432        print('Data has been written using %s.' % (prog))
433    if version and verbose:
434        print('Format version %s' % (version))
435    if np.any([who, date, host] and verbose):
436        print('Written by %s on %s on host %s' % (who, date, host))
437    description = json_dict.get('description', '')
438    if description and verbose:
439        print()
440        print('Description: ', description)
441    obsdata = json_dict['obsdata']
442    ol = []
443    for io in obsdata:
444        if io['type'] == 'Obs':
445            ol.append(get_Obs_from_dict(io))
446        elif io['type'] == 'List':
447            ol.append(get_List_from_dict(io))
448        elif io['type'] == 'Array':
449            ol.append(get_Array_from_dict(io))
450        elif io['type'] == 'Corr':
451            ol.append(get_Corr_from_dict(io))
452        else:
453            raise Exception("Unknown datatype.")
454
455    if full_output:
456        retd = {}
457        retd['program'] = prog
458        retd['version'] = version
459        retd['who'] = who
460        retd['date'] = date
461        retd['host'] = host
462        retd['description'] = description
463        retd['obsdata'] = ol
464
465        return retd
466    else:
467        if len(obsdata) == 1:
468            ol = ol[0]
469
470        return ol
471
472
473def import_json_string(json_string, verbose=True, full_output=False):
474    """Reconstruct a list of Obs or structures containing Obs from a json string.
475
476    The following structures are supported: Obs, list, numpy.ndarray, Corr
477    If the list contains only one element, it is unpacked from the list.
478
479    Parameters
480    ----------
481    json_string : str
482        json string containing the data.
483    verbose : bool
484        Print additional information that was written to the file.
485    full_output : bool
486        If True, a dict containing auxiliary information and the data is returned.
487        If False, only the data is returned.
488
489    Returns
490    -------
491    result : list[Obs]
492        reconstructed list of observables from the json string
493    or
494    result : Obs
495        only one observable if the list only has one entry
496    or
497    result : dict
498        if full_output=True
499    """
500    return _parse_json_dict(json.loads(json_string), verbose, full_output)
501
502
503def load_json(fname, verbose=True, gz=True, full_output=False):
504    """Import a list of Obs or structures containing Obs from a .json(.gz) file.
505
506    The following structures are supported: Obs, list, numpy.ndarray, Corr
507    If the list contains only one element, it is unpacked from the list.
508
509    Parameters
510    ----------
511    fname : str
512        Filename of the input file.
513    verbose : bool
514        Print additional information that was written to the file.
515    gz : bool
516        If True, assumes that data is gzipped. If False, assumes JSON file.
517    full_output : bool
518        If True, a dict containing auxiliary information and the data is returned.
519        If False, only the data is returned.
520
521    Returns
522    -------
523    result : list[Obs]
524        reconstructed list of observables from the json string
525    or
526    result : Obs
527        only one observable if the list only has one entry
528    or
529    result : dict
530        if full_output=True
531    """
532    if not fname.endswith('.json') and not fname.endswith('.gz'):
533        fname += '.json'
534    if gz:
535        if not fname.endswith('.gz'):
536            fname += '.gz'
537        with gzip.open(fname, 'r') as fin:
538            d = json.load(fin)
539    else:
540        if fname.endswith('.gz'):
541            warnings.warn("Trying to read from %s without unzipping!" % fname, UserWarning)
542        with open(fname, 'r', encoding='utf-8') as fin:
543            d = json.loads(fin.read())
544
545    return _parse_json_dict(d, verbose, full_output)
546
547
548def _ol_from_dict(ind, reps='DICTOBS'):
549    """Convert a dictionary of Obs objects to a list and a dictionary that contains
550    placeholders instead of the Obs objects.
551
552    Parameters
553    ----------
554    ind : dict
555        Dict of JSON valid structures and objects that will be exported.
556        At the moment, these object can be either of: Obs, list, numpy.ndarray, Corr.
557        All Obs inside a structure have to be defined on the same set of configurations.
558    reps : str
559        Specify the structure of the placeholder in exported dict to be reps[0-9]+.
560    """
561
562    obstypes = (Obs, Corr, np.ndarray)
563
564    if not reps.isalnum():
565        raise Exception('Placeholder string has to be alphanumeric!')
566    ol = []
567    counter = 0
568
569    def dict_replace_obs(d):
570        nonlocal ol
571        nonlocal counter
572        x = {}
573        for k, v in d.items():
574            if isinstance(v, dict):
575                v = dict_replace_obs(v)
576            elif isinstance(v, list) and all([isinstance(o, Obs) for o in v]):
577                v = obslist_replace_obs(v)
578            elif isinstance(v, list):
579                v = list_replace_obs(v)
580            elif isinstance(v, obstypes):
581                ol.append(v)
582                v = reps + '%d' % (counter)
583                counter += 1
584            elif isinstance(v, str):
585                if bool(re.match(r'%s[0-9]+' % (reps), v)):
586                    raise Exception('Dict contains string %s that matches the placeholder! %s Cannot be safely exported.' % (v, reps))
587            x[k] = v
588        return x
589
590    def list_replace_obs(li):
591        nonlocal ol
592        nonlocal counter
593        x = []
594        for e in li:
595            if isinstance(e, list):
596                e = list_replace_obs(e)
597            elif isinstance(e, list) and all([isinstance(o, Obs) for o in e]):
598                e = obslist_replace_obs(e)
599            elif isinstance(e, dict):
600                e = dict_replace_obs(e)
601            elif isinstance(e, obstypes):
602                ol.append(e)
603                e = reps + '%d' % (counter)
604                counter += 1
605            elif isinstance(e, str):
606                if bool(re.match(r'%s[0-9]+' % (reps), e)):
607                    raise Exception('Dict contains string %s that matches the placeholder! %s Cannot be safely exported.' % (e, reps))
608            x.append(e)
609        return x
610
611    def obslist_replace_obs(li):
612        nonlocal ol
613        nonlocal counter
614        il = []
615        for e in li:
616            il.append(e)
617
618        ol.append(il)
619        x = reps + '%d' % (counter)
620        counter += 1
621        return x
622
623    nd = dict_replace_obs(ind)
624
625    return ol, nd
626
627
628def dump_dict_to_json(od, fname, description='', indent=1, reps='DICTOBS', gz=True):
629    """Export a dict of Obs or structures containing Obs to a .json(.gz) file
630
631    Parameters
632    ----------
633    od : dict
634        Dict of JSON valid structures and objects that will be exported.
635        At the moment, these objects can be either of: Obs, list, numpy.ndarray, Corr.
636        All Obs inside a structure have to be defined on the same set of configurations.
637    fname : str
638        Filename of the output file.
639    description : str
640        Optional string that describes the contents of the json file.
641    indent : int
642        Specify the indentation level of the json file. None or 0 is permissible and
643        saves disk space.
644    reps : str
645        Specify the structure of the placeholder in exported dict to be reps[0-9]+.
646    gz : bool
647        If True, the output is a gzipped json. If False, the output is a json file.
648
649    Returns
650    -------
651    None
652    """
653
654    if not isinstance(od, dict):
655        raise Exception('od has to be a dictionary. Did you want to use dump_to_json?')
656
657    infostring = ('This JSON file contains a python dictionary that has been parsed to a list of structures. '
658                  'OBSDICT contains the dictionary, where Obs or other structures have been replaced by '
659                  '' + reps + '[0-9]+. The field description contains the additional description of this JSON file. '
660                  'This file may be parsed to a dict with the pyerrors routine load_json_dict.')
661
662    desc_dict = {'INFO': infostring, 'OBSDICT': {}, 'description': description}
663    ol, desc_dict['OBSDICT'] = _ol_from_dict(od, reps=reps)
664
665    dump_to_json(ol, fname, description=desc_dict, indent=indent, gz=gz)
666
667
668def _od_from_list_and_dict(ol, ind, reps='DICTOBS'):
669    """Parse a list of Obs or structures containing Obs and an accompanying
670    dict, where the structures have been replaced by placeholders to a
671    dict that contains the structures.
672
673    The following structures are supported: Obs, list, numpy.ndarray, Corr
674
675    Parameters
676    ----------
677    ol : list
678        List of objects -
679        At the moment, these objects can be either of: Obs, list, numpy.ndarray, Corr.
680        All Obs inside a structure have to be defined on the same set of configurations.
681    ind : dict
682        Dict that defines the structure of the resulting dict and contains placeholders
683    reps : str
684        Specify the structure of the placeholder in imported dict to be reps[0-9]+.
685    """
686    if not reps.isalnum():
687        raise Exception('Placeholder string has to be alphanumeric!')
688
689    counter = 0
690
691    def dict_replace_string(d):
692        nonlocal counter
693        nonlocal ol
694        x = {}
695        for k, v in d.items():
696            if isinstance(v, dict):
697                v = dict_replace_string(v)
698            elif isinstance(v, list):
699                v = list_replace_string(v)
700            elif isinstance(v, str) and bool(re.match(r'%s[0-9]+' % (reps), v)):
701                index = int(v[len(reps):])
702                v = ol[index]
703                counter += 1
704            x[k] = v
705        return x
706
707    def list_replace_string(li):
708        nonlocal counter
709        nonlocal ol
710        x = []
711        for e in li:
712            if isinstance(e, list):
713                e = list_replace_string(e)
714            elif isinstance(e, dict):
715                e = dict_replace_string(e)
716            elif isinstance(e, str) and bool(re.match(r'%s[0-9]+' % (reps), e)):
717                index = int(e[len(reps):])
718                e = ol[index]
719                counter += 1
720            x.append(e)
721        return x
722
723    nd = dict_replace_string(ind)
724
725    if counter == 0:
726        raise Exception('No placeholder has been replaced! Check if reps is set correctly.')
727
728    return nd
729
730
731def load_json_dict(fname, verbose=True, gz=True, full_output=False, reps='DICTOBS'):
732    """Import a dict of Obs or structures containing Obs from a .json(.gz) file.
733
734    The following structures are supported: Obs, list, numpy.ndarray, Corr
735
736    Parameters
737    ----------
738    fname : str
739        Filename of the input file.
740    verbose : bool
741        Print additional information that was written to the file.
742    gz : bool
743        If True, assumes that data is gzipped. If False, assumes JSON file.
744    full_output : bool
745        If True, a dict containing auxiliary information and the data is returned.
746        If False, only the data is returned.
747    reps : str
748        Specify the structure of the placeholder in imported dict to be reps[0-9]+.
749
750    Returns
751    -------
752    data : Obs / list / Corr
753        Read data
754    or
755    data : dict
756        Read data and meta-data
757    """
758    indata = load_json(fname, verbose=verbose, gz=gz, full_output=True)
759    description = indata['description']['description']
760    indict = indata['description']['OBSDICT']
761    ol = indata['obsdata']
762    od = _od_from_list_and_dict(ol, indict, reps=reps)
763
764    if full_output:
765        indata['description'] = description
766        indata['obsdata'] = od
767        return indata
768    else:
769        return od
def create_json_string(ol, description='', indent=1):
 18def create_json_string(ol, description='', indent=1):
 19    """Generate the string for the export of a list of Obs or structures containing Obs
 20    to a .json(.gz) file
 21
 22    Parameters
 23    ----------
 24    ol : list
 25        List of objects that will be exported. At the moment, these objects can be
 26        either of: Obs, list, numpy.ndarray, Corr.
 27        All Obs inside a structure have to be defined on the same set of configurations.
 28    description : str
 29        Optional string that describes the contents of the json file.
 30    indent : int
 31        Specify the indentation level of the json file. None or 0 is permissible and
 32        saves disk space.
 33
 34    Returns
 35    -------
 36    json_string : str
 37        String for export to .json(.gz) file
 38    """
 39
 40    def _gen_data_d_from_list(ol):
 41        dl = []
 42        No = len(ol)
 43        for name in ol[0].mc_names:
 44            ed = {}
 45            ed['id'] = name
 46            ed['replica'] = []
 47            for r_name in ol[0].e_content[name]:
 48                rd = {}
 49                rd['name'] = r_name
 50                rd['deltas'] = []
 51                offsets = [o.r_values[r_name] - o.value for o in ol]
 52                deltas = np.column_stack([ol[oi].deltas[r_name] + offsets[oi] for oi in range(No)])
 53                for i in range(len(ol[0].idl[r_name])):
 54                    rd['deltas'].append([ol[0].idl[r_name][i]])
 55                    rd['deltas'][-1] += deltas[i].tolist()
 56                ed['replica'].append(rd)
 57            dl.append(ed)
 58        return dl
 59
 60    def _gen_cdata_d_from_list(ol):
 61        dl = []
 62        for name in ol[0].cov_names:
 63            ed = {}
 64            ed['id'] = name
 65            ed['layout'] = str(ol[0].covobs[name].cov.shape).lstrip('(').rstrip(')').rstrip(',')
 66            ed['cov'] = list(np.ravel(ol[0].covobs[name].cov))
 67            ncov = ol[0].covobs[name].cov.shape[0]
 68            ed['grad'] = []
 69            for i in range(ncov):
 70                ed['grad'].append([])
 71                for o in ol:
 72                    ed['grad'][-1].append(o.covobs[name].grad[i][0])
 73            dl.append(ed)
 74        return dl
 75
 76    def write_Obs_to_dict(o):
 77        d = {}
 78        d['type'] = 'Obs'
 79        d['layout'] = '1'
 80        if o.tag:
 81            d['tag'] = [o.tag]
 82        if o.reweighted:
 83            d['reweighted'] = o.reweighted
 84        d['value'] = [o.value]
 85        data = _gen_data_d_from_list([o])
 86        if len(data) > 0:
 87            d['data'] = data
 88        cdata = _gen_cdata_d_from_list([o])
 89        if len(cdata) > 0:
 90            d['cdata'] = cdata
 91        return d
 92
 93    def write_List_to_dict(ol):
 94        _assert_equal_properties(ol)
 95        d = {}
 96        d['type'] = 'List'
 97        d['layout'] = '%d' % len(ol)
 98        taglist = [o.tag for o in ol]
 99        if np.any([tag is not None for tag in taglist]):
100            d['tag'] = taglist
101        if ol[0].reweighted:
102            d['reweighted'] = ol[0].reweighted
103        d['value'] = [o.value for o in ol]
104        data = _gen_data_d_from_list(ol)
105        if len(data) > 0:
106            d['data'] = data
107        cdata = _gen_cdata_d_from_list(ol)
108        if len(cdata) > 0:
109            d['cdata'] = cdata
110        return d
111
112    def write_Array_to_dict(oa):
113        ol = np.ravel(oa)
114        _assert_equal_properties(ol)
115        d = {}
116        d['type'] = 'Array'
117        d['layout'] = str(oa.shape).lstrip('(').rstrip(')').rstrip(',')
118        taglist = [o.tag for o in ol]
119        if np.any([tag is not None for tag in taglist]):
120            d['tag'] = taglist
121        if ol[0].reweighted:
122            d['reweighted'] = ol[0].reweighted
123        d['value'] = [o.value for o in ol]
124        data = _gen_data_d_from_list(ol)
125        if len(data) > 0:
126            d['data'] = data
127        cdata = _gen_cdata_d_from_list(ol)
128        if len(cdata) > 0:
129            d['cdata'] = cdata
130        return d
131
132    def _nan_Obs_like(obs):
133        samples = []
134        names = []
135        idl = []
136        for key, value in obs.idl.items():
137            samples.append([np.nan] * len(value))
138            names.append(key)
139            idl.append(value)
140        my_obs = Obs(samples, names, idl)
141        my_obs._covobs = obs._covobs
142        for name in obs._covobs:
143            my_obs.names.append(name)
144        my_obs.reweighted = obs.reweighted
145        return my_obs
146
147    def write_Corr_to_dict(my_corr):
148        first_not_none = next(i for i, j in enumerate(my_corr.content) if np.all(j))
149        dummy_array = np.empty((my_corr.N, my_corr.N), dtype=object)
150        dummy_array[:] = _nan_Obs_like(my_corr.content[first_not_none].ravel()[0])
151        content = [o if o is not None else dummy_array for o in my_corr.content]
152        dat = write_Array_to_dict(np.array(content, dtype=object))
153        dat['type'] = 'Corr'
154        corr_meta_data = str(my_corr.tag)
155        if 'tag' in dat.keys():
156            dat['tag'].append(corr_meta_data)
157        else:
158            dat['tag'] = [corr_meta_data]
159        taglist = dat['tag']
160        dat['tag'] = {}  # tag is now a dictionary, that contains the previous taglist in the key "tag"
161        dat['tag']['tag'] = taglist
162        if my_corr.prange is not None:
163            dat['tag']['prange'] = my_corr.prange
164        return dat
165
166    if not isinstance(ol, list):
167        ol = [ol]
168
169    d = {}
170    d['program'] = 'pyerrors %s' % (pyerrorsversion.__version__)
171    d['version'] = '1.1'
172    d['who'] = getpass.getuser()
173    d['date'] = datetime.datetime.now().astimezone().strftime('%Y-%m-%d %H:%M:%S %z')
174    d['host'] = socket.gethostname() + ', ' + platform.platform()
175
176    if description:
177        d['description'] = description
178
179    d['obsdata'] = []
180    for io in ol:
181        if isinstance(io, Obs):
182            d['obsdata'].append(write_Obs_to_dict(io))
183        elif isinstance(io, list):
184            d['obsdata'].append(write_List_to_dict(io))
185        elif isinstance(io, np.ndarray):
186            d['obsdata'].append(write_Array_to_dict(io))
187        elif isinstance(io, Corr):
188            d['obsdata'].append(write_Corr_to_dict(io))
189        else:
190            raise Exception("Unkown datatype.")
191
192    def _jsonifier(obj):
193        if isinstance(obj, dict):
194            result = {}
195            for key in obj:
196                if key is True:
197                    result['true'] = obj[key]
198                elif key is False:
199                    result['false'] = obj[key]
200                elif key is None:
201                    result['null'] = obj[key]
202                elif isinstance(key, (int, float, np.floating, np.integer)):
203                    result[str(key)] = obj[key]
204                else:
205                    raise TypeError('keys must be str, int, float, bool or None')
206            return result
207        elif isinstance(obj, np.integer):
208            return int(obj)
209        elif isinstance(obj, np.floating):
210            return float(obj)
211        else:
212            raise ValueError('%r is not JSON serializable' % (obj,))
213
214    if indent:
215        return json.dumps(d, indent=indent, ensure_ascii=False, default=_jsonifier, write_mode=json.WM_SINGLE_LINE_ARRAY)
216    else:
217        return json.dumps(d, indent=indent, ensure_ascii=False, default=_jsonifier, write_mode=json.WM_COMPACT)

Generate the string for the export of a list of Obs or structures containing Obs to a pyerrors.input.json(.gz) file

Parameters
  • ol (list): List of objects that will be exported. At the moment, these objects can be either of: Obs, list, numpy.ndarray, Corr. All Obs inside a structure have to be defined on the same set of configurations.
  • description (str): Optional string that describes the contents of the json file.
  • indent (int): Specify the indentation level of the json file. None or 0 is permissible and saves disk space.
Returns
def dump_to_json(ol, fname, description='', indent=1, gz=True):
220def dump_to_json(ol, fname, description='', indent=1, gz=True):
221    """Export a list of Obs or structures containing Obs to a .json(.gz) file.
222    Dict keys that are not JSON-serializable such as floats are converted to strings.
223
224    Parameters
225    ----------
226    ol : list
227        List of objects that will be exported. At the moment, these objects can be
228        either of: Obs, list, numpy.ndarray, Corr.
229        All Obs inside a structure have to be defined on the same set of configurations.
230    fname : str
231        Filename of the output file.
232    description : str
233        Optional string that describes the contents of the json file.
234    indent : int
235        Specify the indentation level of the json file. None or 0 is permissible and
236        saves disk space.
237    gz : bool
238        If True, the output is a gzipped json. If False, the output is a json file.
239
240    Returns
241    -------
242    Null
243    """
244
245    jsonstring = create_json_string(ol, description, indent)
246
247    if not fname.endswith('.json') and not fname.endswith('.gz'):
248        fname += '.json'
249
250    if gz:
251        if not fname.endswith('.gz'):
252            fname += '.gz'
253
254        fp = gzip.open(fname, 'wb')
255        fp.write(jsonstring.encode('utf-8'))
256    else:
257        fp = open(fname, 'w', encoding='utf-8')
258        fp.write(jsonstring)
259    fp.close()

Export a list of Obs or structures containing Obs to a pyerrors.input.json(.gz) file. Dict keys that are not JSON-serializable such as floats are converted to strings.

Parameters
  • ol (list): List of objects that will be exported. At the moment, these objects can be either of: Obs, list, numpy.ndarray, Corr. All Obs inside a structure have to be defined on the same set of configurations.
  • fname (str): Filename of the output file.
  • description (str): Optional string that describes the contents of the json file.
  • indent (int): Specify the indentation level of the json file. None or 0 is permissible and saves disk space.
  • gz (bool): If True, the output is a gzipped json. If False, the output is a json file.
Returns
  • Null
def import_json_string(json_string, verbose=True, full_output=False):
474def import_json_string(json_string, verbose=True, full_output=False):
475    """Reconstruct a list of Obs or structures containing Obs from a json string.
476
477    The following structures are supported: Obs, list, numpy.ndarray, Corr
478    If the list contains only one element, it is unpacked from the list.
479
480    Parameters
481    ----------
482    json_string : str
483        json string containing the data.
484    verbose : bool
485        Print additional information that was written to the file.
486    full_output : bool
487        If True, a dict containing auxiliary information and the data is returned.
488        If False, only the data is returned.
489
490    Returns
491    -------
492    result : list[Obs]
493        reconstructed list of observables from the json string
494    or
495    result : Obs
496        only one observable if the list only has one entry
497    or
498    result : dict
499        if full_output=True
500    """
501    return _parse_json_dict(json.loads(json_string), verbose, full_output)

Reconstruct a list of Obs or structures containing Obs from a json string.

The following structures are supported: Obs, list, numpy.ndarray, Corr If the list contains only one element, it is unpacked from the list.

Parameters
  • json_string (str): json string containing the data.
  • verbose (bool): Print additional information that was written to the file.
  • full_output (bool): If True, a dict containing auxiliary information and the data is returned. If False, only the data is returned.
Returns
  • result (list[Obs]): reconstructed list of observables from the json string
  • or
  • result (Obs): only one observable if the list only has one entry
  • or
  • result (dict): if full_output=True
def load_json(fname, verbose=True, gz=True, full_output=False):
504def load_json(fname, verbose=True, gz=True, full_output=False):
505    """Import a list of Obs or structures containing Obs from a .json(.gz) file.
506
507    The following structures are supported: Obs, list, numpy.ndarray, Corr
508    If the list contains only one element, it is unpacked from the list.
509
510    Parameters
511    ----------
512    fname : str
513        Filename of the input file.
514    verbose : bool
515        Print additional information that was written to the file.
516    gz : bool
517        If True, assumes that data is gzipped. If False, assumes JSON file.
518    full_output : bool
519        If True, a dict containing auxiliary information and the data is returned.
520        If False, only the data is returned.
521
522    Returns
523    -------
524    result : list[Obs]
525        reconstructed list of observables from the json string
526    or
527    result : Obs
528        only one observable if the list only has one entry
529    or
530    result : dict
531        if full_output=True
532    """
533    if not fname.endswith('.json') and not fname.endswith('.gz'):
534        fname += '.json'
535    if gz:
536        if not fname.endswith('.gz'):
537            fname += '.gz'
538        with gzip.open(fname, 'r') as fin:
539            d = json.load(fin)
540    else:
541        if fname.endswith('.gz'):
542            warnings.warn("Trying to read from %s without unzipping!" % fname, UserWarning)
543        with open(fname, 'r', encoding='utf-8') as fin:
544            d = json.loads(fin.read())
545
546    return _parse_json_dict(d, verbose, full_output)

Import a list of Obs or structures containing Obs from a pyerrors.input.json(.gz) file.

The following structures are supported: Obs, list, numpy.ndarray, Corr If the list contains only one element, it is unpacked from the list.

Parameters
  • fname (str): Filename of the input file.
  • verbose (bool): Print additional information that was written to the file.
  • gz (bool): If True, assumes that data is gzipped. If False, assumes JSON file.
  • full_output (bool): If True, a dict containing auxiliary information and the data is returned. If False, only the data is returned.
Returns
  • result (list[Obs]): reconstructed list of observables from the json string
  • or
  • result (Obs): only one observable if the list only has one entry
  • or
  • result (dict): if full_output=True
def dump_dict_to_json(od, fname, description='', indent=1, reps='DICTOBS', gz=True):
629def dump_dict_to_json(od, fname, description='', indent=1, reps='DICTOBS', gz=True):
630    """Export a dict of Obs or structures containing Obs to a .json(.gz) file
631
632    Parameters
633    ----------
634    od : dict
635        Dict of JSON valid structures and objects that will be exported.
636        At the moment, these objects can be either of: Obs, list, numpy.ndarray, Corr.
637        All Obs inside a structure have to be defined on the same set of configurations.
638    fname : str
639        Filename of the output file.
640    description : str
641        Optional string that describes the contents of the json file.
642    indent : int
643        Specify the indentation level of the json file. None or 0 is permissible and
644        saves disk space.
645    reps : str
646        Specify the structure of the placeholder in exported dict to be reps[0-9]+.
647    gz : bool
648        If True, the output is a gzipped json. If False, the output is a json file.
649
650    Returns
651    -------
652    None
653    """
654
655    if not isinstance(od, dict):
656        raise Exception('od has to be a dictionary. Did you want to use dump_to_json?')
657
658    infostring = ('This JSON file contains a python dictionary that has been parsed to a list of structures. '
659                  'OBSDICT contains the dictionary, where Obs or other structures have been replaced by '
660                  '' + reps + '[0-9]+. The field description contains the additional description of this JSON file. '
661                  'This file may be parsed to a dict with the pyerrors routine load_json_dict.')
662
663    desc_dict = {'INFO': infostring, 'OBSDICT': {}, 'description': description}
664    ol, desc_dict['OBSDICT'] = _ol_from_dict(od, reps=reps)
665
666    dump_to_json(ol, fname, description=desc_dict, indent=indent, gz=gz)

Export a dict of Obs or structures containing Obs to a pyerrors.input.json(.gz) file

Parameters
  • od (dict): Dict of JSON valid structures and objects that will be exported. At the moment, these objects can be either of: Obs, list, numpy.ndarray, Corr. All Obs inside a structure have to be defined on the same set of configurations.
  • fname (str): Filename of the output file.
  • description (str): Optional string that describes the contents of the json file.
  • indent (int): Specify the indentation level of the json file. None or 0 is permissible and saves disk space.
  • reps (str): Specify the structure of the placeholder in exported dict to be reps[0-9]+.
  • gz (bool): If True, the output is a gzipped json. If False, the output is a json file.
Returns
  • None
def load_json_dict(fname, verbose=True, gz=True, full_output=False, reps='DICTOBS'):
732def load_json_dict(fname, verbose=True, gz=True, full_output=False, reps='DICTOBS'):
733    """Import a dict of Obs or structures containing Obs from a .json(.gz) file.
734
735    The following structures are supported: Obs, list, numpy.ndarray, Corr
736
737    Parameters
738    ----------
739    fname : str
740        Filename of the input file.
741    verbose : bool
742        Print additional information that was written to the file.
743    gz : bool
744        If True, assumes that data is gzipped. If False, assumes JSON file.
745    full_output : bool
746        If True, a dict containing auxiliary information and the data is returned.
747        If False, only the data is returned.
748    reps : str
749        Specify the structure of the placeholder in imported dict to be reps[0-9]+.
750
751    Returns
752    -------
753    data : Obs / list / Corr
754        Read data
755    or
756    data : dict
757        Read data and meta-data
758    """
759    indata = load_json(fname, verbose=verbose, gz=gz, full_output=True)
760    description = indata['description']['description']
761    indict = indata['description']['OBSDICT']
762    ol = indata['obsdata']
763    od = _od_from_list_and_dict(ol, indict, reps=reps)
764
765    if full_output:
766        indata['description'] = description
767        indata['obsdata'] = od
768        return indata
769    else:
770        return od

Import a dict of Obs or structures containing Obs from a pyerrors.input.json(.gz) file.

The following structures are supported: Obs, list, numpy.ndarray, Corr

Parameters
  • fname (str): Filename of the input file.
  • verbose (bool): Print additional information that was written to the file.
  • gz (bool): If True, assumes that data is gzipped. If False, assumes JSON file.
  • full_output (bool): If True, a dict containing auxiliary information and the data is returned. If False, only the data is returned.
  • reps (str): Specify the structure of the placeholder in imported dict to be reps[0-9]+.
Returns
  • data (Obs / list / Corr): Read data
  • or
  • data (dict): Read data and meta-data