From 3c3b7fb40f8a2184887ed0f462ab6124f4613534 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Tue, 22 Dec 2020 12:56:39 +0800 Subject: [PATCH 001/161] bug fixing: not displacing input doc on github --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 778277ffe9..2ce48317fd 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ The typical procedure of using DeePMD-kit includes 5 steps A quick-start on using DeePMD-kit can be found [here](doc/use-deepmd-kit.md). -A full [document](doc/train-input.rst) on options in the training input script is available. +A full [document](doc/train-input-auto.rst) on options in the training input script is available. # Troubleshooting From 45d70baf85378a7c18777aab7b54d761b3317d22 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Tue, 22 Dec 2020 12:56:39 +0800 Subject: [PATCH 002/161] bug fixing: not displaying input doc on github --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 778277ffe9..2ce48317fd 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ The typical procedure of using DeePMD-kit includes 5 steps A quick-start on using DeePMD-kit can be found [here](doc/use-deepmd-kit.md). -A full [document](doc/train-input.rst) on options in the training input script is available. +A full [document](doc/train-input-auto.rst) on options in the training input script is available. # Troubleshooting From b83d4c8fb37c6a5353db10493fb88bf98ed419e6 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Fri, 1 Jan 2021 17:10:53 +0800 Subject: [PATCH 003/161] add doc for short-range tabulated interaction --- doc/train-input-auto.rst | 38 +++++++++++++++++++++++++++++++++++++- source/train/argcheck.py | 9 +++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/doc/train-input-auto.rst b/doc/train-input-auto.rst index e29dd5799b..c7dbe1a9e0 100644 --- a/doc/train-input-auto.rst +++ b/doc/train-input-auto.rst @@ -23,6 +23,42 @@ model: The model determines the normalization from the statistics of the data. This key specifies the number of `frames` in each `system` used for statistics. + .. raw:: html + + + use_srtab: + | type: ``str``, optional + | argument path: ``model/use_srtab`` + + The table for the short-range pairwise interaction added on top of DP. The table is a text data file with (N_t + 1) * N_t / 2 + 1 columes. The first colume is the distance between atoms. The second to the last columes are energies for pairs of certain types. For example we have two atom types, 0 and 1. The columes from 2nd to 4th are for 0-0, 0-1 and 1-1 correspondingly. + + .. raw:: html + + + smin_alpha: + | type: ``float``, optional + | argument path: ``model/smin_alpha`` + + The short-range tabulated interaction will be swithed according to the distance of the nearest neighbor. This distance is calculated by softmin. This parameter is the decaying parameter in the softmin. It is only required when `use_srtab` is provided. + + .. raw:: html + + + sw_rmin: + | type: ``float``, optional + | argument path: ``model/sw_rmin`` + + The lower boundary of the interpolation between short-range tabulated interaction and DP. It is only required when `use_srtab` is provided. + + .. raw:: html + + + sw_rmax: + | type: ``float``, optional + | argument path: ``model/sw_rmax`` + + The upper boundary of the interpolation between short-range tabulated interaction and DP. It is only required when `use_srtab` is provided. + .. raw:: html @@ -962,7 +998,7 @@ training: numb_test: - | type: ``int``, optional, default: ``1`` + | type: ``int`` | ``list`` | ``str``, optional, default: ``1`` | argument path: ``training/numb_test`` Number of frames used for the test during training. diff --git a/source/train/argcheck.py b/source/train/argcheck.py index d87d41857c..032d71ad17 100644 --- a/source/train/argcheck.py +++ b/source/train/argcheck.py @@ -223,9 +223,18 @@ def model_args (): doc_data_stat_nbatch = 'The model determines the normalization from the statistics of the data. This key specifies the number of `frames` in each `system` used for statistics.' doc_descrpt = 'The descriptor of atomic environment.' doc_fitting = 'The fitting of physical properties.' + doc_use_srtab = 'The table for the short-range pairwise interaction added on top of DP. The table is a text data file with (N_t + 1) * N_t / 2 + 1 columes. The first colume is the distance between atoms. The second to the last columes are energies for pairs of certain types. For example we have two atom types, 0 and 1. The columes from 2nd to 4th are for 0-0, 0-1 and 1-1 correspondingly.' + doc_smin_alpha = 'The short-range tabulated interaction will be swithed according to the distance of the nearest neighbor. This distance is calculated by softmin. This parameter is the decaying parameter in the softmin. It is only required when `use_srtab` is provided.' + doc_sw_rmin = 'The lower boundary of the interpolation between short-range tabulated interaction and DP. It is only required when `use_srtab` is provided.' + doc_sw_rmax = 'The upper boundary of the interpolation between short-range tabulated interaction and DP. It is only required when `use_srtab` is provided.' + ca = Argument("model", dict, [Argument("type_map", list, optional = True, doc = doc_type_map), Argument("data_stat_nbatch", int, optional = True, default = 10, doc = doc_data_stat_nbatch), + Argument("use_srtab", str, optional = True, doc = doc_use_srtab), + Argument("smin_alpha", float, optional = True, doc = doc_smin_alpha), + Argument("sw_rmin", float, optional = True, doc = doc_sw_rmin), + Argument("sw_rmax", float, optional = True, doc = doc_sw_rmax), Argument("descriptor", dict, [], [descrpt_variant_type_args()], doc = doc_descrpt), Argument("fitting_net", dict, [], [fitting_variant_type_args()], doc = doc_fitting) ]) From 468b5fe25635dcc1f0e5f5fbe76eb344efbf8267 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Tue, 12 Jan 2021 10:07:38 +0800 Subject: [PATCH 004/161] fix bug of compulsory key `loss` --- source/train/argcheck.py | 1 + 1 file changed, 1 insertion(+) diff --git a/source/train/argcheck.py b/source/train/argcheck.py index 032d71ad17..c358c114ff 100644 --- a/source/train/argcheck.py +++ b/source/train/argcheck.py @@ -299,6 +299,7 @@ def loss_args(): doc_loss = 'The definition of loss function. The type of the loss depends on the type of the fitting. For fitting type `ener`, the prefactors before energy, force, virial and atomic energy losses may be provided. For fitting type `dipole`, `polar` and `global_polar`, the loss may be an empty `dict` or unset.' ca = Argument('loss', dict, [], [loss_variant_type_args()], + optional = True, doc = doc_loss) return ca From e6a3ce6be198338f96af6e0142921e9ced9d7e22 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Tue, 12 Jan 2021 10:39:02 +0800 Subject: [PATCH 005/161] fix bug of compulsory lable requirement --- source/train/Loss.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/train/Loss.py b/source/train/Loss.py index 1f336325a3..68fd8dd660 100644 --- a/source/train/Loss.py +++ b/source/train/Loss.py @@ -38,11 +38,11 @@ def __init__ (self, jdata, **kwarg) : self.has_ae = (self.start_pref_ae != 0 or self.limit_pref_ae != 0) self.has_pf = (self.start_pref_pf != 0 or self.limit_pref_pf != 0) # data required - add_data_requirement('energy', 1, atomic=False, must=self.has_e, high_prec=True) - add_data_requirement('force', 3, atomic=True, must=self.has_f, high_prec=False) - add_data_requirement('virial', 9, atomic=False, must=self.has_v, high_prec=False) - add_data_requirement('atom_ener', 1, atomic=True, must=self.has_ae, high_prec=False) - add_data_requirement('atom_pref', 1, atomic=True, must=self.has_pf, high_prec=False, repeat=3) + add_data_requirement('energy', 1, atomic=False, must=False, high_prec=True) + add_data_requirement('force', 3, atomic=True, must=False, high_prec=False) + add_data_requirement('virial', 9, atomic=False, must=False, high_prec=False) + add_data_requirement('atom_ener', 1, atomic=True, must=False, high_prec=False) + add_data_requirement('atom_pref', 1, atomic=True, must=False, high_prec=False, repeat=3) def build (self, learning_rate, From 2b21c22367a6ebf9417d2fadb0b7425eac820c26 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Tue, 12 Jan 2021 11:41:03 +0800 Subject: [PATCH 006/161] add notice for the consistency of the tf version --- doc/install.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/install.md b/doc/install.md index 5e00d3275a..05a0d3bdb0 100644 --- a/doc/install.md +++ b/doc/install.md @@ -63,7 +63,8 @@ source $tensorflow_venv/bin/activate pip install --upgrade pip pip install --upgrade tensorflow==2.3.0 ``` -It is notice that everytime a new shell is started and one wants to use `DeePMD-kit`, the virtual environment should be activated by +It is highly recommanded to keep the consistency of the TensorFlow version for the python and C++ interfaces. +Everytime a new shell is started and one wants to use `DeePMD-kit`, the virtual environment should be activated by ```bash source $tensorflow_venv/bin/activate ``` From fe3a7ec477960342725bc87a82cfdf66c521fde9 Mon Sep 17 00:00:00 2001 From: denghuilu Date: Tue, 26 Jan 2021 12:46:00 +0800 Subject: [PATCH 007/161] fix bug of nbor sorting When the number of sel is smaller than the lammps nbors, the program may have a gpu sorting error. --- source/op/cuda/descrpt_se_a.cu | 95 +------------------------- source/op/cuda/descrpt_se_r.cu | 95 +------------------------- source/op/descrpt_se_a_multi_device.cc | 8 +-- source/op/descrpt_se_r_multi_device.cc | 8 +-- 4 files changed, 8 insertions(+), 198 deletions(-) diff --git a/source/op/cuda/descrpt_se_a.cu b/source/op/cuda/descrpt_se_a.cu index 5965254111..222c24ff49 100644 --- a/source/op/cuda/descrpt_se_a.cu +++ b/source/op/cuda/descrpt_se_a.cu @@ -228,73 +228,6 @@ __global__ void compute_descriptor_se_a (FPTYPE* descript, } } -template -void format_nbor_list_256 ( - const FPTYPE* coord, - const int* type, - const int* jrange, - const int* jlist, - const int& nloc, - const float& rcut_r, - int * i_idx, - int_64 * key -) -{ - const int LEN = 256; - const int MAGIC_NUMBER = 256; - const int nblock = (MAGIC_NUMBER + LEN - 1) / LEN; - dim3 block_grid(nloc, nblock); - dim3 thread_grid(1, LEN); - format_nlist_fill_a_se_a - <<>> ( - coord, - type, - jrange, - jlist, - rcut_r, - key, - i_idx, - MAGIC_NUMBER - ); - const int ITEMS_PER_THREAD = 4; - const int BLOCK_THREADS = MAGIC_NUMBER / ITEMS_PER_THREAD; - // BlockSortKernel<<>> ( - BlockSortKernel <<>> (key, key + nloc * MAGIC_NUMBER); -} - -template -void format_nbor_list_512 ( - const FPTYPE* coord, - const int* type, - const int* jrange, - const int* jlist, - const int& nloc, - const float& rcut_r, - int * i_idx, - int_64 * key -) -{ - const int LEN = 256; - const int MAGIC_NUMBER = 512; - const int nblock = (MAGIC_NUMBER + LEN - 1) / LEN; - dim3 block_grid(nloc, nblock); - dim3 thread_grid(1, LEN); - format_nlist_fill_a_se_a - <<>> ( - coord, - type, - jrange, - jlist, - rcut_r, - key, - i_idx, - MAGIC_NUMBER - ); - const int ITEMS_PER_THREAD = 4; - const int BLOCK_THREADS = MAGIC_NUMBER / ITEMS_PER_THREAD; - // BlockSortKernel<<>> ( - BlockSortKernel <<>> (key, key + nloc * MAGIC_NUMBER); -} template void format_nbor_list_1024 ( @@ -419,29 +352,7 @@ void DescrptSeAGPUExecuteFunctor::operator()(const FPTYPE * coord, const // cudaProfilerStart(); get_i_idx_se_a<<>> (nloc, ilist, i_idx); - if (nnei <= 256) { - format_nbor_list_256 ( - coord, - type, - jrange, - jlist, - nloc, - rcut_r, - i_idx, - key - ); - } else if (nnei <= 512) { - format_nbor_list_512 ( - coord, - type, - jrange, - jlist, - nloc, - rcut_r, - i_idx, - key - ); - } else if (nnei <= 1024) { + if (MAGIC_NUMBER <= 1024) { format_nbor_list_1024 ( coord, type, @@ -452,7 +363,7 @@ void DescrptSeAGPUExecuteFunctor::operator()(const FPTYPE * coord, const i_idx, key ); - } else if (nnei <= 2048) { + } else if (MAGIC_NUMBER <= 2048) { format_nbor_list_2048 ( coord, type, @@ -463,7 +374,7 @@ void DescrptSeAGPUExecuteFunctor::operator()(const FPTYPE * coord, const i_idx, key ); - } else if (nnei <= 4096) { + } else if (MAGIC_NUMBER <= 4096) { format_nbor_list_4096 ( coord, type, diff --git a/source/op/cuda/descrpt_se_r.cu b/source/op/cuda/descrpt_se_r.cu index a65ba5887a..17e76daae2 100644 --- a/source/op/cuda/descrpt_se_r.cu +++ b/source/op/cuda/descrpt_se_r.cu @@ -210,73 +210,6 @@ __global__ void compute_descriptor_se_r (FPTYPE* descript, } } -template -void format_nbor_list_256 ( - const FPTYPE* coord, - const int* type, - const int* jrange, - const int* jlist, - const int& nloc, - const float& rcut_r, - int * i_idx, - int_64 * key -) -{ - const int LEN = 256; - const int MAGIC_NUMBER = 256; - const int nblock = (MAGIC_NUMBER + LEN - 1) / LEN; - dim3 block_grid(nloc, nblock); - dim3 thread_grid(1, LEN); - format_nlist_fill_a_se_r - <<>> ( - coord, - type, - jrange, - jlist, - rcut_r, - key, - i_idx, - MAGIC_NUMBER - ); - const int ITEMS_PER_THREAD = 4; - const int BLOCK_THREADS = MAGIC_NUMBER / ITEMS_PER_THREAD; - // BlockSortKernel<<>> ( - BlockSortKernel <<>> (key, key + nloc * MAGIC_NUMBER); -} - -template -void format_nbor_list_512 ( - const FPTYPE* coord, - const int* type, - const int* jrange, - const int* jlist, - const int& nloc, - const float& rcut_r, - int * i_idx, - int_64 * key -) -{ - const int LEN = 256; - const int MAGIC_NUMBER = 512; - const int nblock = (MAGIC_NUMBER + LEN - 1) / LEN; - dim3 block_grid(nloc, nblock); - dim3 thread_grid(1, LEN); - format_nlist_fill_a_se_r - <<>> ( - coord, - type, - jrange, - jlist, - rcut_r, - key, - i_idx, - MAGIC_NUMBER - ); - const int ITEMS_PER_THREAD = 4; - const int BLOCK_THREADS = MAGIC_NUMBER / ITEMS_PER_THREAD; - // BlockSortKernel<<>> ( - BlockSortKernel <<>> (key, key + nloc * MAGIC_NUMBER); -} template void format_nbor_list_1024 ( @@ -401,29 +334,7 @@ void DescrptSeRGPUExecuteFunctor::operator()(const FPTYPE * coord, const // cudaProfilerStart(); get_i_idx_se_r<<>> (nloc, ilist, i_idx); - if (nnei <= 256) { - format_nbor_list_256 ( - coord, - type, - jrange, - jlist, - nloc, - rcut_r, - i_idx, - key - ); - } else if (nnei <= 512) { - format_nbor_list_512 ( - coord, - type, - jrange, - jlist, - nloc, - rcut_r, - i_idx, - key - ); - } else if (nnei <= 1024) { + if (MAGIC_NUMBER <= 1024) { format_nbor_list_1024 ( coord, type, @@ -434,7 +345,7 @@ void DescrptSeRGPUExecuteFunctor::operator()(const FPTYPE * coord, const i_idx, key ); - } else if (nnei <= 2048) { + } else if (MAGIC_NUMBER <= 2048) { format_nbor_list_2048 ( coord, type, @@ -445,7 +356,7 @@ void DescrptSeRGPUExecuteFunctor::operator()(const FPTYPE * coord, const i_idx, key ); - } else if (nnei <= 4096) { + } else if (MAGIC_NUMBER <= 4096) { format_nbor_list_4096 ( coord, type, diff --git a/source/op/descrpt_se_a_multi_device.cc b/source/op/descrpt_se_a_multi_device.cc index ae5e623171..914e611e2d 100644 --- a/source/op/descrpt_se_a_multi_device.cc +++ b/source/op/descrpt_se_a_multi_device.cc @@ -271,13 +271,7 @@ class DescrptSeAOp : public OpKernel { } int get_magic_number(int const nnei) { - if (nnei <= 256) { - return 256; - } - else if (nnei <= 512) { - return 512; - } - else if (nnei <= 1024) { + if (nnei <= 1024) { return 1024; } else if (nnei <= 2048) { diff --git a/source/op/descrpt_se_r_multi_device.cc b/source/op/descrpt_se_r_multi_device.cc index c5eaff616c..07a9618270 100644 --- a/source/op/descrpt_se_r_multi_device.cc +++ b/source/op/descrpt_se_r_multi_device.cc @@ -261,13 +261,7 @@ class DescrptSeROp : public OpKernel { } int get_magic_number(int const nnei) { - if (nnei <= 256) { - return 256; - } - else if (nnei <= 512) { - return 512; - } - else if (nnei <= 1024) { + if (nnei <= 1024) { return 1024; } else if (nnei <= 2048) { From 3c9dfc49c43e09f90bf3c955710dd64cc9ce1144 Mon Sep 17 00:00:00 2001 From: denghuilu Date: Tue, 26 Jan 2021 17:07:05 +0800 Subject: [PATCH 008/161] use lammps max_nbor_size as the upper boundary of gpu sorting --- source/lib/include/CustomeOperation.h | 12 +++--- source/op/cuda/descrpt_se_a.cu | 54 +++++++++++++------------- source/op/cuda/descrpt_se_r.cu | 54 +++++++++++++------------- source/op/descrpt_se_a_multi_device.cc | 34 +++++++--------- source/op/descrpt_se_r_multi_device.cc | 35 +++++++---------- 5 files changed, 88 insertions(+), 101 deletions(-) diff --git a/source/lib/include/CustomeOperation.h b/source/lib/include/CustomeOperation.h index c446db8130..f7fd7c2496 100644 --- a/source/lib/include/CustomeOperation.h +++ b/source/lib/include/CustomeOperation.h @@ -169,7 +169,7 @@ void compute_descriptor_se_a_cpu ( } template -void DescrptSeACPULauncher(const FPTYPE * coord, const int * type, const int * ilist, const int * jrange, const int * jlist, const FPTYPE * avg, const FPTYPE * std, FPTYPE * descrpt, FPTYPE * descrpt_deriv, FPTYPE * rij, int * nlist, const int nloc, const int nall, const int nnei, const int ntypes, const int ndescrpt, const float rcut_r, const float rcut_r_smth, const std::vector sec_a, const bool fill_nei_a, const int magic_number) { +void DescrptSeACPULauncher(const FPTYPE * coord, const int * type, const int * ilist, const int * jrange, const int * jlist, const FPTYPE * avg, const FPTYPE * std, FPTYPE * descrpt, FPTYPE * descrpt_deriv, FPTYPE * rij, int * nlist, const int nloc, const int nall, const int nnei, const int ntypes, const int ndescrpt, const float rcut_r, const float rcut_r_smth, const std::vector sec_a, const bool fill_nei_a, const int max_nbor_size) { // set & normalize coord std::vector d_coord3(nall * 3); for (int ii = 0; ii < nall; ++ii) { @@ -235,8 +235,8 @@ void DescrptSeACPULauncher(const FPTYPE * coord, const int * type, const int * i #if GOOGLE_CUDA template -void DescrptSeAGPULauncher(const FPTYPE * coord, const int * type, const int * ilist, const int * jrange, const int * jlist, int * array_int, unsigned long long * array_longlong, const FPTYPE * avg, const FPTYPE * std, FPTYPE * descrpt, FPTYPE * descrpt_deriv, FPTYPE * rij, int * nlist, const int nloc, const int nall, const int nnei, const int ndescrpt, const float rcut_r, const float rcut_r_smth, const std::vector sec_a, const bool fill_nei_a, const int magic_number) { - DescrptSeAGPUExecuteFunctor()(coord, type, ilist, jrange, jlist, array_int, array_longlong, avg, std, descrpt, descrpt_deriv, rij, nlist, nloc, nall, nnei, ndescrpt, rcut_r, rcut_r_smth, sec_a, fill_nei_a, magic_number); +void DescrptSeAGPULauncher(const FPTYPE * coord, const int * type, const int * ilist, const int * jrange, const int * jlist, int * array_int, unsigned long long * array_longlong, const FPTYPE * avg, const FPTYPE * std, FPTYPE * descrpt, FPTYPE * descrpt_deriv, FPTYPE * rij, int * nlist, const int nloc, const int nall, const int nnei, const int ndescrpt, const float rcut_r, const float rcut_r_smth, const std::vector sec_a, const bool fill_nei_a, const int max_nbor_size) { + DescrptSeAGPUExecuteFunctor()(coord, type, ilist, jrange, jlist, array_int, array_longlong, avg, std, descrpt, descrpt_deriv, rij, nlist, nloc, nall, nnei, ndescrpt, rcut_r, rcut_r_smth, sec_a, fill_nei_a, max_nbor_size); } #endif // GOOGLE_CUDA // ****************************************************************************** @@ -432,7 +432,7 @@ void compute_descriptor_se_r_cpu ( } template -void DescrptSeRCPULauncher(const FPTYPE * coord, const int * type, const int * ilist, const int * jrange, const int * jlist, const FPTYPE * avg, const FPTYPE * std, FPTYPE * descrpt, FPTYPE * descrpt_deriv, FPTYPE * rij, int * nlist, const int nloc, const int nall, const int nnei, const int ntypes, const int ndescrpt, const float rcut_r, const float rcut_r_smth, const std::vector sec_a, const bool fill_nei_a, const int magic_number) { +void DescrptSeRCPULauncher(const FPTYPE * coord, const int * type, const int * ilist, const int * jrange, const int * jlist, const FPTYPE * avg, const FPTYPE * std, FPTYPE * descrpt, FPTYPE * descrpt_deriv, FPTYPE * rij, int * nlist, const int nloc, const int nall, const int nnei, const int ntypes, const int ndescrpt, const float rcut_r, const float rcut_r_smth, const std::vector sec_a, const bool fill_nei_a, const int max_nbor_size) { // set & normalize coord std::vector d_coord3(nall * 3); for (int ii = 0; ii < nall; ++ii) { @@ -498,8 +498,8 @@ void DescrptSeRCPULauncher(const FPTYPE * coord, const int * type, const int * i #if GOOGLE_CUDA template -void DescrptSeRGPULauncher(const FPTYPE * coord, const int * type, const int * ilist, const int * jrange, const int * jlist, int * array_int, unsigned long long * array_longlong, const FPTYPE * avg, const FPTYPE * std, FPTYPE * descrpt, FPTYPE * descrpt_deriv, FPTYPE * rij, int * nlist, const int nloc, const int nall, const int nnei, const int ndescrpt, const float rcut_r, const float rcut_r_smth, const std::vector sec_a, const bool fill_nei_a, const int magic_number) { - DescrptSeRGPUExecuteFunctor()(coord, type, ilist, jrange, jlist, array_int, array_longlong, avg, std, descrpt, descrpt_deriv, rij, nlist, nloc, nall, nnei, ndescrpt, rcut_r, rcut_r_smth, sec_a, fill_nei_a, magic_number); +void DescrptSeRGPULauncher(const FPTYPE * coord, const int * type, const int * ilist, const int * jrange, const int * jlist, int * array_int, unsigned long long * array_longlong, const FPTYPE * avg, const FPTYPE * std, FPTYPE * descrpt, FPTYPE * descrpt_deriv, FPTYPE * rij, int * nlist, const int nloc, const int nall, const int nnei, const int ndescrpt, const float rcut_r, const float rcut_r_smth, const std::vector sec_a, const bool fill_nei_a, const int max_nbor_size) { + DescrptSeRGPUExecuteFunctor()(coord, type, ilist, jrange, jlist, array_int, array_longlong, avg, std, descrpt, descrpt_deriv, rij, nlist, nloc, nall, nnei, ndescrpt, rcut_r, rcut_r_smth, sec_a, fill_nei_a, max_nbor_size); } #endif // GOOGLE_CUDA // ****************************************************************************** diff --git a/source/op/cuda/descrpt_se_a.cu b/source/op/cuda/descrpt_se_a.cu index 222c24ff49..a528c4c477 100644 --- a/source/op/cuda/descrpt_se_a.cu +++ b/source/op/cuda/descrpt_se_a.cu @@ -84,9 +84,9 @@ __global__ void format_nlist_fill_a_se_a(const FPTYPE * coord, const float rcut, int_64 * key, int * i_idx, - const int MAGIC_NUMBER) + const int MAX_NBOR_SIZE) { - // <<>> + // <<>> const unsigned int idx = blockIdx.x; const unsigned int idy = blockIdx.y * blockDim.y + threadIdx.y; @@ -98,7 +98,7 @@ __global__ void format_nlist_fill_a_se_a(const FPTYPE * coord, const int * nei_idx = jlist + jrange[i_idx[idx]]; // dev_copy(nei_idx, &jlist[jrange[i_idx]], nsize); - int_64 * key_in = key + idx * MAGIC_NUMBER; + int_64 * key_in = key + idx * MAX_NBOR_SIZE; FPTYPE diff[3]; const int & j_idx = nei_idx[idy]; @@ -121,7 +121,7 @@ __global__ void format_nlist_fill_b_se_a(int * nlist, const int * sec_a, const int sec_a_size, int * nei_iter_dev, - const int MAGIC_NUMBER) + const int MAX_NBOR_SIZE) { const unsigned int idy = blockIdx.x * blockDim.x + threadIdx.x; @@ -132,13 +132,13 @@ __global__ void format_nlist_fill_b_se_a(int * nlist, int * row_nlist = nlist + idy * nlist_size; int * nei_iter = nei_iter_dev + idy * sec_a_size; - int_64 * key_out = key + nloc * MAGIC_NUMBER + idy * MAGIC_NUMBER; + int_64 * key_out = key + nloc * MAX_NBOR_SIZE + idy * MAX_NBOR_SIZE; for (int ii = 0; ii < sec_a_size; ii++) { nei_iter[ii] = sec_a[ii]; } - for (unsigned int kk = 0; key_out[kk] != key_out[MAGIC_NUMBER - 1]; kk++) { + for (unsigned int kk = 0; key_out[kk] != key_out[MAX_NBOR_SIZE - 1]; kk++) { const int & nei_type = key_out[kk] / 1E15; if (nei_iter[nei_type] < sec_a[nei_type + 1]) { row_nlist[nei_iter[nei_type]++] = key_out[kk] % 100000; @@ -242,8 +242,8 @@ void format_nbor_list_1024 ( ) { const int LEN = 256; - const int MAGIC_NUMBER = 1024; - const int nblock = (MAGIC_NUMBER + LEN - 1) / LEN; + const int MAX_NBOR_SIZE = 1024; + const int nblock = (MAX_NBOR_SIZE + LEN - 1) / LEN; dim3 block_grid(nloc, nblock); dim3 thread_grid(1, LEN); format_nlist_fill_a_se_a @@ -255,12 +255,12 @@ void format_nbor_list_1024 ( rcut_r, key, i_idx, - MAGIC_NUMBER + MAX_NBOR_SIZE ); const int ITEMS_PER_THREAD = 8; - const int BLOCK_THREADS = MAGIC_NUMBER / ITEMS_PER_THREAD; + const int BLOCK_THREADS = MAX_NBOR_SIZE / ITEMS_PER_THREAD; // BlockSortKernel<<>> ( - BlockSortKernel <<>> (key, key + nloc * MAGIC_NUMBER); + BlockSortKernel <<>> (key, key + nloc * MAX_NBOR_SIZE); } template @@ -276,8 +276,8 @@ void format_nbor_list_2048 ( ) { const int LEN = 256; - const int MAGIC_NUMBER = 2048; - const int nblock = (MAGIC_NUMBER + LEN - 1) / LEN; + const int MAX_NBOR_SIZE = 2048; + const int nblock = (MAX_NBOR_SIZE + LEN - 1) / LEN; dim3 block_grid(nloc, nblock); dim3 thread_grid(1, LEN); format_nlist_fill_a_se_a @@ -289,12 +289,12 @@ void format_nbor_list_2048 ( rcut_r, key, i_idx, - MAGIC_NUMBER + MAX_NBOR_SIZE ); const int ITEMS_PER_THREAD = 8; - const int BLOCK_THREADS = MAGIC_NUMBER / ITEMS_PER_THREAD; + const int BLOCK_THREADS = MAX_NBOR_SIZE / ITEMS_PER_THREAD; // BlockSortKernel<<>> ( - BlockSortKernel <<>> (key, key + nloc * MAGIC_NUMBER); + BlockSortKernel <<>> (key, key + nloc * MAX_NBOR_SIZE); } template @@ -310,8 +310,8 @@ void format_nbor_list_4096 ( ) { const int LEN = 256; - const int MAGIC_NUMBER = 4096; - const int nblock = (MAGIC_NUMBER + LEN - 1) / LEN; + const int MAX_NBOR_SIZE = 4096; + const int nblock = (MAX_NBOR_SIZE + LEN - 1) / LEN; dim3 block_grid(nloc, nblock); dim3 thread_grid(1, LEN); format_nlist_fill_a_se_a @@ -323,16 +323,16 @@ void format_nbor_list_4096 ( rcut_r, key, i_idx, - MAGIC_NUMBER + MAX_NBOR_SIZE ); const int ITEMS_PER_THREAD = 16; - const int BLOCK_THREADS = MAGIC_NUMBER / ITEMS_PER_THREAD; + const int BLOCK_THREADS = MAX_NBOR_SIZE / ITEMS_PER_THREAD; // BlockSortKernel<<>> ( - BlockSortKernel <<>> (key, key + nloc * MAGIC_NUMBER); + BlockSortKernel <<>> (key, key + nloc * MAX_NBOR_SIZE); } template -void DescrptSeAGPUExecuteFunctor::operator()(const FPTYPE * coord, const int * type, const int * ilist, const int * jrange, const int * jlist, int * array_int, unsigned long long * array_longlong, const FPTYPE * avg, const FPTYPE * std, FPTYPE * descript, FPTYPE * descript_deriv, FPTYPE * rij, int * nlist, const int nloc, const int nall, const int nnei, const int ndescrpt, const float rcut_r, const float rcut_r_smth, const std::vector sec_a, const bool fill_nei_a, const int MAGIC_NUMBER) { +void DescrptSeAGPUExecuteFunctor::operator()(const FPTYPE * coord, const int * type, const int * ilist, const int * jrange, const int * jlist, int * array_int, unsigned long long * array_longlong, const FPTYPE * avg, const FPTYPE * std, FPTYPE * descript, FPTYPE * descript_deriv, FPTYPE * rij, int * nlist, const int nloc, const int nall, const int nnei, const int ndescrpt, const float rcut_r, const float rcut_r_smth, const std::vector sec_a, const bool fill_nei_a, const int max_nbor_size) { const int LEN = 256; int nblock = (nloc + LEN -1) / LEN; int * sec_a_dev = array_int; @@ -342,7 +342,7 @@ void DescrptSeAGPUExecuteFunctor::operator()(const FPTYPE * coord, const cudaError_t res = cudaSuccess; res = cudaMemcpy(sec_a_dev, &sec_a[0], sizeof(int) * sec_a.size(), cudaMemcpyHostToDevice); cudaErrcheck(res); - res = cudaMemset(key, 0xffffffff, sizeof(int_64) * nloc * MAGIC_NUMBER); cudaErrcheck(res); + res = cudaMemset(key, 0xffffffff, sizeof(int_64) * nloc * max_nbor_size); cudaErrcheck(res); res = cudaMemset(nlist, -1, sizeof(int) * nloc * nnei); cudaErrcheck(res); res = cudaMemset(descript, 0.0, sizeof(FPTYPE) * nloc * ndescrpt); cudaErrcheck(res); res = cudaMemset(descript_deriv, 0.0, sizeof(FPTYPE) * nloc * ndescrpt * 3); cudaErrcheck(res); @@ -352,7 +352,7 @@ void DescrptSeAGPUExecuteFunctor::operator()(const FPTYPE * coord, const // cudaProfilerStart(); get_i_idx_se_a<<>> (nloc, ilist, i_idx); - if (MAGIC_NUMBER <= 1024) { + if (max_nbor_size <= 1024) { format_nbor_list_1024 ( coord, type, @@ -363,7 +363,7 @@ void DescrptSeAGPUExecuteFunctor::operator()(const FPTYPE * coord, const i_idx, key ); - } else if (MAGIC_NUMBER <= 2048) { + } else if (max_nbor_size <= 2048) { format_nbor_list_2048 ( coord, type, @@ -374,7 +374,7 @@ void DescrptSeAGPUExecuteFunctor::operator()(const FPTYPE * coord, const i_idx, key ); - } else if (MAGIC_NUMBER <= 4096) { + } else if (max_nbor_size <= 4096) { format_nbor_list_4096 ( coord, type, @@ -397,7 +397,7 @@ void DescrptSeAGPUExecuteFunctor::operator()(const FPTYPE * coord, const sec_a_dev, sec_a.size(), nei_iter, - MAGIC_NUMBER + max_nbor_size ); } diff --git a/source/op/cuda/descrpt_se_r.cu b/source/op/cuda/descrpt_se_r.cu index 17e76daae2..0715f19c5e 100644 --- a/source/op/cuda/descrpt_se_r.cu +++ b/source/op/cuda/descrpt_se_r.cu @@ -84,9 +84,9 @@ __global__ void format_nlist_fill_a_se_r(const FPTYPE * coord, const float rcut, int_64 * key, int * i_idx, - const int MAGIC_NUMBER) + const int MAX_NBOR_SIZE) { - // <<>> + // <<>> const unsigned int idx = blockIdx.x; const unsigned int idy = blockIdx.y * blockDim.y + threadIdx.y; @@ -98,7 +98,7 @@ __global__ void format_nlist_fill_a_se_r(const FPTYPE * coord, const int * nei_idx = jlist + jrange[i_idx[idx]]; // dev_copy(nei_idx, &jlist[jrange[i_idx]], nsize); - int_64 * key_in = key + idx * MAGIC_NUMBER; + int_64 * key_in = key + idx * MAX_NBOR_SIZE; FPTYPE diff[3]; const int & j_idx = nei_idx[idy]; @@ -121,7 +121,7 @@ __global__ void format_nlist_fill_b_se_r(int * nlist, const int * sec_a, const int sec_a_size, int * nei_iter_dev, - const int MAGIC_NUMBER) + const int MAX_NBOR_SIZE) { const unsigned int idy = blockIdx.x * blockDim.x + threadIdx.x; @@ -132,13 +132,13 @@ __global__ void format_nlist_fill_b_se_r(int * nlist, int * row_nlist = nlist + idy * nlist_size; int * nei_iter = nei_iter_dev + idy * sec_a_size; - int_64 * key_out = key + nloc * MAGIC_NUMBER + idy * MAGIC_NUMBER; + int_64 * key_out = key + nloc * MAX_NBOR_SIZE + idy * MAX_NBOR_SIZE; for (int ii = 0; ii < sec_a_size; ii++) { nei_iter[ii] = sec_a[ii]; } - for (unsigned int kk = 0; key_out[kk] != key_out[MAGIC_NUMBER - 1]; kk++) { + for (unsigned int kk = 0; key_out[kk] != key_out[MAX_NBOR_SIZE - 1]; kk++) { const int & nei_type = key_out[kk] / 1E15; if (nei_iter[nei_type] < sec_a[nei_type + 1]) { row_nlist[nei_iter[nei_type]++] = key_out[kk] % 100000; @@ -224,8 +224,8 @@ void format_nbor_list_1024 ( ) { const int LEN = 256; - const int MAGIC_NUMBER = 1024; - const int nblock = (MAGIC_NUMBER + LEN - 1) / LEN; + const int MAX_NBOR_SIZE = 1024; + const int nblock = (MAX_NBOR_SIZE + LEN - 1) / LEN; dim3 block_grid(nloc, nblock); dim3 thread_grid(1, LEN); format_nlist_fill_a_se_r @@ -237,12 +237,12 @@ void format_nbor_list_1024 ( rcut_r, key, i_idx, - MAGIC_NUMBER + MAX_NBOR_SIZE ); const int ITEMS_PER_THREAD = 8; - const int BLOCK_THREADS = MAGIC_NUMBER / ITEMS_PER_THREAD; + const int BLOCK_THREADS = MAX_NBOR_SIZE / ITEMS_PER_THREAD; // BlockSortKernel<<>> ( - BlockSortKernel <<>> (key, key + nloc * MAGIC_NUMBER); + BlockSortKernel <<>> (key, key + nloc * MAX_NBOR_SIZE); } template @@ -258,8 +258,8 @@ void format_nbor_list_2048 ( ) { const int LEN = 256; - const int MAGIC_NUMBER = 2048; - const int nblock = (MAGIC_NUMBER + LEN - 1) / LEN; + const int MAX_NBOR_SIZE = 2048; + const int nblock = (MAX_NBOR_SIZE + LEN - 1) / LEN; dim3 block_grid(nloc, nblock); dim3 thread_grid(1, LEN); format_nlist_fill_a_se_r @@ -271,12 +271,12 @@ void format_nbor_list_2048 ( rcut_r, key, i_idx, - MAGIC_NUMBER + MAX_NBOR_SIZE ); const int ITEMS_PER_THREAD = 8; - const int BLOCK_THREADS = MAGIC_NUMBER / ITEMS_PER_THREAD; + const int BLOCK_THREADS = MAX_NBOR_SIZE / ITEMS_PER_THREAD; // BlockSortKernel<<>> ( - BlockSortKernel <<>> (key, key + nloc * MAGIC_NUMBER); + BlockSortKernel <<>> (key, key + nloc * MAX_NBOR_SIZE); } template @@ -292,8 +292,8 @@ void format_nbor_list_4096 ( ) { const int LEN = 256; - const int MAGIC_NUMBER = 4096; - const int nblock = (MAGIC_NUMBER + LEN - 1) / LEN; + const int MAX_NBOR_SIZE = 4096; + const int nblock = (MAX_NBOR_SIZE + LEN - 1) / LEN; dim3 block_grid(nloc, nblock); dim3 thread_grid(1, LEN); format_nlist_fill_a_se_r @@ -305,16 +305,16 @@ void format_nbor_list_4096 ( rcut_r, key, i_idx, - MAGIC_NUMBER + MAX_NBOR_SIZE ); const int ITEMS_PER_THREAD = 16; - const int BLOCK_THREADS = MAGIC_NUMBER / ITEMS_PER_THREAD; + const int BLOCK_THREADS = MAX_NBOR_SIZE / ITEMS_PER_THREAD; // BlockSortKernel<<>> ( - BlockSortKernel <<>> (key, key + nloc * MAGIC_NUMBER); + BlockSortKernel <<>> (key, key + nloc * MAX_NBOR_SIZE); } template -void DescrptSeRGPUExecuteFunctor::operator()(const FPTYPE * coord, const int * type, const int * ilist, const int * jrange, const int * jlist, int * array_int, unsigned long long * array_longlong, const FPTYPE * avg, const FPTYPE * std, FPTYPE * descript, FPTYPE * descript_deriv, FPTYPE * rij, int * nlist, const int nloc, const int nall, const int nnei, const int ndescrpt, const float rcut_r, const float rcut_r_smth, const std::vector sec_a, const bool fill_nei_a, const int MAGIC_NUMBER) { +void DescrptSeRGPUExecuteFunctor::operator()(const FPTYPE * coord, const int * type, const int * ilist, const int * jrange, const int * jlist, int * array_int, unsigned long long * array_longlong, const FPTYPE * avg, const FPTYPE * std, FPTYPE * descript, FPTYPE * descript_deriv, FPTYPE * rij, int * nlist, const int nloc, const int nall, const int nnei, const int ndescrpt, const float rcut_r, const float rcut_r_smth, const std::vector sec_a, const bool fill_nei_a, const int max_nbor_size) { const int LEN = 256; int nblock = (nloc + LEN -1) / LEN; int * sec_a_dev = array_int; @@ -324,7 +324,7 @@ void DescrptSeRGPUExecuteFunctor::operator()(const FPTYPE * coord, const cudaError_t res = cudaSuccess; res = cudaMemcpy(sec_a_dev, &sec_a[0], sizeof(int) * sec_a.size(), cudaMemcpyHostToDevice); cudaErrcheck(res); - res = cudaMemset(key, 0xffffffff, sizeof(int_64) * nloc * MAGIC_NUMBER); cudaErrcheck(res); + res = cudaMemset(key, 0xffffffff, sizeof(int_64) * nloc * max_nbor_size); cudaErrcheck(res); res = cudaMemset(nlist, -1, sizeof(int) * nloc * nnei); cudaErrcheck(res); res = cudaMemset(descript, 0.0, sizeof(FPTYPE) * nloc * ndescrpt); cudaErrcheck(res); res = cudaMemset(descript_deriv, 0.0, sizeof(FPTYPE) * nloc * ndescrpt * 3); cudaErrcheck(res); @@ -334,7 +334,7 @@ void DescrptSeRGPUExecuteFunctor::operator()(const FPTYPE * coord, const // cudaProfilerStart(); get_i_idx_se_r<<>> (nloc, ilist, i_idx); - if (MAGIC_NUMBER <= 1024) { + if (max_nbor_size <= 1024) { format_nbor_list_1024 ( coord, type, @@ -345,7 +345,7 @@ void DescrptSeRGPUExecuteFunctor::operator()(const FPTYPE * coord, const i_idx, key ); - } else if (MAGIC_NUMBER <= 2048) { + } else if (max_nbor_size <= 2048) { format_nbor_list_2048 ( coord, type, @@ -356,7 +356,7 @@ void DescrptSeRGPUExecuteFunctor::operator()(const FPTYPE * coord, const i_idx, key ); - } else if (MAGIC_NUMBER <= 4096) { + } else if (max_nbor_size <= 4096) { format_nbor_list_4096 ( coord, type, @@ -379,7 +379,7 @@ void DescrptSeRGPUExecuteFunctor::operator()(const FPTYPE * coord, const sec_a_dev, sec_a.size(), nei_iter, - MAGIC_NUMBER + max_nbor_size ); } diff --git a/source/op/descrpt_se_a_multi_device.cc b/source/op/descrpt_se_a_multi_device.cc index 914e611e2d..62676c56a3 100644 --- a/source/op/descrpt_se_a_multi_device.cc +++ b/source/op/descrpt_se_a_multi_device.cc @@ -34,13 +34,13 @@ struct DeviceFunctor { template struct DescrptSeAFunctor { - void operator()(const CPUDevice& d, const FPTYPE * coord, const int * type, const int * mesh, const int * ilist, const int * jrange, const int * jlist, int * array_int, unsigned long long * array_longlong, const FPTYPE * avg, const FPTYPE * std, FPTYPE * descrpt, FPTYPE * descrpt_deriv, FPTYPE * rij, int * nlist, const int nloc, const int nall, const int nnei, const int ntypes, const int ndescrpt, const float rcut_r, const float rcut_r_smth, const std::vector sec_a, const bool fill_nei_a, const int magic_number) { - DescrptSeACPULauncher(coord, type, ilist, jrange, jlist, avg, std, descrpt, descrpt_deriv, rij, nlist, nloc, nall, nnei, ntypes, ndescrpt, rcut_r, rcut_r_smth, sec_a, fill_nei_a, magic_number); + void operator()(const CPUDevice& d, const FPTYPE * coord, const int * type, const int * mesh, const int * ilist, const int * jrange, const int * jlist, int * array_int, unsigned long long * array_longlong, const FPTYPE * avg, const FPTYPE * std, FPTYPE * descrpt, FPTYPE * descrpt_deriv, FPTYPE * rij, int * nlist, const int nloc, const int nall, const int nnei, const int ntypes, const int ndescrpt, const float rcut_r, const float rcut_r_smth, const std::vector sec_a, const bool fill_nei_a, const int max_nbor_size) { + DescrptSeACPULauncher(coord, type, ilist, jrange, jlist, avg, std, descrpt, descrpt_deriv, rij, nlist, nloc, nall, nnei, ntypes, ndescrpt, rcut_r, rcut_r_smth, sec_a, fill_nei_a, max_nbor_size); } #if GOOGLE_CUDA - void operator()(const GPUDevice& d, const FPTYPE * coord, const int * type, const int * mesh, const int * ilist, const int * jrange, const int * jlist, int * array_int, unsigned long long * array_longlong, const FPTYPE * avg, const FPTYPE * std, FPTYPE * descrpt, FPTYPE * descrpt_deriv, FPTYPE * rij, int * nlist, const int nloc, const int nall, const int nnei, const int ntypes, const int ndescrpt, const float rcut_r, const float rcut_r_smth, const std::vector sec_a, const bool fill_nei_a, const int magic_number) { - DescrptSeAGPULauncher(coord, type, ilist, jrange, jlist, array_int, array_longlong, avg, std, descrpt, descrpt_deriv, rij, nlist, nloc, nall, nnei, ndescrpt, rcut_r, rcut_r_smth, sec_a, fill_nei_a, magic_number); + void operator()(const GPUDevice& d, const FPTYPE * coord, const int * type, const int * mesh, const int * ilist, const int * jrange, const int * jlist, int * array_int, unsigned long long * array_longlong, const FPTYPE * avg, const FPTYPE * std, FPTYPE * descrpt, FPTYPE * descrpt_deriv, FPTYPE * rij, int * nlist, const int nloc, const int nall, const int nnei, const int ntypes, const int ndescrpt, const float rcut_r, const float rcut_r_smth, const std::vector sec_a, const bool fill_nei_a, const int max_nbor_size) { + DescrptSeAGPULauncher(coord, type, ilist, jrange, jlist, array_int, array_longlong, avg, std, descrpt, descrpt_deriv, rij, nlist, nloc, nall, nnei, ndescrpt, rcut_r, rcut_r_smth, sec_a, fill_nei_a, max_nbor_size); } #endif // GOOGLE_CUDA }; @@ -66,7 +66,7 @@ class DescrptSeAOp : public OpKernel { nnei_r = sec_r.back(); nnei = nnei_a + nnei_r; fill_nei_a = (rcut_a < 0); - magic_number = get_magic_number(nnei); + max_nbor_size = 1024; } void Compute(OpKernelContext* context) override { @@ -117,7 +117,6 @@ class DescrptSeAOp : public OpKernel { OP_REQUIRES (context, (ntypes == int(sel_a.size())), errors::InvalidArgument ("number of types should match the length of sel array")); OP_REQUIRES (context, (ntypes == int(sel_r.size())), errors::InvalidArgument ("number of types should match the length of sel array")); - OP_REQUIRES (context, (nnei <= 4096), errors::InvalidArgument ("Assert failed, max neighbor size of atom(nnei) " + std::to_string(nnei) + " is larger than 4096, which currently is not supported by deepmd-kit.")); // Create output tensors TensorShape descrpt_shape ; @@ -159,13 +158,14 @@ class DescrptSeAOp : public OpKernel { OP_REQUIRES_OK(context, context->allocate_temp(DT_INT32, int_shape, &int_temp)); Tensor uint64_temp; TensorShape uint64_shape; - uint64_shape.AddDim(nloc * magic_number * 2); + uint64_shape.AddDim(nloc * max_nbor_size * 2); OP_REQUIRES_OK(context, context->allocate_temp(DT_UINT64, uint64_shape, &uint64_temp)); array_int = int_temp.flat().data(); array_longlong = uint64_temp.flat().data(); nbor_update(mesh_tensor.flat().data(), static_cast(mesh_tensor.NumElements())); + OP_REQUIRES (context, (max_nbor_size <= 4096), errors::InvalidArgument ("Assert failed, max neighbor size of atom(lammps) " + std::to_string(max_nbor_size) + " is larger than 4096, which currently is not supported by deepmd-kit.")); } else if (device == "CPU") { memcpy (&ilist, 4 + mesh_tensor.flat().data(), sizeof(int *)); @@ -198,7 +198,7 @@ class DescrptSeAOp : public OpKernel { rcut_r_smth, sec_a, fill_nei_a, - magic_number + max_nbor_size ); } @@ -212,7 +212,7 @@ class DescrptSeAOp : public OpKernel { std::vector sec_a; std::vector sec_r; int ndescrpt, ndescrpt_a, ndescrpt_r; - int nnei, nnei_a, nnei_r, nloc, nall, magic_number; + int nnei, nnei_a, nnei_r, nloc, nall, max_nbor_size; bool fill_nei_a; //private func @@ -266,21 +266,15 @@ class DescrptSeAOp : public OpKernel { cudaErrcheck(cudaMemcpy(ilist, ilist_host, sizeof(int) * mesh_host[1], cudaMemcpyHostToDevice)); cudaErrcheck(cudaMemcpy(jrange, jrange_host, sizeof(int) * mesh_host[2], cudaMemcpyHostToDevice)); cudaErrcheck(cudaMemcpy(jlist, jlist_host, sizeof(int) * mesh_host[3], cudaMemcpyHostToDevice)); + + max_nbor_size = 1024; + for(int ii = 0; ii < mesh_host[2] - 1; ii++) { + max_nbor_size = (jrange_host[ii + 1] - jrange_host[ii]) > max_nbor_size ? (jrange_host[ii + 1] - jrange_host[ii]) : max_nbor_size; + } } delete [] mesh_host; } - int get_magic_number(int const nnei) { - if (nnei <= 1024) { - return 1024; - } - else if (nnei <= 2048) { - return 2048; - } - else if (nnei <= 4096) { - return 4096; - } - } }; // Register the CPU kernels. diff --git a/source/op/descrpt_se_r_multi_device.cc b/source/op/descrpt_se_r_multi_device.cc index 07a9618270..ba2ec377b7 100644 --- a/source/op/descrpt_se_r_multi_device.cc +++ b/source/op/descrpt_se_r_multi_device.cc @@ -31,13 +31,13 @@ struct DeviceFunctor { template struct DescrptSeRFunctor { - void operator()(const CPUDevice& d, const T * coord, const int * type, const int * mesh, const int * ilist, const int * jrange, const int * jlist, int * array_int, unsigned long long * array_longlong, const T * avg, const T * std, T * descrpt, T * descrpt_deriv, T * rij, int * nlist, const int nloc, const int nall, const int nnei, const int ntypes, const int ndescrpt, const float rcut_r, const float rcut_r_smth, const std::vector sec_a, const bool fill_nei_a, const int magic_number) { - DescrptSeRCPULauncher(coord, type, ilist, jrange, jlist, avg, std, descrpt, descrpt_deriv, rij, nlist, nloc, nall, nnei, ntypes, ndescrpt, rcut_r, rcut_r_smth, sec_a, fill_nei_a, magic_number); + void operator()(const CPUDevice& d, const T * coord, const int * type, const int * mesh, const int * ilist, const int * jrange, const int * jlist, int * array_int, unsigned long long * array_longlong, const T * avg, const T * std, T * descrpt, T * descrpt_deriv, T * rij, int * nlist, const int nloc, const int nall, const int nnei, const int ntypes, const int ndescrpt, const float rcut_r, const float rcut_r_smth, const std::vector sec_a, const bool fill_nei_a, const int max_nbor_size) { + DescrptSeRCPULauncher(coord, type, ilist, jrange, jlist, avg, std, descrpt, descrpt_deriv, rij, nlist, nloc, nall, nnei, ntypes, ndescrpt, rcut_r, rcut_r_smth, sec_a, fill_nei_a, max_nbor_size); } #if GOOGLE_CUDA - void operator()(const GPUDevice& d, const T * coord, const int * type, const int * mesh, const int * ilist, const int * jrange, const int * jlist, int * array_int, unsigned long long * array_longlong, const T * avg, const T * std, T * descrpt, T * descrpt_deriv, T * rij, int * nlist, const int nloc, const int nall, const int nnei, const int ntypes, const int ndescrpt, const float rcut_r, const float rcut_r_smth, const std::vector sec_a, const bool fill_nei_a, const int magic_number) { - DescrptSeRGPULauncher(coord, type, ilist, jrange, jlist, array_int, array_longlong, avg, std, descrpt, descrpt_deriv, rij, nlist, nloc, nall, nnei, ndescrpt, rcut_r, rcut_r_smth, sec_a, fill_nei_a, magic_number); + void operator()(const GPUDevice& d, const T * coord, const int * type, const int * mesh, const int * ilist, const int * jrange, const int * jlist, int * array_int, unsigned long long * array_longlong, const T * avg, const T * std, T * descrpt, T * descrpt_deriv, T * rij, int * nlist, const int nloc, const int nall, const int nnei, const int ntypes, const int ndescrpt, const float rcut_r, const float rcut_r_smth, const std::vector sec_a, const bool fill_nei_a, const int max_nbor_size) { + DescrptSeRGPULauncher(coord, type, ilist, jrange, jlist, array_int, array_longlong, avg, std, descrpt, descrpt_deriv, rij, nlist, nloc, nall, nnei, ndescrpt, rcut_r, rcut_r_smth, sec_a, fill_nei_a, max_nbor_size); } #endif // GOOGLE_CUDA }; @@ -55,9 +55,7 @@ class DescrptSeROp : public OpKernel { ndescrpt = sec.back() * 1; nnei = sec.back(); fill_nei_a = true; - magic_number = get_magic_number(nnei); - // count_nei_idx_overflow = 0; - // std::cout << "I'm in descrpt_se_r_gpu.cc" << std::endl; + max_nbor_size = 1024; } void Compute(OpKernelContext* context) override { @@ -149,13 +147,14 @@ class DescrptSeROp : public OpKernel { OP_REQUIRES_OK(context, context->allocate_temp(DT_INT32, int_shape, &int_temp)); Tensor uint64_temp; TensorShape uint64_shape; - uint64_shape.AddDim(nloc * magic_number * 2); + uint64_shape.AddDim(nloc * max_nbor_size * 2); OP_REQUIRES_OK(context, context->allocate_temp(DT_UINT64, uint64_shape, &uint64_temp)); array_int = int_temp.flat().data(); array_longlong = uint64_temp.flat().data(); nbor_update(mesh_tensor.flat().data(), static_cast(mesh_tensor.NumElements())); + OP_REQUIRES (context, (max_nbor_size <= 4096), errors::InvalidArgument ("Assert failed, max neighbor size of atom(lammps) " + std::to_string(max_nbor_size) + " is larger than 4096, which currently is not supported by deepmd-kit.")); } else if (device == "CPU") { memcpy (&ilist, 4 + mesh_tensor.flat().data(), sizeof(int *)); @@ -188,7 +187,7 @@ class DescrptSeROp : public OpKernel { rcut_smth, sec, fill_nei_a, - magic_number + max_nbor_size ); } @@ -213,7 +212,7 @@ class DescrptSeROp : public OpKernel { } } - int magic_number; + int max_nbor_size; std::string device; int *array_int; unsigned long long*array_longlong; @@ -256,21 +255,15 @@ class DescrptSeROp : public OpKernel { cudaErrcheck(cudaMemcpy(ilist, ilist_host, sizeof(int) * mesh_host[1], cudaMemcpyHostToDevice)); cudaErrcheck(cudaMemcpy(jrange, jrange_host, sizeof(int) * mesh_host[2], cudaMemcpyHostToDevice)); cudaErrcheck(cudaMemcpy(jlist, jlist_host, sizeof(int) * mesh_host[3], cudaMemcpyHostToDevice)); + + max_nbor_size = 1024; + for(int ii = 0; ii < mesh_host[2] - 1; ii++) { + max_nbor_size = (jrange_host[ii + 1] - jrange_host[ii]) > max_nbor_size ? (jrange_host[ii + 1] - jrange_host[ii]) : max_nbor_size; + } } delete [] mesh_host; } - int get_magic_number(int const nnei) { - if (nnei <= 1024) { - return 1024; - } - else if (nnei <= 2048) { - return 2048; - } - else if (nnei <= 4096) { - return 4096; - } - } }; // Register the CPU kernels. From 51de5ec5eb1d269616be34c0802e7ddc26f601c4 Mon Sep 17 00:00:00 2001 From: denghuilu Date: Wed, 27 Jan 2021 11:37:59 +0800 Subject: [PATCH 009/161] fix a potential bug --- source/op/descrpt_se_a_multi_device.cc | 2 +- source/op/descrpt_se_r_multi_device.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/op/descrpt_se_a_multi_device.cc b/source/op/descrpt_se_a_multi_device.cc index 62676c56a3..93e2cdccac 100644 --- a/source/op/descrpt_se_a_multi_device.cc +++ b/source/op/descrpt_se_a_multi_device.cc @@ -268,7 +268,7 @@ class DescrptSeAOp : public OpKernel { cudaErrcheck(cudaMemcpy(jlist, jlist_host, sizeof(int) * mesh_host[3], cudaMemcpyHostToDevice)); max_nbor_size = 1024; - for(int ii = 0; ii < mesh_host[2] - 1; ii++) { + for(int ii = 0; ii < mesh_host[2]; ii++) { max_nbor_size = (jrange_host[ii + 1] - jrange_host[ii]) > max_nbor_size ? (jrange_host[ii + 1] - jrange_host[ii]) : max_nbor_size; } } diff --git a/source/op/descrpt_se_r_multi_device.cc b/source/op/descrpt_se_r_multi_device.cc index ba2ec377b7..b94f97d6e1 100644 --- a/source/op/descrpt_se_r_multi_device.cc +++ b/source/op/descrpt_se_r_multi_device.cc @@ -257,7 +257,7 @@ class DescrptSeROp : public OpKernel { cudaErrcheck(cudaMemcpy(jlist, jlist_host, sizeof(int) * mesh_host[3], cudaMemcpyHostToDevice)); max_nbor_size = 1024; - for(int ii = 0; ii < mesh_host[2] - 1; ii++) { + for(int ii = 0; ii < mesh_host[2]; ii++) { max_nbor_size = (jrange_host[ii + 1] - jrange_host[ii]) > max_nbor_size ? (jrange_host[ii + 1] - jrange_host[ii]) : max_nbor_size; } } From a24971f699cc71d577c19f0bf590b9b87194a97a Mon Sep 17 00:00:00 2001 From: hsulab Date: Fri, 29 Jan 2021 22:49:07 +0000 Subject: [PATCH 010/161] fix an error in stress by ase interface --- source/train/calculator.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/source/train/calculator.py b/source/train/calculator.py index de439c3f78..d6d956572c 100644 --- a/source/train/calculator.py +++ b/source/train/calculator.py @@ -24,13 +24,15 @@ ``` """ -from ase.calculators.calculator import Calculator, all_changes +from ase.calculators.calculator import ( + Calculator, all_changes, PropertyNotImplementedError +) import deepmd.DeepPot as DeepPot class DP(Calculator): name = "DP" - implemented_properties = ["energy", "forces", "stress"] + implemented_properties = ["energy", "forces", "virial", "stress"] def __init__(self, model, label="DP", type_dict=None, **kwargs): Calculator.__init__(self, label=label, **kwargs) @@ -40,7 +42,7 @@ def __init__(self, model, label="DP", type_dict=None, **kwargs): else: self.type_dict = dict(zip(self.dp.get_type_map(), range(self.dp.get_ntypes()))) - def calculate(self, atoms=None, properties=["energy", "forces", "stress"], system_changes=all_changes): + def calculate(self, atoms=None, properties=["energy", "forces", "virial"], system_changes=all_changes): coord = atoms.get_positions().reshape([1, -1]) if sum(atoms.get_pbc())>0: cell = atoms.get_cell().reshape([1, -1]) @@ -49,7 +51,17 @@ def calculate(self, atoms=None, properties=["energy", "forces", "stress"], syste symbols = atoms.get_chemical_symbols() atype = [self.type_dict[k] for k in symbols] e, f, v = self.dp.eval(coords=coord, cells=cell, atom_types=atype) - self.results['energy'] = e[0] + self.results['energy'] = e[0][0] self.results['forces'] = f[0] - self.results['stress'] = v[0] + self.results['virial'] = v[0].reshape(3,3) + # convert virial into stress for lattice relaxation + if "stress" in properties: + if sum(atoms.get_pbc()) > 0: + # the usual convention (tensile stress is positive) + # stress = -virial / volume + stress = -0.5*(v[0].copy()+v[0].copy().T) / atoms.get_volume() + # Voigt notation + self.results['stress'] = stress.flat[[0,4,8,5,2,1]] + else: + raise PropertyNotImplementedError From fd660595a1a24f70c90c15bbb0964121befb668e Mon Sep 17 00:00:00 2001 From: denghuilu Date: Thu, 4 Feb 2021 11:05:32 +0800 Subject: [PATCH 011/161] update compiler support for Ampere architecture devices --- source/op/cuda/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/op/cuda/CMakeLists.txt b/source/op/cuda/CMakeLists.txt index 89dd0b5922..0201e50130 100644 --- a/source/op/cuda/CMakeLists.txt +++ b/source/op/cuda/CMakeLists.txt @@ -28,6 +28,8 @@ if (${CUDA_VERSION_MAJOR} GREATER "10") -gencode arch=compute_61,code=sm_61; # Pascal - GTX 1080, GTX 1070, GTX 1060, GTX 1050, GTX 1030, Titan Xp, Tesla P40, Tesla P4, Discrete GPU on the NVIDIA Drive PX2 -gencode arch=compute_70,code=sm_70; # Volta - GV100/Tesla V100, GTX 1180 (GV104) -gencode arch=compute_75,code=sm_75; # Turing - RTX 2080, Titan RTX, Quadro R8000 + -gencode arch=compute_80,code=sm_80; # Anpere - A100 + -gencode arch=compute_86,code=sm_86; # Anpere - RTX 3090 -O3; -Xcompiler -fPIC; ) elseif (${CUDA_VERSION_MAJOR} STREQUAL "10") From 90938c47f74349b53a1bdf02048f9ecca81671fd Mon Sep 17 00:00:00 2001 From: denghuilu Date: Mon, 1 Mar 2021 22:49:42 +0800 Subject: [PATCH 012/161] fix bug of cuda-11 compilation --- source/op/cuda/CMakeLists.txt | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/source/op/cuda/CMakeLists.txt b/source/op/cuda/CMakeLists.txt index 0201e50130..8eab370b1d 100644 --- a/source/op/cuda/CMakeLists.txt +++ b/source/op/cuda/CMakeLists.txt @@ -19,7 +19,17 @@ include_directories(cub) message(STATUS "CUDA major version is " ${CUDA_VERSION_MAJOR}) -if (${CUDA_VERSION_MAJOR} GREATER "10") +if (${CUDA_VERSION_MAJOR} GREATER "11") + # nvcc flags + set(CUDA_NVCC_FLAGS -gencode arch=compute_60,code=sm_60; # Pascal – GP100/Tesla P100 – DGX-1 (Generic Pascal) + -gencode arch=compute_61,code=sm_61; # Pascal - GTX 1080, GTX 1070, GTX 1060, GTX 1050, GTX 1030, Titan Xp, Tesla P40, Tesla P4, Discrete GPU on the NVIDIA Drive PX2 + -gencode arch=compute_70,code=sm_70; # Volta - GV100/Tesla V100, GTX 1180 (GV104) + -gencode arch=compute_75,code=sm_75; # Turing - RTX 2080, Titan RTX, Quadro R8000 + -gencode arch=compute_80,code=sm_80; # Anpere - A100 + -gencode arch=compute_86,code=sm_86; # Anpere - RTX 3090 + -O3; -Xcompiler -fPIC; + ) +elseif (${CUDA_VERSION_MAJOR} STREQUAL "11" AND ${CUDA_VERSION_MINOR} GREATER "0") # nvcc flags set(CUDA_NVCC_FLAGS -gencode arch=compute_50,code=sm_50; -gencode arch=compute_52,code=sm_52; # Tesla M40, Tesla M40, Quadro M6000... @@ -32,6 +42,18 @@ if (${CUDA_VERSION_MAJOR} GREATER "10") -gencode arch=compute_86,code=sm_86; # Anpere - RTX 3090 -O3; -Xcompiler -fPIC; ) +elseif (${CUDA_VERSION_MAJOR} STREQUAL "11" AND ${CUDA_VERSION_MINOR} STREQUAL "0") + # nvcc flags + set(CUDA_NVCC_FLAGS -gencode arch=compute_50,code=sm_50; + -gencode arch=compute_52,code=sm_52; # Tesla M40, Tesla M40, Quadro M6000... + -gencode arch=compute_53,code=sm_53; + -gencode arch=compute_60,code=sm_60; # Pascal – GP100/Tesla P100 – DGX-1 (Generic Pascal) + -gencode arch=compute_61,code=sm_61; # Pascal - GTX 1080, GTX 1070, GTX 1060, GTX 1050, GTX 1030, Titan Xp, Tesla P40, Tesla P4, Discrete GPU on the NVIDIA Drive PX2 + -gencode arch=compute_70,code=sm_70; # Volta - GV100/Tesla V100, GTX 1180 (GV104) + -gencode arch=compute_75,code=sm_75; # Turing - RTX 2080, Titan RTX, Quadro R8000 + -gencode arch=compute_80,code=sm_80; # Anpere - A100 + -O3; -Xcompiler -fPIC; + ) elseif (${CUDA_VERSION_MAJOR} STREQUAL "10") set(CUDA_NVCC_FLAGS -gencode arch=compute_30,code=sm_30; # Tesla K10, Quadro K600 K420 K410, -gencode arch=compute_35,code=sm_35; # Tesla K20 K40, TITAN Z Black, GTX 780Ti 780 From 6cbeb2079e1b505d9f02d545eeda3950f42f6e13 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Tue, 2 Mar 2021 21:06:54 +0800 Subject: [PATCH 013/161] print message to std:cerr and return rather than assertion. The assertions will be bypassed in release building mode --- source/lib/src/NNPInter.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/source/lib/src/NNPInter.cc b/source/lib/src/NNPInter.cc index 8bedd88c9c..ec2b6b87da 100644 --- a/source/lib/src/NNPInter.cc +++ b/source/lib/src/NNPInter.cc @@ -190,7 +190,10 @@ void NNPInter:: init (const string & model, const int & gpu_rank) { - assert (!inited); + if (inited){ + std::cerr << "WARNING: deepmd-kit should not be initialized twice, do nothing at the second call of initializer" << std::endl; + return ; + } SessionOptions options; options.config.set_inter_op_parallelism_threads(num_inter_nthreads); options.config.set_intra_op_parallelism_threads(num_intra_nthreads); @@ -497,7 +500,10 @@ void NNPInterModelDevi:: init (const vector & models, const int & gpu_rank) { - assert (!inited); + if (inited){ + std::cerr << "WARNING: deepmd-kit should not be initialized twice, do nothing at the second call of initializer" << std::endl; + return ; + } numb_models = models.size(); sessions.resize(numb_models); graph_defs.resize(numb_models); From 5597ea2b49f96e99a52a9779b04b6c12e5a79a04 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Tue, 2 Mar 2021 21:33:46 +0800 Subject: [PATCH 014/161] set `restartinfo` to 0. Restarting the pair_style `deepmd` from restart file is not supported --- source/lmp/pair_nnp.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/source/lmp/pair_nnp.cpp b/source/lmp/pair_nnp.cpp index e8cff008f5..b5a64e8b11 100644 --- a/source/lmp/pair_nnp.cpp +++ b/source/lmp/pair_nnp.cpp @@ -206,6 +206,7 @@ PairNNP::PairNNP(LAMMPS *lmp) if (strcmp(update->unit_style,"metal") != 0) { error->all(FLERR,"Pair deepmd requires metal unit, please set it by \"units metal\""); } + restartinfo = 0; pppmflag = 1; respa_enable = 0; writedata = 0; From 7328be08982fbbbe12725ee2121a1899f206f2ce Mon Sep 17 00:00:00 2001 From: denghuilu Date: Wed, 3 Mar 2021 13:54:24 +0800 Subject: [PATCH 015/161] fix bug of max_nbor_size usage --- source/op/descrpt_se_a_multi_device.cc | 19 +++++++++++++++---- source/op/descrpt_se_r_multi_device.cc | 18 +++++++++++++++--- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/source/op/descrpt_se_a_multi_device.cc b/source/op/descrpt_se_a_multi_device.cc index 93e2cdccac..141b2d89bc 100644 --- a/source/op/descrpt_se_a_multi_device.cc +++ b/source/op/descrpt_se_a_multi_device.cc @@ -21,6 +21,8 @@ REGISTER_OP("DescrptSeA") .Output("nlist: int32"); // only sel_a and rcut_r uesd. +#define GPU_MAX_NBOR_SIZE 4096 + struct DeviceFunctor { void operator()(const CPUDevice& d, std::string& device) { device = "CPU"; @@ -158,14 +160,14 @@ class DescrptSeAOp : public OpKernel { OP_REQUIRES_OK(context, context->allocate_temp(DT_INT32, int_shape, &int_temp)); Tensor uint64_temp; TensorShape uint64_shape; - uint64_shape.AddDim(nloc * max_nbor_size * 2); + uint64_shape.AddDim(nloc * GPU_MAX_NBOR_SIZE * 2); OP_REQUIRES_OK(context, context->allocate_temp(DT_UINT64, uint64_shape, &uint64_temp)); array_int = int_temp.flat().data(); array_longlong = uint64_temp.flat().data(); nbor_update(mesh_tensor.flat().data(), static_cast(mesh_tensor.NumElements())); - OP_REQUIRES (context, (max_nbor_size <= 4096), errors::InvalidArgument ("Assert failed, max neighbor size of atom(lammps) " + std::to_string(max_nbor_size) + " is larger than 4096, which currently is not supported by deepmd-kit.")); + OP_REQUIRES (context, (max_nbor_size <= GPU_MAX_NBOR_SIZE), errors::InvalidArgument ("Assert failed, max neighbor size of atom(lammps) " + std::to_string(max_nbor_size) + " is larger than 4096, which currently is not supported by deepmd-kit.")); } else if (device == "CPU") { memcpy (&ilist, 4 + mesh_tensor.flat().data(), sizeof(int *)); @@ -267,14 +269,23 @@ class DescrptSeAOp : public OpKernel { cudaErrcheck(cudaMemcpy(jrange, jrange_host, sizeof(int) * mesh_host[2], cudaMemcpyHostToDevice)); cudaErrcheck(cudaMemcpy(jlist, jlist_host, sizeof(int) * mesh_host[3], cudaMemcpyHostToDevice)); - max_nbor_size = 1024; + max_nbor_size = 0; for(int ii = 0; ii < mesh_host[2]; ii++) { max_nbor_size = (jrange_host[ii + 1] - jrange_host[ii]) > max_nbor_size ? (jrange_host[ii + 1] - jrange_host[ii]) : max_nbor_size; } + assert(max_nbor_size <= GPU_MAX_NBOR_SIZE); + if (max_nbor_size <= 1024) { + max_nbor_size = 1024; + } + else if (max_nbor_size <= 2048) { + max_nbor_size = 2048; + } + else { + max_nbor_size = 4096; + } } delete [] mesh_host; } - }; // Register the CPU kernels. diff --git a/source/op/descrpt_se_r_multi_device.cc b/source/op/descrpt_se_r_multi_device.cc index b94f97d6e1..c355e34f12 100644 --- a/source/op/descrpt_se_r_multi_device.cc +++ b/source/op/descrpt_se_r_multi_device.cc @@ -18,6 +18,8 @@ REGISTER_OP("DescrptSeR") .Output("rij: T") .Output("nlist: int32"); +#define GPU_MAX_NBOR_SIZE 4096 + struct DeviceFunctor { void operator()(const CPUDevice& d, std::string& device) { device = "CPU"; @@ -147,14 +149,14 @@ class DescrptSeROp : public OpKernel { OP_REQUIRES_OK(context, context->allocate_temp(DT_INT32, int_shape, &int_temp)); Tensor uint64_temp; TensorShape uint64_shape; - uint64_shape.AddDim(nloc * max_nbor_size * 2); + uint64_shape.AddDim(nloc * GPU_MAX_NBOR_SIZE * 2); OP_REQUIRES_OK(context, context->allocate_temp(DT_UINT64, uint64_shape, &uint64_temp)); array_int = int_temp.flat().data(); array_longlong = uint64_temp.flat().data(); nbor_update(mesh_tensor.flat().data(), static_cast(mesh_tensor.NumElements())); - OP_REQUIRES (context, (max_nbor_size <= 4096), errors::InvalidArgument ("Assert failed, max neighbor size of atom(lammps) " + std::to_string(max_nbor_size) + " is larger than 4096, which currently is not supported by deepmd-kit.")); + OP_REQUIRES (context, (max_nbor_size <= GPU_MAX_NBOR_SIZE), errors::InvalidArgument ("Assert failed, max neighbor size of atom(lammps) " + std::to_string(max_nbor_size) + " is larger than 4096, which currently is not supported by deepmd-kit.")); } else if (device == "CPU") { memcpy (&ilist, 4 + mesh_tensor.flat().data(), sizeof(int *)); @@ -256,10 +258,20 @@ class DescrptSeROp : public OpKernel { cudaErrcheck(cudaMemcpy(jrange, jrange_host, sizeof(int) * mesh_host[2], cudaMemcpyHostToDevice)); cudaErrcheck(cudaMemcpy(jlist, jlist_host, sizeof(int) * mesh_host[3], cudaMemcpyHostToDevice)); - max_nbor_size = 1024; + max_nbor_size = 0; for(int ii = 0; ii < mesh_host[2]; ii++) { max_nbor_size = (jrange_host[ii + 1] - jrange_host[ii]) > max_nbor_size ? (jrange_host[ii + 1] - jrange_host[ii]) : max_nbor_size; } + assert(max_nbor_size <= GPU_MAX_NBOR_SIZE); + if (max_nbor_size <= 1024) { + max_nbor_size = 1024; + } + else if (max_nbor_size <= 2048) { + max_nbor_size = 2048; + } + else { + max_nbor_size = 4096; + } } delete [] mesh_host; } From 7d259972125924c78d57602ebc436d987a535c71 Mon Sep 17 00:00:00 2001 From: denghuilu Date: Mon, 8 Mar 2021 17:45:26 +0800 Subject: [PATCH 016/161] fix bug of illegal device memory access --- source/op/descrpt_se_a_multi_device.cc | 22 +++++++--------------- source/op/descrpt_se_r_multi_device.cc | 22 +++++++--------------- 2 files changed, 14 insertions(+), 30 deletions(-) diff --git a/source/op/descrpt_se_a_multi_device.cc b/source/op/descrpt_se_a_multi_device.cc index 141b2d89bc..40f2c92eb0 100644 --- a/source/op/descrpt_se_a_multi_device.cc +++ b/source/op/descrpt_se_a_multi_device.cc @@ -237,32 +237,23 @@ class DescrptSeAOp : public OpKernel { int *mesh_host = new int[size], *ilist_host = NULL, *jrange_host = NULL, *jlist_host = NULL; cudaErrcheck(cudaMemcpy(mesh_host, mesh, sizeof(int) * size, cudaMemcpyDeviceToHost)); memcpy (&ilist_host, 4 + mesh_host, sizeof(int *)); - memcpy (&jrange_host, 8 + mesh_host, sizeof(int *)); - memcpy (&jlist_host, 12 + mesh_host, sizeof(int *)); + memcpy (&jrange_host, 8 + mesh_host, sizeof(int *)); + memcpy (&jlist_host, 12 + mesh_host, sizeof(int *)); int const ago = mesh_host[0]; - if (!init) { - ilist_size = (int)(mesh_host[1] * 1.2); - jrange_size = (int)(mesh_host[2] * 1.2); - jlist_size = (int)(mesh_host[3] * 1.2); - cudaErrcheck(cudaMalloc((void **)&ilist, sizeof(int) * ilist_size)); - cudaErrcheck(cudaMalloc((void **)&jrange, sizeof(int) * jrange_size)); - cudaErrcheck(cudaMalloc((void **)&jlist, sizeof(int) * jlist_size)); - init = true; - } - if (ago == 0) { + if (!init || ago == 0) { if (ilist_size < mesh_host[1]) { ilist_size = (int)(mesh_host[1] * 1.2); - cudaErrcheck(cudaFree(ilist)); + if (ilist != NULL) {cudaErrcheck(cudaFree(ilist));} cudaErrcheck(cudaMalloc((void **)&ilist, sizeof(int) * ilist_size)); } if (jrange_size < mesh_host[2]) { jrange_size = (int)(mesh_host[2] * 1.2); - cudaErrcheck(cudaFree(jrange)); + if (jrange != NULL) {cudaErrcheck(cudaFree(jrange));} cudaErrcheck(cudaMalloc((void **)&jrange,sizeof(int) * jrange_size)); } if (jlist_size < mesh_host[3]) { jlist_size = (int)(mesh_host[3] * 1.2); - cudaErrcheck(cudaFree(jlist)); + if (jlist != NULL) {cudaErrcheck(cudaFree(jlist));} cudaErrcheck(cudaMalloc((void **)&jlist, sizeof(int) * jlist_size)); } cudaErrcheck(cudaMemcpy(ilist, ilist_host, sizeof(int) * mesh_host[1], cudaMemcpyHostToDevice)); @@ -284,6 +275,7 @@ class DescrptSeAOp : public OpKernel { max_nbor_size = 4096; } } + init = true; delete [] mesh_host; } }; diff --git a/source/op/descrpt_se_r_multi_device.cc b/source/op/descrpt_se_r_multi_device.cc index c355e34f12..81d2603c79 100644 --- a/source/op/descrpt_se_r_multi_device.cc +++ b/source/op/descrpt_se_r_multi_device.cc @@ -226,32 +226,23 @@ class DescrptSeROp : public OpKernel { int *mesh_host = new int[size], *ilist_host = NULL, *jrange_host = NULL, *jlist_host = NULL; cudaErrcheck(cudaMemcpy(mesh_host, mesh, sizeof(int) * size, cudaMemcpyDeviceToHost)); memcpy (&ilist_host, 4 + mesh_host, sizeof(int *)); - memcpy (&jrange_host, 8 + mesh_host, sizeof(int *)); - memcpy (&jlist_host, 12 + mesh_host, sizeof(int *)); + memcpy (&jrange_host, 8 + mesh_host, sizeof(int *)); + memcpy (&jlist_host, 12 + mesh_host, sizeof(int *)); int const ago = mesh_host[0]; - if (!init) { - ilist_size = (int)(mesh_host[1] * 1.2); - jrange_size = (int)(mesh_host[2] * 1.2); - jlist_size = (int)(mesh_host[3] * 1.2); - cudaErrcheck(cudaMalloc((void **)&ilist, sizeof(int) * ilist_size)); - cudaErrcheck(cudaMalloc((void **)&jrange, sizeof(int) * jrange_size)); - cudaErrcheck(cudaMalloc((void **)&jlist, sizeof(int) * jlist_size)); - init = true; - } - if (ago == 0) { + if (!init || ago == 0) { if (ilist_size < mesh_host[1]) { ilist_size = (int)(mesh_host[1] * 1.2); - cudaErrcheck(cudaFree(ilist)); + if (ilist != NULL) {cudaErrcheck(cudaFree(ilist));} cudaErrcheck(cudaMalloc((void **)&ilist, sizeof(int) * ilist_size)); } if (jrange_size < mesh_host[2]) { jrange_size = (int)(mesh_host[2] * 1.2); - cudaErrcheck(cudaFree(jrange)); + if (jrange != NULL) {cudaErrcheck(cudaFree(jrange));} cudaErrcheck(cudaMalloc((void **)&jrange,sizeof(int) * jrange_size)); } if (jlist_size < mesh_host[3]) { jlist_size = (int)(mesh_host[3] * 1.2); - cudaErrcheck(cudaFree(jlist)); + if (jlist != NULL) {cudaErrcheck(cudaFree(jlist));} cudaErrcheck(cudaMalloc((void **)&jlist, sizeof(int) * jlist_size)); } cudaErrcheck(cudaMemcpy(ilist, ilist_host, sizeof(int) * mesh_host[1], cudaMemcpyHostToDevice)); @@ -273,6 +264,7 @@ class DescrptSeROp : public OpKernel { max_nbor_size = 4096; } } + init = true; delete [] mesh_host; } From 520c3089e216b9fd3cf883ee19b961bc10c5951e Mon Sep 17 00:00:00 2001 From: denghuilu Date: Fri, 19 Mar 2021 23:13:20 +0800 Subject: [PATCH 017/161] fix bug of max_nbor_size error --- source/op/descrpt_se_a_multi_device.cc | 2 +- source/op/descrpt_se_r_multi_device.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/op/descrpt_se_a_multi_device.cc b/source/op/descrpt_se_a_multi_device.cc index 40f2c92eb0..af18909884 100644 --- a/source/op/descrpt_se_a_multi_device.cc +++ b/source/op/descrpt_se_a_multi_device.cc @@ -261,7 +261,7 @@ class DescrptSeAOp : public OpKernel { cudaErrcheck(cudaMemcpy(jlist, jlist_host, sizeof(int) * mesh_host[3], cudaMemcpyHostToDevice)); max_nbor_size = 0; - for(int ii = 0; ii < mesh_host[2]; ii++) { + for(int ii = 0; ii < mesh_host[2] - 1; ii++) { max_nbor_size = (jrange_host[ii + 1] - jrange_host[ii]) > max_nbor_size ? (jrange_host[ii + 1] - jrange_host[ii]) : max_nbor_size; } assert(max_nbor_size <= GPU_MAX_NBOR_SIZE); diff --git a/source/op/descrpt_se_r_multi_device.cc b/source/op/descrpt_se_r_multi_device.cc index 81d2603c79..a1f50fbb95 100644 --- a/source/op/descrpt_se_r_multi_device.cc +++ b/source/op/descrpt_se_r_multi_device.cc @@ -250,7 +250,7 @@ class DescrptSeROp : public OpKernel { cudaErrcheck(cudaMemcpy(jlist, jlist_host, sizeof(int) * mesh_host[3], cudaMemcpyHostToDevice)); max_nbor_size = 0; - for(int ii = 0; ii < mesh_host[2]; ii++) { + for(int ii = 0; ii < mesh_host[2] - 1; ii++) { max_nbor_size = (jrange_host[ii + 1] - jrange_host[ii]) > max_nbor_size ? (jrange_host[ii + 1] - jrange_host[ii]) : max_nbor_size; } assert(max_nbor_size <= GPU_MAX_NBOR_SIZE); From f0d60fc75de2eb7481e082fd772b73b31a917def Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Wed, 24 Mar 2021 11:45:11 +0800 Subject: [PATCH 018/161] Update issue templates Contact me: tuop@deepmd.net --- .github/ISSUE_TEMPLATE/doc-issue-template.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/doc-issue-template.md diff --git a/.github/ISSUE_TEMPLATE/doc-issue-template.md b/.github/ISSUE_TEMPLATE/doc-issue-template.md new file mode 100644 index 0000000000..be8f103e7e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/doc-issue-template.md @@ -0,0 +1,10 @@ +--- +name: Doc issue template +about: Share your experience by writing a doc. +title: "[Doc]" +labels: '' +assignees: '' + +--- + +Do you want to contribute to our community? Start by writing down and share your experience with DP. From b28c85423bf7cf39ca4422e6e887ee1b00fbc4ef Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Wed, 24 Mar 2021 11:48:45 +0800 Subject: [PATCH 019/161] Update issue templates --- .github/ISSUE_TEMPLATE/doc-issue-template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/doc-issue-template.md b/.github/ISSUE_TEMPLATE/doc-issue-template.md index be8f103e7e..be2c368c5f 100644 --- a/.github/ISSUE_TEMPLATE/doc-issue-template.md +++ b/.github/ISSUE_TEMPLATE/doc-issue-template.md @@ -7,4 +7,4 @@ assignees: '' --- -Do you want to contribute to our community? Start by writing down and share your experience with DP. +Do you want to contribute to our community? Start by writing down and share your experience using DP. From e16c82c540457337dd9e3531b1ec5f02d29e730e Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Wed, 24 Mar 2021 13:13:30 +0800 Subject: [PATCH 020/161] Update issue templates --- .github/ISSUE_TEMPLATE/doc-issue-template.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/doc-issue-template.md b/.github/ISSUE_TEMPLATE/doc-issue-template.md index be2c368c5f..209dbb7977 100644 --- a/.github/ISSUE_TEMPLATE/doc-issue-template.md +++ b/.github/ISSUE_TEMPLATE/doc-issue-template.md @@ -8,3 +8,11 @@ assignees: '' --- Do you want to contribute to our community? Start by writing down and share your experience using DP. +!!! Note: tag a label to your issue. +Bug: for bug report +Enhancement: for enhancement suggestions +New feature: for adding feature suggestions +Doc: for manual demandings +There are other specific wont-help type issues for you to choose. If none suit your need, then check other questions. + +We will check each issue solved (The problem is solved), running (We are on it.), later (We will fix it in the next version), wontfix (We might fix it in the future), invalid (What you reported is not actually a bug) or duplicate (The question has been asked before) regularly. From 7ba694b0929c0767809117af97c42e6b347bf346 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Thu, 25 Mar 2021 12:56:34 -0400 Subject: [PATCH 021/161] give the correct tensorflow version in the document --- doc/install.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/install.md b/doc/install.md index 05a0d3bdb0..783abf65be 100644 --- a/doc/install.md +++ b/doc/install.md @@ -61,7 +61,7 @@ We follow the virtual environment approach to install the tensorflow's Python in virtualenv -p python3 $tensorflow_venv source $tensorflow_venv/bin/activate pip install --upgrade pip -pip install --upgrade tensorflow==2.3.0 +pip install --upgrade tensorflow ``` It is highly recommanded to keep the consistency of the TensorFlow version for the python and C++ interfaces. Everytime a new shell is started and one wants to use `DeePMD-kit`, the virtual environment should be activated by @@ -78,7 +78,7 @@ virtualenv -p python3.7 $tensorflow_venv ``` If one does not need the GPU support of deepmd-kit and is concerned about package size, the CPU-only version of tensorflow should be installed by ```bash -pip install --upgrade tensorflow-cpu==2.3.0 +pip install --upgrade tensorflow-cpu ``` To verify the installation, run ```bash From c1948a2daa79a291c699cc78abe2c8385d52b820 Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Fri, 26 Mar 2021 16:08:58 +0800 Subject: [PATCH 022/161] Update issue templates --- .github/ISSUE_TEMPLATE/custom.md | 10 ++++++++++ .github/ISSUE_TEMPLATE/doc-issue-template.md | 18 ------------------ 2 files changed, 10 insertions(+), 18 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/custom.md delete mode 100644 .github/ISSUE_TEMPLATE/doc-issue-template.md diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000000..47cd8a10dc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + +Please tag a label to your issue. So we can locate people to solve your problem sooner. diff --git a/.github/ISSUE_TEMPLATE/doc-issue-template.md b/.github/ISSUE_TEMPLATE/doc-issue-template.md deleted file mode 100644 index 209dbb7977..0000000000 --- a/.github/ISSUE_TEMPLATE/doc-issue-template.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -name: Doc issue template -about: Share your experience by writing a doc. -title: "[Doc]" -labels: '' -assignees: '' - ---- - -Do you want to contribute to our community? Start by writing down and share your experience using DP. -!!! Note: tag a label to your issue. -Bug: for bug report -Enhancement: for enhancement suggestions -New feature: for adding feature suggestions -Doc: for manual demandings -There are other specific wont-help type issues for you to choose. If none suit your need, then check other questions. - -We will check each issue solved (The problem is solved), running (We are on it.), later (We will fix it in the next version), wontfix (We might fix it in the future), invalid (What you reported is not actually a bug) or duplicate (The question has been asked before) regularly. From a066347fa2911c4858f7cf200fdb3fd2e49b3aaa Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Sun, 28 Mar 2021 18:43:46 +0800 Subject: [PATCH 023/161] Update issue templates --- .github/ISSUE_TEMPLATE/custom.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md index 47cd8a10dc..437e66cebf 100644 --- a/.github/ISSUE_TEMPLATE/custom.md +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -7,4 +7,6 @@ assignees: '' --- -Please tag a label to your issue. So we can locate people to solve your problem sooner. +Issue session is for enhancement suggestions. If your question is otherwise, please go to the discussion session. Thank you for your support. And look forward to working together. + +From DeepModeling Community From bc78300e10314485c24fa49f4bd830104f7b30cb Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Thu, 1 Apr 2021 08:21:56 +0800 Subject: [PATCH 024/161] Update issue templates --- .github/ISSUE_TEMPLATE/issue--bug-report.md | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/issue--bug-report.md diff --git a/.github/ISSUE_TEMPLATE/issue--bug-report.md b/.github/ISSUE_TEMPLATE/issue--bug-report.md new file mode 100644 index 0000000000..c72e925017 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/issue--bug-report.md @@ -0,0 +1,27 @@ +--- +name: 'Issue: Bug report' +about: Create a bug report to help us eliminate issues and improve deepmd-kit. If + this doesn’t look right, [choose a different type](https://github.com/deepmodeling/deepmd-kit/issues/new/choose). +title: "[BUG] _Replace With Suitable Title_" +labels: bug +assignees: '' + +--- + +**Summary** + + + +**Deepmd-kit Version, Python Version, Tensorflow Version, GCC Version and Cuda Version.** + + + + + +**Steps to Reproduce** + + + +**Further Information, Files, and Links** + + From f82097f54a77d38ba5355251365a6bc8cd3f2edf Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Thu, 1 Apr 2021 08:24:40 +0800 Subject: [PATCH 025/161] Update issue templates --- .github/ISSUE_TEMPLATE/custom.md | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/custom.md diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md deleted file mode 100644 index 437e66cebf..0000000000 --- a/.github/ISSUE_TEMPLATE/custom.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -name: Custom issue template -about: Describe this issue template's purpose here. -title: '' -labels: '' -assignees: '' - ---- - -Issue session is for enhancement suggestions. If your question is otherwise, please go to the discussion session. Thank you for your support. And look forward to working together. - -From DeepModeling Community From 5e60d189afe0eb4a07d307df1e05f164e32279b4 Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Thu, 1 Apr 2021 08:29:04 +0800 Subject: [PATCH 026/161] Update issue templates --- .github/ISSUE_TEMPLATE/issue--bug-report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/issue--bug-report.md b/.github/ISSUE_TEMPLATE/issue--bug-report.md index c72e925017..488485c60b 100644 --- a/.github/ISSUE_TEMPLATE/issue--bug-report.md +++ b/.github/ISSUE_TEMPLATE/issue--bug-report.md @@ -4,12 +4,12 @@ about: Create a bug report to help us eliminate issues and improve deepmd-kit. I this doesn’t look right, [choose a different type](https://github.com/deepmodeling/deepmd-kit/issues/new/choose). title: "[BUG] _Replace With Suitable Title_" labels: bug -assignees: '' +assignees: njzjz --- **Summary** - +[choose a different type](https://github.com/deepmodeling/deepmd-kit/issues/new/choose) **Deepmd-kit Version, Python Version, Tensorflow Version, GCC Version and Cuda Version.** From 0bc94cc4b038fe90cda7d3cd699e2d6c0b98258d Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Thu, 1 Apr 2021 08:31:15 +0800 Subject: [PATCH 027/161] Update issue templates --- .github/ISSUE_TEMPLATE/issue--bug-report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/issue--bug-report.md b/.github/ISSUE_TEMPLATE/issue--bug-report.md index 488485c60b..c72e925017 100644 --- a/.github/ISSUE_TEMPLATE/issue--bug-report.md +++ b/.github/ISSUE_TEMPLATE/issue--bug-report.md @@ -4,12 +4,12 @@ about: Create a bug report to help us eliminate issues and improve deepmd-kit. I this doesn’t look right, [choose a different type](https://github.com/deepmodeling/deepmd-kit/issues/new/choose). title: "[BUG] _Replace With Suitable Title_" labels: bug -assignees: njzjz +assignees: '' --- **Summary** -[choose a different type](https://github.com/deepmodeling/deepmd-kit/issues/new/choose) + **Deepmd-kit Version, Python Version, Tensorflow Version, GCC Version and Cuda Version.** From 46095bc15f9c4a92c8f6232ecec3b1190497bed4 Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Thu, 1 Apr 2021 08:36:51 +0800 Subject: [PATCH 028/161] Update issue templates --- .../ISSUE_TEMPLATE/issue--feature-request.md | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/issue--feature-request.md diff --git a/.github/ISSUE_TEMPLATE/issue--feature-request.md b/.github/ISSUE_TEMPLATE/issue--feature-request.md new file mode 100644 index 0000000000..6d26779f1f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/issue--feature-request.md @@ -0,0 +1,21 @@ +--- +name: 'Issue: Feature request' +about: Suggest an idea for this project. If this doesn’t work right, [choose a different + type]( https://github.com/deepmodeling/deepmd-kit/issues/new/choose) +title: "[Feature Request] _Replace with Title_" +labels: new feature +assignees: '' + +--- + +**Summary** + + + +**Detailed Description** + + + +**Further Information, Files, and Links** + + From a3b620a5bdf0a86f5cf716c75113eb88e250c7bf Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Thu, 1 Apr 2021 08:50:33 +0800 Subject: [PATCH 029/161] Update issue templates --- .../issue--about-input-files.md | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/issue--about-input-files.md diff --git a/.github/ISSUE_TEMPLATE/issue--about-input-files.md b/.github/ISSUE_TEMPLATE/issue--about-input-files.md new file mode 100644 index 0000000000..c8b6276db8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/issue--about-input-files.md @@ -0,0 +1,29 @@ +--- +name: 'Issue: About input files' +about: For issues that do not fit any of the other categories. If this doesn’t work + right, [choose a different type]( https://github.com/deepmodeling/deepmd-kit/issues/new/choose). +title: "[About input files] _Replace With Suitable Title_" +labels: document +assignees: '' + +--- + +**Summary** + + + +**Expected Behavior** + + + +**Actual Behavior** + + + +**The material system in question** + + + +**Further Information, Files, and Links** + + From 920f4bb98c8d40f90475d610124627dc3a2cd6b9 Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Thu, 1 Apr 2021 08:51:45 +0800 Subject: [PATCH 030/161] Update issue templates --- .github/ISSUE_TEMPLATE/about-input-files.md | 29 +++++++++++++++++++++ .github/ISSUE_TEMPLATE/bug-report.md | 27 +++++++++++++++++++ .github/ISSUE_TEMPLATE/feature-request.md | 21 +++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/about-input-files.md create mode 100644 .github/ISSUE_TEMPLATE/bug-report.md create mode 100644 .github/ISSUE_TEMPLATE/feature-request.md diff --git a/.github/ISSUE_TEMPLATE/about-input-files.md b/.github/ISSUE_TEMPLATE/about-input-files.md new file mode 100644 index 0000000000..4bcc4b7bb5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/about-input-files.md @@ -0,0 +1,29 @@ +--- +name: About input files +about: For issues that do not fit any of the other categories. If this doesn’t work + right, [choose a different type]( https://github.com/deepmodeling/deepmd-kit/issues/new/choose). +title: "[About input files] _Replace With Suitable Title_" +labels: document +assignees: '' + +--- + +**Summary** + + + +**Expected Behavior** + + + +**Actual Behavior** + + + +**The material system in question** + + + +**Further Information, Files, and Links** + + diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 0000000000..9aa06346e6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,27 @@ +--- +name: Bug report +about: Create a bug report to help us eliminate issues and improve deepmd-kit. If + this doesn’t look right, [choose a different type](https://github.com/deepmodeling/deepmd-kit/issues/new/choose). +title: "[BUG] _Replace With Suitable Title_" +labels: bug +assignees: '' + +--- + +**Summary** + + + +**Deepmd-kit Version, Python Version, Tensorflow Version, GCC Version and Cuda Version.** + + + + + +**Steps to Reproduce** + + + +**Further Information, Files, and Links** + + diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 0000000000..2ca72d3932 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,21 @@ +--- +name: Feature request +about: Suggest an idea for this project. If this doesn’t work right, [choose a different + type]( https://github.com/deepmodeling/deepmd-kit/issues/new/choose) +title: "[Feature Request] _Replace with Title_" +labels: new feature +assignees: '' + +--- + +**Summary** + + + +**Detailed Description** + + + +**Further Information, Files, and Links** + + From 3eb7af24cf12f1b1e956d405b7e0b0207f1869f4 Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Thu, 1 Apr 2021 09:06:18 +0800 Subject: [PATCH 031/161] Update issue templates --- .github/ISSUE_TEMPLATE/generic-issue.md | 23 +++++++++++++++ .../issue--about-input-files.md | 29 ------------------- .github/ISSUE_TEMPLATE/issue--bug-report.md | 27 ----------------- .../ISSUE_TEMPLATE/issue--feature-request.md | 21 -------------- .github/ISSUE_TEMPLATE/parameters.md | 25 ++++++++++++++++ 5 files changed, 48 insertions(+), 77 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/generic-issue.md delete mode 100644 .github/ISSUE_TEMPLATE/issue--about-input-files.md delete mode 100644 .github/ISSUE_TEMPLATE/issue--bug-report.md delete mode 100644 .github/ISSUE_TEMPLATE/issue--feature-request.md create mode 100644 .github/ISSUE_TEMPLATE/parameters.md diff --git a/.github/ISSUE_TEMPLATE/generic-issue.md b/.github/ISSUE_TEMPLATE/generic-issue.md new file mode 100644 index 0000000000..8be5cbd9c0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/generic-issue.md @@ -0,0 +1,23 @@ +--- +name: Generic issue +about: For issues that do not fit any of the other categories. If this doesn’t work + right, [choose a different type]( https://github.com/deepmodeling/deepmd-kit/issues/new/choose). +title: _Replace With a Descriptive Title_ +labels: wontfix +assignees: '' + +--- + +**Summary** + + + +**Deepmd-kit Version, Python Version, Tensorflow Version, GCC Version and Cuda Version.** + + + + + +**Details** + + diff --git a/.github/ISSUE_TEMPLATE/issue--about-input-files.md b/.github/ISSUE_TEMPLATE/issue--about-input-files.md deleted file mode 100644 index c8b6276db8..0000000000 --- a/.github/ISSUE_TEMPLATE/issue--about-input-files.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -name: 'Issue: About input files' -about: For issues that do not fit any of the other categories. If this doesn’t work - right, [choose a different type]( https://github.com/deepmodeling/deepmd-kit/issues/new/choose). -title: "[About input files] _Replace With Suitable Title_" -labels: document -assignees: '' - ---- - -**Summary** - - - -**Expected Behavior** - - - -**Actual Behavior** - - - -**The material system in question** - - - -**Further Information, Files, and Links** - - diff --git a/.github/ISSUE_TEMPLATE/issue--bug-report.md b/.github/ISSUE_TEMPLATE/issue--bug-report.md deleted file mode 100644 index c72e925017..0000000000 --- a/.github/ISSUE_TEMPLATE/issue--bug-report.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -name: 'Issue: Bug report' -about: Create a bug report to help us eliminate issues and improve deepmd-kit. If - this doesn’t look right, [choose a different type](https://github.com/deepmodeling/deepmd-kit/issues/new/choose). -title: "[BUG] _Replace With Suitable Title_" -labels: bug -assignees: '' - ---- - -**Summary** - - - -**Deepmd-kit Version, Python Version, Tensorflow Version, GCC Version and Cuda Version.** - - - - - -**Steps to Reproduce** - - - -**Further Information, Files, and Links** - - diff --git a/.github/ISSUE_TEMPLATE/issue--feature-request.md b/.github/ISSUE_TEMPLATE/issue--feature-request.md deleted file mode 100644 index 6d26779f1f..0000000000 --- a/.github/ISSUE_TEMPLATE/issue--feature-request.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: 'Issue: Feature request' -about: Suggest an idea for this project. If this doesn’t work right, [choose a different - type]( https://github.com/deepmodeling/deepmd-kit/issues/new/choose) -title: "[Feature Request] _Replace with Title_" -labels: new feature -assignees: '' - ---- - -**Summary** - - - -**Detailed Description** - - - -**Further Information, Files, and Links** - - diff --git a/.github/ISSUE_TEMPLATE/parameters.md b/.github/ISSUE_TEMPLATE/parameters.md new file mode 100644 index 0000000000..166574eac8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/parameters.md @@ -0,0 +1,25 @@ +--- +name: Parameters +about: Make a suggestion for a change of input parameters or a new output to deepmd-kit. + If this doesn’t work right, [choose a different type]( https://github.com/deepmodeling/deepmd-kit/issues/new/choose). +title: "[About input files] _Replace With Suitable Title_" +labels: document, enhancement +assignees: '' + +--- + +**Summary** + + + +**Summary** + + + +**Detailed Description** + + + +**Further Information, Files, and Links** + + From 6cafa9a39f3c3990a6788d98d8142846c848b25a Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Thu, 1 Apr 2021 09:13:07 +0800 Subject: [PATCH 032/161] Update issue templates --- .github/ISSUE_TEMPLATE/about-input-files.md | 29 --------------------- .github/ISSUE_TEMPLATE/request-for-help.md | 21 +++++++++++++++ 2 files changed, 21 insertions(+), 29 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/about-input-files.md create mode 100644 .github/ISSUE_TEMPLATE/request-for-help.md diff --git a/.github/ISSUE_TEMPLATE/about-input-files.md b/.github/ISSUE_TEMPLATE/about-input-files.md deleted file mode 100644 index 4bcc4b7bb5..0000000000 --- a/.github/ISSUE_TEMPLATE/about-input-files.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -name: About input files -about: For issues that do not fit any of the other categories. If this doesn’t work - right, [choose a different type]( https://github.com/deepmodeling/deepmd-kit/issues/new/choose). -title: "[About input files] _Replace With Suitable Title_" -labels: document -assignees: '' - ---- - -**Summary** - - - -**Expected Behavior** - - - -**Actual Behavior** - - - -**The material system in question** - - - -**Further Information, Files, and Links** - - diff --git a/.github/ISSUE_TEMPLATE/request-for-help.md b/.github/ISSUE_TEMPLATE/request-for-help.md new file mode 100644 index 0000000000..daeb6a85a6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/request-for-help.md @@ -0,0 +1,21 @@ +--- +name: Request for Help +about: Don't post help requests here, go to [discussions](https://github.com/deepmodeling/deepmd-kit/discussions) + instead. +title: '' +labels: '' +assignees: '' + +--- + +Before asking questions, you can + +search the previous issues or discussions +check the [document](https://deepmd.readthedocs.io/en/stable), especially [training parameters](https://deepmd.readthedocs.io/en/stable/train-input.html). + +Please **do not** post requests for help (e.g. with installing or using deepmd-kit) here. +Instead go to [discussions](https://github.com/deepmodeling/deepmd-kit/discussions). + +This issue tracker is for tracking deepmd-kit development related issues only. + +Thanks for your cooperation. From c40414152b57a349351711bb1388183218767242 Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Thu, 1 Apr 2021 09:53:48 +0800 Subject: [PATCH 033/161] Update issue templates --- .github/ISSUE_TEMPLATE/parameters.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/parameters.md b/.github/ISSUE_TEMPLATE/parameters.md index 166574eac8..c11849380e 100644 --- a/.github/ISSUE_TEMPLATE/parameters.md +++ b/.github/ISSUE_TEMPLATE/parameters.md @@ -2,7 +2,7 @@ name: Parameters about: Make a suggestion for a change of input parameters or a new output to deepmd-kit. If this doesn’t work right, [choose a different type]( https://github.com/deepmodeling/deepmd-kit/issues/new/choose). -title: "[About input files] _Replace With Suitable Title_" +title: "[Parameters] _Replace With Suitable Title_" labels: document, enhancement assignees: '' From fc6f860a9b05f9aaaf04e45c108806309907b2ec Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Thu, 1 Apr 2021 10:05:38 +0800 Subject: [PATCH 034/161] Update issue templates --- .github/ISSUE_TEMPLATE/generic-issue.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/generic-issue.md b/.github/ISSUE_TEMPLATE/generic-issue.md index 8be5cbd9c0..57ff296bef 100644 --- a/.github/ISSUE_TEMPLATE/generic-issue.md +++ b/.github/ISSUE_TEMPLATE/generic-issue.md @@ -10,7 +10,7 @@ assignees: '' **Summary** - + **Deepmd-kit Version, Python Version, Tensorflow Version, GCC Version and Cuda Version.** From 6e9e553891976f19a61267ace4ae0b49e055c74a Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Thu, 1 Apr 2021 10:22:17 +0800 Subject: [PATCH 035/161] Update issue templates --- .github/ISSUE_TEMPLATE/bug-report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 9aa06346e6..5e115bf029 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -16,7 +16,7 @@ assignees: '' - + **Steps to Reproduce** From a86984093dfe789e2586f82338d2de8d4300c856 Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Thu, 1 Apr 2021 10:23:44 +0800 Subject: [PATCH 036/161] Update issue templates --- .github/ISSUE_TEMPLATE/feature-request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md index 2ca72d3932..00e939342c 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.md +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -14,7 +14,7 @@ assignees: '' **Detailed Description** - + **Further Information, Files, and Links** From 512cf02cd4a9d64d9b34b099595ff46c1fe3fa72 Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Thu, 1 Apr 2021 10:25:12 +0800 Subject: [PATCH 037/161] Update issue templates --- .github/ISSUE_TEMPLATE/generic-issue.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/generic-issue.md b/.github/ISSUE_TEMPLATE/generic-issue.md index 57ff296bef..00b0913afb 100644 --- a/.github/ISSUE_TEMPLATE/generic-issue.md +++ b/.github/ISSUE_TEMPLATE/generic-issue.md @@ -16,7 +16,7 @@ assignees: '' - + **Details** From 1550bc384a74d92808f47fec16fc65c7c00b840d Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Fri, 9 Apr 2021 08:45:07 +0800 Subject: [PATCH 038/161] Update issue templates --- .github/ISSUE_TEMPLATE/bug-report.md | 4 ++-- .github/ISSUE_TEMPLATE/feature-request.md | 2 +- .github/ISSUE_TEMPLATE/parameters.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 5e115bf029..a56dd16a9c 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -12,9 +12,9 @@ assignees: '' -**Deepmd-kit Version, Python Version, Tensorflow Version, GCC Version and Cuda Version.** +**AS DETAILED AS POSSIBLE.** - + diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md index 00e939342c..ec57b4605e 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.md +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -3,7 +3,7 @@ name: Feature request about: Suggest an idea for this project. If this doesn’t work right, [choose a different type]( https://github.com/deepmodeling/deepmd-kit/issues/new/choose) title: "[Feature Request] _Replace with Title_" -labels: new feature +labels: '' assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/parameters.md b/.github/ISSUE_TEMPLATE/parameters.md index c11849380e..1a9c5dbcaa 100644 --- a/.github/ISSUE_TEMPLATE/parameters.md +++ b/.github/ISSUE_TEMPLATE/parameters.md @@ -3,7 +3,7 @@ name: Parameters about: Make a suggestion for a change of input parameters or a new output to deepmd-kit. If this doesn’t work right, [choose a different type]( https://github.com/deepmodeling/deepmd-kit/issues/new/choose). title: "[Parameters] _Replace With Suitable Title_" -labels: document, enhancement +labels: enhancement assignees: '' --- From 95a80571d14a3e7add4299426b80fa83a6553319 Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Fri, 9 Apr 2021 09:08:21 +0800 Subject: [PATCH 039/161] Update issue templates --- .github/ISSUE_TEMPLATE/feature-request.md | 2 +- .github/ISSUE_TEMPLATE/parameters.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md index ec57b4605e..d099345615 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.md +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -3,7 +3,7 @@ name: Feature request about: Suggest an idea for this project. If this doesn’t work right, [choose a different type]( https://github.com/deepmodeling/deepmd-kit/issues/new/choose) title: "[Feature Request] _Replace with Title_" -labels: '' +labels: enhancement assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/parameters.md b/.github/ISSUE_TEMPLATE/parameters.md index 1a9c5dbcaa..ff9188f06e 100644 --- a/.github/ISSUE_TEMPLATE/parameters.md +++ b/.github/ISSUE_TEMPLATE/parameters.md @@ -3,7 +3,7 @@ name: Parameters about: Make a suggestion for a change of input parameters or a new output to deepmd-kit. If this doesn’t work right, [choose a different type]( https://github.com/deepmodeling/deepmd-kit/issues/new/choose). title: "[Parameters] _Replace With Suitable Title_" -labels: enhancement +labels: documentation, enhancement assignees: '' --- From 5c764165e83031089ce053fdef4b68adadeacc25 Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Fri, 9 Apr 2021 15:27:51 +0800 Subject: [PATCH 040/161] Update issue templates --- .github/ISSUE_TEMPLATE/bug-report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index a56dd16a9c..3b6d0b60d7 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -12,7 +12,7 @@ assignees: '' -**AS DETAILED AS POSSIBLE.** +**Deepmd-kit version, installation way, input file, running commands, error log, etc.** From f66c78ec3ac21248d2a0dc0427f8d87d503e9636 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 19 Apr 2021 19:25:00 -0400 Subject: [PATCH 041/161] Do not find protobuf for python In conda-forge's recent released tensorflow, protobuf is external from tensorflow and not inside the tensorflow directory. --- source/cmake/Findtensorflow.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/cmake/Findtensorflow.cmake b/source/cmake/Findtensorflow.cmake index 708b8e86d5..e77376ad3c 100644 --- a/source/cmake/Findtensorflow.cmake +++ b/source/cmake/Findtensorflow.cmake @@ -32,6 +32,7 @@ find_path(TensorFlow_INCLUDE_DIRS PATH_SUFFIXES "/include" NO_DEFAULT_PATH ) +if (BUILD_CPP_IF) find_path(TensorFlow_INCLUDE_DIRS_GOOGLE NAMES google/protobuf/type.pb.h @@ -40,6 +41,7 @@ find_path(TensorFlow_INCLUDE_DIRS_GOOGLE NO_DEFAULT_PATH ) list(APPEND TensorFlow_INCLUDE_DIRS ${TensorFlow_INCLUDE_DIRS_GOOGLE}) +endif () if (NOT TensorFlow_INCLUDE_DIRS AND tensorflow_FIND_REQUIRED) message(FATAL_ERROR From ee8c419517fad9d7bcbf9fa13321a024889e5440 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 19 Apr 2021 20:09:23 -0400 Subject: [PATCH 042/161] only enable link what you use on GNU compilers Clang doesn't support this flag, cause an error on osx: > ld: unknown option: --no-as-needed This commit should be cherry-picked to `r1.2` branch. --- source/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index dc35ee5dc0..b92ea9574d 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required(VERSION 3.7) project(DeePMD) -set(CMAKE_LINK_WHAT_YOU_USE TRUE) +if (CMAKE_COMPILER_IS_GNU) + set(CMAKE_LINK_WHAT_YOU_USE TRUE) +endif () # build cpp or python interfaces if (NOT DEFINED BUILD_CPP_IF) From 58d775aca715fc91a08d0a8743886c687be8d3dc Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sat, 24 Apr 2021 03:41:38 -0400 Subject: [PATCH 043/161] add more badges --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 2ce48317fd..b383d7c55f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ DeePMD-kit Manual ======== [![GitHub release](https://img.shields.io/github/release/deepmodeling/deepmd-kit.svg?maxAge=86400)](https://github.com/deepmodeling/deepmd-kit/releases) +[![doi:10.1016/j.cpc.2018.03.016](https://img.shields.io/badge/DOI-10.1016%2Fj.cpc.2018.03.016-blue)](https://doi.org/10.1016/j.cpc.2020.107206) +[![offline packages](https://img.shields.io/github/downloads/deepmodeling/deepmd-kit/total?label=offline%20packages)](https://github.com/deepmodeling/deepmd-kit/releases) +[![conda install](https://img.shields.io/badge/downloads-9k%20total-green.svg?style=round-square&label=conda%20install)](https://anaconda.org/deepmodeling/deepmd-kit) +[![pip install](https://img.shields.io/pypi/dm/deepmd-kit?label=pip%20install)](https://pypi.org/project/deepmd-kit) +[![docker pull](https://img.shields.io/docker/pulls/deepmodeling/deepmd-kit)](https://hub.docker.com/r/deepmodeling/deepmd-kit) [![Documentation Status](https://readthedocs.org/projects/deepmd/badge/?version=latest)](https://deepmd.readthedocs.io/en/latest/?badge=latest) # Table of contents From 1e6dae6507975d1010ba8d5f531e1abd0242a6b2 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sat, 24 Apr 2021 04:04:26 -0400 Subject: [PATCH 044/161] fix the document badge link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b383d7c55f..77c867bc49 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![conda install](https://img.shields.io/badge/downloads-9k%20total-green.svg?style=round-square&label=conda%20install)](https://anaconda.org/deepmodeling/deepmd-kit) [![pip install](https://img.shields.io/pypi/dm/deepmd-kit?label=pip%20install)](https://pypi.org/project/deepmd-kit) [![docker pull](https://img.shields.io/docker/pulls/deepmodeling/deepmd-kit)](https://hub.docker.com/r/deepmodeling/deepmd-kit) -[![Documentation Status](https://readthedocs.org/projects/deepmd/badge/?version=latest)](https://deepmd.readthedocs.io/en/latest/?badge=latest) +[![Documentation Status](https://readthedocs.org/projects/deepmd/badge/)](https://deepmd.readthedocs.io/) # Table of contents - [About DeePMD-kit](#about-deepmd-kit) From 816b803f33e12559b9bc335e45de3310dafcc3fc Mon Sep 17 00:00:00 2001 From: Han Wang Date: Sat, 8 May 2021 09:01:50 +0800 Subject: [PATCH 045/161] fix issue #598 --- source/lmp/pair_nnp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/lmp/pair_nnp.cpp b/source/lmp/pair_nnp.cpp index b5a64e8b11..f472e0e81e 100644 --- a/source/lmp/pair_nnp.cpp +++ b/source/lmp/pair_nnp.cpp @@ -482,10 +482,10 @@ void PairNNP::compute(int eflag, int vflag) nnp_inter_model_devi.compute_avg (tmp_avg_f_, all_force_); nnp_inter_model_devi.compute_std_f (std_f_, tmp_avg_f_, all_force_); std_f.resize(std_f_.size()); - for (int dd = 0; dd < std_f_.size(); ++dd) std_f[dd] = std_f_[dd]; if (out_rel == 1){ nnp_inter_model_devi.compute_relative_std_f (std_f_, tmp_avg_f_, eps); } + for (int dd = 0; dd < std_f_.size(); ++dd) std_f[dd] = std_f_[dd]; #endif double min = numeric_limits::max(), max = 0, avg = 0; ana_st(max, min, avg, std_f, nlocal); From 495270a82d1da10445a87424a2108a6d8c8da6df Mon Sep 17 00:00:00 2001 From: denghuilu Date: Wed, 12 May 2021 21:26:35 +0800 Subject: [PATCH 046/161] add GPU UUID support for DP --- source/train/Local.py | 2 +- source/train/RunOptions.py.in | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/train/Local.py b/source/train/Local.py index d2564ff17c..1f228fe8ba 100644 --- a/source/train/Local.py +++ b/source/train/Local.py @@ -7,7 +7,7 @@ def get_resource (): if gpus is not None : if gpus != "" : gpus = gpus.split(",") - gpus = [int(ii) for ii in gpus] + gpus = [ii for ii in gpus] else : gpus = [] return nodename, nodelist, gpus diff --git a/source/train/RunOptions.py.in b/source/train/RunOptions.py.in index 63d3544ca6..607519b87e 100644 --- a/source/train/RunOptions.py.in +++ b/source/train/RunOptions.py.in @@ -227,7 +227,7 @@ class RunOptions (object) : self.my_task_index = 0 self.my_socket = None if gpus is not None and len(gpus) > 0: - self.my_device = "gpu:%d" % gpus[0] + self.my_device = "gpu:" + str(gpus[0]) # self.my_device = "gpu:0" else : self.my_device = "cpu:0" From d0f5f188244559ab0a19908f2da8d6d5776c057c Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Tue, 18 May 2021 19:05:31 -0400 Subject: [PATCH 047/161] append to out_file when LAMMPS restarts (#641) This ensures the out file will not be override when LAMMPS restarts. This commit may be conflicted with #392. Commit @5597ea2b49f96e99a52a9779b04b6c12e5a79a04 should be dropped. --- source/lmp/pair_nnp.cpp | 18 +++++++++++++++++- source/lmp/pair_nnp.h.in | 3 +++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/source/lmp/pair_nnp.cpp b/source/lmp/pair_nnp.cpp index f472e0e81e..5d3cfbc691 100644 --- a/source/lmp/pair_nnp.cpp +++ b/source/lmp/pair_nnp.cpp @@ -206,7 +206,7 @@ PairNNP::PairNNP(LAMMPS *lmp) if (strcmp(update->unit_style,"metal") != 0) { error->all(FLERR,"Pair deepmd requires metal unit, please set it by \"units metal\""); } - restartinfo = 0; + restartinfo = 1; pppmflag = 1; respa_enable = 0; writedata = 0; @@ -222,6 +222,7 @@ PairNNP::PairNNP(LAMMPS *lmp) single_model = false; multi_models_mod_devi = false; multi_models_no_mod_devi = false; + is_restart = false; // set comm size needed by this Pair comm_reverse = 1; @@ -754,6 +755,7 @@ void PairNNP::settings(int narg, char **arg) if (comm->me == 0){ if (numb_models > 1 && out_freq > 0){ + if (!is_restart) { fp.open (out_file); fp << scientific; fp << "#" @@ -765,6 +767,10 @@ void PairNNP::settings(int narg, char **arg) << setw(18+1) << "min_devi_f" << setw(18+1) << "avg_devi_f" << endl; + } else { + fp.open (out_file, std::ofstream::out | std::ofstream::app); + fp << scientific; + } } string pre = " "; cout << pre << ">>> Info of model(s):" << endl @@ -800,6 +806,16 @@ void PairNNP::settings(int narg, char **arg) all_force.resize(numb_models); } +void PairDeepMD::read_restart(FILE *) +{ + is_restart = true; +} + +void PairDeepMD::write_restart(FILE *) +{ + // pass +} + /* ---------------------------------------------------------------------- set coeffs for one or more type pairs ------------------------------------------------------------------------- */ diff --git a/source/lmp/pair_nnp.h.in b/source/lmp/pair_nnp.h.in index d41546438c..8a3c03bba4 100644 --- a/source/lmp/pair_nnp.h.in +++ b/source/lmp/pair_nnp.h.in @@ -59,6 +59,8 @@ class PairNNP : public Pair { void settings(int, char **); virtual void coeff(int, char **); void init_style(); + virtual void write_restart(FILE *); + virtual void read_restart(FILE *); double init_one(int i, int j); int pack_reverse_comm(int, int, double *); void unpack_reverse_comm(int, int *, double *); @@ -85,6 +87,7 @@ private: bool single_model; bool multi_models_mod_devi; bool multi_models_no_mod_devi; + bool is_restart; #ifdef HIGH_PREC vector fparam; vector aparam; From ceb07e537ee01f473478463dd22b79dd4479c6e5 Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Tue, 25 May 2021 09:55:46 +0800 Subject: [PATCH 048/161] Update bug-report.md Add request not to use image to show error log. --- .github/ISSUE_TEMPLATE/bug-report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 3b6d0b60d7..80c0ef7a13 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -14,7 +14,7 @@ assignees: '' **Deepmd-kit version, installation way, input file, running commands, error log, etc.** - + From 91f2ffcc41470950beac7ecad849494d51964cd9 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sat, 29 May 2021 06:08:54 -0400 Subject: [PATCH 049/161] fix class name in #641 --- source/lmp/pair_nnp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/lmp/pair_nnp.cpp b/source/lmp/pair_nnp.cpp index 5d3cfbc691..4a2b0dd8d1 100644 --- a/source/lmp/pair_nnp.cpp +++ b/source/lmp/pair_nnp.cpp @@ -806,12 +806,12 @@ void PairNNP::settings(int narg, char **arg) all_force.resize(numb_models); } -void PairDeepMD::read_restart(FILE *) +void PairNNP::read_restart(FILE *) { is_restart = true; } -void PairDeepMD::write_restart(FILE *) +void PairNNP::write_restart(FILE *) { // pass } From 23c59e1a2a9fe7e3d57ea210726c7e890fdc196f Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sun, 8 Aug 2021 19:37:46 -0400 Subject: [PATCH 050/161] add aliases to Arguments (#932) fix #846. --- source/train/argcheck.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/train/argcheck.py b/source/train/argcheck.py index c358c114ff..931782d30f 100644 --- a/source/train/argcheck.py +++ b/source/train/argcheck.py @@ -60,7 +60,7 @@ def descrpt_se_a_args(): Argument("rcut", float, optional = True, default = 6.0, doc = doc_rcut), Argument("rcut_smth", float, optional = True, default = 0.5, doc = doc_rcut_smth), Argument("neuron", list, optional = True, default = [10,20,40], doc = doc_neuron), - Argument("axis_neuron", int, optional = True, default = 4, doc = doc_axis_neuron), + Argument("axis_neuron", int, optional = True, default = 4, n_axis_neuron = ['n_axis_neuron'], doc = doc_axis_neuron), Argument("activation_function", str, optional = True, default = 'tanh', doc = doc_activation_function), Argument("resnet_dt", bool, optional = True, default = False, doc = doc_resnet_dt), Argument("type_one_side", bool, optional = True, default = False, doc = doc_type_one_side), @@ -146,7 +146,7 @@ def fitting_ener(): return [ Argument("numb_fparam", int, optional = True, default = 0, doc = doc_numb_fparam), Argument("numb_aparam", int, optional = True, default = 0, doc = doc_numb_aparam), - Argument("neuron", list, optional = True, default = [120,120,120], doc = doc_neuron), + Argument("neuron", list, optional = True, default = [120,120,120], alias = ['n_neuron'], doc = doc_neuron), Argument("activation_function", str, optional = True, default = 'tanh', doc = doc_activation_function), Argument("precision", str, optional = True, default = 'float64', doc = doc_precision), Argument("resnet_dt", bool, optional = True, default = True, doc = doc_resnet_dt), @@ -169,14 +169,14 @@ def fitting_polar(): doc_seed = 'Random seed for parameter initialization of the fitting net' return [ - Argument("neuron", list, optional = True, default = [120,120,120], doc = doc_neuron), + Argument("neuron", list, optional = True, default = [120,120,120], alias = ['n_neuron'], doc = doc_neuron), Argument("activation_function", str, optional = True, default = 'tanh', doc = doc_activation_function), Argument("resnet_dt", bool, optional = True, default = True, doc = doc_resnet_dt), Argument("precision", str, optional = True, default = 'float64', doc = doc_precision), Argument("fit_diag", bool, optional = True, default = True, doc = doc_fit_diag), Argument("scale", [list,float], optional = True, default = 1.0, doc = doc_scale), Argument("diag_shift", [list,float], optional = True, default = 0.0, doc = doc_diag_shift), - Argument("sel_type", [list,int,None], optional = True, doc = doc_sel_type), + Argument("sel_type", [list,int,None], optional = True, alias = ['pol_type'], doc = doc_sel_type), Argument("seed", [int,None], optional = True, doc = doc_seed) ] @@ -193,11 +193,11 @@ def fitting_dipole(): doc_sel_type = 'The atom types for which the atomic dipole will be provided. If not set, all types will be selected.' doc_seed = 'Random seed for parameter initialization of the fitting net' return [ - Argument("neuron", list, optional = True, default = [120,120,120], doc = doc_neuron), + Argument("neuron", list, optional = True, default = [120,120,120], alias = ['n_neuron'], doc = doc_neuron), Argument("activation_function", str, optional = True, default = 'tanh', doc = doc_activation_function), Argument("resnet_dt", bool, optional = True, default = True, doc = doc_resnet_dt), Argument("precision", str, optional = True, default = 'float64', doc = doc_precision), - Argument("sel_type", [list,int,None], optional = True, doc = doc_sel_type), + Argument("sel_type", [list,int,None], optional = True, alias = ['dipole_type'], doc = doc_sel_type), Argument("seed", [int,None], optional = True, doc = doc_seed) ] From d7e7dbbecc052316dca225dfe8a345fa39037472 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sun, 8 Aug 2021 20:19:27 -0400 Subject: [PATCH 051/161] fix a typo in #932 (#934) * add aliases to Arguments fix #846. * fix a typo in #932 --- source/train/argcheck.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/train/argcheck.py b/source/train/argcheck.py index 931782d30f..2d72340da7 100644 --- a/source/train/argcheck.py +++ b/source/train/argcheck.py @@ -60,7 +60,7 @@ def descrpt_se_a_args(): Argument("rcut", float, optional = True, default = 6.0, doc = doc_rcut), Argument("rcut_smth", float, optional = True, default = 0.5, doc = doc_rcut_smth), Argument("neuron", list, optional = True, default = [10,20,40], doc = doc_neuron), - Argument("axis_neuron", int, optional = True, default = 4, n_axis_neuron = ['n_axis_neuron'], doc = doc_axis_neuron), + Argument("axis_neuron", int, optional = True, default = 4, alias = ['n_axis_neuron'], doc = doc_axis_neuron), Argument("activation_function", str, optional = True, default = 'tanh', doc = doc_activation_function), Argument("resnet_dt", bool, optional = True, default = False, doc = doc_resnet_dt), Argument("type_one_side", bool, optional = True, default = False, doc = doc_type_one_side), From c9fb8f485e23f64c319526b966346890aaa74ebb Mon Sep 17 00:00:00 2001 From: Han Wang Date: Mon, 9 Aug 2021 11:22:31 +0800 Subject: [PATCH 052/161] rm `load_ckpt` (#936) * rm load_ckpt * rm wrong files Co-authored-by: Han Wang --- doc/use-deepmd-kit.md | 4 ++-- examples/fparam/train/input.json | 2 +- examples/fparam/train/input_aparam.json | 2 +- examples/water/train/polar.json | 2 +- examples/water/train/polar_se_a.json | 2 +- examples/water/train/wannier.json | 2 +- examples/water/train/water.json | 2 +- examples/water/train/water_se_a.json | 2 +- examples/water/train/water_se_ar.json | 2 +- examples/water/train/water_se_r.json | 2 +- examples/water/train/water_srtab_example.json | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/use-deepmd-kit.md b/doc/use-deepmd-kit.md index cbf7a91cbc..a6e3915b3b 100644 --- a/doc/use-deepmd-kit.md +++ b/doc/use-deepmd-kit.md @@ -156,7 +156,7 @@ An example of `training` is "numb_test": 10, "save_freq": 1000, "save_ckpt": "model.ckpt", - "load_ckpt": "model.ckpt", + "disp_training":true, "time_training":true, "profiling": false, @@ -189,7 +189,7 @@ During the training, the error of the model is tested every **`disp_freq`** batc ``` The first column displays the number of batches. The second and third columns display the loss function evaluated by `numb_test` frames randomly chosen from the test set and that evaluated by the current training batch, respectively. The fourth and fifth columns display the RMS energy error (normalized by number of atoms) evaluated by `numb_test` frames randomly chosen from the test set and that evaluated by the current training batch, respectively. The sixth and seventh columns display the RMS force error (component-wise) evaluated by `numb_test` frames randomly chosen from the test set and that evaluated by the current training batch, respectively. The last column displays the current learning rate. -Checkpoints will be written to files with prefix **`save_ckpt`** every **`save_freq`** batches. If **`restart`** is set to `true`, then the training will start from the checkpoint named **`load_ckpt`**, rather than from scratch. +Checkpoints will be written to files with prefix **`save_ckpt`** every **`save_freq`** batches. Several command line options can be passed to `dp train`, which can be checked with ```bash diff --git a/examples/fparam/train/input.json b/examples/fparam/train/input.json index c57afdfb7f..0455f11b42 100644 --- a/examples/fparam/train/input.json +++ b/examples/fparam/train/input.json @@ -51,7 +51,7 @@ "numb_test": 10, "save_freq": 1000, "save_ckpt": "model.ckpt", - "load_ckpt": "model.ckpt", + "disp_training":true, "time_training":true, "profiling": false, diff --git a/examples/fparam/train/input_aparam.json b/examples/fparam/train/input_aparam.json index 86be27ef29..5774130fe1 100644 --- a/examples/fparam/train/input_aparam.json +++ b/examples/fparam/train/input_aparam.json @@ -51,7 +51,7 @@ "numb_test": 10, "save_freq": 1000, "save_ckpt": "model.ckpt", - "load_ckpt": "model.ckpt", + "disp_training":true, "time_training":true, "profiling": false, diff --git a/examples/water/train/polar.json b/examples/water/train/polar.json index 60e3fa3494..3437747e49 100644 --- a/examples/water/train/polar.json +++ b/examples/water/train/polar.json @@ -53,7 +53,7 @@ "numb_test": 10, "save_freq": 1000, "save_ckpt": "model.ckpt", - "load_ckpt": "model.ckpt", + "disp_training":true, "time_training":true, "_comment": "that's all" diff --git a/examples/water/train/polar_se_a.json b/examples/water/train/polar_se_a.json index dc90e481ce..e0c2939722 100644 --- a/examples/water/train/polar_se_a.json +++ b/examples/water/train/polar_se_a.json @@ -51,7 +51,7 @@ "numb_test": 10, "save_freq": 1000, "save_ckpt": "model.ckpt", - "load_ckpt": "model.ckpt", + "disp_training":true, "time_training":true, "_comment": "that's all" diff --git a/examples/water/train/wannier.json b/examples/water/train/wannier.json index f23f5e0d62..06b3bc906d 100644 --- a/examples/water/train/wannier.json +++ b/examples/water/train/wannier.json @@ -54,7 +54,7 @@ "numb_test": 10, "save_freq": 1000, "save_ckpt": "model.ckpt", - "load_ckpt": "model.ckpt", + "disp_training":true, "time_training":true, "_comment": "that's all" diff --git a/examples/water/train/water.json b/examples/water/train/water.json index 23ba559aed..ccc47a5d3a 100644 --- a/examples/water/train/water.json +++ b/examples/water/train/water.json @@ -61,7 +61,7 @@ "numb_test": 10, "save_freq": 1000, "save_ckpt": "model.ckpt", - "load_ckpt": "model.ckpt", + "disp_training":true, "time_training":true, "_comment": "that's all" diff --git a/examples/water/train/water_se_a.json b/examples/water/train/water_se_a.json index cb005530c1..5c9b2d8e70 100644 --- a/examples/water/train/water_se_a.json +++ b/examples/water/train/water_se_a.json @@ -56,7 +56,7 @@ "numb_test": 10, "save_freq": 1000, "save_ckpt": "model.ckpt", - "load_ckpt": "model.ckpt", + "disp_training":true, "time_training":true, "profiling": false, diff --git a/examples/water/train/water_se_ar.json b/examples/water/train/water_se_ar.json index 2173f2e1d9..c97b3d1f70 100644 --- a/examples/water/train/water_se_ar.json +++ b/examples/water/train/water_se_ar.json @@ -67,7 +67,7 @@ "numb_test": 10, "save_freq": 1000, "save_ckpt": "model.ckpt", - "load_ckpt": "model.ckpt", + "disp_training":true, "time_training":true, "profiling": false, diff --git a/examples/water/train/water_se_r.json b/examples/water/train/water_se_r.json index 7faf55a3c3..3954e0b9aa 100644 --- a/examples/water/train/water_se_r.json +++ b/examples/water/train/water_se_r.json @@ -56,7 +56,7 @@ "numb_test": 10, "save_freq": 1000, "save_ckpt": "model.ckpt", - "load_ckpt": "model.ckpt", + "disp_training":true, "time_training":true, "profiling": false, diff --git a/examples/water/train/water_srtab_example.json b/examples/water/train/water_srtab_example.json index f2a0a4a39c..3baabd44fd 100644 --- a/examples/water/train/water_srtab_example.json +++ b/examples/water/train/water_srtab_example.json @@ -64,7 +64,7 @@ "numb_test": 10, "save_freq": 1000, "save_ckpt": "model.ckpt", - "load_ckpt": "model.ckpt", + "disp_training":true, "time_training":true, "_comment": "that's all" From 90b1f5270d1e44a0b32870632438f1baec35763f Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Thu, 26 Aug 2021 08:58:10 -0400 Subject: [PATCH 053/161] Fix typo: CMAKE_COMPILER_IS_GNUCXX (#1038) (#1041) The flag won't work without language specified. See https://cmake.org/cmake/help/v3.4/variable/CMAKE_COMPILER_IS_GNULANG.html (cherry picked from commit 8bbe565c876d3e1424ac9840c466e502e98da528) Co-authored-by: Chun Cai --- source/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index b92ea9574d..556a7da17a 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.7) project(DeePMD) -if (CMAKE_COMPILER_IS_GNU) +if (CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_LINK_WHAT_YOU_USE TRUE) endif () From f923ee55748c7644011e1a3f3ce6122828e6f71a Mon Sep 17 00:00:00 2001 From: Han Wang Date: Fri, 27 Aug 2021 18:05:03 +0800 Subject: [PATCH 054/161] change to almostEqual in UT to avoid difference at FP precision --- source/tests/test_model_devi.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/tests/test_model_devi.py b/source/tests/test_model_devi.py index 3e5161b44d..4d5414cea8 100644 --- a/source/tests/test_model_devi.py +++ b/source/tests/test_model_devi.py @@ -36,11 +36,11 @@ def test_calc_model_devi(self): frequency=self.freq, nopbc=True, fname=self.output) - self.assertEqual(model_devi[0][0], 0) - self.assertEqual(model_devi[1][0], self.freq) + self.assertAlmostEqual(model_devi[0][0], 0) + self.assertAlmostEqual(model_devi[1][0], self.freq) for ii in range(1, 7): self.assertAlmostEqual(model_devi[0][ii], self.expect[ii]) - self.assertEqual(model_devi[0][ii], model_devi[1][ii]) + self.assertAlmostEqual(model_devi[0][ii], model_devi[1][ii]) self.assertTrue(os.path.isfile(self.output)) def tearDown(self): From 1fbf4746a475788bed17fd0774fde5dddbe6dec5 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Wed, 8 Sep 2021 07:56:41 -0400 Subject: [PATCH 055/161] fix LAMMPS_VERSION_NUMBER condition (#1116) When I debug #1109, I accidentally find the LAMMPS_VERSION_NUMBER condition is wrong, making builds fail. (but this is not related to #1109) --- source/lmp/fix_dplr.cpp | 4 ++-- source/lmp/pppm_dplr.cpp | 2 +- source/lmp/pppm_dplr.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/lmp/fix_dplr.cpp b/source/lmp/fix_dplr.cpp index bfb2b9f543..1d2323a027 100644 --- a/source/lmp/fix_dplr.cpp +++ b/source/lmp/fix_dplr.cpp @@ -41,7 +41,7 @@ FixDPLR::FixDPLR(LAMMPS *lmp, int narg, char **arg) efield_fsum_all(4, 0.0), efield_force_flag(0) { -#if LAMMPS_VERSION_NUMBER>=20210702 +#if LAMMPS_VERSION_NUMBER>=20210210 // lammps/lammps#2560 energy_global_flag = 1; virial_global_flag = 1; @@ -123,7 +123,7 @@ FixDPLR::FixDPLR(LAMMPS *lmp, int narg, char **arg) int FixDPLR::setmask() { int mask = 0; -#if LAMMPS_VERSION_NUMBER<20210702 +#if LAMMPS_VERSION_NUMBER<20210210 // THERMO_ENERGY removed in lammps/lammps#2560 mask |= THERMO_ENERGY; #endif diff --git a/source/lmp/pppm_dplr.cpp b/source/lmp/pppm_dplr.cpp index cd4e7da2ba..0fda38e4f3 100644 --- a/source/lmp/pppm_dplr.cpp +++ b/source/lmp/pppm_dplr.cpp @@ -28,7 +28,7 @@ enum{FORWARD_IK,FORWARD_AD,FORWARD_IK_PERATOM,FORWARD_AD_PERATOM}; /* ---------------------------------------------------------------------- */ -#if LAMMPS_VERSION_NUMBER<20190201 +#if LAMMPS_VERSION_NUMBER<20181109 // See lammps/lammps#1165 PPPMDPLR::PPPMDPLR(LAMMPS *lmp, int narg, char **arg) : PPPM(lmp, narg, arg) diff --git a/source/lmp/pppm_dplr.h b/source/lmp/pppm_dplr.h index df436170f9..20e4107385 100644 --- a/source/lmp/pppm_dplr.h +++ b/source/lmp/pppm_dplr.h @@ -21,7 +21,7 @@ namespace LAMMPS_NS { class PPPMDPLR : public PPPM { public: -#if LAMMPS_VERSION_NUMBER<20190201 +#if LAMMPS_VERSION_NUMBER<20181109 // See lammps/lammps#1165 PPPMDPLR(class LAMMPS *, int, char **); #else From 60797e0ceb2da0d4871939ab4e258e79face67d9 Mon Sep 17 00:00:00 2001 From: Chenxing Luo Date: Wed, 8 Sep 2021 10:41:51 -0400 Subject: [PATCH 056/161] Fix missing `std::numeric_limits` (#1113) * Fix missing `std::numeric_limits` - Include `` header, fix missing `std::numeric_limits` when compiling with GCC 11.0. --- source/lib/src/neighbor_list.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/lib/src/neighbor_list.cc b/source/lib/src/neighbor_list.cc index c3cd376fbe..cae7630430 100644 --- a/source/lib/src/neighbor_list.cc +++ b/source/lib/src/neighbor_list.cc @@ -1,6 +1,7 @@ #include "neighbor_list.h" #include "device.h" #include +#include // #include // using namespace std; From 97be2f5a1014b9838af4235c7ca9248f8b8b1923 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Wed, 8 Sep 2021 10:50:28 -0400 Subject: [PATCH 057/161] support init_frz_model for hybrid descriptor (#1112) * support init_frz_model for hybrid descriptor Refactors some methods to implement it. Also fixes some typos. * rename `graph_def` to `model_file` Co-authored-by: Denghui Lu Co-authored-by: Denghui Lu --- deepmd/descriptor/descriptor.py | 57 ++++++++++++++++++++---- deepmd/descriptor/hybrid.py | 52 ++++++++++++++++++++++ deepmd/descriptor/se_a.py | 78 ++++++++++++--------------------- deepmd/model/ener.py | 7 +-- deepmd/model/tensor.py | 9 ++-- deepmd/train/trainer.py | 2 +- deepmd/utils/graph.py | 20 ++++++--- 7 files changed, 150 insertions(+), 75 deletions(-) diff --git a/deepmd/descriptor/descriptor.py b/deepmd/descriptor/descriptor.py index f241f6ee15..893493a201 100644 --- a/deepmd/descriptor/descriptor.py +++ b/deepmd/descriptor/descriptor.py @@ -280,26 +280,65 @@ def get_feed_dict(self, feed_dict : dict[str, tf.Tensor] The output feed_dict of current descriptor """ - # TODO: currently only SeA has this method, but I think the method can be - # moved here as it doesn't contain anything related to a specific descriptor - raise NotImplementedError + feed_dict = { + 't_coord:0' :coord_, + 't_type:0' :atype_, + 't_natoms:0' :natoms, + 't_box:0' :box, + 't_mesh:0' :mesh + } + return feed_dict def init_variables(self, - embedding_net_variables: dict - ) -> None: + model_file: str, + suffix : str = "", + ) -> None: """ Init the embedding net variables with the given dict Parameters ---------- - embedding_net_variables - The input dict which stores the embedding net variables + model_file : str + The input model file + suffix : str, optional + The suffix of the scope Notes ----- This method is called by others when the descriptor supported initialization from the given variables. """ - # TODO: currently only SeA has this method, but I think the method can be - # moved here as it doesn't contain anything related to a specific descriptor raise NotImplementedError( "Descriptor %s doesn't support initialization from the given variables!" % type(self).__name__) + + def get_tensor_names(self, suffix : str = "") -> Tuple[str]: + """Get names of tensors. + + Parameters + ---------- + suffix : str + The suffix of the scope + + Returns + ------- + Tuple[str] + Names of tensors + """ + raise NotImplementedError("Descriptor %s doesn't support this property!" % type(self).__name__) + + def pass_tensors_from_frz_model(self, + *tensors : tf.Tensor, + ) -> None: + """ + Pass the descrpt_reshape tensor as well as descrpt_deriv tensor from the frz graph_def + + Parameters + ---------- + *tensors : tf.Tensor + passed tensors + + Notes + ----- + The number of parameters in the method must be equal to the numbers of returns in + :meth:`get_tensor_names`. + """ + raise NotImplementedError("Descriptor %s doesn't support this method!" % type(self).__name__) diff --git a/deepmd/descriptor/hybrid.py b/deepmd/descriptor/hybrid.py index bff59518a1..37b9578b4e 100644 --- a/deepmd/descriptor/hybrid.py +++ b/deepmd/descriptor/hybrid.py @@ -253,3 +253,55 @@ def enable_compression(self, """ for idx, ii in enumerate(self.descrpt_list): ii.enable_compression(min_nbor_dist, model_file, table_extrapolate, table_stride_1, table_stride_2, check_frequency, suffix=f"{suffix}_{idx}") + + def init_variables(self, + model_file : str, + suffix : str = "", + ) -> None: + """ + Init the embedding net variables with the given dict + + Parameters + ---------- + model_file : str + The input frozen model file + suffix : str, optional + The suffix of the scope + """ + for idx, ii in enumerate(self.descrpt_list): + ii.init_variables(model_file, suffix=f"{suffix}_{idx}") + + def get_tensor_names(self, suffix : str = "") -> Tuple[str]: + """Get names of tensors. + + Parameters + ---------- + suffix : str + The suffix of the scope + + Returns + ------- + Tuple[str] + Names of tensors + """ + tensor_names = [] + for idx, ii in enumerate(self.descrpt_list): + tensor_names.extend(ii.get_tensor_names(suffix=f"{suffix}_{idx}")) + return tuple(tensor_names) + + def pass_tensors_from_frz_model(self, + *tensors : tf.Tensor, + ) -> None: + """ + Pass the descrpt_reshape tensor as well as descrpt_deriv tensor from the frz graph_def + + Parameters + ---------- + *tensors : tf.Tensor + passed tensors + """ + jj = 0 + for ii in self.descrpt_list: + n_tensors = len(ii.get_tensor_names()) + ii.pass_tensors_from_frz_model(*tensors[jj:jj+n_tensors]) + jj += n_tensors diff --git a/deepmd/descriptor/se_a.py b/deepmd/descriptor/se_a.py index 47e28522a9..39485463a9 100644 --- a/deepmd/descriptor/se_a.py +++ b/deepmd/descriptor/se_a.py @@ -13,7 +13,7 @@ from deepmd.utils.tabulate import DPTabulate from deepmd.utils.type_embed import embed_atom_type from deepmd.utils.sess import run_sess -from deepmd.utils.graph import load_graph_def, get_tensor_by_name_from_graph +from deepmd.utils.graph import load_graph_def, get_tensor_by_name_from_graph, get_embedding_net_variables from .descriptor import Descriptor class DescrptSeA (Descriptor): @@ -433,10 +433,10 @@ def build (self, tf.summary.histogram('nlist', self.nlist) self.descrpt_reshape = tf.reshape(self.descrpt, [-1, self.ndescrpt]) - self.descrpt_reshape = tf.identity(self.descrpt_reshape, name = 'o_rmat') - self.descrpt_deriv = tf.identity(self.descrpt_deriv, name = 'o_rmat_deriv') - self.rij = tf.identity(self.rij, name = 'o_rij') - self.nlist = tf.identity(self.nlist, name = 'o_nlist') + self.descrpt_reshape = tf.identity(self.descrpt_reshape, name = 'o_rmat' + suffix) + self.descrpt_deriv = tf.identity(self.descrpt_deriv, name = 'o_rmat_deriv' + suffix) + self.rij = tf.identity(self.rij, name = 'o_rij' + suffix) + self.nlist = tf.identity(self.nlist, name = 'o_nlist' + suffix) self.dout, self.qmat = self._pass_filter(self.descrpt_reshape, atype, @@ -456,6 +456,21 @@ def get_rot_mat(self) -> tf.Tensor: """ return self.qmat + def get_tensor_names(self, suffix : str = "") -> Tuple[str]: + """Get names of tensors. + + Parameters + ---------- + suffix : str + The suffix of the scope + + Returns + ------- + Tuple[str] + Names of tensors + """ + return (f'o_rmat{suffix}:0', f'o_rmat_deriv{suffix}:0', f'o_rij{suffix}:0', f'o_nlist{suffix}:0') + def pass_tensors_from_frz_model(self, descrpt_reshape : tf.Tensor, descrpt_deriv : tf.Tensor, @@ -481,60 +496,21 @@ def pass_tensors_from_frz_model(self, self.descrpt_deriv = descrpt_deriv self.descrpt_reshape = descrpt_reshape - def get_feed_dict(self, - coord_, - atype_, - natoms, - box, - mesh): - """ - generate the deed_dict for current descriptor - - Parameters - ---------- - coord_ - The coordinate of atoms - atype_ - The type of atoms - natoms - The number of atoms. This tensor has the length of Ntypes + 2 - natoms[0]: number of local atoms - natoms[1]: total number of atoms held by this processor - natoms[i]: 2 <= i < Ntypes+2, number of type i atoms - box - The box. Can be generated by deepmd.model.make_stat_input - mesh - For historical reasons, only the length of the Tensor matters. - if size of mesh == 6, pbc is assumed. - if size of mesh == 0, no-pbc is assumed. - - Returns - ------- - feed_dict - The output feed_dict of current descriptor - """ - feed_dict = { - 't_coord:0' :coord_, - 't_type:0' :atype_, - 't_natoms:0' :natoms, - 't_box:0' :box, - 't_mesh:0' :mesh - } - return feed_dict - - def init_variables(self, - embedding_net_variables: dict + model_file : str, + suffix : str = "", ) -> None: """ Init the embedding net variables with the given dict Parameters ---------- - embedding_net_variables - The input dict which stores the embedding net variables + model_file : str + The input frozen model file + suffix : str, optional + The suffix of the scope """ - self.embedding_net_variables = embedding_net_variables + self.embedding_net_variables = get_embedding_net_variables(model_file, suffix = suffix) def prod_force_virial(self, diff --git a/deepmd/model/ener.py b/deepmd/model/ener.py index dec6ca66f4..441709caff 100644 --- a/deepmd/model/ener.py +++ b/deepmd/model/ener.py @@ -173,10 +173,11 @@ def build (self, name = 'descrpt_attr/ntypes', dtype = tf.int32) feed_dict = self.descrpt.get_feed_dict(coord_, atype_, natoms, box, mesh) - return_elements = ['o_rmat:0', 'o_rmat_deriv:0', 'o_rij:0', 'o_nlist:0', 'o_descriptor:0'] - descrpt_reshape, descrpt_deriv, rij, nlist, dout \ + return_elements = [*self.descrpt.get_tensor_names(), 'o_descriptor:0'] + imported_tensors \ = self._import_graph_def_from_frz_model(frz_model, feed_dict, return_elements) - self.descrpt.pass_tensors_from_frz_model(descrpt_reshape, descrpt_deriv, rij, nlist) + dout = imported_tensors[-1] + self.descrpt.pass_tensors_from_frz_model(*imported_tensors[:-1]) if self.srtab is not None : diff --git a/deepmd/model/tensor.py b/deepmd/model/tensor.py index 5c996ec38d..2a63eda4d6 100644 --- a/deepmd/model/tensor.py +++ b/deepmd/model/tensor.py @@ -3,7 +3,7 @@ from deepmd.env import tf from deepmd.common import ClassArg -from deepmd.env import global_cvt_2_ener_float, MODEL_VERSION +from deepmd.env import global_cvt_2_ener_float, MODEL_VERSION, GLOBAL_TF_FLOAT_PRECISION from deepmd.env import op_module from deepmd.utils.graph import load_graph_def from .model_stat import make_stat_input, merge_sys_stat @@ -138,10 +138,11 @@ def build (self, name = 'descrpt_attr/ntypes', dtype = tf.int32) feed_dict = self.descrpt.get_feed_dict(coord_, atype_, natoms, box, mesh) - return_elements = ['o_rmat:0', 'o_rmat_deriv:0', 'o_rij:0', 'o_nlist:0', 'o_descriptor:0'] - descrpt_reshape, descrpt_deriv, rij, nlist, dout \ + return_elements = [*self.descrpt.get_tensor_names(), 'o_descriptor:0'] + imported_tensors \ = self._import_graph_def_from_frz_model(frz_model, feed_dict, return_elements) - self.descrpt.pass_tensors_from_frz_model(descrpt_reshape, descrpt_deriv, rij, nlist) + dout = imported_tensors[-1] + self.descrpt.pass_tensors_from_frz_model(*imported_tensors[:-1]) rot_mat = self.descrpt.get_rot_mat() rot_mat = tf.identity(rot_mat, name = 'o_rot_mat'+suffix) diff --git a/deepmd/train/trainer.py b/deepmd/train/trainer.py index 4701ed0848..dc888ad3e0 100644 --- a/deepmd/train/trainer.py +++ b/deepmd/train/trainer.py @@ -691,7 +691,7 @@ def _init_from_frz_model(self): # self.frz_model will control the self.model to import the descriptor from the given frozen model instead of building from scratch... # initialize fitting net with the given compressed frozen model if self.model_type == 'original_model': - self.descrpt.init_variables(get_embedding_net_variables(self.run_opt.init_frz_model)) + self.descrpt.init_variables(self.run_opt.init_frz_model) self.fitting.init_variables(get_fitting_net_variables(self.run_opt.init_frz_model)) tf.constant("original_model", name = 'model_type', dtype = tf.string) elif self.model_type == 'compressed_model': diff --git a/deepmd/utils/graph.py b/deepmd/utils/graph.py index ed10108dbb..53fd05cc67 100644 --- a/deepmd/utils/graph.py +++ b/deepmd/utils/graph.py @@ -108,7 +108,7 @@ def get_tensor_by_type(node, elif data_type == np.float32: tensor = np.array(node.float_val) else: - raise RunTimeError('model compression does not support the half precision') + raise RuntimeError('model compression does not support the half precision') return tensor @@ -139,7 +139,7 @@ def get_embedding_net_nodes_from_graph_def(graph_def: tf.GraphDef, suffix: str = return embedding_net_nodes -def get_embedding_net_nodes(model_file: str) -> Dict: +def get_embedding_net_nodes(model_file: str, suffix: str = "") -> Dict: """ Get the embedding net nodes with the given frozen model(model_file) @@ -147,6 +147,8 @@ def get_embedding_net_nodes(model_file: str) -> Dict: ---------- model_file The input frozen model path + suffix : str, optional + The suffix of the scope Returns ---------- @@ -154,10 +156,10 @@ def get_embedding_net_nodes(model_file: str) -> Dict: The embedding net nodes with the given frozen model """ _, graph_def = load_graph_def(model_file) - return get_embedding_net_nodes_from_graph_def(graph_def) + return get_embedding_net_nodes_from_graph_def(graph_def, suffix=suffix) -def get_embedding_net_variables_from_graph_def(graph_def : tf.GraphDef) -> Dict: +def get_embedding_net_variables_from_graph_def(graph_def : tf.GraphDef, suffix: str = "") -> Dict: """ Get the embedding net variables with the given tf.GraphDef object @@ -165,6 +167,8 @@ def get_embedding_net_variables_from_graph_def(graph_def : tf.GraphDef) -> Dict: ---------- graph_def The input tf.GraphDef object + suffix : str, optional + The suffix of the scope Returns ---------- @@ -172,7 +176,7 @@ def get_embedding_net_variables_from_graph_def(graph_def : tf.GraphDef) -> Dict: The embedding net variables within the given tf.GraphDef object """ embedding_net_variables = {} - embedding_net_nodes = get_embedding_net_nodes_from_graph_def(graph_def) + embedding_net_nodes = get_embedding_net_nodes_from_graph_def(graph_def, suffix=suffix) for item in embedding_net_nodes: node = embedding_net_nodes[item] dtype = tf.as_dtype(node.dtype).as_numpy_dtype @@ -184,7 +188,7 @@ def get_embedding_net_variables_from_graph_def(graph_def : tf.GraphDef) -> Dict: embedding_net_variables[item] = np.reshape(tensor_value, tensor_shape) return embedding_net_variables -def get_embedding_net_variables(model_file : str) -> Dict: +def get_embedding_net_variables(model_file : str, suffix: str = "") -> Dict: """ Get the embedding net variables with the given frozen model(model_file) @@ -192,6 +196,8 @@ def get_embedding_net_variables(model_file : str) -> Dict: ---------- model_file The input frozen model path + suffix : str, optional + The suffix of the scope Returns ---------- @@ -199,7 +205,7 @@ def get_embedding_net_variables(model_file : str) -> Dict: The embedding net variables within the given frozen model """ _, graph_def = load_graph_def(model_file) - return get_embedding_net_variables_from_graph_def(graph_def) + return get_embedding_net_variables_from_graph_def(graph_def, suffix=suffix) def get_fitting_net_nodes_from_graph_def(graph_def: tf.GraphDef) -> Dict: From ed36276d0d5f26695e3c713ae534febca0c7ec7e Mon Sep 17 00:00:00 2001 From: Han Wang Date: Thu, 9 Sep 2021 07:42:43 +0800 Subject: [PATCH 058/161] fix data_modifier OOM problem when set size is too large (#1117) Co-authored-by: Han Wang --- deepmd/infer/data_modifier.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/deepmd/infer/data_modifier.py b/deepmd/infer/data_modifier.py index 6b473e93e6..224b8fbd06 100644 --- a/deepmd/infer/data_modifier.py +++ b/deepmd/infer/data_modifier.py @@ -358,7 +358,16 @@ def _extend_system(self, coord, box, atype, charge): ref_coord = coord3[:,sel_idx_map,:] ref_coord = np.reshape(ref_coord, [nframes, nsel * 3]) - dipole = DeepDipole.eval(self, coord, box, atype) + batch_size = 8 + all_dipole = [] + for ii in range(0,nframes,batch_size): + dipole = DeepDipole.eval(self, + coord[ii:ii+batch_size], + box[ii:ii+batch_size], + atype) + all_dipole.append(dipole) + dipole = np.concatenate(all_dipole, axis = 0) + assert(dipole.shape[0] == nframes) dipole = np.reshape(dipole, [nframes, nsel * 3]) wfcc_coord = ref_coord + dipole From 1526587c24fded4b2f870f22f482ad47707f5337 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Fri, 10 Sep 2021 19:42:29 +0800 Subject: [PATCH 059/161] fix bugs of dipole charge modifier: binary str and missing frozen nodes (#1124) Co-authored-by: Han Wang --- deepmd/entrypoints/freeze.py | 2 ++ deepmd/infer/deep_pot.py | 1 + 2 files changed, 3 insertions(+) diff --git a/deepmd/entrypoints/freeze.py b/deepmd/entrypoints/freeze.py index a6d8926109..511f58598d 100755 --- a/deepmd/entrypoints/freeze.py +++ b/deepmd/entrypoints/freeze.py @@ -111,10 +111,12 @@ def _make_node_names(model_type: str, modifier_type: Optional[str] = None) -> Li "modifier_attr/sys_charge_map", "modifier_attr/ewald_h", "modifier_attr/ewald_beta", + "dipole_charge/model_type", "dipole_charge/descrpt_attr/rcut", "dipole_charge/descrpt_attr/ntypes", "dipole_charge/model_attr/tmap", "dipole_charge/model_attr/model_type", + "dipole_charge/model_attr/model_version", "o_dm_force", "dipole_charge/model_attr/sel_type", "dipole_charge/o_dipole", diff --git a/deepmd/infer/deep_pot.py b/deepmd/infer/deep_pot.py index 3c5fb28f6e..63625905e8 100644 --- a/deepmd/infer/deep_pot.py +++ b/deepmd/infer/deep_pot.py @@ -137,6 +137,7 @@ def __init__( t_ewald_h = self._get_tensor("modifier_attr/ewald_h:0") t_ewald_beta = self._get_tensor("modifier_attr/ewald_beta:0") [mdl_name, mdl_charge_map, sys_charge_map, ewald_h, ewald_beta] = run_sess(self.sess, [t_mdl_name, t_mdl_charge_map, t_sys_charge_map, t_ewald_h, t_ewald_beta]) + mdl_name = mdl_name.decode("UTF-8") mdl_charge_map = [int(ii) for ii in mdl_charge_map.decode("UTF-8").split()] sys_charge_map = [int(ii) for ii in sys_charge_map.decode("UTF-8").split()] self.dm = DipoleChargeModifier(mdl_name, mdl_charge_map, sys_charge_map, ewald_h = ewald_h, ewald_beta = ewald_beta) From e263aa3ec820621e89529995f73fa7197a0f5b78 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Fri, 10 Sep 2021 07:44:21 -0400 Subject: [PATCH 060/161] fix "Call to method DeepTensor.__init__ with too many arguments" (#1125) As detected by LGTM, here is an error: Call to method DeepTensor.__init__ with too many arguments; should be no more than 4. I agree with the automatic code analysis tool. --- deepmd/infer/deep_polar.py | 1 - 1 file changed, 1 deletion(-) diff --git a/deepmd/infer/deep_polar.py b/deepmd/infer/deep_polar.py index 846efc9bde..d594be32f8 100644 --- a/deepmd/infer/deep_polar.py +++ b/deepmd/infer/deep_polar.py @@ -84,7 +84,6 @@ def __init__( DeepTensor.__init__( self, model_file, - 9, load_prefix=load_prefix, default_tf_graph=default_tf_graph, ) From 700773f591c4fd6e1d70564c57ac7a16026317eb Mon Sep 17 00:00:00 2001 From: Han Wang Date: Sat, 11 Sep 2021 15:49:32 +0800 Subject: [PATCH 061/161] explicitly set neighbor request to full to fix bug #1109 (#1128) Co-authored-by: Han Wang --- source/lmp/pair_deepmd.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/lmp/pair_deepmd.cpp b/source/lmp/pair_deepmd.cpp index 80b64eefb9..5cb058e1e8 100644 --- a/source/lmp/pair_deepmd.cpp +++ b/source/lmp/pair_deepmd.cpp @@ -992,7 +992,7 @@ void PairDeepMD::init_style() { int irequest = neighbor->request(this,instance_me); neighbor->requests[irequest]->half = 0; - // neighbor->requests[irequest]->full = 1; + neighbor->requests[irequest]->full = 1; // neighbor->requests[irequest]->newton = 2; if (out_each == 1){ int ntotal = atom->natoms; From 90d71ca90d97b184361dfbb1b92697530b652044 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sat, 11 Sep 2021 04:00:03 -0400 Subject: [PATCH 062/161] implement descriptor plugin; pass params to `Descriptor` (#1118) * implement descriptor plugin; pass params to `Descriptor` * avoid class name conflict * add ArgsPlugin * fix lint warning * revert changes to DescrptSeAR * remove descrpt_se_ar which is replaced by hybrid --- deepmd/__init__.py | 8 ++ deepmd/descriptor/__init__.py | 2 +- deepmd/descriptor/descriptor.py | 45 ++++++++++- deepmd/descriptor/hybrid.py | 12 ++- deepmd/descriptor/se_a.py | 2 + deepmd/descriptor/se_a_ebd.py | 3 + deepmd/descriptor/se_a_ef.py | 1 + deepmd/descriptor/se_ar.py | 94 ----------------------- deepmd/descriptor/se_r.py | 2 + deepmd/descriptor/se_t.py | 3 + deepmd/train/trainer.py | 48 +----------- deepmd/utils/__init__.py | 1 + deepmd/utils/argcheck.py | 79 ++++++++++++++----- deepmd/utils/plugin.py | 84 ++++++++++++++++++++ source/tests/test_descrpt_se_ar.py | 118 ----------------------------- 15 files changed, 221 insertions(+), 281 deletions(-) delete mode 100644 deepmd/descriptor/se_ar.py create mode 100644 deepmd/utils/plugin.py delete mode 100644 source/tests/test_descrpt_se_ar.py diff --git a/deepmd/__init__.py b/deepmd/__init__.py index 3a295dcbef..9c3af2a6c7 100644 --- a/deepmd/__init__.py +++ b/deepmd/__init__.py @@ -1,5 +1,9 @@ """Root of the deepmd package, exposes all public classes and submodules.""" +try: + from importlib import metadata +except ImportError: # for Python<3.8 + import importlib_metadata as metadata import deepmd.utils.network as network from . import cluster, descriptor, fit, loss, utils @@ -14,6 +18,10 @@ except ImportError: from .__about__ import __version__ +# load third-party plugins +for ep in metadata.entry_points().get('deepmd', []): + ep.load() + __all__ = [ "descriptor", "fit", diff --git a/deepmd/descriptor/__init__.py b/deepmd/descriptor/__init__.py index 7c3d910091..dd022a4e08 100644 --- a/deepmd/descriptor/__init__.py +++ b/deepmd/descriptor/__init__.py @@ -1,7 +1,7 @@ +from .descriptor import Descriptor from .hybrid import DescrptHybrid from .se_a import DescrptSeA from .se_r import DescrptSeR -from .se_ar import DescrptSeAR from .se_t import DescrptSeT from .se_a_ebd import DescrptSeAEbd from .se_a_ef import DescrptSeAEf diff --git a/deepmd/descriptor/descriptor.py b/deepmd/descriptor/descriptor.py index 893493a201..d179660a9d 100644 --- a/deepmd/descriptor/descriptor.py +++ b/deepmd/descriptor/descriptor.py @@ -3,21 +3,64 @@ import numpy as np from deepmd.env import tf +from deepmd.utils import Plugin, PluginVariant -class Descriptor(ABC): +class Descriptor(PluginVariant): r"""The abstract class for descriptors. All specific descriptors should be based on this class. The descriptor :math:`\mathcal{D}` describes the environment of an atom, which should be a function of coordinates and types of its neighbour atoms. + Examples + -------- + >>> descript = Descriptor(type="se_e2_a", rcut=6., rcut_smth=0.5, sel=[50]) + >>> type(descript) + + Notes ----- Only methods and attributes defined in this class are generally public, that can be called by other classes. """ + __plugins = Plugin() + + @staticmethod + def register(key: str) -> "Descriptor": + """Regiester a descriptor plugin. + + Parameters + ---------- + key : str + the key of a descriptor + + Returns + ------- + Descriptor + the regiestered descriptor + + Examples + -------- + >>> @Descriptor.register("some_descrpt") + class SomeDescript(Descriptor): + pass + """ + return Descriptor.__plugins.register(key) + + def __new__(cls, *args, **kwargs): + if cls is Descriptor: + try: + descrpt_type = kwargs['type'] + except KeyError: + raise KeyError('the type of descriptor should be set by `type`') + if descrpt_type in Descriptor.__plugins.plugins: + cls = Descriptor.__plugins.plugins[descrpt_type] + else: + raise RuntimeError('Unknown descriptor type: ' + descrpt_type) + return super().__new__(cls) + @abstractmethod def get_rcut(self) -> float: """ diff --git a/deepmd/descriptor/hybrid.py b/deepmd/descriptor/hybrid.py index 37b9578b4e..66a11be959 100644 --- a/deepmd/descriptor/hybrid.py +++ b/deepmd/descriptor/hybrid.py @@ -15,12 +15,12 @@ from .descriptor import Descriptor from .se_a import DescrptSeA from .se_r import DescrptSeR -from .se_ar import DescrptSeAR from .se_t import DescrptSeT from .se_a_ebd import DescrptSeAEbd from .se_a_ef import DescrptSeAEf from .loc_frame import DescrptLocFrame +@Descriptor.register("hybrid") class DescrptHybrid (Descriptor): """Concate a list of descriptors to form a new descriptor. @@ -37,11 +37,19 @@ def __init__ (self, """ if descrpt_list == [] or descrpt_list is None: raise RuntimeError('cannot build descriptor from an empty list of descriptors.') + formatted_descript_list = [] + for ii in descrpt_list: + if isinstance(ii, Descriptor): + formatted_descript_list.append(ii) + elif isinstance(ii, dict): + formatted_descript_list.append(Descriptor(**ii)) + else: + raise NotImplementedError # args = ClassArg()\ # .add('list', list, must = True) # class_data = args.parse(jdata) # dict_list = class_data['list'] - self.descrpt_list = descrpt_list + self.descrpt_list = formatted_descript_list self.numb_descrpt = len(self.descrpt_list) for ii in range(1, self.numb_descrpt): assert(self.descrpt_list[ii].get_ntypes() == diff --git a/deepmd/descriptor/se_a.py b/deepmd/descriptor/se_a.py index 39485463a9..893c152382 100644 --- a/deepmd/descriptor/se_a.py +++ b/deepmd/descriptor/se_a.py @@ -16,6 +16,8 @@ from deepmd.utils.graph import load_graph_def, get_tensor_by_name_from_graph, get_embedding_net_variables from .descriptor import Descriptor +@Descriptor.register("se_e2_a") +@Descriptor.register("se_a") class DescrptSeA (Descriptor): r"""DeepPot-SE constructed from all information (both angular and radial) of atomic configurations. The embedding takes the distance between atoms as input. diff --git a/deepmd/descriptor/se_a_ebd.py b/deepmd/descriptor/se_a_ebd.py index b30501963f..7a1f640153 100644 --- a/deepmd/descriptor/se_a_ebd.py +++ b/deepmd/descriptor/se_a_ebd.py @@ -10,7 +10,10 @@ from deepmd.env import default_tf_session_config from deepmd.utils.network import embedding_net from .se_a import DescrptSeA +from .descriptor import Descriptor +@Descriptor.register("se_a_tpe") +@Descriptor.register("se_a_ebd") class DescrptSeAEbd (DescrptSeA): """DeepPot-SE descriptor with type embedding approach. diff --git a/deepmd/descriptor/se_a_ef.py b/deepmd/descriptor/se_a_ef.py index e2424aeae4..b037475722 100644 --- a/deepmd/descriptor/se_a_ef.py +++ b/deepmd/descriptor/se_a_ef.py @@ -12,6 +12,7 @@ from .se_a import DescrptSeA from .descriptor import Descriptor +@Descriptor.register("se_a_ef") class DescrptSeAEf (Descriptor): """ diff --git a/deepmd/descriptor/se_ar.py b/deepmd/descriptor/se_ar.py deleted file mode 100644 index 518cf9315c..0000000000 --- a/deepmd/descriptor/se_ar.py +++ /dev/null @@ -1,94 +0,0 @@ -import numpy as np -from deepmd.env import tf -from deepmd.common import ClassArg - -from .se_a import DescrptSeA -from .se_r import DescrptSeR -from deepmd.env import op_module -from .descriptor import Descriptor - -class DescrptSeAR (Descriptor): - def __init__ (self, jdata): - args = ClassArg()\ - .add('a', dict, must = True) \ - .add('r', dict, must = True) - class_data = args.parse(jdata) - self.param_a = class_data['a'] - self.param_r = class_data['r'] - self.descrpt_a = DescrptSeA(**self.param_a) - self.descrpt_r = DescrptSeR(**self.param_r) - assert(self.descrpt_a.get_ntypes() == self.descrpt_r.get_ntypes()) - self.davg = None - self.dstd = None - - def get_rcut (self) : - return np.max([self.descrpt_a.get_rcut(), self.descrpt_r.get_rcut()]) - - def get_ntypes (self) : - return self.descrpt_r.get_ntypes() - - def get_dim_out (self) : - return (self.descrpt_a.get_dim_out() + self.descrpt_r.get_dim_out()) - - def get_nlist_a (self) : - return self.descrpt_a.nlist, self.descrpt_a.rij, self.descrpt_a.sel_a, self.descrpt_a.sel_r - - def get_nlist_r (self) : - return self.descrpt_r.nlist, self.descrpt_r.rij, self.descrpt_r.sel_a, self.descrpt_r.sel_r - - def compute_input_stats (self, - data_coord, - data_box, - data_atype, - natoms_vec, - mesh, - input_dict) : - self.descrpt_a.compute_input_stats(data_coord, data_box, data_atype, natoms_vec, mesh, input_dict) - self.descrpt_r.compute_input_stats(data_coord, data_box, data_atype, natoms_vec, mesh, input_dict) - self.davg = [self.descrpt_a.davg, self.descrpt_r.davg] - self.dstd = [self.descrpt_a.dstd, self.descrpt_r.dstd] - - - def build (self, - coord_, - atype_, - natoms, - box, - mesh, - input_dict, - suffix = '', - reuse = None): - davg = self.davg - dstd = self.dstd - if davg is None: - davg = [np.zeros([self.descrpt_a.ntypes, self.descrpt_a.ndescrpt]), - np.zeros([self.descrpt_r.ntypes, self.descrpt_r.ndescrpt])] - if dstd is None: - dstd = [np.ones ([self.descrpt_a.ntypes, self.descrpt_a.ndescrpt]), - np.ones ([self.descrpt_r.ntypes, self.descrpt_r.ndescrpt])] - # dout - self.dout_a = self.descrpt_a.build(coord_, atype_, natoms, box, mesh, input_dict, suffix=suffix+'_a', reuse=reuse) - self.dout_r = self.descrpt_r.build(coord_, atype_, natoms, box, mesh, input_dict, suffix=suffix , reuse=reuse) - self.dout_a = tf.reshape(self.dout_a, [-1, self.descrpt_a.get_dim_out()]) - self.dout_r = tf.reshape(self.dout_r, [-1, self.descrpt_r.get_dim_out()]) - self.dout = tf.concat([self.dout_a, self.dout_r], axis = 1) - self.dout = tf.reshape(self.dout, [-1, natoms[0] * self.get_dim_out()]) - - tf.summary.histogram('embedding_net_output', self.dout) - return self.dout - - - def prod_force_virial(self, atom_ener, natoms) : - f_a, v_a, av_a = self.descrpt_a.prod_force_virial(atom_ener, natoms) - f_r, v_r, av_r = self.descrpt_r.prod_force_virial(atom_ener, natoms) - force = f_a + f_r - virial = v_a + v_r - atom_virial = av_a + av_r - tf.summary.histogram('force', force) - tf.summary.histogram('virial', virial) - tf.summary.histogram('atom_virial', atom_virial) - return force, virial, atom_virial - - - - diff --git a/deepmd/descriptor/se_r.py b/deepmd/descriptor/se_r.py index b6de76be76..8b014a2501 100644 --- a/deepmd/descriptor/se_r.py +++ b/deepmd/descriptor/se_r.py @@ -12,6 +12,8 @@ from deepmd.utils.sess import run_sess from .descriptor import Descriptor +@Descriptor.register("se_e2_r") +@Descriptor.register("se_r") class DescrptSeR (Descriptor): """DeepPot-SE constructed from radial information of atomic configurations. diff --git a/deepmd/descriptor/se_t.py b/deepmd/descriptor/se_t.py index 2ab7a732be..5663a33644 100644 --- a/deepmd/descriptor/se_t.py +++ b/deepmd/descriptor/se_t.py @@ -12,6 +12,9 @@ from deepmd.utils.sess import run_sess from .descriptor import Descriptor +@Descriptor.register("se_e3") +@Descriptor.register("se_at") +@Descriptor.register("se_a_3be") class DescrptSeT (Descriptor): """DeepPot-SE constructed from all information (both angular and radial) of atomic configurations. diff --git a/deepmd/train/trainer.py b/deepmd/train/trainer.py index dc888ad3e0..7556cc1ebb 100644 --- a/deepmd/train/trainer.py +++ b/deepmd/train/trainer.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +from deepmd.descriptor.descriptor import Descriptor import logging import os import glob @@ -11,14 +12,7 @@ from deepmd.env import GLOBAL_TF_FLOAT_PRECISION from deepmd.env import GLOBAL_ENER_FLOAT_PRECISION from deepmd.fit import EnerFitting, WFCFitting, PolarFittingLocFrame, PolarFittingSeA, GlobalPolarFittingSeA, DipoleFittingSeA -from deepmd.descriptor import DescrptLocFrame -from deepmd.descriptor import DescrptSeA -from deepmd.descriptor import DescrptSeT -from deepmd.descriptor import DescrptSeAEbd -from deepmd.descriptor import DescrptSeAEf -from deepmd.descriptor import DescrptSeR -from deepmd.descriptor import DescrptSeAR -from deepmd.descriptor import DescrptHybrid +from deepmd.descriptor import Descriptor from deepmd.model import EnerModel, WFCModel, DipoleModel, PolarModel, GlobalPolarModel from deepmd.loss import EnerStdLoss, EnerDipoleLoss, TensorLoss from deepmd.utils.errors import GraphTooLargeError @@ -47,36 +41,6 @@ def _is_subdir(path, directory): return False relative = os.path.relpath(path, directory) + os.sep return not relative.startswith(os.pardir + os.sep) - -def _generate_descrpt_from_param_dict(descrpt_param): - try: - descrpt_type = descrpt_param['type'] - except KeyError: - raise KeyError('the type of descriptor should be set by `type`') - descrpt_param.pop('type', None) - to_pop = [] - for kk in descrpt_param: - if kk[0] == '_': - to_pop.append(kk) - for kk in to_pop: - descrpt_param.pop(kk, None) - if descrpt_type == 'loc_frame': - descrpt = DescrptLocFrame(**descrpt_param) - elif descrpt_type == 'se_e2_a' or descrpt_type == 'se_a' : - descrpt = DescrptSeA(**descrpt_param) - elif descrpt_type == 'se_e2_r' or descrpt_type == 'se_r' : - descrpt = DescrptSeR(**descrpt_param) - elif descrpt_type == 'se_e3' or descrpt_type == 'se_at' or descrpt_type == 'se_a_3be' : - descrpt = DescrptSeT(**descrpt_param) - elif descrpt_type == 'se_a_tpe' or descrpt_type == 'se_a_ebd' : - descrpt = DescrptSeAEbd(**descrpt_param) - elif descrpt_type == 'se_a_ef' : - descrpt = DescrptSeAEf(**descrpt_param) - elif descrpt_type == 'se_ar' : - descrpt = DescrptSeAR(descrpt_param) - else : - raise RuntimeError('unknow model type ' + descrpt_type) - return descrpt class DPTrainer (object): @@ -103,13 +67,7 @@ def _init_param(self, jdata): except KeyError: raise KeyError('the type of descriptor should be set by `type`') - if descrpt_type != 'hybrid': - self.descrpt = _generate_descrpt_from_param_dict(descrpt_param) - else : - descrpt_list = [] - for ii in descrpt_param.get('list', []): - descrpt_list.append(_generate_descrpt_from_param_dict(ii)) - self.descrpt = DescrptHybrid(descrpt_list) + self.descrpt = Descriptor(**descrpt_param) # fitting net fitting_type = fitting_param.get('type', 'ener') diff --git a/deepmd/utils/__init__.py b/deepmd/utils/__init__.py index a54f69b853..e81b474095 100644 --- a/deepmd/utils/__init__.py +++ b/deepmd/utils/__init__.py @@ -7,3 +7,4 @@ from .data_system import DataSystem from .pair_tab import PairTab from .learning_rate import LearningRateExp +from .plugin import Plugin, PluginVariant diff --git a/deepmd/utils/argcheck.py b/deepmd/utils/argcheck.py index 6279b3e088..e5c4d19a7b 100644 --- a/deepmd/utils/argcheck.py +++ b/deepmd/utils/argcheck.py @@ -1,5 +1,9 @@ +from typing import List, Callable + from dargs import dargs, Argument, Variant, ArgumentEncoder +from deepmd import descriptor from deepmd.common import ACTIVATION_FN_DICT, PRECISION_DICT +from deepmd.utils.plugin import Plugin import json @@ -38,6 +42,55 @@ def type_embedding_args(): # --- Descriptor configurations: --- # + +class ArgsPlugin: + def __init__(self) -> None: + self.__plugin = Plugin() + + def register(self, name : str, alias : List[str] = None) -> Callable[[], List[Argument]]: + """Regiester a descriptor argument plugin. + + Parameters + ---------- + name : str + the name of a descriptor + alias : List[str], optional + the list of aliases of this descriptor + + Returns + ------- + Callable[[], List[Argument]] + the regiestered descriptor argument method + + Examples + -------- + >>> some_plugin = ArgsPlugin() + >>> @some_plugin.register("some_descrpt") + def descrpt_some_descrpt_args(): + return [] + """ + # convert alias to hashed item + if isinstance(alias, list): + alias = tuple(alias) + return self.__plugin.register((name, alias)) + + def get_all_argument(self) -> List[Argument]: + """Get all arguments. + + Returns + ------- + List[Argument] + all arguments + """ + arguments = [] + for (name, alias), metd in self.__plugin.plugins.items(): + arguments.append(Argument(name=name, dtype=dict, sub_fields=metd(), alias=alias)) + return arguments + + +descrpt_args_plugin = ArgsPlugin() + +@descrpt_args_plugin.register("local_frame") def descrpt_local_frame_args (): doc_sel_a = 'A list of integers. The length of the list should be the same as the number of atom types in the system. `sel_a[i]` gives the selected number of type-i neighbors. The full relative coordinates of the neighbors are used by the descriptor.' doc_sel_r = 'A list of integers. The length of the list should be the same as the number of atom types in the system. `sel_r[i]` gives the selected number of type-i neighbors. Only relative distance of the neighbors are used by the descriptor. sel_a[i] + sel_r[i] is recommended to be larger than the maximally possible number of type-i neighbors in the cut-off radius.' @@ -58,6 +111,7 @@ def descrpt_local_frame_args (): ] +@descrpt_args_plugin.register("se_e2_a", alias=["se_a"]) def descrpt_se_a_args(): doc_sel = 'This parameter set the number of selected neighbors for each type of atom. It can be:\n\n\ - `List[int]`. The length of the list should be the same as the number of atom types in the system. `sel[i]` gives the selected number of type-i neighbors. `sel[i]` is recommended to be larger than the maximally possible number of type-i neighbors in the cut-off radius. It is noted that the total sel value must be less than 4096 in a GPU environment.\n\n\ @@ -92,6 +146,7 @@ def descrpt_se_a_args(): ] +@descrpt_args_plugin.register("se_e3", alias=['se_at', 'se_a_3be', 'se_t']) def descrpt_se_t_args(): doc_sel = 'This parameter set the number of selected neighbors for each type of atom. It can be:\n\n\ - `List[int]`. The length of the list should be the same as the number of atom types in the system. `sel[i]` gives the selected number of type-i neighbors. `sel[i]` is recommended to be larger than the maximally possible number of type-i neighbors in the cut-off radius. It is noted that the total sel value must be less than 4096 in a GPU environment.\n\n\ @@ -121,6 +176,7 @@ def descrpt_se_t_args(): +@descrpt_args_plugin.register("se_a_tpe", alias=['se_a_ebd']) def descrpt_se_a_tpe_args(): doc_type_nchanl = 'number of channels for type embedding' doc_type_nlayer = 'number of hidden layers of type embedding net' @@ -133,6 +189,7 @@ def descrpt_se_a_tpe_args(): ] +@descrpt_args_plugin.register("se_e2_r", alias=['se_r']) def descrpt_se_r_args(): doc_sel = 'This parameter set the number of selected neighbors for each type of atom. It can be:\n\n\ - `List[int]`. The length of the list should be the same as the number of atom types in the system. `sel[i]` gives the selected number of type-i neighbors. `sel[i]` is recommended to be larger than the maximally possible number of type-i neighbors in the cut-off radius. It is noted that the total sel value must be less than 4096 in a GPU environment.\n\n\ @@ -165,18 +222,7 @@ def descrpt_se_r_args(): ] -def descrpt_se_ar_args(): - link = make_link('se_a', 'model/descriptor[se_a]') - doc_a = f'The parameters of descriptor {link}' - link = make_link('se_r', 'model/descriptor[se_r]') - doc_r = f'The parameters of descriptor {link}' - - return [ - Argument("a", dict, optional = False, doc = doc_a), - Argument("r", dict, optional = False, doc = doc_r), - ] - - +@descrpt_args_plugin.register("hybrid") def descrpt_hybrid_args(): doc_list = f'A list of descriptor definitions' @@ -200,14 +246,7 @@ def descrpt_variant_type_args(): - `se_a_tpe`: Used by the smooth edition of Deep Potential. The full relative coordinates are used to construct the descriptor. Type embedding will be used by this descriptor.\n\n\ - `hybrid`: Concatenate of a list of descriptors as a new descriptor.' - return Variant("type", [ - Argument("loc_frame", dict, descrpt_local_frame_args()), - Argument("se_e2_a", dict, descrpt_se_a_args(), alias = ['se_a']), - Argument("se_e2_r", dict, descrpt_se_r_args(), alias = ['se_r']), - Argument("se_e3", dict, descrpt_se_t_args(), alias = ['se_at', 'se_a_3be', 'se_t']), - Argument("se_a_tpe", dict, descrpt_se_a_tpe_args(), alias = ['se_a_ebd']), - Argument("hybrid", dict, descrpt_hybrid_args()), - ], doc = doc_descrpt_type) + return Variant("type", descrpt_args_plugin.get_all_argument(), doc = doc_descrpt_type) # --- Fitting net configurations: --- # diff --git a/deepmd/utils/plugin.py b/deepmd/utils/plugin.py new file mode 100644 index 0000000000..f195b7808c --- /dev/null +++ b/deepmd/utils/plugin.py @@ -0,0 +1,84 @@ + +"""Base of plugin systems.""" +# copied from https://github.com/deepmodeling/dpdata/blob/a3e76d75de53f6076254de82d18605a010dc3b00/dpdata/plugin.py + +from abc import ABCMeta +from typing import Callable + + +class Plugin: + """A class to register and restore plugins. + + Attributes + ---------- + plugins : Dict[str, object] + plugins + + Examples + -------- + >>> plugin = Plugin() + >>> @plugin.register("xx") + def xxx(): + pass + >>> print(plugin.plugins['xx']) + """ + def __init__(self): + self.plugins = {} + + def __add__(self, other) -> "Plugin": + self.plugins.update(other.plugins) + return self + + def register(self, key : str) -> Callable[[object], object]: + """Register a plugin. + + Parameter + --------- + key : str + key of the plugin + + Returns + ------- + Callable[[object], object] + decorator + """ + def decorator(object : object) -> object: + self.plugins[key] = object + return object + return decorator + + def get_plugin(self, key) -> object: + """Visit a plugin by key. + + Parameters + ---------- + key : str + key of the plugin + + Returns + ------- + object + the plugin + """ + return self.plugins[key] + +class VariantMeta: + def __call__(cls, *args, **kwargs): + """Remove `type` and keys that starts with underline.""" + obj = cls.__new__(cls, *args, **kwargs) + kwargs.pop('type', None) + to_pop = [] + for kk in kwargs: + if kk[0] == '_': + to_pop.append(kk) + for kk in to_pop: + kwargs.pop(kk, None) + obj.__init__(*args, **kwargs) + return obj + +class VariantABCMeta(VariantMeta, ABCMeta): + pass + +class PluginVariant(metaclass=VariantABCMeta): + """A class to remove `type` from input arguments.""" + pass \ No newline at end of file diff --git a/source/tests/test_descrpt_se_ar.py b/source/tests/test_descrpt_se_ar.py deleted file mode 100644 index 9ebb085bab..0000000000 --- a/source/tests/test_descrpt_se_ar.py +++ /dev/null @@ -1,118 +0,0 @@ -import os,sys -import numpy as np -import unittest - -from deepmd.env import tf -from tensorflow.python.framework import ops - -# load grad of force module -import deepmd.op - -from common import force_test -from common import virial_test -from common import force_dw_test -from common import virial_dw_test -from common import Data - -from deepmd.descriptor import DescrptSeAR - -from deepmd.env import GLOBAL_TF_FLOAT_PRECISION -from deepmd.env import GLOBAL_NP_FLOAT_PRECISION -from deepmd.env import GLOBAL_ENER_FLOAT_PRECISION - -class Inter(): - def setUp (self, - data, - sess = None) : - self.sess = sess - self.data = data - self.natoms = self.data.get_natoms() - self.ntypes = self.data.get_ntypes() - param_a = { - 'sel' : [12,24], - 'rcut': 4, - 'rcut_smth' : 3.5, - 'neuron': [5, 10, 20], - 'seed': 1, - } - param_r = { - 'sel' : [20,40], - 'rcut': 6, - 'rcut_smth' : 6.5, - 'neuron': [10, 20, 40], - 'seed': 1, - } - param = {'a': param_a, 'r': param_r} - self.descrpt = DescrptSeAR(param) - self.ndescrpt = self.descrpt.get_dim_out() - # davg = np.zeros ([self.ntypes, self.ndescrpt]) - # dstd = np.ones ([self.ntypes, self.ndescrpt]) - # self.t_avg = tf.constant(davg.astype(np.float64)) - # self.t_std = tf.constant(dstd.astype(np.float64)) - avg_a = np.zeros([self.ntypes, self.descrpt.descrpt_a.ndescrpt]) - std_a = np.ones ([self.ntypes, self.descrpt.descrpt_a.ndescrpt]) - avg_r = np.zeros([self.ntypes, self.descrpt.descrpt_r.ndescrpt]) - std_r = np.ones ([self.ntypes, self.descrpt.descrpt_r.ndescrpt]) - self.avg = [avg_a, avg_r] - self.std = [std_a, std_r] - self.default_mesh = np.zeros (6, dtype = np.int32) - self.default_mesh[3] = 2 - self.default_mesh[4] = 2 - self.default_mesh[5] = 2 - # make place holder - self.coord = tf.placeholder(GLOBAL_TF_FLOAT_PRECISION, [None, self.natoms[0] * 3], name='t_coord') - self.box = tf.placeholder(GLOBAL_TF_FLOAT_PRECISION, [None, 9], name='t_box') - self.type = tf.placeholder(tf.int32, [None, self.natoms[0]], name = "t_type") - self.tnatoms = tf.placeholder(tf.int32, [None], name = "t_natoms") - self.efield = tf.placeholder(GLOBAL_TF_FLOAT_PRECISION, [None, self.natoms[0] * 3], name='t_efield') - - def _net (self, - inputs, - name, - reuse = False) : - with tf.variable_scope(name, reuse=reuse): - net_w = tf.get_variable ('net_w', - [self.descrpt.get_dim_out()], - GLOBAL_TF_FLOAT_PRECISION, - tf.constant_initializer (self.net_w_i)) - dot_v = tf.matmul (tf.reshape (inputs, [-1, self.descrpt.get_dim_out()]), - tf.reshape (net_w, [self.descrpt.get_dim_out(), 1])) - return tf.reshape (dot_v, [-1]) - - def comp_ef (self, - dcoord, - dbox, - dtype, - tnatoms, - name, - reuse = None) : - dout = self.descrpt.build(dcoord, dtype, tnatoms, dbox, self.default_mesh, {"efield": self.efield}, suffix=name, reuse=reuse) - inputs_reshape = tf.reshape (dout, [-1, self.descrpt.get_dim_out()]) - atom_ener = self._net (inputs_reshape, name, reuse = reuse) - atom_ener_reshape = tf.reshape(atom_ener, [-1, self.natoms[0]]) - energy = tf.reduce_sum (atom_ener_reshape, axis = 1) - force, virial, av = self.descrpt.prod_force_virial(atom_ener_reshape, tnatoms) - return energy, force, virial - - -class TestDescrptAR(Inter, tf.test.TestCase): - # def __init__ (self, *args, **kwargs): - # data = Data() - # Inter.__init__(self, data) - # tf.test.TestCase.__init__(self, *args, **kwargs) - # self.controller = object() - - def setUp(self): - self.places = 5 - data = Data() - Inter.setUp(self, data, sess=self.test_session().__enter__()) - - def test_force (self) : - force_test(self, self, suffix = '_se_ar') - - def test_virial (self) : - virial_test(self, self, suffix = '_se_ar') - - -if __name__ == '__main__': - unittest.main() From 298b12a28991506abdb5a69ccd10822d571a5764 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sat, 11 Sep 2021 22:15:18 -0400 Subject: [PATCH 063/161] support lammps 20210831 (#1129) * support lammps 20210831 * fix typo Co-authored-by: Han Wang Co-authored-by: Han Wang --- source/lmp/pppm_dplr.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/source/lmp/pppm_dplr.cpp b/source/lmp/pppm_dplr.cpp index 0fda38e4f3..dc3782e058 100644 --- a/source/lmp/pppm_dplr.cpp +++ b/source/lmp/pppm_dplr.cpp @@ -109,8 +109,13 @@ void PPPMDPLR::compute(int eflag, int vflag) // to fully sum contribution in their 3d bricks // remap from 3d decomposition to FFT decomposition +#if LAMMPS_VERSION_NUMBER>=20210831 + gc->reverse_comm(GridComm::KSPACE,this,1,sizeof(FFT_SCALAR),REVERSE_RHO, + gc_buf1,gc_buf2,MPI_FFT_SCALAR); +#else gc->reverse_comm_kspace(this,1,sizeof(FFT_SCALAR),REVERSE_RHO, gc_buf1,gc_buf2,MPI_FFT_SCALAR); +#endif brick2fft(); // compute potential gradient on my FFT grid and @@ -124,21 +129,41 @@ void PPPMDPLR::compute(int eflag, int vflag) // to fill ghost cells surrounding their 3d bricks if (differentiation_flag == 1) +#if LAMMPS_VERSION_NUMBER>=20210831 + gc->forward_comm(GridComm::KSPACE,this,1,sizeof(FFT_SCALAR),FORWARD_AD, + gc_buf1,gc_buf2,MPI_FFT_SCALAR); +#else gc->forward_comm_kspace(this,1,sizeof(FFT_SCALAR),FORWARD_AD, gc_buf1,gc_buf2,MPI_FFT_SCALAR); +#endif else +#if LAMMPS_VERSION_NUMBER>=20210831 + gc->forward_comm(GridComm::KSPACE,this,1,sizeof(FFT_SCALAR),FORWARD_IK, + gc_buf1,gc_buf2,MPI_FFT_SCALAR); +#else gc->forward_comm_kspace(this,3,sizeof(FFT_SCALAR),FORWARD_IK, gc_buf1,gc_buf2,MPI_FFT_SCALAR); +#endif // extra per-atom energy/virial communication if (evflag_atom) { if (differentiation_flag == 1 && vflag_atom) +#if LAMMPS_VERSION_NUMBER>=20210831 + gc->forward_comm(GridComm::KSPACE,this,6,sizeof(FFT_SCALAR),FORWARD_AD_PERATOM, + gc_buf1,gc_buf2,MPI_FFT_SCALAR); +#else gc->forward_comm_kspace(this,6,sizeof(FFT_SCALAR),FORWARD_AD_PERATOM, gc_buf1,gc_buf2,MPI_FFT_SCALAR); +#endif else if (differentiation_flag == 0) +#if LAMMPS_VERSION_NUMBER>=20210831 + gc->forward_comm(GridComm::KSPACE,this,7,sizeof(FFT_SCALAR),FORWARD_IK_PERATOM, + gc_buf1,gc_buf2,MPI_FFT_SCALAR); +#else gc->forward_comm_kspace(this,7,sizeof(FFT_SCALAR),FORWARD_IK_PERATOM, gc_buf1,gc_buf2,MPI_FFT_SCALAR); +#endif } // calculate the force on my particles From 823cb147dfa987b64c8432feb7a2d6e6397484d2 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sat, 11 Sep 2021 23:52:39 -0400 Subject: [PATCH 064/161] fix a typo in Eq. of se_a (#1131) This commit fixes a typo in Eq. of se_a. --- deepmd/descriptor/se_a.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deepmd/descriptor/se_a.py b/deepmd/descriptor/se_a.py index 893c152382..b3db13f019 100644 --- a/deepmd/descriptor/se_a.py +++ b/deepmd/descriptor/se_a.py @@ -33,7 +33,7 @@ class DescrptSeA (Descriptor): .. math:: (\mathcal{R}^i)_j = [ \begin{array}{c} - s(r_{ji}) & x_{ji} & y_{ji} & z_{ji} + s(r_{ji}) & \frac{s(r_{ji})x_{ji}}{r_{ji}} & \frac{s(r_{ji})y_{ji}}{r_{ji}} & \frac{s(r_{ji})z_{ji}}{r_{ji}} \end{array} ] From 76bc05c62e328beecdd6f217d9e506bd9e7fbadb Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sun, 12 Sep 2021 23:11:01 -0400 Subject: [PATCH 065/161] use https for mirror_gitee; do not run on forks (#1130) --- .github/workflows/mirror_gitee.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/mirror_gitee.yml b/.github/workflows/mirror_gitee.yml index 4cb2c4f620..2d090c0539 100644 --- a/.github/workflows/mirror_gitee.yml +++ b/.github/workflows/mirror_gitee.yml @@ -8,11 +8,12 @@ concurrency: jobs: git-mirror: + if: github.repository_owner == 'deepmodeling' runs-on: ubuntu-latest steps: - uses: wearerequired/git-mirror-action@v1 env: SSH_PRIVATE_KEY: ${{ secrets.SYNC_GITEE_PRIVATE_KEY }} with: - source-repo: "git@github.com:deepmodeling/deepmd-kit.git" + source-repo: "https://github.com/deepmodeling/deepmd-kit.git" destination-repo: "git@gitee.com:deepmodeling/deepmd-kit.git" From df10d84e3dcb4c9bc4702c53a3cdccb8849266f1 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 13 Sep 2021 20:03:36 -0400 Subject: [PATCH 066/161] fix a typo in #1118 (#1142) This fixes a typo in #1118. --- deepmd/utils/argcheck.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deepmd/utils/argcheck.py b/deepmd/utils/argcheck.py index e5c4d19a7b..8e55187574 100644 --- a/deepmd/utils/argcheck.py +++ b/deepmd/utils/argcheck.py @@ -90,7 +90,7 @@ def get_all_argument(self) -> List[Argument]: descrpt_args_plugin = ArgsPlugin() -@descrpt_args_plugin.register("local_frame") +@descrpt_args_plugin.register("loc_frame") def descrpt_local_frame_args (): doc_sel_a = 'A list of integers. The length of the list should be the same as the number of atom types in the system. `sel_a[i]` gives the selected number of type-i neighbors. The full relative coordinates of the neighbors are used by the descriptor.' doc_sel_r = 'A list of integers. The length of the list should be the same as the number of atom types in the system. `sel_r[i]` gives the selected number of type-i neighbors. Only relative distance of the neighbors are used by the descriptor. sel_a[i] + sel_r[i] is recommended to be larger than the maximally possible number of type-i neighbors in the cut-off radius.' From dae40681e0fd6c5702a571f7bd4a3245f0f166a1 Mon Sep 17 00:00:00 2001 From: Denghui Lu Date: Tue, 14 Sep 2021 08:05:59 +0800 Subject: [PATCH 067/161] Add init-frz-model command support to the frozen model generated by this command (#1137) --- deepmd/model/ener.py | 2 +- deepmd/model/tensor.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deepmd/model/ener.py b/deepmd/model/ener.py index 441709caff..c179f00f7b 100644 --- a/deepmd/model/ener.py +++ b/deepmd/model/ener.py @@ -270,4 +270,4 @@ def build (self, def _import_graph_def_from_frz_model(self, frz_model, feed_dict, return_elements): graph, graph_def = load_graph_def(frz_model) - return tf.import_graph_def(graph_def, input_map = feed_dict, return_elements = return_elements) \ No newline at end of file + return tf.import_graph_def(graph_def, input_map = feed_dict, return_elements = return_elements, name = "") \ No newline at end of file diff --git a/deepmd/model/tensor.py b/deepmd/model/tensor.py index 2a63eda4d6..17b9ace29d 100644 --- a/deepmd/model/tensor.py +++ b/deepmd/model/tensor.py @@ -191,7 +191,7 @@ def build (self, def _import_graph_def_from_frz_model(self, frz_model, feed_dict, return_elements): graph, graph_def = load_graph_def(frz_model) - return tf.import_graph_def(graph_def, input_map = feed_dict, return_elements = return_elements) + return tf.import_graph_def(graph_def, input_map = feed_dict, return_elements = return_elements, name = "") class WFCModel(TensorModel): def __init__( From f0e731362726cc881c7902197588414a6c097c0c Mon Sep 17 00:00:00 2001 From: Denghui Lu Date: Tue, 14 Sep 2021 08:06:53 +0800 Subject: [PATCH 068/161] Add error message for repeated model compression (#1136) * add error message for replicated model compression * fix typo --- deepmd/entrypoints/compress.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/deepmd/entrypoints/compress.py b/deepmd/entrypoints/compress.py index 58c3c2a075..b03e8cf653 100644 --- a/deepmd/entrypoints/compress.py +++ b/deepmd/entrypoints/compress.py @@ -87,6 +87,8 @@ def compress( jdata = j_loader(training_script) t_min_nbor_dist = get_min_nbor_dist(jdata, get_rcut(jdata)) + _check_compress_type(input) + tf.constant(t_min_nbor_dist, name = 'train_attr/min_nbor_dist', dtype = GLOBAL_ENER_FLOAT_PRECISION) @@ -137,3 +139,13 @@ def compress( log.info("\n\n") log.info("stage 2: freeze the model") freeze(checkpoint_folder=checkpoint_folder, output=output, node_names=None) + +def _check_compress_type(model_file): + try: + t_model_type = bytes.decode(get_tensor_by_name(model_file, 'model_type')) + except GraphWithoutTensorError as e: + # Compatible with the upgraded model, which has no 'model_type' info + t_model_type = None + + if t_model_type == "compressed_model": + raise RuntimeError("The input frozen model %s has already been compressed! Please do not compress the model repeatedly. " % model_file) \ No newline at end of file From b0328785b5dadbba8250e19d37a71b128e5000a0 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 13 Sep 2021 21:23:57 -0400 Subject: [PATCH 069/161] add doc: create a model (#1143) * add doc: create a model * use another reference style --- doc/development/create-a-model.md | 71 +++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 doc/development/create-a-model.md diff --git a/doc/development/create-a-model.md b/doc/development/create-a-model.md new file mode 100644 index 0000000000..b3eeffee30 --- /dev/null +++ b/doc/development/create-a-model.md @@ -0,0 +1,71 @@ +# Create a model + +If you'd like to create a new model that isn't covered by the existing DeePMD-kit library, but reuse DeePMD-kit's other efficient module such as data processing, trainner, etc, you may want to read this section. + +To incorporate your custom model you'll need to: +1. Register and implement new components (e.g. descriptor) in a Python file. You may also want to regiester new TensorFlow OPs if necessary. +2. Register new arguments for user inputs. +3. Package new codes into a Python package. +4. Test new models. + +## Design a new component + +When creating a new component, take descriptor as the example, you should inherit {py:class}`deepmd.descriptor.descriptor.Descriptor` class and override several methods. Abstract methods such as {py:class}`deepmd.descriptor.descriptor.Descriptor.build` must be implemented and others are not. You should keep arguments of these methods unchanged. + +After implementation, you need to register the component with a key: +```py +from deepmd.descriptor import Descriptor + +@Descriptor.register("some_descrpt") +class SomeDescript(Descriptor): + def __init__(self, arg1: bool, arg2: float) -> None: + pass +``` + +## Register new arguments + +To let some one uses your new component in their input file, you need to create a new methods that returns some `Argument` of your new component, and then register new arguments. For example, the code below + +```py +from typing import List + +from dargs import Argument +from deepmd.utils.argcheck import descrpt_args_plugin + +@descrpt_args_plugin.register("some_descrpt") +def descrpt_some_args() -> List[Argument]: + return [ + Argument("arg1", bool, optional=False, doc="balabala"), + Argument("arg2", float, optional=True, default=6.0, doc="haha"), + ] +``` + +allows one to use your new descriptor as below: + +```json +"descriptor" :{ + "type": "some_descrpt", + "arg1": true, + "arg2": 6.0 +} +``` + +The arguments here should be consistent with the class arguments of your new componenet. + +## Package new codes + +You may use `setuptools` to package new codes into a new Python package. It's crirical to add your new component to `entry_points['deepmd']` in `setup.py`: + +```py + entry_points={ + 'deepmd': [ + 'some_descrpt=deepmd_some_descrtpt:SomeDescript', + ], + }, +``` + +where `deepmd_some_descrtpt` is the module of your codes. It is equivalent to `from deepmd_some_descrtpt import SomeDescript`. + +If you place `SomeDescript` and `descrpt_some_args` into different modules, you are also expected to add `descrpt_some_args` to `entry_points`. + +After you install your new package, you can now use `dp train` to run your new model. From 37f4c5d092094e5f911998791377062fcf8ea9c7 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Tue, 14 Sep 2021 19:37:04 -0400 Subject: [PATCH 070/161] bump doxygen and sphinx version on RTD (#1141) * bump doxygen and sphinx version on RTD This fixes #847. The idea comes fom https://github.com/readthedocs/readthedocs.org/issues/8151#issuecomment-890359661. * use version 2 * add other dependencies * fix error * fix path * got a memory error... * 3.9 is not supported * Revert "3.9 is not supported" This reverts commit 3732e621118e3a2ed1cbf395ced142232b5773b2. * Revert "got a memory error..." This reverts commit cb596f10f1f30939878975d7da7768a645540903. --- .readthedocs.yml | 3 +++ doc/environment.yml | 10 ++++++++++ setup.py | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 .readthedocs.yml create mode 100644 doc/environment.yml diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000000..a105ddb2b2 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,3 @@ +version: 2 +conda: + environment: doc/environment.yml \ No newline at end of file diff --git a/doc/environment.yml b/doc/environment.yml new file mode 100644 index 0000000000..f3928e4015 --- /dev/null +++ b/doc/environment.yml @@ -0,0 +1,10 @@ +name: RTD +channels: + - conda-forge + - defaults +dependencies: + - doxygen>=1.9.1 + - python=3.9 + - pip>=20.1 + - pip: + - ..[docs,cpu] diff --git a/setup.py b/setup.py index 363cd0015f..d46703e463 100644 --- a/setup.py +++ b/setup.py @@ -127,7 +127,7 @@ cmake_minimum_required_version="3.0", extras_require={ "test": ["dpdata>=0.1.9", "ase", "pytest", "pytest-cov", "pytest-sugar"], - "docs": ["sphinx>=3.1.1,<4.1.0", "recommonmark", "sphinx_rtd_theme>=1.0.0rc1", "sphinx_markdown_tables", "myst-parser", "breathe", "exhale", "numpydoc", "ase"], + "docs": ["sphinx>=3.1.1", "recommonmark", "sphinx_rtd_theme>=1.0.0rc1", "sphinx_markdown_tables", "myst-parser", "breathe", "exhale", "numpydoc", "ase"], **extras_require, }, entry_points={"console_scripts": ["dp = deepmd.entrypoints.main:main"]}, From 424970e4e975fba19c8e3470673e15fb1d57ce55 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Tue, 14 Sep 2021 19:43:18 -0400 Subject: [PATCH 071/161] support init-frz-model for se_r and se_t (#1144) * support init-frz-model for se_r and se_t I just copied several codes from se_a, which can also be applied to se_r and se_t. * add a base class for smooth descriptors * clean other changes * modify `embedding_net_pattern` to support se_e3 * fix typo in `embedding_net_pattern` --- deepmd/descriptor/se.py | 108 ++++++++++++++++++++++++++++++++++++++ deepmd/descriptor/se_a.py | 65 ++--------------------- deepmd/descriptor/se_r.py | 14 ++--- deepmd/descriptor/se_t.py | 13 ++--- deepmd/utils/graph.py | 2 +- 5 files changed, 127 insertions(+), 75 deletions(-) create mode 100644 deepmd/descriptor/se.py diff --git a/deepmd/descriptor/se.py b/deepmd/descriptor/se.py new file mode 100644 index 0000000000..72bf908ecb --- /dev/null +++ b/deepmd/descriptor/se.py @@ -0,0 +1,108 @@ +from typing import Tuple, List + +from deepmd.env import tf +from deepmd.utils.graph import get_embedding_net_variables +from .descriptor import Descriptor + + +class DescrptSe (Descriptor): + """A base class for smooth version of descriptors. + + Notes + ----- + All of these descriptors have an environmental matrix and an + embedding network (:meth:`deepmd.utils.network.embedding_net`), so + they can share some similiar methods without defining them twice. + + Attributes + ---------- + embedding_net_variables : dict + initial embedding network variables + descrpt_reshape : tf.Tensor + the reshaped descriptor + descrpt_deriv : tf.Tensor + the descriptor derivative + rij : tf.Tensor + distances between two atoms + nlist : tf.Tensor + the neighbor list + + """ + def _identity_tensors(self, suffix : str = "") -> None: + """Identify tensors which are expected to be stored and restored. + + Notes + ----- + These tensors will be indentitied: + self.descrpt_reshape : o_rmat + self.descrpt_deriv : o_rmat_deriv + self.rij : o_rij + self.nlist : o_nlist + Thus, this method should be called during building the descriptor and + after these tensors are initialized. + + Parameters + ---------- + suffix : str + The suffix of the scope + """ + self.descrpt_reshape = tf.identity(self.descrpt_reshape, name = 'o_rmat' + suffix) + self.descrpt_deriv = tf.identity(self.descrpt_deriv, name = 'o_rmat_deriv' + suffix) + self.rij = tf.identity(self.rij, name = 'o_rij' + suffix) + self.nlist = tf.identity(self.nlist, name = 'o_nlist' + suffix) + + def get_tensor_names(self, suffix : str = "") -> Tuple[str]: + """Get names of tensors. + + Parameters + ---------- + suffix : str + The suffix of the scope + + Returns + ------- + Tuple[str] + Names of tensors + """ + return (f'o_rmat{suffix}:0', f'o_rmat_deriv{suffix}:0', f'o_rij{suffix}:0', f'o_nlist{suffix}:0') + + def pass_tensors_from_frz_model(self, + descrpt_reshape : tf.Tensor, + descrpt_deriv : tf.Tensor, + rij : tf.Tensor, + nlist : tf.Tensor + ): + """ + Pass the descrpt_reshape tensor as well as descrpt_deriv tensor from the frz graph_def + + Parameters + ---------- + descrpt_reshape + The passed descrpt_reshape tensor + descrpt_deriv + The passed descrpt_deriv tensor + rij + The passed rij tensor + nlist + The passed nlist tensor + """ + self.rij = rij + self.nlist = nlist + self.descrpt_deriv = descrpt_deriv + self.descrpt_reshape = descrpt_reshape + + def init_variables(self, + model_file : str, + suffix : str = "", + ) -> None: + """ + Init the embedding net variables with the given dict + + Parameters + ---------- + model_file : str + The input frozen model file + suffix : str, optional + The suffix of the scope + """ + self.embedding_net_variables = get_embedding_net_variables(model_file, suffix = suffix) diff --git a/deepmd/descriptor/se_a.py b/deepmd/descriptor/se_a.py index b3db13f019..8881ac3a14 100644 --- a/deepmd/descriptor/se_a.py +++ b/deepmd/descriptor/se_a.py @@ -15,10 +15,11 @@ from deepmd.utils.sess import run_sess from deepmd.utils.graph import load_graph_def, get_tensor_by_name_from_graph, get_embedding_net_variables from .descriptor import Descriptor +from .se import DescrptSe @Descriptor.register("se_e2_a") @Descriptor.register("se_a") -class DescrptSeA (Descriptor): +class DescrptSeA (DescrptSe): r"""DeepPot-SE constructed from all information (both angular and radial) of atomic configurations. The embedding takes the distance between atoms as input. @@ -435,10 +436,7 @@ def build (self, tf.summary.histogram('nlist', self.nlist) self.descrpt_reshape = tf.reshape(self.descrpt, [-1, self.ndescrpt]) - self.descrpt_reshape = tf.identity(self.descrpt_reshape, name = 'o_rmat' + suffix) - self.descrpt_deriv = tf.identity(self.descrpt_deriv, name = 'o_rmat_deriv' + suffix) - self.rij = tf.identity(self.rij, name = 'o_rij' + suffix) - self.nlist = tf.identity(self.nlist, name = 'o_nlist' + suffix) + self._identity_tensors(suffix=suffix) self.dout, self.qmat = self._pass_filter(self.descrpt_reshape, atype, @@ -458,63 +456,6 @@ def get_rot_mat(self) -> tf.Tensor: """ return self.qmat - def get_tensor_names(self, suffix : str = "") -> Tuple[str]: - """Get names of tensors. - - Parameters - ---------- - suffix : str - The suffix of the scope - - Returns - ------- - Tuple[str] - Names of tensors - """ - return (f'o_rmat{suffix}:0', f'o_rmat_deriv{suffix}:0', f'o_rij{suffix}:0', f'o_nlist{suffix}:0') - - def pass_tensors_from_frz_model(self, - descrpt_reshape : tf.Tensor, - descrpt_deriv : tf.Tensor, - rij : tf.Tensor, - nlist : tf.Tensor - ): - """ - Pass the descrpt_reshape tensor as well as descrpt_deriv tensor from the frz graph_def - - Parameters - ---------- - descrpt_reshape - The passed descrpt_reshape tensor - descrpt_deriv - The passed descrpt_deriv tensor - rij - The passed rij tensor - nlist - The passed nlist tensor - """ - self.rij = rij - self.nlist = nlist - self.descrpt_deriv = descrpt_deriv - self.descrpt_reshape = descrpt_reshape - - def init_variables(self, - model_file : str, - suffix : str = "", - ) -> None: - """ - Init the embedding net variables with the given dict - - Parameters - ---------- - model_file : str - The input frozen model file - suffix : str, optional - The suffix of the scope - """ - self.embedding_net_variables = get_embedding_net_variables(model_file, suffix = suffix) - - def prod_force_virial(self, atom_ener : tf.Tensor, natoms : tf.Tensor diff --git a/deepmd/descriptor/se_r.py b/deepmd/descriptor/se_r.py index 8b014a2501..4d4a39074b 100644 --- a/deepmd/descriptor/se_r.py +++ b/deepmd/descriptor/se_r.py @@ -11,10 +11,12 @@ from deepmd.utils.network import embedding_net, embedding_net_rand_seed_shift from deepmd.utils.sess import run_sess from .descriptor import Descriptor +from .se import DescrptSe + @Descriptor.register("se_e2_r") @Descriptor.register("se_r") -class DescrptSeR (Descriptor): +class DescrptSeR (DescrptSe): """DeepPot-SE constructed from radial information of atomic configurations. The embedding takes the distance between atoms as input. @@ -114,6 +116,7 @@ def __init__ (self, self.useBN = False self.davg = None self.dstd = None + self.embedding_net_variables = None self.place_holders = {} avg_zero = np.zeros([self.ntypes,self.ndescrpt]).astype(GLOBAL_NP_FLOAT_PRECISION) @@ -312,10 +315,7 @@ def build (self, sel = self.sel_r) self.descrpt_reshape = tf.reshape(self.descrpt, [-1, self.ndescrpt]) - self.descrpt_reshape = tf.identity(self.descrpt_reshape, name = 'o_rmat') - self.descrpt_deriv = tf.identity(self.descrpt_deriv, name = 'o_rmat_deriv') - self.rij = tf.identity(self.rij, name = 'o_rij') - self.nlist = tf.identity(self.nlist, name = 'o_nlist') + self._identity_tensors(suffix=suffix) # only used when tensorboard was set as true tf.summary.histogram('descrpt', self.descrpt) @@ -485,7 +485,9 @@ def _filter_r(self, bavg = bavg, seed = self.seed, trainable = trainable, - uniform_seed = self.uniform_seed) + uniform_seed = self.uniform_seed, + initial_variables = self.embedding_net_variables, + ) if (not self.uniform_seed) and (self.seed is not None): self.seed += self.seed_shift # natom x nei_type_i x out_size xyz_scatter = tf.reshape(xyz_scatter, (-1, shape_i[1], outputs_size[-1])) diff --git a/deepmd/descriptor/se_t.py b/deepmd/descriptor/se_t.py index 5663a33644..f43fb6f40c 100644 --- a/deepmd/descriptor/se_t.py +++ b/deepmd/descriptor/se_t.py @@ -11,11 +11,12 @@ from deepmd.utils.network import embedding_net, embedding_net_rand_seed_shift from deepmd.utils.sess import run_sess from .descriptor import Descriptor +from .se import DescrptSe @Descriptor.register("se_e3") @Descriptor.register("se_at") @Descriptor.register("se_a_3be") -class DescrptSeT (Descriptor): +class DescrptSeT (DescrptSe): """DeepPot-SE constructed from all information (both angular and radial) of atomic configurations. @@ -97,6 +98,7 @@ def __init__ (self, self.useBN = False self.dstd = None self.davg = None + self.embedding_net_variables = None self.place_holders = {} avg_zero = np.zeros([self.ntypes,self.ndescrpt]).astype(GLOBAL_NP_FLOAT_PRECISION) @@ -311,10 +313,7 @@ def build (self, sel_r = self.sel_r) self.descrpt_reshape = tf.reshape(self.descrpt, [-1, self.ndescrpt]) - self.descrpt_reshape = tf.identity(self.descrpt_reshape, name = 'o_rmat') - self.descrpt_deriv = tf.identity(self.descrpt_deriv, name = 'o_rmat_deriv') - self.rij = tf.identity(self.rij, name = 'o_rij') - self.nlist = tf.identity(self.nlist, name = 'o_nlist') + self._identity_tensors(suffix=suffix) self.dout, self.qmat = self._pass_filter(self.descrpt_reshape, atype, @@ -509,7 +508,9 @@ def _filter(self, bavg = bavg, seed = self.seed, trainable = trainable, - uniform_seed = self.uniform_seed) + uniform_seed = self.uniform_seed, + initial_variables = self.embedding_net_variables, + ) if (not self.uniform_seed) and (self.seed is not None): self.seed += self.seed_shift # with natom x nei_type_i x nei_type_j x out_size ebd_env_ij = tf.reshape(ebd_env_ij, [-1, nei_type_i, nei_type_j, outputs_size[-1]]) diff --git a/deepmd/utils/graph.py b/deepmd/utils/graph.py index 53fd05cc67..6766750e4b 100644 --- a/deepmd/utils/graph.py +++ b/deepmd/utils/graph.py @@ -129,7 +129,7 @@ def get_embedding_net_nodes_from_graph_def(graph_def: tf.GraphDef, suffix: str = The embedding net nodes within the given tf.GraphDef object """ embedding_net_nodes = {} - embedding_net_pattern = f"filter_type_\d+{suffix}/matrix_\d+_\d+|filter_type_\d+{suffix}/bias_\d+_\d+|filter_type_\d+{suffix}/idt_\d+_\d+|filter_type_all{suffix}/matrix_\d+_\d+|filter_type_all{suffix}/bias_\d+_\d+|filter_type_all{suffix}/idt_\d+_\d" + embedding_net_pattern = f"filter_type_\d+{suffix}/matrix_\d+_\d+|filter_type_\d+{suffix}/bias_\d+_\d+|filter_type_\d+{suffix}/idt_\d+_\d+|filter_type_all{suffix}/matrix_\d+_\d+|filter_type_all{suffix}/matrix_\d+_\d+_\d+|filter_type_all{suffix}/bias_\d+_\d+|filter_type_all{suffix}/bias_\d+_\d+_\d+|filter_type_all{suffix}/idt_\d+_\d+" for node in graph_def.node: if re.fullmatch(embedding_net_pattern, node.name) != None: embedding_net_nodes[node.name] = node.attr["value"].tensor From 0a466417be1f10804e5604d5c300cc0a50a94db6 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Wed, 15 Sep 2021 19:53:06 -0400 Subject: [PATCH 072/161] move horovod installation to installation part (#1147) as now easy installation also contains horovod, so we may want to discuss separately --- doc/install/easy-install.md | 6 +++--- doc/install/install-from-source.md | 16 ++++++++++++++++ doc/train/parallel-training.md | 13 ------------- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/doc/install/easy-install.md b/doc/install/easy-install.md index cb529acda3..55720b59e4 100644 --- a/doc/install/easy-install.md +++ b/doc/install/easy-install.md @@ -2,7 +2,7 @@ There various easy methods to install DeePMD-kit. Choose one that you prefer. If you want to build by yourself, jump to the next two sections. -After your easy installation, DeePMD-kit (`dp`) and LAMMPS (`lmp`) will be available to execute. You can try `dp -h` and `lmp -h` to see the help. `mpirun` is also available considering you may want to run LAMMPS in parallel. +After your easy installation, DeePMD-kit (`dp`) and LAMMPS (`lmp`) will be available to execute. You can try `dp -h` and `lmp -h` to see the help. `mpirun` is also available considering you may want to train models or run LAMMPS in parallel. - [Install off-line packages](#install-off-line-packages) - [Install with conda](#install-with-conda) @@ -27,13 +27,13 @@ conda create -n deepmd deepmd-kit=*=*cpu libdeepmd=*=*cpu lammps-dp -c https://c Or one may want to create a GPU environment containing [CUDA Toolkit](https://docs.nvidia.com/deploy/cuda-compatibility/index.html#binary-compatibility__table-toolkit-driver): ```bash -conda create -n deepmd deepmd-kit=*=*gpu libdeepmd=*=*gpu lammps-dp cudatoolkit=11.3 -c https://conda.deepmodeling.org +conda create -n deepmd deepmd-kit=*=*gpu libdeepmd=*=*gpu lammps-dp cudatoolkit=11.3 horovod -c https://conda.deepmodeling.org ``` One could change the CUDA Toolkit version from `10.1` or `11.3`. One may speficy the DeePMD-kit version such as `2.0.0` using ```bash -conda create -n deepmd deepmd-kit=2.0.0=*cpu libdeepmd=2.0.0=*cpu lammps-dp=2.0.0 -c https://conda.deepmodeling.org +conda create -n deepmd deepmd-kit=2.0.0=*cpu libdeepmd=2.0.0=*cpu lammps-dp=2.0.0 horovod -c https://conda.deepmodeling.org ``` One may enable the environment using diff --git a/doc/install/install-from-source.md b/doc/install/install-from-source.md index b0e6f468b1..7f69427517 100644 --- a/doc/install/install-from-source.md +++ b/doc/install/install-from-source.md @@ -92,6 +92,22 @@ Valid subcommands: test test the model ``` +### Install horovod and mpi4py + +[Horovod](https://github.com/horovod/horovod) and [mpi4py](https://github.com/mpi4py/mpi4py) is used for parallel training. For better performance on GPU, please follow tuning steps in [Horovod on GPU](https://github.com/horovod/horovod/blob/master/docs/gpus.rst). +```bash +# With GPU, prefer NCCL as communicator. +HOROVOD_WITHOUT_GLOO=1 HOROVOD_WITH_TENSORFLOW=1 HOROVOD_GPU_OPERATIONS=NCCL HOROVOD_NCCL_HOME=/path/to/nccl pip install horovod mpi4py +``` + +If your work in CPU environment, please prepare runtime as below: +```bash +# By default, MPI is used as communicator. +HOROVOD_WITHOUT_GLOO=1 HOROVOD_WITH_TENSORFLOW=1 pip install horovod mpi4py +``` + +If you don't install horovod, DeePMD-kit will fallback to serial mode. + ## Install the C++ interface If one does not need to use DeePMD-kit with Lammps or I-Pi, then the python interface installed in the previous section does everything and he/she can safely skip this section. diff --git a/doc/train/parallel-training.md b/doc/train/parallel-training.md index d619569c8d..5609468a76 100644 --- a/doc/train/parallel-training.md +++ b/doc/train/parallel-training.md @@ -10,21 +10,8 @@ Testing `examples/water/se_e2_a` on a 8-GPU host, linear acceleration can be obs | 4 | 1.7635 | 56.71*4 | 3.29 | | 8 | 1.7267 | 57.91*8 | 6.72 | -To experience this powerful feature, please intall Horovod and [mpi4py](https://github.com/mpi4py/mpi4py) first. For better performance on GPU, please follow tuning steps in [Horovod on GPU](https://github.com/horovod/horovod/blob/master/docs/gpus.rst). -```bash -# With GPU, prefer NCCL as communicator. -HOROVOD_WITHOUT_GLOO=1 HOROVOD_WITH_TENSORFLOW=1 HOROVOD_GPU_OPERATIONS=NCCL HOROVOD_NCCL_HOME=/path/to/nccl pip3 install horovod mpi4py -``` - -If your work in CPU environment, please prepare runtime as below: -```bash -# By default, MPI is used as communicator. -HOROVOD_WITHOUT_GLOO=1 HOROVOD_WITH_TENSORFLOW=1 pip install horovod mpi4py -``` - Horovod works in the data-parallel mode resulting a larger global batch size. For example, the real batch size is 8 when `batch_size` is set to 2 in the input file and you lauch 4 workers. Thus, `learning_rate` is automatically scaled by the number of workers for better convergence. Technical details of such heuristic rule are discussed at [Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour](https://arxiv.org/abs/1706.02677). -With dependencies installed, have a quick try! ```bash # Launch 4 processes on the same host CUDA_VISIBLE_DEVICES=4,5,6,7 horovodrun -np 4 \ From 57dd29bde6b1ca3409514707b35131da636ec374 Mon Sep 17 00:00:00 2001 From: Chenxing Luo Date: Fri, 17 Sep 2021 02:25:47 -0400 Subject: [PATCH 073/161] Update parallel-training.md (#1156) * Update parallel-training.md * Fix typo * Update parallel training description Trying to be more specific in the parallel training description * Update parallel-training.md --- doc/install/install-from-source.md | 26 ++++++++++++++++++ doc/train/parallel-training.md | 44 ++++++++++++++++++++++++++---- 2 files changed, 65 insertions(+), 5 deletions(-) diff --git a/doc/install/install-from-source.md b/doc/install/install-from-source.md index 7f69427517..ab6280441d 100644 --- a/doc/install/install-from-source.md +++ b/doc/install/install-from-source.md @@ -106,6 +106,32 @@ If your work in CPU environment, please prepare runtime as below: HOROVOD_WITHOUT_GLOO=1 HOROVOD_WITH_TENSORFLOW=1 pip install horovod mpi4py ``` +To ensure Horovod has been built with proper framework support enabled, one can invoke the `horovodrun --check-build` command, e.g., + +```bash +$ horovodrun --check-build + +Horovod v0.22.1: + +Available Frameworks: + [X] TensorFlow + [X] PyTorch + [ ] MXNet + +Available Controllers: + [X] MPI + [X] Gloo + +Available Tensor Operations: + [X] NCCL + [ ] DDL + [ ] CCL + [X] MPI + [X] Gloo +``` + +From version 2.0.1, Horovod and mpi4py with MPICH support is shipped with the installer. + If you don't install horovod, DeePMD-kit will fallback to serial mode. ## Install the C++ interface diff --git a/doc/train/parallel-training.md b/doc/train/parallel-training.md index 5609468a76..bd1bdb889a 100644 --- a/doc/train/parallel-training.md +++ b/doc/train/parallel-training.md @@ -1,8 +1,16 @@ # Parallel training -Currently, parallel training is enabled in a sychoronized way with help of [Horovod](https://github.com/horovod/horovod). DeePMD-kit will decide parallel training or not according to MPI context. Thus, there is no difference in your json/yaml input file. +Currently, parallel training is enabled in a sychoronized way with help of [Horovod](https://github.com/horovod/horovod). +Depend on the number of training processes (according to MPI context) and number of GPU cards avaliable, DeePMD-kit will decide whether to launch the training in parallel (distributed) mode or in serial mode. Therefore, no additional options is specified in your JSON/YAML input file. + +Horovod works in the data-parallel mode, resulting in a larger global batch size. For example, the real batch size is 8 when `batch_size` is set to 2 in the input file and you launch 4 workers. Thus, `learning_rate` is automatically scaled by the number of workers for better convergence. The number of decay steps required to achieve same accuracy will also reduce based on the number of cards (e.g., 1/4 of steps in the above case), but needs to be scaled manually in the input file. + +Technical details of such heuristic rule are discussed at [Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour](https://arxiv.org/abs/1706.02677). + +## Scaling test Testing `examples/water/se_e2_a` on a 8-GPU host, linear acceleration can be observed with increasing number of cards. + | Num of GPU cards | Seconds every 100 samples | Samples per second | Speed up | | -- | -- | -- | -- | | 1 | 1.4515 | 68.89 | 1.00 | @@ -10,17 +18,43 @@ Testing `examples/water/se_e2_a` on a 8-GPU host, linear acceleration can be obs | 4 | 1.7635 | 56.71*4 | 3.29 | | 8 | 1.7267 | 57.91*8 | 6.72 | -Horovod works in the data-parallel mode resulting a larger global batch size. For example, the real batch size is 8 when `batch_size` is set to 2 in the input file and you lauch 4 workers. Thus, `learning_rate` is automatically scaled by the number of workers for better convergence. Technical details of such heuristic rule are discussed at [Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour](https://arxiv.org/abs/1706.02677). +## How to use + +Training workers can be launched with `horovodrun`. The following command launches 4 processes on the same host: ```bash -# Launch 4 processes on the same host CUDA_VISIBLE_DEVICES=4,5,6,7 horovodrun -np 4 \ dp train --mpi-log=workers input.json ``` Need to mention, environment variable `CUDA_VISIBLE_DEVICES` must be set to control parallelism on the occupied host where one process is bound to one GPU card. -What's more, 2 command-line arguments are defined to control the logging behvaior. +When using MPI with Horovod, `horovodrun` is a simple wrapper around `mpirun`. In the case where fine-grained control over options passed to `mpirun`, [`mpirun` can be invoked directly](https://horovod.readthedocs.io/en/stable/mpi_include.html), and it will be detected automatically by Horovod, e.g., +```bash +CUDA_VISIBLE_DEVICES=4,5,6,7 mpirun -l -launcher=fork -hosts=localhost -np 4 \ + dp train --mpi-log=workers input.json +``` +this is sometimes neccessary on HPC environment. + +Whether distributed workers are initiated can be observed at the "Summary of the training" section in the log (`world size` > 1, and `distributed`). +``` +[0] DEEPMD INFO ---Summary of the training--------------------------------------- +[0] DEEPMD INFO distributed +[0] DEEPMD INFO world size: 4 +[0] DEEPMD INFO my rank: 0 +[0] DEEPMD INFO node list: ['exp-13-57'] +[0] DEEPMD INFO running on: exp-13-57 +[0] DEEPMD INFO computing device: gpu:0 +[0] DEEPMD INFO CUDA_VISIBLE_DEVICES: 0,1,2,3 +[0] DEEPMD INFO Count of visible GPU: 4 +[0] DEEPMD INFO num_intra_threads: 0 +[0] DEEPMD INFO num_inter_threads: 0 +[0] DEEPMD INFO ----------------------------------------------------------------- +``` + +## Logging + +What's more, 2 command-line arguments are defined to control the logging behvaior when performing parallel training with MPI. ``` optional arguments: -l LOG_PATH, --log-path LOG_PATH @@ -33,4 +67,4 @@ optional arguments: broadcasts logs from workers to master and 'workers' means each process will output its own log (default: master) -``` \ No newline at end of file +``` From 077df3d6a8b4303cebfda7e39436f9f533c717ab Mon Sep 17 00:00:00 2001 From: AnguseZhang <529133328@qq.com> Date: Fri, 17 Sep 2021 15:00:16 +0800 Subject: [PATCH 074/161] Add doc for model deviation (#1153) * Add .idea to .gitignore * Add doc for models in model deviation. * Update doc/third-party/lammps-command.md Co-authored-by: Jinzhe Zeng Co-authored-by: Han Wang Co-authored-by: Jinzhe Zeng --- .gitignore | 1 + doc/third-party/lammps-command.md | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e9b29e2382..b5c08d1f7a 100644 --- a/.gitignore +++ b/.gitignore @@ -30,5 +30,6 @@ API_CC doc/api_py/ dp/ build_lammps/ +.idea/ build_tests/ build_cc_tests diff --git a/doc/third-party/lammps-command.md b/doc/third-party/lammps-command.md index 7218b75090..ff6c490aeb 100644 --- a/doc/third-party/lammps-command.md +++ b/doc/third-party/lammps-command.md @@ -18,7 +18,9 @@ The DeePMD-kit package provides the pair_style `deepmd` pair_style deepmd models ... keyword value ... ``` - deepmd = style of this pair_style -- models = frozen model(s) to compute the interaction. If multiple models are provided, then the model deviation will be computed +- models = frozen model(s) to compute the interaction. +If multiple models are provided, then only the first model serves to provide energy and force prediction for each timestep of molecular dynamics, +and the model deviation will be computed among all models every `out_freq` timesteps. - keyword = *out_file* or *out_freq* or *fparam* or *atomic* or *relative*
     out_file value = filename

From 5b0ff594fc37a369fb4750365de9065305e34471 Mon Sep 17 00:00:00 2001
From: Yingze Wang 
Date: Wed, 22 Sep 2021 07:44:11 +0800
Subject: [PATCH 075/161] Add plugin for GROMACS (#1160)

* Add gromacs examples

* Add doc for gmx/dp plugin

* Add gmx plugin in README.md

* Add gmx/dp plugin code

* Upgrade json.hpp to 3.9.1

* Update doc of installing gromacs

* Change the min gcc version to 4.8

* Compile TENSORFLOW_ROOT and DEEPMD_ROOT in dp_gmx_patch

* Remove extra files

* Update doc to use gcc version 4.8

* Resolve inconsistency in gcc version
---
 README.md                                     |    10 +-
 doc/install/index.md                          |     1 +
 doc/install/index.rst                         |     1 +
 doc/install/install-from-source.md            |     2 +-
 doc/install/install-gromacs.md                |    31 +
 doc/third-party/gromacs.md                    |   120 +
 doc/third-party/index.md                      |     5 +-
 doc/third-party/index.rst                     |     1 +
 doc/third-party/lammps.md                     |     2 +-
 doc/troubleshooting/installation.md           |     4 +-
 examples/methane/index.raw                    |     1 +
 examples/methane/input.json                   |     7 +
 examples/methane/lig_solv.gro                 |   662 +
 examples/methane/md.mdp                       |    56 +
 examples/methane/methane.itp                  |    31 +
 examples/methane/run.sh                       |     4 +
 examples/methane/topol.top                    |    15 +
 examples/methane/type.raw                     |     1 +
 examples/water/gmx/index.raw                  |     1 +
 examples/water/gmx/input.json                 |     6 +
 examples/water/gmx/md.mdp                     |    49 +
 examples/water/gmx/md.sh                      |     5 +
 examples/water/gmx/rdf.png                    |   Bin 0 -> 171170 bytes
 examples/water/gmx/type.raw                   |     1 +
 examples/water/gmx/water.gro                  |   771 +
 examples/water/gmx/water.top                  |    43 +
 source/3rdparty/json.hpp                      | 33288 ++++++++++------
 source/CMakeLists.txt                         |    10 +-
 source/gmx/CMakeLists.txt                     |    48 +
 source/gmx/dp_gmx_patch                       |   133 +
 source/gmx/include/gmx_plugin.h               |    28 +
 .../patches/2020.2/CMakeLists.txt.patch.in    |    29 +
 .../src/gromacs/mdlib/forcerec.cpp.patch      |    33 +
 .../2020.2/src/gromacs/mdlib/forcerec.h.patch |    13 +
 .../src/gromacs/mdlib/sim_util.cpp.patch      |   103 +
 source/gmx/src/gmx_plugin.cpp                 |   128 +
 source/ipi/driver.cc                          |     2 +
 37 files changed, 24456 insertions(+), 11189 deletions(-)
 create mode 100644 doc/install/install-gromacs.md
 create mode 100644 doc/third-party/gromacs.md
 create mode 100644 examples/methane/index.raw
 create mode 100644 examples/methane/input.json
 create mode 100644 examples/methane/lig_solv.gro
 create mode 100755 examples/methane/md.mdp
 create mode 100644 examples/methane/methane.itp
 create mode 100644 examples/methane/run.sh
 create mode 100644 examples/methane/topol.top
 create mode 100644 examples/methane/type.raw
 create mode 100644 examples/water/gmx/index.raw
 create mode 100644 examples/water/gmx/input.json
 create mode 100755 examples/water/gmx/md.mdp
 create mode 100755 examples/water/gmx/md.sh
 create mode 100644 examples/water/gmx/rdf.png
 create mode 100644 examples/water/gmx/type.raw
 create mode 100644 examples/water/gmx/water.gro
 create mode 100644 examples/water/gmx/water.top
 create mode 100644 source/gmx/CMakeLists.txt
 create mode 100644 source/gmx/dp_gmx_patch
 create mode 100644 source/gmx/include/gmx_plugin.h
 create mode 100644 source/gmx/patches/2020.2/CMakeLists.txt.patch.in
 create mode 100644 source/gmx/patches/2020.2/src/gromacs/mdlib/forcerec.cpp.patch
 create mode 100644 source/gmx/patches/2020.2/src/gromacs/mdlib/forcerec.h.patch
 create mode 100644 source/gmx/patches/2020.2/src/gromacs/mdlib/sim_util.cpp.patch
 create mode 100644 source/gmx/src/gmx_plugin.cpp

diff --git a/README.md b/README.md
index c05be445c8..3c1a42e6f3 100644
--- a/README.md
+++ b/README.md
@@ -60,7 +60,7 @@ Please follow our [GitHub](https://github.com/deepmodeling/deepmd-kit) webpage t
 
 DeePMD-kit offers multiple installation methods. It is recommend using easily methods like [offline packages](doc/install/easy-install.md#offline-packages), [conda](doc/install/easy-install.md#with-conda) and [docker](doc/install/easy-install.md#with-docker). 
 
-One may manually install DeePMD-kit by following the instuctions on [installing the Python interface](doc/install/install-from-source.md#install-the-python-interface) and [installing the C++ interface](doc/install/install-from-source.md#install-the-c-interface). The C++ interface is necessary when using DeePMD-kit with LAMMPS and i-PI.
+One may manually install DeePMD-kit by following the instuctions on [installing the Python interface](doc/install/install-from-source.md#install-the-python-interface) and [installing the C++ interface](doc/install/install-from-source.md#install-the-c-interface). The C++ interface is necessary when using DeePMD-kit with LAMMPS, i-PI or GROMACS.
 
 
 # Use DeePMD-kit
@@ -71,7 +71,7 @@ A quick-start on using DeePMD-kit can be found as follows:
 - [Training a model](doc/train/training.md)
 - [Freeze a model](doc/freeze/freeze.md)
 - [Test a model](doc/test/test.md)
-- [Running MD with LAMMPS](doc/third-party/lammps.md)
+- [Run MD with LAMMPS](doc/third-party/lammps.md)
 
 A full [document](doc/train/train-input-auto.rst) on options in the training input script is available.
 
@@ -113,10 +113,10 @@ A full [document](doc/train/train-input-auto.rst) on options in the training inp
     - [C++ interface](doc/inference/cxx.md)
 - [Integrate with third-party packages](doc/third-party/index.rst)
     - [Use deep potential with ASE](doc/third-party/ase.md)
-    - [Running MD with LAMMPS](doc/third-party/lammps.md)
+    - [Run MD with LAMMPS](doc/third-party/lammps.md)
     - [LAMMPS commands](doc/third-party/lammps-command.md)
     - [Run path-integral MD with i-PI](doc/third-party/ipi.md)
-
+    - [Run MD with GROMACS](doc/third-party/gromacs.md)
 
 # Code structure
 The code is organized as follows:
@@ -135,6 +135,8 @@ The code is organized as follows:
 
 * `source/lmp`: source code of Lammps module.
 
+* `source/gmx`: source code of Gromacs plugin.
+
 * `source/op`: tensorflow op implementation. working with library.
 
 
diff --git a/doc/install/index.md b/doc/install/index.md
index 997efe7a35..b6cc195b6f 100644
--- a/doc/install/index.md
+++ b/doc/install/index.md
@@ -4,4 +4,5 @@
 - [Install from source code](install-from-source.md)
 - [Install LAMMPS](install-lammps.md)
 - [Install i-PI](install-ipi.md)
+- [Install GROMACS](install-gromacs.md)
 - [Building conda packages](build-conda.md)
\ No newline at end of file
diff --git a/doc/install/index.rst b/doc/install/index.rst
index b36b3e68ff..8d86696cf4 100644
--- a/doc/install/index.rst
+++ b/doc/install/index.rst
@@ -8,4 +8,5 @@ Installation
    install-from-source
    install-lammps
    install-ipi
+   install-gromacs
    build-conda
diff --git a/doc/install/install-from-source.md b/doc/install/install-from-source.md
index ab6280441d..8efa20a374 100644
--- a/doc/install/install-from-source.md
+++ b/doc/install/install-from-source.md
@@ -146,7 +146,7 @@ Check the compiler version on your machine
 gcc --version
 ```
 
-The C++ interface of DeePMD-kit was tested with compiler gcc >= 4.8. It is noticed that the I-Pi support is only compiled with gcc >= 4.9.
+The C++ interface of DeePMD-kit was tested with compiler gcc >= 4.8. It is noticed that the I-Pi support is only compiled with gcc >= 4.8.
 
 First the C++ interface of Tensorflow should be installed. It is noted that the version of Tensorflow should be in consistent with the python interface. You may follow [the instruction](install-tf.2.3.md) to install the corresponding C++ interface.
 
diff --git a/doc/install/install-gromacs.md b/doc/install/install-gromacs.md
new file mode 100644
index 0000000000..398ed8ccba
--- /dev/null
+++ b/doc/install/install-gromacs.md
@@ -0,0 +1,31 @@
+# Install GROMACS with DeepMD
+## Patch source code of GROMACS 
+Download source code of a supported gromacs version (2020.2) from https://manual.gromacs.org/2020.2/download.html. Run the following command:
+```bash
+export PATH=$PATH:$deepmd_kit_root/bin
+dp_gmx_patch -d $gromacs_root -v $version -p
+```
+where `deepmd_kit_root` is the directory where the latest version of deepmd-kit is installed, and `gromacs_root` refers to source code directory of gromacs. And `version` represents the version of gromacs, **only support 2020.2 now**. You may patch another version of gromacs but still setting `version` to `2020.2`. However, we cannot ensure that it works.
+
+
+
+
+## Compile GROMACS with deepmd-kit
+The C++ interface of `deepmd-kit 2.x` and `tensorflow 2.x` are required. And be aware that only deepmd-kit with **high precision** is supported now, since we cannot ensure single precision is enough for a GROMACS simulation. Here is a sample compile scipt:
+```bash
+#!/bin/bash
+export CC=/usr/bin/gcc
+export CXX=/usr/bin/g++
+export CMAKE_PREFIX_PATH="/path/to/fftw-3.3.9" # fftw libraries
+mkdir build
+cd build
+
+cmake3 .. -DCMAKE_CXX_STANDARD=14 \ # not required, but c++14 seems to be more compatible with higher version of tensorflow
+          -DGMX_MPI=ON \
+          -DGMX_GPU=CUDA \ # Gromacs on ROCm has not been fully developed yet
+          -DCUDA_TOOLKIT_ROOT_DIR=/path/to/cuda \
+          -DCMAKE_INSTALL_PREFIX=/path/to/gromacs-2020.2-deepmd
+make -j
+make install
+```
\ No newline at end of file
diff --git a/doc/third-party/gromacs.md b/doc/third-party/gromacs.md
new file mode 100644
index 0000000000..e504c43f59
--- /dev/null
+++ b/doc/third-party/gromacs.md
@@ -0,0 +1,120 @@
+# Running MD with GROMACS
+## DP/MM Simulation
+This part gives a simple tutorial on how to run a DP/MM simulation for methane in water, which means using DP for methane and TIP3P for water. All relevant files can be found in `examples/methane`.
+### Topology Preparation
+Similar to QM/MM simulation, the internal interactions (including bond, angle, dihedrals, LJ, Columb) of the region descibed by a neural network potential (NNP) have to be **turned off**. In GROMACS, bonded interactions can be turned off by modifying `[ bonds ]`, `[ angles ]`, `[ dihedrals ]` and `[ pairs ]` sections. And LJ and Columb interactions must be turned off by `[ exclusions ]` section.
+
+For example, if one wants to simulate ethane in water, using DeepPotential for methane and TIP3P for water, the topology of methane should be like the following (as presented in `examples/methane/methane.itp`):
+```
+[ atomtypes ]
+;name btype  mass  charge ptype    sigma  epsilon
+  c3    c3   0.0     0.0     A 0.339771 0.451035
+  hc    hc   0.0     0.0     A 0.260018 0.087027
+
+[ moleculetype ]
+;name            nrexcl
+ methane          3
+
+[ atoms ]
+; nr type  resnr residue atom  cgnr  charge   mass
+  1   c3      1     MOL   C1     1 -0.1068 12.010
+  2   hc      1     MOL   H1     2  0.0267  1.008
+  3   hc      1     MOL   H2     3  0.0267  1.008
+  4   hc      1     MOL   H3     4  0.0267  1.008
+  5   hc      1     MOL   H4     5  0.0267  1.008
+
+[ bonds ]
+; i  j  func  b0  kb
+ 1  2     5        
+ 1  3     5        
+ 1  4     5        
+ 1  5     5        
+
+[ exclusions ]
+; ai  aj1  aj2  aj3  aj4
+  1    2    3    4    5
+  2    1    3    4    5
+  3    1    2    4    5
+  4    1    2    3    5
+  5    1    2    3    4
+```
+For comparsion, the original topology file genearted by `acpype` will be:
+```
+; methane_GMX.itp created by acpype (v: 2021-02-05T22:15:50CET) on Wed Sep  8 01:21:53 2021
+
+[ atomtypes ]
+;name   bond_type     mass     charge   ptype   sigma         epsilon       Amb
+ c3       c3          0.00000  0.00000   A     3.39771e-01   4.51035e-01 ; 1.91  0.1078
+ hc       hc          0.00000  0.00000   A     2.60018e-01   8.70272e-02 ; 1.46  0.0208
+
+[ moleculetype ]
+;name            nrexcl
+ methane          3
+
+[ atoms ]
+;   nr  type  resi  res  atom  cgnr     charge      mass       ; qtot   bond_type
+     1   c3     1   MOL    C1    1    -0.106800     12.01000 ; qtot -0.107
+     2   hc     1   MOL    H1    2     0.026700      1.00800 ; qtot -0.080
+     3   hc     1   MOL    H2    3     0.026700      1.00800 ; qtot -0.053
+     4   hc     1   MOL    H3    4     0.026700      1.00800 ; qtot -0.027
+     5   hc     1   MOL    H4    5     0.026700      1.00800 ; qtot 0.000
+
+[ bonds ]
+;   ai     aj funct   r             k
+     1      2   1    1.0970e-01    3.1455e+05 ;     C1 - H1    
+     1      3   1    1.0970e-01    3.1455e+05 ;     C1 - H2    
+     1      4   1    1.0970e-01    3.1455e+05 ;     C1 - H3    
+     1      5   1    1.0970e-01    3.1455e+05 ;     C1 - H4    
+
+[ angles ]
+;   ai     aj     ak    funct   theta         cth
+     2      1      3      1    1.0758e+02    3.2635e+02 ;     H1 - C1     - H2    
+     2      1      4      1    1.0758e+02    3.2635e+02 ;     H1 - C1     - H3    
+     2      1      5      1    1.0758e+02    3.2635e+02 ;     H1 - C1     - H4    
+     3      1      4      1    1.0758e+02    3.2635e+02 ;     H2 - C1     - H3    
+     3      1      5      1    1.0758e+02    3.2635e+02 ;     H2 - C1     - H4    
+     4      1      5      1    1.0758e+02    3.2635e+02 ;     H3 - C1     - H4    
+```
+### DeepMD Settings
+Before running simulation, we need to tell GROMACS to use DeepPotential by setting environment variable `GMX_DEEPMD_INPUT_JSON`:
+```bash
+export GMX_DEEPMD_INPUT_JSON=input.json
+```
+Then, in your working directories, we have to write `input.json` file:
+```json
+{
+    "graph_file": "/path/to/graph.pb",
+    "type_file": "type.raw",
+    "index_file": "index.raw",
+    "lambda": 1.0,
+    "pbc": false
+}
+```
+Here is an explanation for these settings:
++ `graph_file` : The graph file (with suffix .pb) generated by `dp freeze` command
++ `type_file` : File to specify DP atom types (in space-sepreated format). Here, `type.raw` looks like
+```
+1 0 0 0 0
+```
++ `index_file` : File containing indices of DP atoms (in space-seperated format), which should be in consistent with indices' order in .gro file but **starting from zero**. Here, `index.raw` looks like
+```
+0 1 2 3 4
+```
++ `lambda`: Optional, default 1.0. Used in alchemical calculations.
++ `pbc`: Optional, default true. If true, the GROMACS peroidic condition is passed to DeepMD.
+
+### Run Simulation
+Finally, you can run GROMACS using `gmx mdrun` as usual.
+
+## All-atom DP Simulation
+This part gives an example on how to run a simulation with all atoms described by a DeepPotential with Gromacs, taking water as an example. Instead of using `[ exclusions ]` to turn off the non-bonded energies, we can simply do this by setting LJ parameters (i.e. epsilon and sigma) and partial charges to 0, as shown in `examples/water/gmx/water.top`:
+```
+[ atomtypes ]
+; name      at.num  mass     charge ptype  sigma      epsilon
+HW           1       1.008   0.0000  A   0.00000e+00  0.00000e+00
+OW           8      16.00    0.0000  A   0.00000e+00  0.00000e+00
+``` 
+As mentioned in the above section, `input.json` and relevant files (`index.raw`, `type.raw`) should also be created. Then, we can start the simulation under NVT ensemble and plot the radial distribution function (RDF) by `gmx rdf` command. We can see that the RDF given by Gromacs+DP matches perfectly with Lammps+DP, which further provides an evidence on the validity of our simulation.
+![rdf](../../examples/water/gmx/rdf.png)
+
+However, we still recommend you run all-atom DP simulation using LAMMPS since it is more stable and efficient.
\ No newline at end of file
diff --git a/doc/third-party/index.md b/doc/third-party/index.md
index fd38898596..d5e5fb1bdd 100644
--- a/doc/third-party/index.md
+++ b/doc/third-party/index.md
@@ -3,6 +3,7 @@
 Note that the model for inference is required to be compatible with the DeePMD-kit package. See [Model compatibility](../troubleshooting/model-compatability.html) for details.
 
 - [Use deep potential with ASE](ase.md)
-- [Running MD with LAMMPS](lammps.md)
+- [Run MD with LAMMPS](lammps.md)
 - [LAMMPS commands](lammps-command.md)
-- [Run path-integral MD with i-PI](ipi.md)
\ No newline at end of file
+- [Run path-integral MD with i-PI](ipi.md)
+- [Run MD with GROMACS](gromacs.md)
\ No newline at end of file
diff --git a/doc/third-party/index.rst b/doc/third-party/index.rst
index 6308d9969f..b87d8e3a97 100644
--- a/doc/third-party/index.rst
+++ b/doc/third-party/index.rst
@@ -10,3 +10,4 @@ Note that the model for inference is required to be compatible with the DeePMD-k
    lammps
    lammps-command
    ipi
+   gromacs
diff --git a/doc/third-party/lammps.md b/doc/third-party/lammps.md
index 39da05d873..78c6fc060e 100644
--- a/doc/third-party/lammps.md
+++ b/doc/third-party/lammps.md
@@ -1,4 +1,4 @@
-# Running MD with LAMMPS
+# Run MD with LAMMPS
 
 Running an MD simulation with LAMMPS is simpler. In the LAMMPS input file, one needs to specify the pair style as follows
 
diff --git a/doc/troubleshooting/installation.md b/doc/troubleshooting/installation.md
index 6b2a7caf3f..696b18d5c9 100644
--- a/doc/troubleshooting/installation.md
+++ b/doc/troubleshooting/installation.md
@@ -1,13 +1,11 @@
 # Installation
 ## Inadequate versions of gcc/g++
-Sometimes you may use a gcc/g++ of version <4.9. If you have a gcc/g++ of version > 4.9, say, 7.2.0, you may choose to use it by doing 
+Sometimes you may use a gcc/g++ of version < 4.8. In this way, you can still compile all the parts of TensorFlow and most of the parts of DeePMD-kit, but i-Pi and GROMACS plugin will be disabled automatically. Or if you have a gcc/g++ of version > 4.8, say, 7.2.0, you may choose to use it by doing 
 ```bash
 export CC=/path/to/gcc-7.2.0/bin/gcc
 export CXX=/path/to/gcc-7.2.0/bin/g++
 ```
 
-If, for any reason, for example, you only have a gcc/g++ of version 4.8.5, you can still compile all the parts of TensorFlow and most of the parts of DeePMD-kit. i-Pi will be disabled automatically.
-
 ## Build files left in DeePMD-kit
 When you try to build a second time when installing DeePMD-kit, files produced before may contribute to failure. Thus, you may clear them by
 ```bash
diff --git a/examples/methane/index.raw b/examples/methane/index.raw
new file mode 100644
index 0000000000..17847fb721
--- /dev/null
+++ b/examples/methane/index.raw
@@ -0,0 +1 @@
+0 1 2 3 4
\ No newline at end of file
diff --git a/examples/methane/input.json b/examples/methane/input.json
new file mode 100644
index 0000000000..cbd5830cac
--- /dev/null
+++ b/examples/methane/input.json
@@ -0,0 +1,7 @@
+{
+    "graph_file": "frozen_model.pb",
+    "type_file": "type.raw",
+    "index_file": "index.raw",
+    "lambda": 1.0,
+    "pbc": false
+}
diff --git a/examples/methane/lig_solv.gro b/examples/methane/lig_solv.gro
new file mode 100644
index 0000000000..ae3474606e
--- /dev/null
+++ b/examples/methane/lig_solv.gro
@@ -0,0 +1,662 @@
+methane_GMX.gro created by acpype (v: 2021-02-05T22:15:50CET) on Tue Sep  7 15:59:39 2021
+  659
+    1MOL     C1    1   1.635   1.635   0.771
+    1MOL     H1    2   1.567   1.720   0.762
+    1MOL     H2    3   1.595   1.551   0.713
+    1MOL     H3    4   1.644   1.606   0.876
+    1MOL     H4    5   1.734   1.663   0.733
+    2SOL     OW    6   0.230   0.628   0.113
+    2SOL    HW1    7   0.137   0.626   0.150
+    2SOL    HW2    8   0.231   0.589   0.021
+    3SOL     OW    9   0.225   0.275   0.996
+    3SOL    HW1   10   0.260   0.258   1.088
+    3SOL    HW2   11   0.137   0.230   0.984
+    4SOL     OW   12   0.019   0.368   0.647
+    4SOL    HW1   13  -0.063   0.411   0.686
+    4SOL    HW2   14  -0.009   0.295   0.584
+    5SOL     OW   15   0.569   1.275   1.165
+    5SOL    HW1   16   0.476   1.268   1.128
+    5SOL    HW2   17   0.580   1.364   1.209
+    6SOL     OW   18   1.135   0.703   0.717
+    6SOL    HW1   19   1.192   0.781   0.692
+    6SOL    HW2   20   1.075   0.729   0.793
+    7SOL     OW   21   1.755   0.607   0.231
+    7SOL    HW1   22   1.743   0.594   0.132
+    7SOL    HW2   23   1.725   0.526   0.280
+    8SOL     OW   24   0.768   1.144   1.023
+    8SOL    HW1   25   0.690   1.161   1.083
+    8SOL    HW2   26   0.802   1.231   0.987
+    9SOL     OW   27   0.850   0.798   1.823
+    9SOL    HW1   28   0.846   0.874   1.888
+    9SOL    HW2   29   0.872   0.834   1.732
+   10SOL     OW   30   0.685   1.012   0.665
+   10SOL    HW1   31   0.754   0.996   0.735
+   10SOL    HW2   32   0.612   1.069   0.703
+   11SOL     OW   33   0.686   1.161   1.803
+   11SOL    HW1   34   0.746   1.240   1.817
+   11SOL    HW2   35   0.600   1.192   1.762
+   12SOL     OW   36   0.335   1.435   1.061
+   12SOL    HW1   37   0.257   1.404   1.008
+   12SOL    HW2   38   0.393   1.493   1.004
+   13SOL     OW   39   1.460   1.505   1.339
+   13SOL    HW1   40   1.484   1.599   1.365
+   13SOL    HW2   41   1.444   1.451   1.421
+   14SOL     OW   42   0.438   0.392   1.499
+   14SOL    HW1   43   0.520   0.336   1.508
+   14SOL    HW2   44   0.357   0.334   1.503
+   15SOL     OW   45   1.603   0.447   0.737
+   15SOL    HW1   46   1.529   0.493   0.687
+   15SOL    HW2   47   1.654   0.515   0.790
+   16SOL     OW   48   0.231   1.713   0.483
+   16SOL    HW1   49   0.265   1.790   0.537
+   16SOL    HW2   50   0.275   1.713   0.393
+   17SOL     OW   51   1.127   1.341   1.690
+   17SOL    HW1   52   1.174   1.341   1.778
+   17SOL    HW2   53   1.079   1.254   1.679
+   18SOL     OW   54   0.230   1.434   0.538
+   18SOL    HW1   55   0.204   1.530   0.538
+   18SOL    HW2   56   0.159   1.380   0.583
+   19SOL     OW   57   0.240   1.091   0.886
+   19SOL    HW1   58   0.254   1.007   0.938
+   19SOL    HW2   59   0.185   1.155   0.941
+   20SOL     OW   60   0.620   1.786   1.439
+   20SOL    HW1   61   0.528   1.769   1.474
+   20SOL    HW2   62   0.648   1.878   1.465
+   21SOL     OW   63   0.606   0.964   0.123
+   21SOL    HW1   64   0.613   1.048   0.069
+   21SOL    HW2   65   0.652   0.977   0.211
+   22SOL     OW   66   1.594   0.114   1.480
+   22SOL    HW1   67   1.576   0.181   1.408
+   22SOL    HW2   68   1.591   0.160   1.569
+   23SOL     OW   69   0.122   0.643   0.563
+   23SOL    HW1   70   0.077   0.555   0.580
+   23SOL    HW2   71   0.121   0.697   0.647
+   24SOL     OW   72   1.842   1.767   0.359
+   24SOL    HW1   73   1.896   1.738   0.439
+   24SOL    HW2   74   1.872   1.857   0.330
+   25SOL     OW   75   0.027   1.596   0.117
+   25SOL    HW1   76   0.008   1.500   0.138
+   25SOL    HW2   77  -0.006   1.654   0.192
+   26SOL     OW   78   1.689   0.922   0.612
+   26SOL    HW1   79   1.784   0.893   0.620
+   26SOL    HW2   80   1.681   0.987   0.537
+   27SOL     OW   81   1.641   1.108   0.432
+   27SOL    HW1   82   1.727   1.110   0.380
+   27SOL    HW2   83   1.655   1.155   0.520
+   28SOL     OW   84   0.113   0.737   1.597
+   28SOL    HW1   85   0.201   0.724   1.642
+   28SOL    HW2   86   0.100   0.834   1.575
+   29SOL     OW   87   0.613   1.365   0.726
+   29SOL    HW1   88   0.564   1.278   0.735
+   29SOL    HW2   89   0.590   1.408   0.639
+   30SOL     OW   90   1.293   1.228   1.423
+   30SOL    HW1   91   1.330   1.155   1.365
+   30SOL    HW2   92   1.345   1.233   1.508
+   31SOL     OW   93   0.809   0.004   0.502
+   31SOL    HW1   94   0.849   0.095   0.493
+   31SOL    HW2   95   0.709   0.012   0.508
+   32SOL     OW   96   0.197   0.976   1.264
+   32SOL    HW1   97   0.286   0.931   1.250
+   32SOL    HW2   98   0.124   0.911   1.245
+   33SOL     OW   99   1.525   0.999   0.190
+   33SOL    HW1  100   1.462   0.923   0.203
+   33SOL    HW2  101   1.573   1.017   0.276
+   34SOL     OW  102   1.187   1.792   1.616
+   34SOL    HW1  103   1.211   1.852   1.540
+   34SOL    HW2  104   1.194   1.697   1.586
+   35SOL     OW  105   0.317   0.251   1.801
+   35SOL    HW1  106   0.388   0.322   1.807
+   35SOL    HW2  107   0.229   0.290   1.829
+   36SOL     OW  108   1.466   1.417   0.953
+   36SOL    HW1  109   1.407   1.423   1.033
+   36SOL    HW2  110   1.451   1.329   0.907
+   37SOL     OW  111   0.598   0.729   0.270
+   37SOL    HW1  112   0.622   0.798   0.202
+   37SOL    HW2  113   0.520   0.762   0.324
+   38SOL     OW  114   1.281   0.345   0.944
+   38SOL    HW1  115   1.195   0.295   0.931
+   38SOL    HW2  116   1.343   0.291   1.000
+   39SOL     OW  117   1.576   1.662   0.307
+   39SOL    HW1  118   1.665   1.708   0.310
+   39SOL    HW2  119   1.555   1.638   0.212
+   40SOL     OW  120   0.807   0.605   1.465
+   40SOL    HW1  121   0.760   0.602   1.554
+   40SOL    HW2  122   0.756   0.550   1.399
+   41SOL     OW  123   1.394   0.469   1.674
+   41SOL    HW1  124   1.374   0.512   1.762
+   41SOL    HW2  125   1.472   0.407   1.683
+   42SOL     OW  126   0.973   0.890   1.572
+   42SOL    HW1  127   1.019   0.806   1.543
+   42SOL    HW2  128   0.917   0.924   1.497
+   43SOL     OW  129   0.991   0.410   1.242
+   43SOL    HW1  130   0.914   0.444   1.296
+   43SOL    HW2  131   0.957   0.359   1.163
+   44SOL     OW  132   1.041   0.701   0.429
+   44SOL    HW1  133   1.067   0.697   0.525
+   44SOL    HW2  134   0.956   0.650   0.415
+   45SOL     OW  135   0.076   0.811   0.789
+   45SOL    HW1  136   0.175   0.799   0.798
+   45SOL    HW2  137   0.052   0.906   0.810
+   46SOL     OW  138   0.130   1.821   1.571
+   46SOL    HW1  139   0.120   1.806   1.670
+   46SOL    HW2  140   0.044   1.857   1.535
+   47SOL     OW  141   0.865   0.348   0.195
+   47SOL    HW1  142   0.924   0.411   0.146
+   47SOL    HW2  143   0.884   0.254   0.166
+   48SOL     OW  144   1.719   0.585   1.831
+   48SOL    HW1  145   1.693   0.674   1.795
+   48SOL    HW2  146   1.717   0.517   1.758
+   49SOL     OW  147   1.362   1.144   0.545
+   49SOL    HW1  148   1.445   1.115   0.497
+   49SOL    HW2  149   1.313   1.211   0.489
+   50SOL     OW  150   0.550   0.196   0.885
+   50SOL    HW1  151   0.545   0.191   0.985
+   50SOL    HW2  152   0.552   0.292   0.856
+   51SOL     OW  153   1.008   1.456   0.477
+   51SOL    HW1  154   0.962   1.528   0.425
+   51SOL    HW2  155   1.004   1.476   0.575
+   52SOL     OW  156   0.351   1.801   0.853
+   52SOL    HW1  157   0.401   1.715   0.859
+   52SOL    HW2  158   0.416   1.878   0.850
+   53SOL     OW  159   1.795   1.066   0.873
+   53SOL    HW1  160   1.733   1.051   0.797
+   53SOL    HW2  161   1.743   1.077   0.958
+   54SOL     OW  162   1.227   1.550   1.506
+   54SOL    HW1  163   1.233   1.473   1.570
+   54SOL    HW2  164   1.175   1.524   1.426
+   55SOL     OW  165   0.321   0.943   0.242
+   55SOL    HW1  166   0.403   0.982   0.200
+   55SOL    HW2  167   0.294   0.861   0.193
+   56SOL     OW  168   1.458   0.735   0.728
+   56SOL    HW1  169   1.453   0.670   0.803
+   56SOL    HW2  170   1.538   0.794   0.741
+   57SOL     OW  171   0.461   1.266   1.727
+   57SOL    HW1  172   0.411   1.267   1.641
+   57SOL    HW2  173   0.398   1.248   1.803
+   58SOL     OW  174   1.111   1.776   0.237
+   58SOL    HW1  175   1.051   1.714   0.287
+   58SOL    HW2  176   1.142   1.732   0.152
+   59SOL     OW  177   0.202   0.285   1.498
+   59SOL    HW1  178   0.122   0.345   1.485
+   59SOL    HW2  179   0.192   0.236   1.584
+   60SOL     OW  180   1.632   1.377   0.081
+   60SOL    HW1  181   1.600   1.471   0.071
+   60SOL    HW2  182   1.556   1.314   0.069
+   61SOL     OW  183   0.464   1.743   0.323
+   61SOL    HW1  184   0.497   1.782   0.409
+   61SOL    HW2  185   0.540   1.736   0.258
+   62SOL     OW  186   1.400   0.107   0.426
+   62SOL    HW1  187   1.376   0.070   0.336
+   62SOL    HW2  188   1.499   0.123   0.430
+   63SOL     OW  189   0.249   1.785   1.241
+   63SOL    HW1  190   0.306   1.720   1.291
+   63SOL    HW2  191   0.233   1.752   1.148
+   64SOL     OW  192   0.940   1.698   0.904
+   64SOL    HW1  193   1.020   1.641   0.925
+   64SOL    HW2  194   0.891   1.658   0.827
+   65SOL     OW  195   0.382   0.700   0.480
+   65SOL    HW1  196   0.427   0.610   0.477
+   65SOL    HW2  197   0.288   0.689   0.513
+   66SOL     OW  198   1.547   0.222   1.729
+   66SOL    HW1  199   1.542   0.259   1.821
+   66SOL    HW2  200   1.475   0.153   1.717
+   67SOL     OW  201   0.614   0.122   0.117
+   67SOL    HW1  202   0.712   0.100   0.124
+   67SOL    HW2  203   0.583   0.105   0.024
+   68SOL     OW  204   0.781   0.264   1.749
+   68SOL    HW1  205   0.848   0.203   1.792
+   68SOL    HW2  206   0.708   0.283   1.814
+   69SOL     OW  207   0.888   1.514   1.195
+   69SOL    HW1  208   0.865   1.489   1.101
+   69SOL    HW2  209   0.949   1.445   1.234
+   70SOL     OW  210   1.351   0.590   1.433
+   70SOL    HW1  211   1.379   0.547   1.518
+   70SOL    HW2  212   1.376   0.686   1.434
+   71SOL     OW  213   0.803   1.402   0.924
+   71SOL    HW1  214   0.893   1.416   0.882
+   71SOL    HW2  215   0.732   1.404   0.853
+   72SOL     OW  216   0.922   0.503   0.899
+   72SOL    HW1  217   0.897   0.494   0.803
+   72SOL    HW2  218   0.970   0.421   0.930
+   73SOL     OW  219   0.539   0.064   0.512
+   73SOL    HW1  220   0.458   0.065   0.570
+   73SOL    HW2  221   0.542   0.147   0.457
+   74SOL     OW  222   1.434   1.188   0.041
+   74SOL    HW1  223   1.466   1.112   0.098
+   74SOL    HW2  224   1.342   1.215   0.071
+   75SOL     OW  225   0.297   0.035   0.171
+   75SOL    HW1  226   0.346   0.119   0.150
+   75SOL    HW2  227   0.359  -0.030   0.216
+   76SOL     OW  228   0.935   0.236   0.480
+   76SOL    HW1  229   0.887   0.277   0.402
+   76SOL    HW2  230   1.034   0.234   0.461
+   77SOL     OW  231   1.076   0.683   1.464
+   77SOL    HW1  232   0.996   0.622   1.467
+   77SOL    HW2  233   1.157   0.630   1.440
+   78SOL     OW  234   1.227   1.570   0.793
+   78SOL    HW1  235   1.248   1.644   0.728
+   78SOL    HW2  236   1.295   1.570   0.866
+   79SOL     OW  237   0.459   1.152   0.741
+   79SOL    HW1  238   0.388   1.125   0.806
+   79SOL    HW2  239   0.433   1.124   0.648
+   80SOL     OW  240   1.271   1.797   0.591
+   80SOL    HW1  241   1.315   1.861   0.527
+   80SOL    HW2  242   1.221   1.849   0.661
+   81SOL     OW  243   1.032   0.549   0.016
+   81SOL    HW1  244   0.991   0.631  -0.023
+   81SOL    HW2  245   1.096   0.575   0.089
+   82SOL     OW  246   0.078   0.556   1.386
+   82SOL    HW1  247   0.170   0.555   1.345
+   82SOL    HW2  248   0.072   0.630   1.453
+   83SOL     OW  249   0.561   0.222   1.147
+   83SOL    HW1  250   0.599   0.138   1.184
+   83SOL    HW2  251   0.473   0.241   1.191
+   84SOL     OW  252   0.866   0.454   0.642
+   84SOL    HW1  253   0.834   0.526   0.580
+   84SOL    HW2  254   0.890   0.373   0.589
+   85SOL     OW  255   1.017   0.039   0.753
+   85SOL    HW1  256   0.945   0.044   0.684
+   85SOL    HW2  257   0.993  -0.030   0.822
+   86SOL     OW  258   1.429   1.173   0.867
+   86SOL    HW1  259   1.374   1.089   0.860
+   86SOL    HW2  260   1.455   1.202   0.775
+   87SOL     OW  261   1.466   0.590   0.992
+   87SOL    HW1  262   1.436   0.495   0.999
+   87SOL    HW2  263   1.539   0.606   1.058
+   88SOL     OW  264   1.857   0.833   0.377
+   88SOL    HW1  265   1.899   0.769   0.441
+   88SOL    HW2  266   1.819   0.782   0.299
+   89SOL     OW  267   0.488   1.385   0.174
+   89SOL    HW1  268   0.401   1.370   0.221
+   89SOL    HW2  269   0.471   1.411   0.079
+   90SOL     OW  270   1.664   1.280   0.657
+   90SOL    HW1  271   1.763   1.288   0.671
+   90SOL    HW2  272   1.619   1.364   0.688
+   91SOL     OW  273   1.390   0.575   0.078
+   91SOL    HW1  274   1.336   0.554   0.159
+   91SOL    HW2  275   1.481   0.534   0.087
+   92SOL     OW  276   0.527   0.256   0.328
+   92SOL    HW1  277   0.554   0.197   0.253
+   92SOL    HW2  278   0.527   0.351   0.297
+   93SOL     OW  279   1.754   1.223   1.588
+   93SOL    HW1  280   1.845   1.184   1.575
+   93SOL    HW2  281   1.762   1.319   1.612
+   94SOL     OW  282   1.064   1.347   1.340
+   94SOL    HW1  283   0.984   1.324   1.395
+   94SOL    HW2  284   1.147   1.321   1.389
+   95SOL     OW  285   1.592   1.629   1.625
+   95SOL    HW1  286   1.619   1.663   1.535
+   95SOL    HW2  287   1.671   1.591   1.671
+   96SOL     OW  288   1.111   1.195   1.100
+   96SOL    HW1  289   1.071   1.239   1.181
+   96SOL    HW2  290   1.070   1.232   1.017
+   97SOL     OW  291   1.638   1.099   1.079
+   97SOL    HW1  292   1.643   1.180   1.138
+   97SOL    HW2  293   1.552   1.101   1.028
+   98SOL     OW  294   0.915   0.089   1.402
+   98SOL    HW1  295   0.940   0.069   1.307
+   98SOL    HW2  296   0.987   0.145   1.444
+   99SOL     OW  297   0.980   1.116   1.719
+   99SOL    HW1  298   0.881   1.122   1.729
+   99SOL    HW2  299   1.003   1.036   1.663
+  100SOL     OW  300   0.705   1.050   0.368
+  100SOL    HW1  301   0.691   1.057   0.467
+  100SOL    HW2  302   0.789   0.999   0.350
+  101SOL     OW  303   0.410   0.813   1.251
+  101SOL    HW1  304   0.496   0.825   1.301
+  101SOL    HW2  305   0.368   0.726   1.278
+  102SOL     OW  306   1.274   0.386   1.262
+  102SOL    HW1  307   1.295   0.460   1.326
+  102SOL    HW2  308   1.185   0.403   1.219
+  103SOL     OW  309   0.064   1.564   1.331
+  103SOL    HW1  310   0.018   1.646   1.297
+  103SOL    HW2  311   0.162   1.583   1.340
+  104SOL     OW  312   0.367   1.100   0.501
+  104SOL    HW1  313   0.360   1.183   0.445
+  104SOL    HW2  314   0.371   1.020   0.441
+  105SOL     OW  315   0.566   0.537   0.865
+  105SOL    HW1  316   0.578   0.603   0.791
+  105SOL    HW2  317   0.612   0.571   0.948
+  106SOL     OW  318   1.252   1.348   0.388
+  106SOL    HW1  319   1.302   1.425   0.428
+  106SOL    HW2  320   1.157   1.350   0.420
+  107SOL     OW  321   1.272   1.445   1.142
+  107SOL    HW1  322   1.319   1.458   1.229
+  107SOL    HW2  323   1.206   1.371   1.151
+  108SOL     OW  324   1.582   0.639   0.472
+  108SOL    HW1  325   1.551   0.700   0.545
+  108SOL    HW2  326   1.632   0.691   0.403
+  109SOL     OW  327   0.354   1.510   1.329
+  109SOL    HW1  328   0.333   1.466   1.242
+  109SOL    HW2  329   0.451   1.536   1.332
+  110SOL     OW  330   0.402   0.751   1.598
+  110SOL    HW1  331   0.470   0.806   1.551
+  110SOL    HW2  332   0.442   0.663   1.625
+  111SOL     OW  333   1.587   0.779   1.670
+  111SOL    HW1  334   1.495   0.817   1.665
+  111SOL    HW2  335   1.647   0.826   1.605
+  112SOL     OW  336   1.013   0.105   1.770
+  112SOL    HW1  337   1.019   0.190   1.718
+  112SOL    HW2  338   1.045   0.029   1.713
+  113SOL     OW  339   0.504   0.050   1.740
+  113SOL    HW1  340   0.462  -0.007   1.670
+  113SOL    HW2  341   0.438   0.119   1.772
+  114SOL     OW  342   0.573   0.870   1.029
+  114SOL    HW1  343   0.617   0.959   1.020
+  114SOL    HW2  344   0.510   0.870   1.106
+  115SOL     OW  345   1.360   0.862   1.045
+  115SOL    HW1  346   1.285   0.862   0.979
+  115SOL    HW2  347   1.397   0.770   1.054
+  116SOL     OW  348   1.209   0.525   0.275
+  116SOL    HW1  349   1.222   0.441   0.329
+  116SOL    HW2  350   1.180   0.599   0.335
+  117SOL     OW  351   0.307   0.213   1.231
+  117SOL    HW1  352   0.284   0.250   1.321
+  117SOL    HW2  353   0.277   0.118   1.225
+  118SOL     OW  354   0.037   1.310   1.282
+  118SOL    HW1  355   0.090   1.261   1.350
+  118SOL    HW2  356   0.059   1.408   1.287
+  119SOL     OW  357   0.732   0.634   1.064
+  119SOL    HW1  358   0.791   0.608   0.988
+  119SOL    HW2  359   0.704   0.730   1.053
+  120SOL     OW  360   1.728   0.935   1.854
+  120SOL    HW1  361   1.682   0.928   1.765
+  120SOL    HW2  362   1.666   0.979   1.920
+  121SOL     OW  363   0.307   0.063   0.618
+  121SOL    HW1  364   0.296   0.157   0.651
+  121SOL    HW2  365   0.302   0.000   0.695
+  122SOL     OW  366   1.622   0.367   0.374
+  122SOL    HW1  367   1.624   0.291   0.438
+  122SOL    HW2  368   1.574   0.444   0.414
+  123SOL     OW  369   1.023   0.766   0.966
+  123SOL    HW1  370   1.038   0.787   1.062
+  123SOL    HW2  371   0.993   0.671   0.957
+  124SOL     OW  372   0.980   1.573   1.700
+  124SOL    HW1  373   0.960   1.617   1.612
+  124SOL    HW2  374   1.019   1.482   1.684
+  125SOL     OW  375   1.859   1.518   1.605
+  125SOL    HW1  376   1.873   1.545   1.510
+  125SOL    HW2  377   1.942   1.540   1.658
+  126SOL     OW  378   0.350   0.898   1.804
+  126SOL    HW1  379   0.426   0.942   1.852
+  126SOL    HW2  380   0.385   0.851   1.722
+  127SOL     OW  381   1.540   0.274   0.125
+  127SOL    HW1  382   1.479   0.199   0.148
+  127SOL    HW2  383   1.562   0.326   0.208
+  128SOL     OW  384   1.303   0.838   0.042
+  128SOL    HW1  385   1.337   0.745   0.057
+  128SOL    HW2  386   1.321   0.865  -0.053
+  129SOL     OW  387   1.068   1.333   0.849
+  129SOL    HW1  388   1.075   1.249   0.794
+  129SOL    HW2  389   1.130   1.402   0.813
+  130SOL     OW  390   0.319   0.810   0.949
+  130SOL    HW1  391   0.412   0.846   0.954
+  130SOL    HW2  392   0.313   0.725   1.001
+  131SOL     OW  393   0.339   0.509   1.006
+  131SOL    HW1  394   0.287   0.426   0.989
+  131SOL    HW2  395   0.416   0.514   0.942
+  132SOL     OW  396   1.138   0.380   1.678
+  132SOL    HW1  397   1.093   0.443   1.742
+  132SOL    HW2  398   1.231   0.411   1.661
+  133SOL     OW  399   1.160   0.207   1.477
+  133SOL    HW1  400   1.160   0.271   1.554
+  133SOL    HW2  401   1.188   0.255   1.394
+  134SOL     OW  402   0.008   1.326   0.200
+  134SOL    HW1  403  -0.085   1.347   0.169
+  134SOL    HW2  404   0.018   1.227   0.213
+  135SOL     OW  405   0.088   1.801   0.927
+  135SOL    HW1  406   0.046   1.715   0.900
+  135SOL    HW2  407   0.182   1.804   0.893
+  136SOL     OW  408   0.504   1.568   0.910
+  136SOL    HW1  409   0.570   1.642   0.919
+  136SOL    HW2  410   0.548   1.489   0.868
+  137SOL     OW  411   1.002   0.796   1.238
+  137SOL    HW1  412   1.043   0.764   1.324
+  137SOL    HW2  413   0.906   0.769   1.235
+  138SOL     OW  414   0.040   0.544   1.114
+  138SOL    HW1  415   0.125   0.511   1.073
+  138SOL    HW2  416   0.053   0.559   1.212
+  139SOL     OW  417   0.189   0.520   1.722
+  139SOL    HW1  418   0.248   0.480   1.652
+  139SOL    HW2  419   0.131   0.591   1.681
+  140SOL     OW  420   1.369   0.950   1.660
+  140SOL    HW1  421   1.408   1.039   1.680
+  140SOL    HW2  422   1.379   0.930   1.563
+  141SOL     OW  423   0.815   0.572   0.325
+  141SOL    HW1  424   0.822   0.483   0.279
+  141SOL    HW2  425   0.721   0.606   0.317
+  142SOL     OW  426   1.657   0.604   1.206
+  142SOL    HW1  427   1.619   0.535   1.268
+  142SOL    HW2  428   1.739   0.568   1.162
+  143SOL     OW  429   0.252   1.564   1.744
+  143SOL    HW1  430   0.222   1.621   1.820
+  143SOL    HW2  431   0.245   1.467   1.770
+  144SOL     OW  432   0.671   0.464   1.269
+  144SOL    HW1  433   0.637   0.375   1.239
+  144SOL    HW2  434   0.697   0.518   1.189
+  145SOL     OW  435   0.930   1.678   1.465
+  145SOL    HW1  436   0.906   1.660   1.370
+  145SOL    HW2  437   0.960   1.772   1.475
+  146SOL     OW  438   0.473   0.500   0.191
+  146SOL    HW1  439   0.534   0.580   0.195
+  146SOL    HW2  440   0.378   0.531   0.198
+  147SOL     OW  441   0.159   1.137   1.466
+  147SOL    HW1  442   0.181   1.076   1.542
+  147SOL    HW2  443   0.169   1.088   1.380
+  148SOL     OW  444   1.347   1.059   1.234
+  148SOL    HW1  445   1.371   0.996   1.160
+  148SOL    HW2  446   1.257   1.099   1.216
+  149SOL     OW  447   1.302   0.855   0.309
+  149SOL    HW1  448   1.216   0.824   0.351
+  149SOL    HW2  449   1.298   0.841   0.210
+  150SOL     OW  450   1.759   1.747   1.154
+  150SOL    HW1  451   1.820   1.777   1.081
+  150SOL    HW2  452   1.721   1.658   1.132
+  151SOL     OW  453   1.252   1.731   1.128
+  151SOL    HW1  454   1.336   1.736   1.074
+  151SOL    HW2  455   1.229   1.635   1.146
+  152SOL     OW  456   0.083   1.258   1.022
+  152SOL    HW1  457   0.078   1.257   1.122
+  152SOL    HW2  458   0.000   1.217   0.984
+  153SOL     OW  459   0.688   1.662   1.716
+  153SOL    HW1  460   0.632   1.743   1.725
+  153SOL    HW2  461   0.740   1.666   1.630
+  154SOL     OW  462   0.903   0.086   0.133
+  154SOL    HW1  463   0.954   0.087   0.047
+  154SOL    HW2  464   0.959   0.044   0.204
+  155SOL     OW  465   1.726   0.135   0.523
+  155SOL    HW1  466   1.799   0.118   0.456
+  155SOL    HW2  467   1.695   0.048   0.561
+  156SOL     OW  468   1.388   1.573   0.477
+  156SOL    HW1  469   1.455   1.585   0.403
+  156SOL    HW2  470   1.348   1.662   0.500
+  157SOL     OW  471   0.130   1.794   1.851
+  157SOL    HW1  472   0.089   1.720   1.904
+  157SOL    HW2  473   0.194   1.845   1.909
+  158SOL     OW  474   1.280   0.927   0.672
+  158SOL    HW1  475   1.340   0.846   0.674
+  158SOL    HW2  476   1.320   0.996   0.612
+  159SOL     OW  477   0.830   1.273   1.422
+  159SOL    HW1  478   0.825   1.306   1.517
+  159SOL    HW2  479   0.744   1.292   1.376
+  160SOL     OW  480   0.672   1.616   0.154
+  160SOL    HW1  481   0.681   1.626   0.055
+  160SOL    HW2  482   0.632   1.527   0.175
+  161SOL     OW  483   1.650   1.720   1.394
+  161SOL    HW1  484   1.703   1.730   1.310
+  161SOL    HW2  485   1.623   1.810   1.428
+  162SOL     OW  486   1.841   0.175   0.963
+  162SOL    HW1  487   1.880   0.090   0.927
+  162SOL    HW2  488   1.743   0.177   0.944
+  163SOL     OW  489   0.263   0.326   0.720
+  163SOL    HW1  490   0.184   0.377   0.686
+  163SOL    HW2  491   0.254   0.311   0.818
+  164SOL     OW  492   1.194   1.612   0.031
+  164SOL    HW1  493   1.200   1.519   0.068
+  164SOL    HW2  494   1.135   1.612  -0.049
+  165SOL     OW  495   0.822   1.002   1.372
+  165SOL    HW1  496   0.862   1.001   1.280
+  165SOL    HW2  497   0.832   1.094   1.412
+  166SOL     OW  498   0.916   0.910   0.291
+  166SOL    HW1  499   0.979   0.948   0.223
+  166SOL    HW2  500   0.956   0.827   0.330
+  167SOL     OW  501   1.504   1.607   0.044
+  167SOL    HW1  502   1.412   1.644   0.051
+  167SOL    HW2  503   1.542   1.627  -0.046
+  168SOL     OW  504   0.372   1.288   1.490
+  168SOL    HW1  505   0.359   1.381   1.456
+  168SOL    HW2  506   0.288   1.236   1.477
+  169SOL     OW  507   1.614   1.292   1.289
+  169SOL    HW1  508   1.674   1.295   1.369
+  169SOL    HW2  509   1.539   1.356   1.302
+  170SOL     OW  510   1.039   1.098   0.696
+  170SOL    HW1  511   0.969   1.051   0.750
+  170SOL    HW2  512   1.098   1.030   0.653
+  171SOL     OW  513   1.014   0.236   0.971
+  171SOL    HW1  514   1.006   0.200   0.878
+  171SOL    HW2  515   1.012   0.160   1.036
+  172SOL     OW  516   0.590   1.487   0.491
+  172SOL    HW1  517   0.632   1.429   0.421
+  172SOL    HW2  518   0.546   1.566   0.447
+  173SOL     OW  519   1.709   0.385   1.381
+  173SOL    HW1  520   1.782   0.454   1.385
+  173SOL    HW2  521   1.737   0.310   1.322
+  174SOL     OW  522   0.255   1.348   0.290
+  174SOL    HW1  523   0.159   1.349   0.263
+  174SOL    HW2  524   0.267   1.401   0.374
+  175SOL     OW  525   0.105   1.013   1.726
+  175SOL    HW1  526   0.028   0.980   1.780
+  175SOL    HW2  527   0.190   0.983   1.768
+  176SOL     OW  528   0.672   0.203   1.489
+  176SOL    HW1  529   0.762   0.187   1.449
+  176SOL    HW2  530   0.680   0.208   1.588
+  177SOL     OW  531   0.075   0.345   0.033
+  177SOL    HW1  532  -0.017   0.317   0.004
+  177SOL    HW2  533   0.106   0.422  -0.023
+  178SOL     OW  534   1.440   0.856   1.398
+  178SOL    HW1  535   1.383   0.908   1.335
+  178SOL    HW2  536   1.536   0.868   1.374
+  179SOL     OW  537   0.072   0.166   0.318
+  179SOL    HW1  538   0.055   0.249   0.264
+  179SOL    HW2  539   0.162   0.129   0.296
+  180SOL     OW  540   1.183   1.335   0.119
+  180SOL    HW1  541   1.084   1.324   0.121
+  180SOL    HW2  542   1.217   1.350   0.212
+  181SOL     OW  543   0.613   0.842   1.431
+  181SOL    HW1  544   0.669   0.923   1.414
+  181SOL    HW2  545   0.672   0.762   1.434
+  182SOL     OW  546   1.493   1.767   0.959
+  182SOL    HW1  547   1.526   1.831   0.890
+  182SOL    HW2  548   1.559   1.761   1.034
+  183SOL     OW  549   0.716   0.565   1.708
+  183SOL    HW1  550   0.735   0.630   1.782
+  183SOL    HW2  551   0.776   0.485   1.717
+  184SOL     OW  552   1.450   1.220   1.633
+  184SOL    HW1  553   1.441   1.210   1.732
+  184SOL    HW2  554   1.546   1.213   1.607
+  185SOL     OW  555   0.390   1.741   1.560
+  185SOL    HW1  556   0.299   1.782   1.558
+  185SOL    HW2  557   0.383   1.647   1.592
+  186SOL     OW  558   1.674   0.883   1.254
+  186SOL    HW1  559   1.647   0.794   1.217
+  186SOL    HW2  560   1.675   0.951   1.181
+  187SOL     OW  561   1.225   0.325   0.449
+  187SOL    HW1  562   1.290   0.251   0.438
+  187SOL    HW2  563   1.245   0.375   0.533
+  188SOL     OW  564   0.594   0.745   0.652
+  188SOL    HW1  565   0.644   0.830   0.633
+  188SOL    HW2  566   0.506   0.747   0.604
+  189SOL     OW  567   1.777   0.342   1.642
+  189SOL    HW1  568   1.760   0.373   1.548
+  189SOL    HW2  569   1.693   0.305   1.680
+  190SOL     OW  570   1.730   0.934   1.517
+  190SOL    HW1  571   1.768   1.025   1.532
+  190SOL    HW2  572   1.722   0.917   1.418
+  191SOL     OW  573   0.859   1.374   0.016
+  191SOL    HW1  574   0.813   1.389   0.104
+  191SOL    HW2  575   0.903   1.459  -0.014
+  192SOL     OW  576   0.661   1.790   0.953
+  192SOL    HW1  577   0.615   1.878   0.940
+  192SOL    HW2  578   0.760   1.802   0.946
+  193SOL     OW  579   0.859   0.956   0.861
+  193SOL    HW1  580   0.913   0.887   0.909
+  193SOL    HW2  581   0.827   1.025   0.927
+  194SOL     OW  582   1.083   0.984   0.087
+  194SOL    HW1  583   1.060   1.037   0.005
+  194SOL    HW2  584   1.164   0.928   0.068
+  195SOL     OW  585   0.221   1.314   1.844
+  195SOL    HW1  586   0.156   1.241   1.823
+  195SOL    HW2  587   0.225   1.328   1.942
+  196SOL     OW  588   0.079   1.240   0.653
+  196SOL    HW1  589   0.078   1.193   0.741
+  196SOL    HW2  590   0.161   1.212   0.602
+  197SOL     OW  591   0.672   1.391   1.624
+  197SOL    HW1  592   0.594   1.341   1.662
+  197SOL    HW2  593   0.669   1.486   1.655
+  198SOL     OW  594   1.824   0.192   1.227
+  198SOL    HW1  595   1.820   0.102   1.271
+  198SOL    HW2  596   1.827   0.181   1.128
+  199SOL     OW  597   0.428   0.424   0.520
+  199SOL    HW1  598   0.458   0.352   0.458
+  199SOL    HW2  599   0.389   0.384   0.603
+  200SOL     OW  600   1.705   1.487   1.104
+  200SOL    HW1  601   1.612   1.462   1.077
+  200SOL    HW2  602   1.731   1.437   1.186
+  201SOL     OW  603   0.317   0.547   1.280
+  201SOL    HW1  604   0.355   0.488   1.352
+  201SOL    HW2  605   0.357   0.521   1.192
+  202SOL     OW  606   0.812   1.586   0.687
+  202SOL    HW1  607   0.844   1.596   0.593
+  202SOL    HW2  608   0.733   1.524   0.689
+  203SOL     OW  609   1.424   0.214   1.112
+  203SOL    HW1  610   1.476   0.149   1.167
+  203SOL    HW2  611   1.375   0.277   1.173
+  204SOL     OW  612   1.001   0.034   1.154
+  204SOL    HW1  613   0.938  -0.038   1.123
+  204SOL    HW2  614   1.094  -0.002   1.154
+  205SOL     OW  615   0.770   1.330   0.301
+  205SOL    HW1  616   0.724   1.243   0.318
+  205SOL    HW2  617   0.861   1.327   0.342
+  206SOL     OW  618   0.618   1.567   1.284
+  206SOL    HW1  619   0.613   1.649   1.341
+  206SOL    HW2  620   0.707   1.564   1.239
+  207SOL     OW  621   1.352   0.052   0.168
+  207SOL    HW1  622   1.387   0.011   0.084
+  207SOL    HW2  623   1.262   0.014   0.188
+  208SOL     OW  624   1.300   0.453   0.691
+  208SOL    HW1  625   1.241   0.533   0.695
+  208SOL    HW2  626   1.315   0.418   0.784
+  209SOL     OW  627   1.593   0.221   0.882
+  209SOL    HW1  628   1.509   0.220   0.936
+  209SOL    HW2  629   1.595   0.304   0.826
+  210SOL     OW  630   0.039   1.077   0.300
+  210SOL    HW1  631   0.138   1.066   0.291
+  210SOL    HW2  632  -0.001   0.991   0.332
+  211SOL     OW  633   0.875   1.646   0.337
+  211SOL    HW1  634   0.798   1.611   0.283
+  211SOL    HW2  635   0.843   1.717   0.399
+  212SOL     OW  636   0.297   1.897   2.033
+  212SOL    HW1  637   0.346   1.981   2.012
+  212SOL    HW2  638   0.359   1.832   2.078
+  213SOL     OW  639   0.903   1.948   1.995
+  213SOL    HW1  640   0.954   1.949   1.909
+  213SOL    HW2  641   0.959   1.906   2.066
+  214SOL     OW  642   1.889   1.596   1.979
+  214SOL    HW1  643   1.870   1.500   2.000
+  214SOL    HW2  644   1.856   1.654   2.054
+  215SOL     OW  645   2.183   0.943   2.104
+  215SOL    HW1  646   2.265   0.982   2.062
+  215SOL    HW2  647   2.156   0.861   2.055
+  216SOL     OW  648   1.934   0.166   2.180
+  216SOL    HW1  649   1.917   0.249   2.126
+  216SOL    HW2  650   2.024   0.129   2.158
+  217SOL     OW  651   2.064   2.147   1.498
+  217SOL    HW1  652   1.984   2.207   1.485
+  217SOL    HW2  653   2.054   2.098   1.584
+  218SOL     OW  654   2.169   1.925   0.618
+  218SOL    HW1  655   2.158   2.019   0.651
+  218SOL    HW2  656   2.164   1.862   0.695
+  219SOL     OW  657   1.937   2.207   1.895
+  219SOL    HW1  658   1.845   2.179   1.866
+  219SOL    HW2  659   1.968   2.284   1.839
+   2.00000   2.00000   2.00000
diff --git a/examples/methane/md.mdp b/examples/methane/md.mdp
new file mode 100755
index 0000000000..1c8bded8af
--- /dev/null
+++ b/examples/methane/md.mdp
@@ -0,0 +1,56 @@
+; TI/FEP mdp template for solution
+; Note: this is for Gromacs 2016 and later
+integrator = sd
+ld-seed = -1
+bd-fric = 0
+dt = 0.001
+nsteps = 100
+nstcomm = 5
+
+nstxout = 5
+nstvout = 0
+nstfout = 0
+nstlog = 5
+nstenergy = 5
+nstxout-compressed = 0
+
+tcoupl = no
+nsttcouple = 10
+tc_grps = System
+tau_t = 0.2
+ref_t = 298
+
+constraints = h-bonds
+constraint_algorithm = lincs
+lincs_order = 4
+lincs_warnangle = 30
+
+comm-mode = Linear
+
+cutoff-scheme = verlet
+nstlist = 80
+ns_type = grid
+pbc = xyz
+rlist = 0.8
+
+coulombtype = pme
+coulomb-modifier = none
+rcoulomb = 0.8
+fourierspacing = 0.1
+pme_order = 4
+ewald_rtol = 1e-05
+
+vdwtype = cut-off
+vdw-modifier = none
+rvdw = 0.8
+DispCorr = AllEnerPres
+
+pcoupl = Parrinello-Rahman
+pcoupltype = isotropic
+tau_p = 5.0
+compressibility = 4.5e-05
+ref_p = 1.0
+refcoord-scaling = com
+
+gen-vel = yes
+continuation = no
diff --git a/examples/methane/methane.itp b/examples/methane/methane.itp
new file mode 100644
index 0000000000..d9cec75123
--- /dev/null
+++ b/examples/methane/methane.itp
@@ -0,0 +1,31 @@
+[ atomtypes ]
+;name btype  mass  charge ptype    sigma  epsilon
+  c3    c3   0.0     0.0     A 0.339771 0.451035
+  hc    hc   0.0     0.0     A 0.260018 0.087027
+
+[ moleculetype ]
+;name            nrexcl
+ methane          3
+
+[ atoms ]
+; nr type  resnr residue atom  cgnr  charge   mass
+  1   c3      1     MOL   C1     1 -0.1068 12.010
+  2   hc      1     MOL   H1     2  0.0267  1.008
+  3   hc      1     MOL   H2     3  0.0267  1.008
+  4   hc      1     MOL   H3     4  0.0267  1.008
+  5   hc      1     MOL   H4     5  0.0267  1.008
+
+[ bonds ]
+; i  j  func  b0  kb
+ 1  2     5        
+ 1  3     5        
+ 1  4     5        
+ 1  5     5        
+
+[ exclusions ]
+; ai  aj1  aj2  aj3  aj4
+  1    2    3    4    5
+  2    1    3    4    5
+  3    1    2    4    5
+  4    1    2    3    5
+  5    1    2    3    4
diff --git a/examples/methane/run.sh b/examples/methane/run.sh
new file mode 100644
index 0000000000..ef4178b05f
--- /dev/null
+++ b/examples/methane/run.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+gmx_mpi grompp -f md.mdp -c lig_solv.gro -p topol.top -o md.tpr -maxwarn 3;
+export GMX_DEEPMD_INPUT_JSON=input.json
+gmx_mpi mdrun -deffnm md;
\ No newline at end of file
diff --git a/examples/methane/topol.top b/examples/methane/topol.top
new file mode 100644
index 0000000000..58350bc91d
--- /dev/null
+++ b/examples/methane/topol.top
@@ -0,0 +1,15 @@
+#include "amber99sb.ff/forcefield.itp"
+
+; Include methane_GMX.itp topology
+#include "methane.itp"
+
+; Include water topology
+#include "amber99sb.ff/tip3p.itp"
+
+[ system ]
+methane in water
+
+[ molecules ]
+; Compound        nmols
+methane          1     
+SOL               218
diff --git a/examples/methane/type.raw b/examples/methane/type.raw
new file mode 100644
index 0000000000..13d50a8964
--- /dev/null
+++ b/examples/methane/type.raw
@@ -0,0 +1 @@
+1 0 0 0 0
\ No newline at end of file
diff --git a/examples/water/gmx/index.raw b/examples/water/gmx/index.raw
new file mode 100644
index 0000000000..0b9395650e
--- /dev/null
+++ b/examples/water/gmx/index.raw
@@ -0,0 +1 @@
+0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
\ No newline at end of file
diff --git a/examples/water/gmx/input.json b/examples/water/gmx/input.json
new file mode 100644
index 0000000000..c4a5113f1b
--- /dev/null
+++ b/examples/water/gmx/input.json
@@ -0,0 +1,6 @@
+{
+    "graph_file" : "frozen_model.pb",
+    "type_file": "type.raw",
+    "index_file": "index.raw",
+    "lambda": 1.0
+}
diff --git a/examples/water/gmx/md.mdp b/examples/water/gmx/md.mdp
new file mode 100755
index 0000000000..058040ea09
--- /dev/null
+++ b/examples/water/gmx/md.mdp
@@ -0,0 +1,49 @@
+integrator               = md
+ld-seed                  = -1
+bd-fric                  = 0
+dt                       = 0.0005
+nsteps                   = 1000000
+nstcomm                  = 100
+
+nstxout                  = 100  ; != nstdhdl (in case of -rerun)
+nstvout                  = 0
+nstfout                  = 0
+nstlog                   = 0
+nstenergy                = 100
+nstxout-compressed       = 0
+
+tcoupl                   = nose-hoover
+nsttcouple               = 10
+tc_grps                  = System
+tau_t                    = 0.5
+ref_t                    = 298.0
+
+constraints              = none
+constraint_algorithm     = Lincs
+lincs_order              = 4
+lincs_warnangle          = 30
+
+comm-mode                = Linear
+
+cutoff-scheme            = Verlet
+nstlist                  = 10
+ns_type                  = grid
+pbc                      = xyz
+rlist                    = 0.8
+
+coulombtype              = cutoff
+coulomb-modifier         = none
+rcoulomb                 = 0.8
+fourierspacing           = 0.1
+pme_order                = 4
+ewald_rtol               = 1.0E-5
+
+vdwtype                  = cut-off
+vdw-modifier             = none
+rvdw                     = 0.8
+DispCorr                 = AllEnerPres
+
+pcoupl                   = no
+
+gen-vel                  = yes
+
diff --git a/examples/water/gmx/md.sh b/examples/water/gmx/md.sh
new file mode 100755
index 0000000000..9a5f19ee8f
--- /dev/null
+++ b/examples/water/gmx/md.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+export GMX_DEEPMD_INPUT_JSON=input.json
+gmx_mpi grompp -f md.mdp -c water.gro -p water.top -o md.tpr -maxwarn 3
+gmx_mpi mdrun -deffnm md
+gmx_mpi rdf -f md.trr -s md.tpr -o md_rdf.xvg -ref "name OW" -sel "name OW"
\ No newline at end of file
diff --git a/examples/water/gmx/rdf.png b/examples/water/gmx/rdf.png
new file mode 100644
index 0000000000000000000000000000000000000000..ea87475b7b304b780ea7733eb4f45023c28d3322
GIT binary patch
literal 171170
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

HMQttV9;f4 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 literal 0 HcmV?d00001 diff --git a/examples/water/gmx/type.raw b/examples/water/gmx/type.raw new file mode 100644 index 0000000000..664d90548b --- /dev/null +++ b/examples/water/gmx/type.raw @@ -0,0 +1 @@ +0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 \ No newline at end of file diff --git a/examples/water/gmx/water.gro b/examples/water/gmx/water.gro new file mode 100644 index 0000000000..5b365e3ba0 --- /dev/null +++ b/examples/water/gmx/water.gro @@ -0,0 +1,771 @@ +lw_256.pdb +768 + 1SOL OW 1 0.688 0.269 0.813 + 1SOL HW1 2 0.711 0.197 0.878 + 1SOL HW2 3 0.673 0.351 0.863 + 2SOL OW 4 1.194 0.162 0.823 + 2SOL HW1 5 1.107 0.131 0.843 + 2SOL HW2 6 1.194 0.194 0.732 + 3SOL OW 7 0.218 1.736 0.871 + 3SOL HW1 8 0.248 1.725 0.968 + 3SOL HW2 9 0.126 1.767 0.876 + 4SOL OW 10 0.024 1.133 0.467 + 4SOL HW1 11 0.054 1.159 0.382 + 4SOL HW2 12 0.099 1.132 0.526 + 5SOL OW 13 0.569 0.801 -0.014 + 5SOL HW1 14 0.631 0.776 0.059 + 5SOL HW2 15 0.569 0.899 -0.034 + 6SOL OW 16 0.089 0.773 0.648 + 6SOL HW1 17 0.019 0.834 0.682 + 6SOL HW2 18 0.109 0.708 0.718 + 7SOL OW 19 1.735 0.616 0.112 + 7SOL HW1 20 1.680 0.610 0.034 + 7SOL HW2 21 1.799 0.537 0.107 + 8SOL OW 22 1.252 1.097 1.469 + 8SOL HW1 23 1.201 1.173 1.424 + 8SOL HW2 24 1.315 1.067 1.406 + 9SOL OW 25 0.941 0.010 0.824 + 9SOL HW1 26 0.929 0.022 0.723 + 9SOL HW2 27 0.855 0.020 0.865 + 10SOL OW 28 0.063 1.590 0.261 + 10SOL HW1 29 -0.015 1.648 0.268 + 10SOL HW2 30 0.037 1.521 0.193 + 11SOL OW 31 0.143 0.347 1.659 + 11SOL HW1 32 0.054 0.315 1.684 + 11SOL HW2 33 0.193 0.309 1.739 + 12SOL OW 34 0.356 0.607 1.341 + 12SOL HW1 35 0.296 0.601 1.411 + 12SOL HW2 36 0.396 0.694 1.344 + 13SOL OW 37 1.354 1.718 1.183 + 13SOL HW1 38 1.278 1.769 1.146 + 13SOL HW2 39 1.377 1.754 1.267 + 14SOL OW 40 1.613 0.478 0.730 + 14SOL HW1 41 1.590 0.424 0.812 + 14SOL HW2 42 1.659 0.559 0.759 + 15SOL OW 43 0.409 1.565 1.546 + 15SOL HW1 44 0.365 1.520 1.472 + 15SOL HW2 45 0.497 1.527 1.544 + 16SOL OW 46 1.734 0.691 0.858 + 16SOL HW1 47 1.789 0.769 0.826 + 16SOL HW2 48 1.642 0.726 0.862 + 17SOL OW 49 0.937 0.449 0.917 + 17SOL HW1 50 0.949 0.380 0.982 + 17SOL HW2 51 0.847 0.482 0.912 + 18SOL OW 52 0.722 1.472 1.516 + 18SOL HW1 53 0.748 1.566 1.515 + 18SOL HW2 54 0.811 1.435 1.537 + 19SOL OW 55 0.202 1.975 0.060 + 19SOL HW1 56 0.276 1.915 0.049 + 19SOL HW2 57 0.152 1.951 0.136 + 20SOL OW 58 1.578 0.584 0.324 + 20SOL HW1 59 1.657 0.593 0.262 + 20SOL HW2 60 1.590 0.648 0.396 + 21SOL OW 61 1.294 0.324 1.015 + 21SOL HW1 62 1.262 0.267 0.938 + 21SOL HW2 63 1.264 0.413 1.003 + 22SOL OW 64 0.305 0.749 0.035 + 22SOL HW1 65 0.400 0.762 0.021 + 22SOL HW2 66 0.299 0.722 0.125 + 23SOL OW 67 0.291 1.606 0.091 + 23SOL HW1 68 0.359 1.550 0.142 + 23SOL HW2 69 0.218 1.615 0.154 + 24SOL OW 70 1.493 0.076 0.750 + 24SOL HW1 71 1.436 0.153 0.728 + 24SOL HW2 72 1.423 0.010 0.770 + 25SOL OW 73 1.226 0.603 0.935 + 25SOL HW1 74 1.205 0.696 0.955 + 25SOL HW2 75 1.136 0.570 0.944 + 26SOL OW 76 1.438 1.068 0.255 + 26SOL HW1 77 1.401 1.116 0.180 + 26SOL HW2 78 1.538 1.051 0.242 + 27SOL OW 79 0.290 1.754 0.500 + 27SOL HW1 80 0.308 1.831 0.555 + 27SOL HW2 81 0.374 1.719 0.462 + 28SOL OW 82 1.627 1.220 1.021 + 28SOL HW1 83 1.724 1.206 1.038 + 28SOL HW2 84 1.585 1.214 1.107 + 29SOL OW 85 0.252 1.745 1.159 + 29SOL HW1 86 0.273 1.673 1.213 + 29SOL HW2 87 0.331 1.799 1.165 + 30SOL OW 88 0.426 1.312 1.800 + 30SOL HW1 89 0.446 1.292 1.709 + 30SOL HW2 90 0.506 1.354 1.833 + 31SOL OW 91 0.844 0.274 1.561 + 31SOL HW1 92 0.826 0.328 1.642 + 31SOL HW2 93 0.765 0.213 1.573 + 32SOL OW 94 1.254 0.446 1.284 + 32SOL HW1 95 1.343 0.444 1.332 + 32SOL HW2 96 1.271 0.404 1.195 + 33SOL OW 97 1.000 1.155 1.767 + 33SOL HW1 98 1.067 1.154 1.839 + 33SOL HW2 99 1.021 1.075 1.716 + 34SOL OW 100 0.123 1.162 0.237 + 34SOL HW1 101 0.120 1.079 0.193 + 34SOL HW2 102 0.100 1.235 0.177 + 35SOL OW 103 0.701 1.396 1.259 + 35SOL HW1 104 0.708 1.397 1.356 + 35SOL HW2 105 0.606 1.362 1.226 + 36SOL OW 106 0.570 0.117 0.160 + 36SOL HW1 107 0.636 0.171 0.207 + 36SOL HW2 108 0.592 0.025 0.181 + 37SOL OW 109 0.682 1.157 1.523 + 37SOL HW1 110 0.594 1.194 1.532 + 37SOL HW2 111 0.745 1.224 1.565 + 38SOL OW 112 0.570 0.545 1.493 + 38SOL HW1 113 0.481 0.589 1.467 + 38SOL HW2 114 0.601 0.582 1.580 + 39SOL OW 115 0.879 0.069 0.586 + 39SOL HW1 116 0.938 0.020 0.526 + 39SOL HW2 117 0.864 0.158 0.546 + 40SOL OW 118 1.388 1.301 1.687 + 40SOL HW1 119 1.360 1.213 1.671 + 40SOL HW2 120 1.467 1.305 1.624 + 41SOL OW 121 0.399 0.218 0.761 + 41SOL HW1 122 0.385 0.267 0.666 + 41SOL HW2 123 0.494 0.223 0.773 + 42SOL OW 124 0.037 1.500 1.087 + 42SOL HW1 125 0.006 1.534 1.176 + 42SOL HW2 126 -0.031 1.528 1.031 + 43SOL OW 127 1.429 0.103 1.216 + 43SOL HW1 128 1.331 0.128 1.212 + 43SOL HW2 129 1.464 0.155 1.291 + 44SOL OW 130 1.892 0.735 1.396 + 44SOL HW1 131 1.951 0.691 1.454 + 44SOL HW2 132 1.877 0.826 1.434 + 45SOL OW 133 1.379 0.800 0.584 + 45SOL HW1 134 1.330 0.877 0.609 + 45SOL HW2 135 1.363 0.798 0.490 + 46SOL OW 136 0.242 1.158 0.618 + 46SOL HW1 137 0.295 1.231 0.590 + 46SOL HW2 138 0.291 1.074 0.621 + 47SOL OW 139 0.625 0.469 0.989 + 47SOL HW1 140 0.677 0.546 1.017 + 47SOL HW2 141 0.560 0.458 1.065 + 48SOL OW 142 1.651 0.757 0.552 + 48SOL HW1 143 1.563 0.784 0.585 + 48SOL HW2 144 1.686 0.843 0.526 + 49SOL OW 145 0.046 0.124 0.569 + 49SOL HW1 146 -0.035 0.116 0.620 + 49SOL HW2 147 0.124 0.099 0.631 + 50SOL OW 148 0.106 1.936 0.325 + 50SOL HW1 149 0.071 1.996 0.399 + 50SOL HW2 150 0.164 1.863 0.357 + 51SOL OW 151 0.913 0.282 1.132 + 51SOL HW1 152 0.928 0.328 1.220 + 51SOL HW2 153 0.831 0.242 1.162 + 52SOL OW 154 0.932 0.803 0.888 + 52SOL HW1 155 0.937 0.738 0.811 + 52SOL HW2 156 1.018 0.819 0.929 + 53SOL OW 157 0.983 0.426 1.360 + 53SOL HW1 158 0.975 0.392 1.449 + 53SOL HW2 159 1.083 0.445 1.350 + 54SOL OW 160 1.036 1.771 1.388 + 54SOL HW1 161 1.023 1.866 1.418 + 54SOL HW2 162 1.118 1.743 1.432 + 55SOL OW 163 0.834 1.911 1.753 + 55SOL HW1 164 0.910 1.960 1.790 + 55SOL HW2 165 0.774 1.968 1.709 + 56SOL OW 166 1.587 1.493 1.288 + 56SOL HW1 167 1.608 1.455 1.375 + 56SOL HW2 168 1.513 1.439 1.264 + 57SOL OW 169 0.545 0.221 1.864 + 57SOL HW1 170 0.553 0.153 1.938 + 57SOL HW2 171 0.448 0.245 1.877 + 58SOL OW 172 0.565 0.504 0.253 + 58SOL HW1 173 0.483 0.473 0.299 + 58SOL HW2 174 0.611 0.430 0.224 + 59SOL OW 175 1.280 1.446 1.204 + 59SOL HW1 176 1.210 1.419 1.270 + 59SOL HW2 177 1.292 1.544 1.208 + 60SOL OW 178 0.613 1.319 0.909 + 60SOL HW1 179 0.709 1.325 0.911 + 60SOL HW2 180 0.602 1.233 0.867 + 61SOL OW 181 0.981 0.365 -0.023 + 61SOL HW1 182 0.993 0.359 0.076 + 61SOL HW2 183 0.892 0.392 -0.052 + 62SOL OW 184 0.872 1.348 0.053 + 62SOL HW1 185 0.872 1.250 0.040 + 62SOL HW2 186 0.968 1.373 0.048 + 63SOL OW 187 1.917 1.795 0.738 + 63SOL HW1 188 1.841 1.846 0.704 + 63SOL HW2 189 1.941 1.728 0.665 + 64SOL OW 190 1.450 0.480 1.445 + 64SOL HW1 191 1.538 0.505 1.422 + 64SOL HW2 192 1.416 0.552 1.503 + 65SOL OW 193 0.080 1.611 0.558 + 65SOL HW1 194 0.059 1.595 0.470 + 65SOL HW2 195 0.162 1.667 0.551 + 66SOL OW 196 0.791 1.611 0.665 + 66SOL HW1 197 0.748 1.699 0.670 + 66SOL HW2 198 0.820 1.590 0.756 + 67SOL OW 199 0.489 0.937 1.648 + 67SOL HW1 200 0.509 0.923 1.556 + 67SOL HW2 201 0.561 0.880 1.685 + 68SOL OW 202 1.470 0.778 0.887 + 68SOL HW1 203 1.421 0.730 0.815 + 68SOL HW2 204 1.477 0.871 0.853 + 69SOL OW 205 0.258 0.002 0.676 + 69SOL HW1 206 0.243 -0.057 0.753 + 69SOL HW2 207 0.323 0.065 0.705 + 70SOL OW 208 0.333 0.903 0.583 + 70SOL HW1 209 0.249 0.862 0.612 + 70SOL HW2 210 0.387 0.836 0.548 + 71SOL OW 211 0.521 1.917 0.861 + 71SOL HW1 212 0.503 1.820 0.881 + 71SOL HW2 213 0.440 1.970 0.889 + 72SOL OW 214 0.554 1.035 0.772 + 72SOL HW1 215 0.496 1.011 0.695 + 72SOL HW2 216 0.651 1.014 0.757 + 73SOL OW 217 1.351 1.770 0.176 + 73SOL HW1 218 1.293 1.697 0.212 + 73SOL HW2 219 1.377 1.823 0.253 + 74SOL OW 220 1.683 0.585 1.286 + 74SOL HW1 221 1.632 0.660 1.251 + 74SOL HW2 222 1.746 0.634 1.339 + 75SOL OW 223 0.480 0.745 0.435 + 75SOL HW1 224 0.552 0.686 0.474 + 75SOL HW2 225 0.519 0.831 0.409 + 76SOL OW 226 0.134 0.953 0.066 + 76SOL HW1 227 0.197 0.881 0.041 + 76SOL HW2 228 0.077 0.902 0.118 + 77SOL OW 229 0.711 1.431 0.291 + 77SOL HW1 230 0.716 1.358 0.356 + 77SOL HW2 231 0.750 1.400 0.212 + 78SOL OW 232 0.941 1.673 1.171 + 78SOL HW1 233 0.988 1.733 1.239 + 78SOL HW2 234 0.975 1.582 1.177 + 79SOL OW 235 1.936 0.785 0.209 + 79SOL HW1 236 1.866 0.748 0.147 + 79SOL HW2 237 1.952 0.715 0.276 + 80SOL OW 238 1.350 0.437 1.868 + 80SOL HW1 239 1.403 0.359 1.876 + 80SOL HW2 240 1.352 0.475 1.958 + 81SOL OW 241 0.131 0.381 0.555 + 81SOL HW1 242 0.086 0.294 0.567 + 81SOL HW2 243 0.142 0.420 0.646 + 82SOL OW 244 0.150 0.598 1.545 + 82SOL HW1 245 0.136 0.514 1.593 + 82SOL HW2 246 0.200 0.659 1.612 + 83SOL OW 247 1.552 0.966 1.566 + 83SOL HW1 248 1.641 0.960 1.526 + 83SOL HW2 249 1.491 0.968 1.488 + 84SOL OW 250 0.641 0.114 1.645 + 84SOL HW1 251 0.566 0.072 1.594 + 84SOL HW2 252 0.590 0.141 1.720 + 85SOL OW 253 0.565 1.650 1.180 + 85SOL HW1 254 0.626 1.583 1.216 + 85SOL HW2 255 0.566 1.722 1.248 + 86SOL OW 256 0.804 1.612 0.944 + 86SOL HW1 257 0.710 1.631 0.942 + 86SOL HW2 258 0.832 1.626 1.035 + 87SOL OW 259 0.537 1.705 0.424 + 87SOL HW1 260 0.583 1.617 0.447 + 87SOL HW2 261 0.565 1.777 0.495 + 88SOL OW 262 1.140 0.133 1.212 + 88SOL HW1 263 1.080 0.200 1.181 + 88SOL HW2 264 1.109 0.100 1.299 + 89SOL OW 265 1.700 0.173 0.143 + 89SOL HW1 266 1.677 0.181 0.240 + 89SOL HW2 267 1.768 0.246 0.134 + 90SOL OW 268 1.255 1.961 1.641 + 90SOL HW1 269 1.267 1.969 1.734 + 90SOL HW2 270 1.263 1.856 1.627 + 91SOL OW 271 0.217 1.044 1.516 + 91SOL HW1 272 0.149 1.077 1.451 + 91SOL HW2 273 0.165 1.022 1.595 + 92SOL OW 274 0.055 0.648 0.430 + 92SOL HW1 275 0.063 0.710 0.516 + 92SOL HW2 276 0.059 0.555 0.468 + 93SOL OW 277 0.636 1.402 0.623 + 93SOL HW1 278 0.694 1.479 0.642 + 93SOL HW2 279 0.615 1.355 0.706 + 94SOL OW 280 1.269 0.044 -0.020 + 94SOL HW1 281 1.177 0.068 -0.024 + 94SOL HW2 282 1.267 -0.024 0.050 + 95SOL OW 283 0.445 1.448 0.252 + 95SOL HW1 284 0.415 1.451 0.346 + 95SOL HW2 285 0.547 1.457 0.258 + 96SOL OW 286 0.006 1.531 1.663 + 96SOL HW1 287 0.029 1.619 1.611 + 96SOL HW2 288 0.070 1.514 1.733 + 97SOL OW 289 0.759 0.691 1.058 + 97SOL HW1 290 0.778 0.681 1.149 + 97SOL HW2 291 0.838 0.735 1.025 + 98SOL OW 292 1.617 1.353 1.509 + 98SOL HW1 293 1.643 1.265 1.471 + 98SOL HW2 294 1.662 1.360 1.597 + 99SOL OW 295 1.348 1.807 0.812 + 99SOL HW1 296 1.292 1.826 0.892 + 99SOL HW2 297 1.280 1.809 0.744 + 100SOL OW 298 0.756 0.274 0.246 + 100SOL HW1 299 0.847 0.243 0.209 + 100SOL HW2 300 0.772 0.306 0.339 + 101SOL OW 301 1.924 1.413 0.731 + 101SOL HW1 302 1.979 1.475 0.678 + 101SOL HW2 303 1.876 1.464 0.797 + 102SOL OW 304 0.684 0.099 1.251 + 102SOL HW1 305 0.678 0.021 1.310 + 102SOL HW2 306 0.672 0.181 1.302 + 103SOL OW 307 0.425 0.002 1.523 + 103SOL HW1 308 0.362 0.077 1.486 + 103SOL HW2 309 0.389 -0.049 1.591 + 104SOL OW 310 0.679 1.743 1.911 + 104SOL HW1 311 0.729 1.812 1.867 + 104SOL HW2 312 0.709 1.647 1.901 + 105SOL OW 313 1.685 1.405 0.123 + 105SOL HW1 314 1.629 1.337 0.073 + 105SOL HW2 315 1.643 1.491 0.103 + 106SOL OW 316 0.675 1.468 1.822 + 106SOL HW1 317 0.727 1.431 1.895 + 106SOL HW2 318 0.734 1.473 1.743 + 107SOL OW 319 1.267 1.324 0.953 + 107SOL HW1 320 1.282 1.375 1.037 + 107SOL HW2 321 1.337 1.355 0.890 + 108SOL OW 322 1.608 1.748 1.070 + 108SOL HW1 323 1.514 1.742 1.102 + 108SOL HW2 324 1.622 1.846 1.059 + 109SOL OW 325 0.194 1.145 1.823 + 109SOL HW1 326 0.171 1.074 1.889 + 109SOL HW2 327 0.242 1.212 1.873 + 110SOL OW 328 1.856 0.260 1.798 + 110SOL HW1 329 1.790 0.250 1.725 + 110SOL HW2 330 1.897 0.170 1.809 + 111SOL OW 331 0.823 1.011 0.766 + 111SOL HW1 332 0.855 0.965 0.686 + 111SOL HW2 333 0.853 0.941 0.823 + 112SOL OW 334 0.787 0.618 1.329 + 112SOL HW1 335 0.711 0.587 1.383 + 112SOL HW2 336 0.855 0.553 1.343 + 113SOL OW 337 1.170 0.852 1.029 + 113SOL HW1 338 1.198 0.847 1.124 + 113SOL HW2 339 1.212 0.929 0.987 + 114SOL OW 340 0.360 1.389 0.532 + 114SOL HW1 341 0.458 1.399 0.545 + 114SOL HW2 342 0.327 1.444 0.602 + 115SOL OW 343 1.488 1.702 1.448 + 115SOL HW1 344 1.570 1.735 1.499 + 115SOL HW2 345 1.520 1.626 1.401 + 116SOL OW 346 1.649 1.123 1.276 + 116SOL HW1 347 1.574 1.067 1.283 + 116SOL HW2 348 1.708 1.072 1.219 + 117SOL OW 349 1.753 0.057 0.695 + 117SOL HW1 350 1.782 0.140 0.745 + 117SOL HW2 351 1.657 0.055 0.701 + 118SOL OW 352 1.873 0.956 0.766 + 118SOL HW1 353 1.936 1.024 0.807 + 118SOL HW2 354 1.806 0.997 0.711 + 119SOL OW 355 1.655 0.149 0.426 + 119SOL HW1 356 1.596 0.222 0.452 + 119SOL HW2 357 1.724 0.164 0.489 + 120SOL OW 358 1.210 0.789 1.288 + 120SOL HW1 359 1.121 0.827 1.309 + 120SOL HW2 360 1.216 0.694 1.278 + 121SOL OW 361 1.768 1.290 0.541 + 121SOL HW1 362 1.858 1.250 0.509 + 121SOL HW2 363 1.788 1.343 0.620 + 122SOL OW 364 0.829 1.074 1.267 + 122SOL HW1 365 0.791 1.109 1.356 + 122SOL HW2 366 0.875 1.148 1.223 + 123SOL OW 367 0.036 -0.003 0.992 + 123SOL HW1 368 -0.012 -0.028 0.906 + 123SOL HW2 369 -0.019 0.057 1.043 + 124SOL OW 370 1.028 0.081 1.471 + 124SOL HW1 371 1.098 0.072 1.537 + 124SOL HW2 372 0.959 0.139 1.519 + 125SOL OW 373 0.271 1.382 1.033 + 125SOL HW1 374 0.191 1.429 1.056 + 125SOL HW2 375 0.237 1.290 1.020 + 126SOL OW 376 1.555 0.372 0.950 + 126SOL HW1 377 1.459 0.378 0.979 + 126SOL HW2 378 1.568 0.277 0.967 + 127SOL OW 379 1.052 0.569 0.373 + 127SOL HW1 380 1.032 0.647 0.319 + 127SOL HW2 381 1.124 0.600 0.425 + 128SOL OW 382 0.183 0.534 0.762 + 128SOL HW1 383 0.279 0.552 0.758 + 128SOL HW2 384 0.163 0.513 0.857 + 129SOL OW 385 0.927 1.602 0.405 + 129SOL HW1 386 0.847 1.557 0.373 + 129SOL HW2 387 0.930 1.601 0.504 + 130SOL OW 388 1.340 0.255 0.235 + 130SOL HW1 389 1.396 0.222 0.169 + 130SOL HW2 390 1.344 0.347 0.211 + 131SOL OW 391 0.340 0.194 0.287 + 131SOL HW1 392 0.293 0.110 0.302 + 131SOL HW2 393 0.414 0.170 0.220 + 132SOL OW 394 1.812 0.154 1.189 + 132SOL HW1 395 1.835 0.248 1.201 + 132SOL HW2 396 1.752 0.150 1.117 + 133SOL OW 397 0.058 1.775 1.556 + 133SOL HW1 398 0.141 1.794 1.614 + 133SOL HW2 399 0.076 1.817 1.467 + 134SOL OW 400 1.797 1.604 0.924 + 134SOL HW1 401 1.814 1.687 0.871 + 134SOL HW2 402 1.720 1.630 0.988 + 135SOL OW 403 1.109 1.331 0.656 + 135SOL HW1 404 1.190 1.279 0.665 + 135SOL HW2 405 1.078 1.303 0.563 + 136SOL OW 406 0.822 1.721 1.552 + 136SOL HW1 407 0.886 1.735 1.486 + 136SOL HW2 408 0.837 1.788 1.624 + 137SOL OW 409 1.558 0.791 1.154 + 137SOL HW1 410 1.509 0.758 1.077 + 137SOL HW2 411 1.640 0.825 1.115 + 138SOL OW 412 1.030 1.872 0.422 + 138SOL HW1 413 1.115 1.860 0.468 + 138SOL HW2 414 0.996 1.782 0.408 + 139SOL OW 415 0.273 0.267 1.887 + 139SOL HW1 416 0.226 0.193 1.932 + 139SOL HW2 417 0.252 0.340 1.955 + 140SOL OW 418 0.929 1.312 1.157 + 140SOL HW1 419 0.934 1.305 1.063 + 140SOL HW2 420 0.843 1.355 1.174 + 141SOL OW 421 1.438 0.203 1.481 + 141SOL HW1 422 1.363 0.158 1.525 + 141SOL HW2 423 1.457 0.294 1.497 + 142SOL OW 424 1.489 1.868 0.409 + 142SOL HW1 425 1.530 1.952 0.424 + 142SOL HW2 426 1.560 1.802 0.424 + 143SOL OW 427 0.408 1.814 -0.006 + 143SOL HW1 428 0.381 1.730 0.031 + 143SOL HW2 429 0.506 1.800 -0.024 + 144SOL OW 430 0.269 1.474 1.345 + 144SOL HW1 431 0.333 1.413 1.310 + 144SOL HW2 432 0.182 1.423 1.360 + 145SOL OW 433 1.811 0.919 0.414 + 145SOL HW1 434 1.861 0.867 0.343 + 145SOL HW2 435 1.880 0.994 0.431 + 146SOL OW 436 1.838 0.996 1.535 + 146SOL HW1 437 1.813 1.052 1.602 + 146SOL HW2 438 1.880 1.048 1.463 + 147SOL OW 439 1.817 1.817 0.274 + 147SOL HW1 440 1.776 1.857 0.201 + 147SOL HW2 441 1.906 1.867 0.288 + 148SOL OW 442 1.261 1.063 0.877 + 148SOL HW1 443 1.359 1.053 0.857 + 148SOL HW2 444 1.257 1.149 0.931 + 149SOL OW 445 1.036 1.732 0.861 + 149SOL HW1 446 0.994 1.828 0.855 + 149SOL HW2 447 0.960 1.686 0.898 + 150SOL OW 448 0.190 1.479 1.825 + 150SOL HW1 449 0.265 1.421 1.805 + 150SOL HW2 450 0.217 1.538 1.905 + 151SOL OW 451 1.180 1.862 1.059 + 151SOL HW1 452 1.150 1.949 1.096 + 151SOL HW2 453 1.101 1.816 1.022 + 152SOL OW 454 1.170 1.558 0.278 + 152SOL HW1 455 1.206 1.520 0.360 + 152SOL HW2 456 1.085 1.585 0.313 + 153SOL OW 457 0.268 0.110 0.972 + 153SOL HW1 458 0.298 0.167 0.900 + 153SOL HW2 459 0.185 0.060 0.948 + 154SOL OW 460 0.798 0.893 1.614 + 154SOL HW1 461 0.863 0.858 1.550 + 154SOL HW2 462 0.746 0.971 1.576 + 155SOL OW 463 0.992 0.652 0.000 + 155SOL HW1 464 0.901 0.631 0.038 + 155SOL HW2 465 1.034 0.568 -0.021 + 156SOL OW 466 0.900 0.670 0.647 + 156SOL HW1 467 0.801 0.652 0.630 + 156SOL HW2 468 0.948 0.584 0.655 + 157SOL OW 469 0.529 1.660 0.932 + 157SOL HW1 470 0.479 1.589 0.883 + 157SOL HW2 471 0.522 1.648 1.030 + 158SOL OW 472 1.316 0.637 1.664 + 158SOL HW1 473 1.360 0.720 1.692 + 158SOL HW2 474 1.345 0.562 1.728 + 159SOL OW 475 1.157 1.592 0.653 + 159SOL HW1 476 1.143 1.493 0.673 + 159SOL HW2 477 1.109 1.627 0.732 + 160SOL OW 478 0.667 0.691 1.718 + 160SOL HW1 479 0.700 0.757 1.657 + 160SOL HW2 480 0.645 0.734 1.800 + 161SOL OW 481 1.134 0.366 1.660 + 161SOL HW1 482 1.084 0.369 1.741 + 161SOL HW2 483 1.225 0.343 1.680 + 162SOL OW 484 1.800 0.437 1.099 + 162SOL HW1 485 1.715 0.433 1.052 + 162SOL HW2 486 1.766 0.499 1.171 + 163SOL OW 487 0.382 0.341 0.523 + 163SOL HW1 488 0.291 0.375 0.515 + 163SOL HW2 489 0.390 0.266 0.459 + 164SOL OW 490 1.218 1.021 0.623 + 164SOL HW1 491 1.280 1.087 0.571 + 164SOL HW2 492 1.225 1.047 0.712 + 165SOL OW 493 0.434 0.872 1.351 + 165SOL HW1 494 0.501 0.914 1.292 + 165SOL HW2 495 0.351 0.910 1.339 + 166SOL OW 496 0.190 0.380 0.173 + 166SOL HW1 497 0.209 0.465 0.218 + 166SOL HW2 498 0.232 0.308 0.225 + 167SOL OW 499 1.881 1.232 1.079 + 167SOL HW1 500 1.903 1.330 1.099 + 167SOL HW2 501 1.951 1.205 1.009 + 168SOL OW 502 1.100 1.284 1.342 + 168SOL HW1 503 1.044 1.309 1.416 + 168SOL HW2 504 1.038 1.286 1.265 + 169SOL OW 505 1.364 0.517 0.170 + 169SOL HW1 506 1.332 0.604 0.153 + 169SOL HW2 507 1.447 0.531 0.220 + 170SOL OW 508 0.444 0.414 1.178 + 170SOL HW1 509 0.477 0.361 1.256 + 170SOL HW2 510 0.405 0.495 1.226 + 171SOL OW 511 0.279 0.626 0.275 + 171SOL HW1 512 0.350 0.670 0.329 + 171SOL HW2 513 0.193 0.644 0.316 + 172SOL OW 514 1.178 1.487 1.711 + 172SOL HW1 515 1.165 1.479 1.813 + 172SOL HW2 516 1.241 1.415 1.692 + 173SOL OW 517 0.642 0.593 0.594 + 173SOL HW1 518 0.574 0.591 0.668 + 173SOL HW2 519 0.645 0.504 0.551 + 174SOL OW 520 1.434 0.947 1.331 + 174SOL HW1 521 1.354 0.895 1.305 + 174SOL HW2 522 1.498 0.888 1.280 + 175SOL OW 523 0.557 1.056 1.922 + 175SOL HW1 524 0.495 1.095 1.982 + 175SOL HW2 525 0.542 1.066 1.829 + 176SOL OW 526 0.057 0.769 1.153 + 176SOL HW1 527 0.073 0.678 1.120 + 176SOL HW2 528 0.028 0.752 1.249 + 177SOL OW 529 1.670 0.117 1.654 + 177SOL HW1 530 1.637 0.143 1.740 + 177SOL HW2 531 1.612 0.145 1.579 + 178SOL OW 532 0.793 0.327 0.542 + 178SOL HW1 533 0.750 0.301 0.626 + 178SOL HW2 534 0.876 0.357 0.573 + 179SOL OW 535 1.031 0.409 0.670 + 179SOL HW1 536 1.125 0.434 0.654 + 179SOL HW2 537 1.013 0.414 0.768 + 180SOL OW 538 1.043 1.258 0.407 + 180SOL HW1 539 1.072 1.331 0.353 + 180SOL HW2 540 1.071 1.166 0.367 + 181SOL OW 541 0.103 0.532 1.055 + 181SOL HW1 542 0.138 0.452 1.104 + 181SOL HW2 543 0.004 0.508 1.062 + 182SOL OW 544 1.693 1.041 0.211 + 182SOL HW1 545 1.722 1.134 0.218 + 182SOL HW2 546 1.737 0.996 0.285 + 183SOL OW 547 0.350 1.520 0.784 + 183SOL HW1 548 0.319 1.472 0.865 + 183SOL HW2 549 0.289 1.594 0.793 + 184SOL OW 550 0.589 1.035 1.165 + 184SOL HW1 551 0.561 0.974 1.095 + 184SOL HW2 552 0.684 1.034 1.183 + 185SOL OW 553 0.304 1.770 1.696 + 185SOL HW1 554 0.346 1.777 1.785 + 185SOL HW2 555 0.332 1.697 1.645 + 186SOL OW 556 0.569 0.998 0.373 + 186SOL HW1 557 0.528 1.068 0.318 + 186SOL HW2 558 0.638 1.050 0.417 + 187SOL OW 559 1.526 0.218 1.872 + 187SOL HW1 560 1.457 0.143 1.884 + 187SOL HW2 561 1.579 0.207 1.950 + 188SOL OW 562 1.858 1.604 1.293 + 188SOL HW1 563 1.901 1.694 1.286 + 188SOL HW2 564 1.768 1.616 1.270 + 189SOL OW 565 1.061 0.328 0.232 + 189SOL HW1 566 1.091 0.414 0.263 + 189SOL HW2 567 1.147 0.284 0.200 + 190SOL OW 568 0.880 0.852 0.297 + 190SOL HW1 569 0.908 0.877 0.392 + 190SOL HW2 570 0.803 0.910 0.277 + 191SOL OW 571 1.872 1.161 1.764 + 191SOL HW1 572 1.974 1.166 1.769 + 191SOL HW2 573 1.838 1.251 1.772 + 192SOL OW 574 1.222 0.773 0.095 + 192SOL HW1 575 1.244 0.833 0.016 + 192SOL HW2 576 1.128 0.731 0.070 + 193SOL OW 577 1.481 1.356 0.788 + 193SOL HW1 578 1.533 1.331 0.861 + 193SOL HW2 579 1.491 1.449 0.755 + 194SOL OW 580 1.531 1.235 1.933 + 194SOL HW1 581 1.457 1.267 1.882 + 194SOL HW2 582 1.589 1.174 1.884 + 195SOL OW 583 1.159 1.422 0.022 + 195SOL HW1 584 1.172 1.475 0.102 + 195SOL HW2 585 1.206 1.333 0.039 + 196SOL OW 586 1.621 1.084 0.604 + 196SOL HW1 587 1.532 1.117 0.587 + 196SOL HW2 588 1.679 1.156 0.582 + 197SOL OW 589 1.265 1.797 0.536 + 197SOL HW1 590 1.236 1.703 0.554 + 197SOL HW2 591 1.352 1.787 0.489 + 198SOL OW 592 0.833 1.085 1.980 + 198SOL HW1 593 0.736 1.081 1.955 + 198SOL HW2 594 0.877 1.079 1.897 + 199SOL OW 595 0.265 0.790 1.700 + 199SOL HW1 596 0.256 0.763 1.795 + 199SOL HW2 597 0.348 0.836 1.684 + 200SOL OW 598 0.271 0.228 1.447 + 200SOL HW1 599 0.226 0.216 1.363 + 200SOL HW2 600 0.220 0.279 1.520 + 201SOL OW 601 0.955 0.826 1.328 + 201SOL HW1 602 0.889 0.758 1.360 + 201SOL HW2 603 0.908 0.908 1.303 + 202SOL OW 604 0.692 1.808 0.239 + 202SOL HW1 605 0.690 1.767 0.150 + 202SOL HW2 606 0.633 1.757 0.306 + 203SOL OW 607 1.696 1.801 1.557 + 203SOL HW1 608 1.694 1.886 1.606 + 203SOL HW2 609 1.789 1.777 1.554 + 204SOL OW 610 0.752 0.680 0.140 + 204SOL HW1 611 0.699 0.619 0.203 + 204SOL HW2 612 0.811 0.738 0.197 + 205SOL OW 613 1.481 1.605 0.666 + 205SOL HW1 614 1.551 1.662 0.638 + 205SOL HW2 615 1.424 1.656 0.725 + 206SOL OW 616 1.185 1.148 0.019 + 206SOL HW1 617 1.161 1.099 0.097 + 206SOL HW2 618 1.243 1.088 -0.031 + 207SOL OW 619 0.644 1.861 0.612 + 207SOL HW1 620 0.715 1.922 0.600 + 207SOL HW2 621 0.611 1.881 0.705 + 208SOL OW 622 1.689 1.692 0.467 + 208SOL HW1 623 1.664 1.599 0.436 + 208SOL HW2 624 1.736 1.737 0.396 + 209SOL OW 625 1.246 1.706 1.555 + 209SOL HW1 626 1.227 1.627 1.613 + 209SOL HW2 627 1.334 1.674 1.516 + 210SOL OW 628 0.511 0.827 0.962 + 210SOL HW1 629 0.527 0.900 0.903 + 210SOL HW2 630 0.589 0.806 1.016 + 211SOL OW 631 0.456 0.609 0.796 + 211SOL HW1 632 0.455 0.700 0.837 + 211SOL HW2 633 0.502 0.560 0.870 + 212SOL OW 634 0.225 0.934 1.027 + 212SOL HW1 635 0.314 0.906 0.997 + 212SOL HW2 636 0.197 0.861 1.081 + 213SOL OW 637 0.920 1.249 0.878 + 213SOL HW1 638 0.893 1.162 0.843 + 213SOL HW2 639 0.976 1.281 0.801 + 214SOL OW 640 0.037 1.403 1.429 + 214SOL HW1 641 0.017 1.423 1.525 + 214SOL HW2 642 -0.015 1.476 1.381 + 215SOL OW 643 0.427 1.174 0.199 + 215SOL HW1 644 0.418 1.269 0.220 + 215SOL HW2 645 0.333 1.152 0.205 + 216SOL OW 646 0.198 0.292 1.178 + 216SOL HW1 647 0.195 0.231 1.099 + 216SOL HW2 648 0.296 0.322 1.172 + 217SOL OW 649 1.610 0.084 1.020 + 217SOL HW1 650 1.565 0.060 0.938 + 217SOL HW2 651 1.547 0.091 1.093 + 218SOL OW 652 1.878 0.379 0.088 + 218SOL HW1 653 1.884 0.365 -0.009 + 218SOL HW2 654 1.965 0.370 0.128 + 219SOL OW 655 0.030 1.151 1.336 + 219SOL HW1 656 0.010 1.167 1.241 + 219SOL HW2 657 0.029 1.242 1.372 + 220SOL OW 658 1.568 1.674 0.092 + 220SOL HW1 659 1.627 1.741 0.049 + 220SOL HW2 660 1.490 1.729 0.124 + 221SOL OW 661 0.105 1.114 0.872 + 221SOL HW1 662 0.153 1.038 0.922 + 221SOL HW2 663 0.169 1.157 0.815 + 222SOL OW 664 1.294 0.929 1.857 + 222SOL HW1 665 1.243 0.916 1.776 + 222SOL HW2 666 1.384 0.896 1.825 + 223SOL OW 667 1.525 0.831 1.785 + 223SOL HW1 668 1.573 0.893 1.841 + 223SOL HW2 669 1.527 0.879 1.698 + 224SOL OW 670 1.102 1.013 0.260 + 224SOL HW1 671 1.015 0.963 0.258 + 224SOL HW2 672 1.173 0.945 0.270 + 225SOL OW 673 0.430 0.028 1.175 + 225SOL HW1 674 0.518 0.066 1.189 + 225SOL HW2 675 0.378 0.081 1.112 + 226SOL OW 676 1.068 0.678 1.558 + 226SOL HW1 677 1.143 0.637 1.600 + 226SOL HW2 678 0.990 0.619 1.589 + 227SOL OW 679 1.270 0.511 0.580 + 227SOL HW1 680 1.321 0.589 0.606 + 227SOL HW2 681 1.343 0.448 0.584 + 228SOL OW 682 1.820 0.288 0.838 + 228SOL HW1 683 1.849 0.327 0.920 + 228SOL HW2 684 1.773 0.358 0.794 + 229SOL OW 685 0.732 0.055 0.993 + 229SOL HW1 686 0.649 0.016 0.960 + 229SOL HW2 687 0.715 0.066 1.088 + 230SOL OW 688 1.276 0.211 0.552 + 230SOL HW1 689 1.354 0.244 0.506 + 230SOL HW2 690 1.207 0.212 0.481 + 231SOL OW 691 0.771 1.189 0.466 + 231SOL HW1 692 0.863 1.223 0.452 + 231SOL HW2 693 0.728 1.240 0.538 + 232SOL OW 694 0.624 1.828 1.397 + 232SOL HW1 695 0.685 1.805 1.469 + 232SOL HW2 696 0.540 1.876 1.431 + 233SOL OW 697 0.011 1.947 1.838 + 233SOL HW1 698 0.100 1.954 1.892 + 233SOL HW2 699 0.012 1.899 1.750 + 234SOL OW 700 1.525 1.041 0.839 + 234SOL HW1 701 1.577 1.057 0.749 + 234SOL HW2 702 1.573 1.103 0.898 + 235SOL OW 703 0.557 0.299 1.393 + 235SOL HW1 704 0.588 0.362 1.461 + 235SOL HW2 705 0.464 0.275 1.426 + 236SOL OW 706 1.726 1.392 1.755 + 236SOL HW1 707 1.801 1.451 1.728 + 236SOL HW2 708 1.655 1.449 1.782 + 237SOL OW 709 1.690 0.997 1.905 + 237SOL HW1 710 1.760 1.042 1.859 + 237SOL HW2 711 1.715 0.995 1.998 + 238SOL OW 712 1.632 0.567 1.813 + 238SOL HW1 713 1.555 0.509 1.801 + 238SOL HW2 714 1.603 0.663 1.791 + 239SOL OW 715 0.962 1.357 1.595 + 239SOL HW1 716 0.964 1.269 1.644 + 239SOL HW2 717 1.025 1.418 1.633 + 240SOL OW 718 1.304 0.853 0.335 + 240SOL HW1 719 1.270 0.811 0.248 + 240SOL HW2 720 1.343 0.939 0.310 + 241SOL OW 721 0.023 1.853 1.289 + 241SOL HW1 722 -0.046 1.913 1.260 + 241SOL HW2 723 0.096 1.860 1.226 + 242SOL OW 724 0.443 1.231 1.488 + 242SOL HW1 725 0.466 1.235 1.392 + 242SOL HW2 726 0.362 1.182 1.504 + 243SOL OW 727 1.596 1.405 0.380 + 243SOL HW1 728 1.627 1.396 0.285 + 243SOL HW2 729 1.666 1.366 0.440 + 244SOL OW 730 1.965 1.366 0.095 + 244SOL HW1 731 1.863 1.370 0.091 + 244SOL HW2 732 1.994 1.383 0.006 + 245SOL OW 733 0.752 0.401 1.793 + 245SOL HW1 734 0.690 0.329 1.824 + 245SOL HW2 735 0.701 0.481 1.794 + 246SOL OW 736 1.529 0.379 0.494 + 246SOL HW1 737 1.554 0.437 0.416 + 246SOL HW2 738 1.578 0.409 0.573 + 247SOL OW 739 1.467 1.518 1.848 + 247SOL HW1 740 1.457 1.560 1.761 + 247SOL HW2 741 1.497 1.586 1.913 + 248SOL OW 742 1.786 0.913 1.083 + 248SOL HW1 743 1.800 0.924 0.984 + 248SOL HW2 744 1.864 0.865 1.119 + 249SOL OW 745 1.349 1.429 0.488 + 249SOL HW1 746 1.362 1.488 0.565 + 249SOL HW2 747 1.432 1.446 0.436 + 250SOL OW 748 0.961 0.910 0.549 + 250SOL HW1 749 1.047 0.949 0.573 + 250SOL HW2 750 0.961 0.813 0.580 + 251SOL OW 751 1.401 1.171 0.499 + 251SOL HW1 752 1.415 1.134 0.407 + 251SOL HW2 753 1.394 1.268 0.502 + 252SOL OW 754 0.916 1.961 0.202 + 252SOL HW1 755 0.830 1.929 0.241 + 252SOL HW2 756 0.989 1.947 0.259 + 253SOL OW 757 1.142 0.947 1.643 + 253SOL HW1 758 1.103 0.862 1.622 + 253SOL HW2 759 1.172 0.987 1.560 + 254SOL OW 760 1.694 1.883 1.927 + 254SOL HW1 761 1.787 1.871 1.891 + 254SOL HW2 762 1.707 1.958 1.986 + 255SOL OW 763 1.014 0.085 1.917 + 255SOL HW1 764 0.976 0.057 1.998 + 255SOL HW2 765 1.005 0.182 1.909 + 256SOL OW 766 0.468 1.262 1.205 + 256SOL HW1 767 0.525 1.186 1.162 + 256SOL HW2 768 0.414 1.303 1.131 + 2.00000 2.00000 2.00000 diff --git a/examples/water/gmx/water.top b/examples/water/gmx/water.top new file mode 100644 index 0000000000..fcc9f8f6de --- /dev/null +++ b/examples/water/gmx/water.top @@ -0,0 +1,43 @@ +[ defaults ] +; nbfunc comb-rule gen-pairs fudgeLJ fudgeQQ +1 2 yes 0.5 0.8333 + +[ atomtypes ] +; name at.num mass charge ptype sigma epsilon +HW 1 1.008 0.0000 A 0.00000e+00 0.00000e+00 +OW 8 16.00 0.0000 A 0.00000e+00 0.00000e+00 + + +[ bondtypes ] +; i j func b0 kb + OW HW 1 0.09572 462750.4 ; P water + HW HW 1 0.15136 462750.4 ; P water + + +[ angletypes ] +; i j k func th0 cth +HW OW HW 1 104.520 836.800 ; TIP3P water +HW HW OW 1 127.740 0.000 ; (found in crystallographic water with 3 bonds) + + +; Include water topology +[ moleculetype ] +; molname nrexcl +SOL 2 + +[ atoms ] +; id at type res nr res name at name cg nr charge mass + ; 1 OW 1 SOL OW 1 -0.834 16.00000 + ; 2 HW 1 SOL HW1 1 0.417 1.00800 + ; 3 HW 1 SOL HW2 1 0.417 1.00800 + 1 OW 1 SOL OW 1 0.000 16.00000 + 2 HW 1 SOL HW1 1 0.000 1.00800 + 3 HW 1 SOL HW2 1 0.00 1.00800 + +[ system ] +; Name +lw_256.pdb + +[ molecules ] +; Compound #mols +SOL 256 diff --git a/source/3rdparty/json.hpp b/source/3rdparty/json.hpp index 1dacd8d847..242f034c3c 100644 --- a/source/3rdparty/json.hpp +++ b/source/3rdparty/json.hpp @@ -1,11 +1,12 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 2.1.1 +| | |__ | | | | | | version 3.9.1 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2017 Niels Lohmann . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2019 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -26,13659 +27,24358 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef NLOHMANN_JSON_HPP -#define NLOHMANN_JSON_HPP +#ifndef INCLUDE_NLOHMANN_JSON_HPP_ +#define INCLUDE_NLOHMANN_JSON_HPP_ -#include // all_of, copy, fill, find, for_each, none_of, remove, reverse, transform -#include // array -#include // assert -#include // and, not, or -#include // lconv, localeconv -#include // isfinite, labs, ldexp, signbit +#define NLOHMANN_JSON_VERSION_MAJOR 3 +#define NLOHMANN_JSON_VERSION_MINOR 9 +#define NLOHMANN_JSON_VERSION_PATCH 1 + +#include // all_of, find, for_each #include // nullptr_t, ptrdiff_t, size_t -#include // int64_t, uint64_t -#include // abort, strtod, strtof, strtold, strtoul, strtoll, strtoull -#include // memcpy, strlen -#include // forward_list -#include // function, hash, less +#include // hash, less #include // initializer_list -#include // hex -#include // istream, ostream -#include // advance, begin, back_inserter, bidirectional_iterator_tag, distance, end, inserter, iterator, iterator_traits, next, random_access_iterator_tag, reverse_iterator -#include // numeric_limits -#include // locale -#include // map -#include // addressof, allocator, allocator_traits, unique_ptr +#include // istream, ostream +#include // random_access_iterator_tag +#include // unique_ptr #include // accumulate -#include // stringstream -#include // getline, stoi, string, to_string -#include // add_pointer, conditional, decay, enable_if, false_type, integral_constant, is_arithmetic, is_base_of, is_const, is_constructible, is_convertible, is_default_constructible, is_enum, is_floating_point, is_integral, is_nothrow_move_assignable, is_nothrow_move_constructible, is_pointer, is_reference, is_same, is_scalar, is_signed, remove_const, remove_cv, remove_pointer, remove_reference, true_type, underlying_type -#include // declval, forward, make_pair, move, pair, swap +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap #include // vector -// exclude unsupported compilers -#if defined(__clang__) - #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 - #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" - #endif -#elif defined(__GNUC__) - #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40900 - #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" - #endif -#endif - -// disable float-equal warnings on GCC/clang -#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wfloat-equal" -#endif +// #include -// disable documentation warnings on clang -#if defined(__clang__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdocumentation" -#endif -// allow for portable deprecation warnings -#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) - #define JSON_DEPRECATED __attribute__((deprecated)) -#elif defined(_MSC_VER) - #define JSON_DEPRECATED __declspec(deprecated) -#else - #define JSON_DEPRECATED -#endif +#include -// allow to disable exceptions -#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && not defined(JSON_NOEXCEPTION) - #define JSON_THROW(exception) throw exception - #define JSON_TRY try - #define JSON_CATCH(exception) catch(exception) -#else - #define JSON_THROW(exception) std::abort() - #define JSON_TRY if(true) - #define JSON_CATCH(exception) if(false) -#endif +// #include -// manual branch prediction -#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) - #define JSON_LIKELY(x) __builtin_expect(!!(x), 1) - #define JSON_UNLIKELY(x) __builtin_expect(!!(x), 0) -#else - #define JSON_LIKELY(x) x - #define JSON_UNLIKELY(x) x -#endif -/*! -@brief namespace for Niels Lohmann -@see https://github.com/nlohmann -@since version 1.0.0 -*/ -namespace nlohmann -{ +#include // transform +#include // array +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray -/*! -@brief unnamed namespace with internal helper functions +// #include -This namespace collects some functions that could not be defined inside the -@ref basic_json class. -@since version 2.1.0 -*/ -namespace detail -{ -//////////////// -// exceptions // -//////////////// +#include // exception +#include // runtime_error +#include // to_string -/*! -@brief general exception of the @ref basic_json class +// #include -Extension of std::exception objects with a member @a id for exception ids. -@note To have nothrow-copy-constructible exceptions, we internally use - std::runtime_error which can cope with arbitrary-length error messages. - Intermediate strings are built with static functions and then passed to - the actual constructor. +#include // size_t -@since version 3.0.0 -*/ -class exception : public std::exception +namespace nlohmann { - public: - /// returns the explanatory string - virtual const char* what() const noexcept override - { - return m.what(); - } - - /// the id of the exception - const int id; - - protected: - exception(int id_, const char* what_arg) - : id(id_), m(what_arg) - {} - - static std::string name(const std::string& ename, int id) +namespace detail +{ +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const { - return "[json.exception." + ename + "." + std::to_string(id) + "] "; + return chars_read_total; } - - private: - /// an exception object as storage for error messages - std::runtime_error m; }; -/*! -@brief exception indicating a parse error +} // namespace detail +} // namespace nlohmann -This excpetion is thrown by the library when a parse error occurs. Parse -errors can occur during the deserialization of JSON text as well as when -using JSON Patch. +// #include + + +#include // pair +// #include +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to + * the public domain worldwide. This software is distributed without + * any warranty. + * + * For details, see . + * SPDX-License-Identifier: CC0-1.0 + */ + +#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 13) +#if defined(JSON_HEDLEY_VERSION) + #undef JSON_HEDLEY_VERSION +#endif +#define JSON_HEDLEY_VERSION 13 -Member @a byte holds the byte index of the last read character in the input -file. +#if defined(JSON_HEDLEY_STRINGIFY_EX) + #undef JSON_HEDLEY_STRINGIFY_EX +#endif +#define JSON_HEDLEY_STRINGIFY_EX(x) #x -@note For an input with n bytes, 1 is the index of the first character - and n+1 is the index of the terminating null byte or the end of - file. This also holds true when reading a byte vector (CBOR or - MessagePack). +#if defined(JSON_HEDLEY_STRINGIFY) + #undef JSON_HEDLEY_STRINGIFY +#endif +#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) -Exceptions have ids 1xx. +#if defined(JSON_HEDLEY_CONCAT_EX) + #undef JSON_HEDLEY_CONCAT_EX +#endif +#define JSON_HEDLEY_CONCAT_EX(a,b) a##b -name / id | example massage | description ------------------------------- | --------------- | ------------------------- -json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position. -json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. -json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid. -json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects. -json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors. -json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number wihtout a leading `0`. -json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character. -json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences. -json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. -json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. -json.exception.parse_error.111 | parse error: bad input stream | Parsing CBOR or MessagePack from an input stream where the [`badbit` or `failbit`](http://en.cppreference.com/w/cpp/io/ios_base/iostate) is set. -json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xf8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. -json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. +#if defined(JSON_HEDLEY_CONCAT) + #undef JSON_HEDLEY_CONCAT +#endif +#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) -@since version 3.0.0 -*/ -class parse_error : public exception -{ - public: - /*! - @brief create a parse error exception - @param[in] id the id of the exception - @param[in] byte_ the byte index where the error occured (or 0 if - the position cannot be determined) - @param[in] what_arg the explanatory string - @return parse_error object - */ - static parse_error create(int id, size_t byte_, const std::string& what_arg) - { - std::string w = exception::name("parse_error", id) + "parse error" + - (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") + - ": " + what_arg; - return parse_error(id, byte_, w.c_str()); - } +#if defined(JSON_HEDLEY_CONCAT3_EX) + #undef JSON_HEDLEY_CONCAT3_EX +#endif +#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c - /*! - @brief byte index of the parse error +#if defined(JSON_HEDLEY_CONCAT3) + #undef JSON_HEDLEY_CONCAT3 +#endif +#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) - The byte index of the last read character in the input file. +#if defined(JSON_HEDLEY_VERSION_ENCODE) + #undef JSON_HEDLEY_VERSION_ENCODE +#endif +#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) - @note For an input with n bytes, 1 is the index of the first character - and n+1 is the index of the terminating null byte or the end of - file. This also holds true when reading a byte vector (CBOR or - MessagePack). - */ - const size_t byte; +#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) + #undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) - private: - parse_error(int id_, size_t byte_, const char* what_arg) - : exception(id_, what_arg), byte(byte_) - {} -}; +#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) + #undef JSON_HEDLEY_VERSION_DECODE_MINOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) -/*! -@brief exception indicating errors with iterators +#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) + #undef JSON_HEDLEY_VERSION_DECODE_REVISION +#endif +#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) -Exceptions have ids 2xx. +#if defined(JSON_HEDLEY_GNUC_VERSION) + #undef JSON_HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif -name / id | example massage | description ------------------------------------ | --------------- | ------------------------- -json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. -json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion. -json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from. -json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid. -json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid. -json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range. -json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key. -json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. -json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. -json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. -json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to. -json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. -json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compated, because JSON objects are unordered. -json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin(). +#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) + #undef JSON_HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GNUC_VERSION) + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) +#endif -@since version 3.0.0 -*/ -class invalid_iterator : public exception -{ - public: - static invalid_iterator create(int id, const std::string& what_arg) - { - std::string w = exception::name("invalid_iterator", id) + what_arg; - return invalid_iterator(id, w.c_str()); - } +#if defined(JSON_HEDLEY_MSVC_VERSION) + #undef JSON_HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif - private: - invalid_iterator(int id_, const char* what_arg) - : exception(id_, what_arg) - {} -}; +#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) + #undef JSON_HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(_MSC_VER) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif -/*! -@brief exception indicating executing a member function with a wrong type +#if defined(JSON_HEDLEY_INTEL_VERSION) + #undef JSON_HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif -Exceptions have ids 3xx. +#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_VERSION) + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) +#endif -name / id | example message | description ------------------------------ | --------------- | ------------------------- -json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. -json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. -json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t&. -json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. -json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. -json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. -json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. -json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types. -json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. -json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. -json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. -json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined. -json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. -json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. +#if defined(JSON_HEDLEY_PGI_VERSION) + #undef JSON_HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) + #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif -@since version 3.0.0 -*/ -class type_error : public exception -{ - public: - static type_error create(int id, const std::string& what_arg) - { - std::string w = exception::name("type_error", id) + what_arg; - return type_error(id, w.c_str()); - } +#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) + #undef JSON_HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PGI_VERSION) + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) +#endif - private: - type_error(int id_, const char* what_arg) - : exception(id_, what_arg) - {} -}; +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #undef JSON_HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) +#endif -/*! -@brief exception indicating access out of the defined range +#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) + #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) +#endif -Exceptions have ids 4xx. +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif -name / id | example message | description -------------------------------- | --------------- | ------------------------- -json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. -json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it. -json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. -json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. -json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. -json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) +#endif -@since version 3.0.0 -*/ -class out_of_range : public exception -{ - public: - static out_of_range create(int id, const std::string& what_arg) - { - std::string w = exception::name("out_of_range", id) + what_arg; - return out_of_range(id, w.c_str()); - } +#if defined(JSON_HEDLEY_ARM_VERSION) + #undef JSON_HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) +#endif - private: - out_of_range(int id_, const char* what_arg) - : exception(id_, what_arg) - {} -}; +#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) + #undef JSON_HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_ARM_VERSION) + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) +#endif -/*! -@brief exception indicating other errors +#if defined(JSON_HEDLEY_IBM_VERSION) + #undef JSON_HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif -Exceptions have ids 5xx. +#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) + #undef JSON_HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IBM_VERSION) + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) +#endif -name / id | example message | description ------------------------------- | --------------- | ------------------------- -json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. -json.exception.other_error.502 | invalid object size for conversion | Some conversions to user-defined types impose constraints on the object size (e.g. std::pair) +#if defined(JSON_HEDLEY_TI_VERSION) + #undef JSON_HEDLEY_TI_VERSION +#endif +#if \ + defined(__TI_COMPILER_VERSION__) && \ + ( \ + defined(__TMS470__) || defined(__TI_ARM__) || \ + defined(__MSP430__) || \ + defined(__TMS320C2000__) \ + ) +#if (__TI_COMPILER_VERSION__ >= 16000000) + #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif +#endif -@since version 3.0.0 -*/ -class other_error : public exception -{ - public: - static other_error create(int id, const std::string& what_arg) - { - std::string w = exception::name("other_error", id) + what_arg; - return other_error(id, w.c_str()); - } +#if defined(JSON_HEDLEY_TI_VERSION_CHECK) + #undef JSON_HEDLEY_TI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_VERSION) + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) +#endif - private: - other_error(int id_, const char* what_arg) - : exception(id_, what_arg) - {} -}; +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #undef JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #undef JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif -/////////////////////////// -// JSON type enumeration // -/////////////////////////// +#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) +#endif -/*! -@brief the JSON type enumeration +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #undef JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif -This enumeration collects the different JSON types. It is internally used to -distinguish the stored values, and the functions @ref basic_json::is_null(), -@ref basic_json::is_object(), @ref basic_json::is_array(), -@ref basic_json::is_string(), @ref basic_json::is_boolean(), -@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), -@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), -@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and -@ref basic_json::is_structured() rely on it. +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) + #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) +#endif -@note There are three enumeration entries (number_integer, number_unsigned, and -number_float), because the library distinguishes these three types for numbers: -@ref basic_json::number_unsigned_t is used for unsigned integers, -@ref basic_json::number_integer_t is used for signed integers, and -@ref basic_json::number_float_t is used for floating-point numbers or to -approximate integers which do not fit in the limits of their respective type. +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #undef JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif -@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON -value with the default value for a given type +#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) +#endif -@since version 1.0.0 -*/ -enum class value_t : uint8_t -{ - null, ///< null value - object, ///< object (unordered set of name/value pairs) - array, ///< array (ordered collection of values) - string, ///< string value - boolean, ///< boolean value - number_integer, ///< number value (signed integer) - number_unsigned, ///< number value (unsigned integer) - number_float, ///< number value (floating-point) - discarded ///< discarded by the the parser callback function -}; +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #undef JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif -/*! -@brief comparison operator for JSON types +#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) +#endif -Returns an ordering that is similar to Python: -- order: null < boolean < number < object < array < string -- furthermore, each type is not smaller than itself +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #undef JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif -@since version 1.0.0 -*/ -inline bool operator<(const value_t lhs, const value_t rhs) noexcept -{ - static constexpr std::array order = {{ - 0, // null - 3, // object - 4, // array - 5, // string - 1, // boolean - 2, // integer - 2, // unsigned - 2, // float - } - }; +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) +#endif - // discarded values are not comparable - if (lhs == value_t::discarded or rhs == value_t::discarded) - { - return false; - } +#if defined(JSON_HEDLEY_CRAY_VERSION) + #undef JSON_HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) + #if defined(_RELEASE_PATCHLEVEL) + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) + #else + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) + #endif +#endif - return order[static_cast(lhs)] < - order[static_cast(rhs)]; -} +#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) + #undef JSON_HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_CRAY_VERSION) + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) +#endif +#if defined(JSON_HEDLEY_IAR_VERSION) + #undef JSON_HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) + #if __VER__ > 1000 + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) + #else + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(VER / 100, __VER__ % 100, 0) + #endif +#endif -///////////// -// helpers // -///////////// +#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) + #undef JSON_HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IAR_VERSION) + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) +#endif -// alias templates to reduce boilerplate -template -using enable_if_t = typename std::enable_if::type; +#if defined(JSON_HEDLEY_TINYC_VERSION) + #undef JSON_HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) + #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif -template -using uncvref_t = typename std::remove_cv::type>::type; +#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) + #undef JSON_HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) +#endif -/* -Implementation of two C++17 constructs: conjunction, negation. This is needed -to avoid evaluating all the traits in a condition +#if defined(JSON_HEDLEY_DMC_VERSION) + #undef JSON_HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) + #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif -For example: not std::is_same::value and has_value_type::value -will not compile when T = void (on MSVC at least). Whereas -conjunction>, has_value_type>::value will -stop evaluating if negation<...>::value == false +#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) + #undef JSON_HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_DMC_VERSION) + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) +#endif -Please note that those constructs must be used with caution, since symbols can -become very long quickly (which can slow down compilation and cause MSVC -internal compiler errors). Only use it when you have to (see example ahead). -*/ -template struct conjunction : std::true_type {}; -template struct conjunction : B1 {}; -template -struct conjunction : std::conditional, B1>::type {}; +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #undef JSON_HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) + #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) +#endif -template struct negation : std::integral_constant < bool, !B::value > {}; +#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) + #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) +#endif -// dispatch utility (taken from ranges-v3) -template struct priority_tag : priority_tag < N - 1 > {}; -template<> struct priority_tag<0> {}; +#if defined(JSON_HEDLEY_PELLES_VERSION) + #undef JSON_HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) + #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif +#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) + #undef JSON_HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PELLES_VERSION) + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) +#endif -////////////////// -// constructors // -////////////////// +#if defined(JSON_HEDLEY_GCC_VERSION) + #undef JSON_HEDLEY_GCC_VERSION +#endif +#if \ + defined(JSON_HEDLEY_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(JSON_HEDLEY_INTEL_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_ARM_VERSION) && \ + !defined(JSON_HEDLEY_TI_VERSION) && \ + !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ + !defined(__COMPCERT__) + #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION +#endif -template struct external_constructor; +#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif -template<> -struct external_constructor -{ - template - static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept - { - j.m_type = value_t::boolean; - j.m_value = b; - j.assert_invariant(); - } -}; +#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif -template<> -struct external_constructor -{ - template - static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) - { - j.m_type = value_t::string; - j.m_value = s; - j.assert_invariant(); - } -}; +#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif -template<> -struct external_constructor -{ - template - static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept - { - j.m_type = value_t::number_float; - j.m_value = val; - j.assert_invariant(); - } -}; +#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif -template<> -struct external_constructor -{ - template - static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept - { - j.m_type = value_t::number_unsigned; - j.m_value = val; - j.assert_invariant(); - } -}; +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif -template<> -struct external_constructor -{ - template - static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept - { - j.m_type = value_t::number_integer; - j.m_value = val; - j.assert_invariant(); - } -}; +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#elif \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#endif -template<> -struct external_constructor -{ - template - static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) - { - j.m_type = value_t::array; - j.m_value = arr; - j.assert_invariant(); - } +#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif - template::value, - int> = 0> - static void construct(BasicJsonType& j, const CompatibleArrayType& arr) - { - using std::begin; - using std::end; - j.m_type = value_t::array; - j.m_value.array = j.template create(begin(arr), end(arr)); - j.assert_invariant(); - } +#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif - template - static void construct(BasicJsonType& j, const std::vector& arr) - { - j.m_type = value_t::array; - j.m_value = value_t::array; - j.m_value.array->reserve(arr.size()); - for (bool x : arr) - { - j.m_value.array->push_back(x); - } - j.assert_invariant(); - } -}; +#if defined(JSON_HEDLEY_HAS_BUILTIN) + #undef JSON_HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else + #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) +#endif -template<> -struct external_constructor -{ - template - static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) - { - j.m_type = value_t::object; - j.m_value = obj; - j.assert_invariant(); - } +#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) + #undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif - template::value, - int> = 0> - static void construct(BasicJsonType& j, const CompatibleObjectType& obj) - { - using std::begin; - using std::end; +#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) + #undef JSON_HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif - j.m_type = value_t::object; - j.m_value.object = j.template create(begin(obj), end(obj)); - j.assert_invariant(); - } -}; +#if defined(JSON_HEDLEY_HAS_FEATURE) + #undef JSON_HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else + #define JSON_HEDLEY_HAS_FEATURE(feature) (0) +#endif +#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) + #undef JSON_HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif -//////////////////////// -// has_/is_ functions // -//////////////////////// +#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) + #undef JSON_HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif -/*! -@brief Helper to determine whether there's a key_type for T. +#if defined(JSON_HEDLEY_HAS_EXTENSION) + #undef JSON_HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else + #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) +#endif -This helper is used to tell associative containers apart from other containers -such as sequence containers. For instance, `std::map` passes the test as it -contains a `mapped_type`, whereas `std::vector` fails the test. +#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) + #undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif -@sa http://stackoverflow.com/a/7728728/266378 -@since version 1.0.0, overworked in version 2.0.6 -*/ -#define NLOHMANN_JSON_HAS_HELPER(type) \ - template struct has_##type { \ - private: \ - template \ - static int detect(U &&); \ - static void detect(...); \ - public: \ - static constexpr bool value = \ - std::is_integral()))>::value; \ - } +#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) + #undef JSON_HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif -NLOHMANN_JSON_HAS_HELPER(mapped_type); -NLOHMANN_JSON_HAS_HELPER(key_type); -NLOHMANN_JSON_HAS_HELPER(value_type); -NLOHMANN_JSON_HAS_HELPER(iterator); +#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif -#undef NLOHMANN_JSON_HAS_HELPER +#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif +#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif -template -struct is_compatible_object_type_impl : std::false_type {}; +#if defined(JSON_HEDLEY_HAS_WARNING) + #undef JSON_HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else + #define JSON_HEDLEY_HAS_WARNING(warning) (0) +#endif -template -struct is_compatible_object_type_impl -{ - static constexpr auto value = - std::is_constructible::value and - std::is_constructible::value; -}; +#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) + #undef JSON_HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif -template -struct is_compatible_object_type -{ - static auto constexpr value = is_compatible_object_type_impl < - conjunction>, - has_mapped_type, - has_key_type>::value, - typename BasicJsonType::object_t, CompatibleObjectType >::value; -}; +#if defined(JSON_HEDLEY_GCC_HAS_WARNING) + #undef JSON_HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif -template -struct is_basic_json_nested_type -{ - static auto constexpr value = std::is_same::value or - std::is_same::value or - std::is_same::value or - std::is_same::value or - std::is_same::value; -}; +/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# endif +#endif +#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif -template -struct is_compatible_array_type -{ - static auto constexpr value = - conjunction>, - negation>, - negation>, - negation>, - has_value_type, - has_iterator>::value; -}; +#if defined(JSON_HEDLEY_CONST_CAST) + #undef JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif -template -struct is_compatible_integer_type_impl : std::false_type {}; +#if defined(JSON_HEDLEY_REINTERPRET_CAST) + #undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) +#endif -template -struct is_compatible_integer_type_impl -{ - // is there an assert somewhere on overflows? - using RealLimits = std::numeric_limits; - using CompatibleLimits = std::numeric_limits; +#if defined(JSON_HEDLEY_STATIC_CAST) + #undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else + #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif - static constexpr auto value = - std::is_constructible::value and - CompatibleLimits::is_integer and - RealLimits::is_signed == CompatibleLimits::is_signed; -}; +#if defined(JSON_HEDLEY_CPP_CAST) + #undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + JSON_HEDLEY_DIAGNOSTIC_POP \ +# else +# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) +# endif +#else +# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif -template -struct is_compatible_integer_type -{ - static constexpr auto value = - is_compatible_integer_type_impl < - std::is_integral::value and - not std::is_same::value, - RealIntegerType, CompatibleNumberIntegerType > ::value; -}; +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) + #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_PRAGMA(value) __pragma(value) +#else + #define JSON_HEDLEY_PRAGMA(value) +#endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) + #undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) + #undef JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) + #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_PUSH + #define JSON_HEDLEY_DIAGNOSTIC_POP +#endif -// trait checking if JSONSerializer::from_json(json const&, udt&) exists -template -struct has_from_json -{ - private: - // also check the return type of from_json - template::from_json( - std::declval(), std::declval()))>::value>> - static int detect(U&&); - static void detect(...); +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif - public: - static constexpr bool value = std::is_integral>()))>::value; -}; +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif -// This trait checks if JSONSerializer::from_json(json const&) exists -// this overload is used for non-default-constructible user-defined-types -template -struct has_non_default_from_json -{ - private: - template < - typename U, - typename = enable_if_t::from_json(std::declval()))>::value >> - static int detect(U&&); - static void detect(...); +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif - public: - static constexpr bool value = std::is_integral>()))>::value; -}; +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif -// This trait checks if BasicJsonType::json_serializer::to_json exists -template -struct has_to_json -{ - private: - template::to_json( - std::declval(), std::declval()))> - static int detect(U&&); - static void detect(...); +#if defined(JSON_HEDLEY_DEPRECATED) + #undef JSON_HEDLEY_DEPRECATED +#endif +#if defined(JSON_HEDLEY_DEPRECATED_FOR) + #undef JSON_HEDLEY_DEPRECATED_FOR +#endif +#if JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) + #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) +#elif \ + JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else + #define JSON_HEDLEY_DEPRECATED(since) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) +#endif - public: - static constexpr bool value = std::is_integral>()))>::value; -}; +#if defined(JSON_HEDLEY_UNAVAILABLE) + #undef JSON_HEDLEY_UNAVAILABLE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else + #define JSON_HEDLEY_UNAVAILABLE(available_since) +#endif +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) +#elif defined(_Check_return_) /* SAL */ + #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ +#else + #define JSON_HEDLEY_WARN_UNUSED_RESULT + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) +#endif -///////////// -// to_json // -///////////// +#if defined(JSON_HEDLEY_SENTINEL) + #undef JSON_HEDLEY_SENTINEL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) + #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else + #define JSON_HEDLEY_SENTINEL(position) +#endif -template::value, int> = 0> -void to_json(BasicJsonType& j, T b) noexcept -{ - external_constructor::construct(j, b); -} +#if defined(JSON_HEDLEY_NO_RETURN) + #undef JSON_HEDLEY_NO_RETURN +#endif +#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NO_RETURN __noreturn +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define JSON_HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) + #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#else + #define JSON_HEDLEY_NO_RETURN +#endif -template::value, int> = 0> -void to_json(BasicJsonType& j, const CompatibleString& s) -{ - external_constructor::construct(j, s); -} +#if defined(JSON_HEDLEY_NO_ESCAPE) + #undef JSON_HEDLEY_NO_ESCAPE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) + #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else + #define JSON_HEDLEY_NO_ESCAPE +#endif -template::value, int> = 0> -void to_json(BasicJsonType& j, FloatType val) noexcept -{ - external_constructor::construct(j, static_cast(val)); -} +#if defined(JSON_HEDLEY_UNREACHABLE) + #undef JSON_HEDLEY_UNREACHABLE +#endif +#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) + #undef JSON_HEDLEY_UNREACHABLE_RETURN +#endif +#if defined(JSON_HEDLEY_ASSUME) + #undef JSON_HEDLEY_ASSUME +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_ASSUME(expr) __assume(expr) +#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) + #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #if defined(__cplusplus) + #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) + #else + #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) + #endif +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) + #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(JSON_HEDLEY_ASSUME) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(JSON_HEDLEY_ASSUME) + #if defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) + #else + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) + #endif +#endif +#if defined(JSON_HEDLEY_UNREACHABLE) + #if \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) + #else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() + #endif +#else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif -template < - typename BasicJsonType, typename CompatibleNumberUnsignedType, - enable_if_t::value, int> = 0 > -void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept -{ - external_constructor::construct(j, static_cast(val)); -} +JSON_HEDLEY_DIAGNOSTIC_PUSH +#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") + #pragma clang diagnostic ignored "-Wpedantic" +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) + #if defined(__clang__) + #pragma clang diagnostic ignored "-Wvariadic-macros" + #elif defined(JSON_HEDLEY_GCC_VERSION) + #pragma GCC diagnostic ignored "-Wvariadic-macros" + #endif +#endif +#if defined(JSON_HEDLEY_NON_NULL) + #undef JSON_HEDLEY_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else + #define JSON_HEDLEY_NON_NULL(...) +#endif +JSON_HEDLEY_DIAGNOSTIC_POP -template < - typename BasicJsonType, typename CompatibleNumberIntegerType, - enable_if_t::value, int> = 0 > -void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept -{ - external_constructor::construct(j, static_cast(val)); -} +#if defined(JSON_HEDLEY_PRINTF_FORMAT) + #undef JSON_HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) +#else + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) +#endif -template::value, int> = 0> -void to_json(BasicJsonType& j, EnumType e) noexcept -{ - using underlying_type = typename std::underlying_type::type; - external_constructor::construct(j, static_cast(e)); -} +#if defined(JSON_HEDLEY_CONSTEXPR) + #undef JSON_HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) + #endif +#endif +#if !defined(JSON_HEDLEY_CONSTEXPR) + #define JSON_HEDLEY_CONSTEXPR +#endif -template -void to_json(BasicJsonType& j, const std::vector& e) -{ - external_constructor::construct(j, e); -} +#if defined(JSON_HEDLEY_PREDICT) + #undef JSON_HEDLEY_PREDICT +#endif +#if defined(JSON_HEDLEY_LIKELY) + #undef JSON_HEDLEY_LIKELY +#endif +#if defined(JSON_HEDLEY_UNLIKELY) + #undef JSON_HEDLEY_UNLIKELY +#endif +#if defined(JSON_HEDLEY_UNPREDICTABLE) + #undef JSON_HEDLEY_UNPREDICTABLE +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) + #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) +# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) +#elif \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) +# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) +# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(JSON_HEDLEY_UNPREDICTABLE) + #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) +#endif -template < - typename BasicJsonType, typename CompatibleArrayType, - enable_if_t < - is_compatible_array_type::value or - std::is_same::value, - int > = 0 > -void to_json(BasicJsonType& j, const CompatibleArrayType& arr) -{ - external_constructor::construct(j, arr); -} +#if defined(JSON_HEDLEY_MALLOC) + #undef JSON_HEDLEY_MALLOC +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(14, 0, 0) + #define JSON_HEDLEY_MALLOC __declspec(restrict) +#else + #define JSON_HEDLEY_MALLOC +#endif -template < - typename BasicJsonType, typename CompatibleObjectType, - enable_if_t::value, - int> = 0 > -void to_json(BasicJsonType& j, const CompatibleObjectType& arr) -{ - external_constructor::construct(j, arr); -} +#if defined(JSON_HEDLEY_PURE) + #undef JSON_HEDLEY_PURE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) +# define JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) +# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) +# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +# define JSON_HEDLEY_PURE +#endif -template ::value, - int> = 0> -void to_json(BasicJsonType& j, T (&arr)[N]) -{ - external_constructor::construct(j, arr); -} +#if defined(JSON_HEDLEY_CONST) + #undef JSON_HEDLEY_CONST +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_CONST __attribute__((__const__)) +#elif \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_CONST _Pragma("no_side_effect") +#else + #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE +#endif -template ::value, int> = 0> -void to_json(BasicJsonType& j, std::pair const& p) -{ - j[p.first] = p.second; -} +#if defined(JSON_HEDLEY_RESTRICT) + #undef JSON_HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT restrict +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) + #define JSON_HEDLEY_RESTRICT __restrict +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT _Restrict +#else + #define JSON_HEDLEY_RESTRICT +#endif -/////////////// -// from_json // -/////////////// +#if defined(JSON_HEDLEY_INLINE) + #undef JSON_HEDLEY_INLINE +#endif +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) + #define JSON_HEDLEY_INLINE inline +#elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) + #define JSON_HEDLEY_INLINE __inline__ +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_INLINE __inline +#else + #define JSON_HEDLEY_INLINE +#endif -// overloads for basic_json template parameters -template::value and - not std::is_same::value, - int> = 0> -void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) -{ - switch (static_cast(j)) - { - case value_t::number_unsigned: - { - val = static_cast( - *j.template get_ptr()); - break; - } - case value_t::number_integer: - { - val = static_cast( - *j.template get_ptr()); - break; - } - case value_t::number_float: - { - val = static_cast( - *j.template get_ptr()); - break; - } - default: - { - JSON_THROW(type_error::create(302, "type must be number, but is " + j.type_name())); - } - } -} +#if defined(JSON_HEDLEY_ALWAYS_INLINE) + #undef JSON_HEDLEY_ALWAYS_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) +# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) +# define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +#endif -template -void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) -{ - if (not j.is_boolean()) - { - JSON_THROW(type_error::create(302, "type must be boolean, but is " + j.type_name())); - } - b = *j.template get_ptr(); -} +#if defined(JSON_HEDLEY_NEVER_INLINE) + #undef JSON_HEDLEY_NEVER_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#else + #define JSON_HEDLEY_NEVER_INLINE +#endif -template -void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) -{ - if (not j.is_string()) - { - JSON_THROW(type_error::create(302, "type must be string, but is " + j.type_name())); - } - s = *j.template get_ptr(); -} +#if defined(JSON_HEDLEY_PRIVATE) + #undef JSON_HEDLEY_PRIVATE +#endif +#if defined(JSON_HEDLEY_PUBLIC) + #undef JSON_HEDLEY_PUBLIC +#endif +#if defined(JSON_HEDLEY_IMPORT) + #undef JSON_HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC __declspec(dllexport) +# define JSON_HEDLEY_IMPORT __declspec(dllimport) +#else +# if \ + JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) +# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC +# endif +# define JSON_HEDLEY_IMPORT extern +#endif -template -void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val) -{ - get_arithmetic_value(j, val); -} +#if defined(JSON_HEDLEY_NO_THROW) + #undef JSON_HEDLEY_NO_THROW +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NO_THROW __declspec(nothrow) +#else + #define JSON_HEDLEY_NO_THROW +#endif -template -void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val) -{ - get_arithmetic_value(j, val); -} +#if defined(JSON_HEDLEY_FALL_THROUGH) + #undef JSON_HEDLEY_FALL_THROUGH +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) + #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ + #define JSON_HEDLEY_FALL_THROUGH __fallthrough +#else + #define JSON_HEDLEY_FALL_THROUGH +#endif -template -void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val) -{ - get_arithmetic_value(j, val); -} +#if defined(JSON_HEDLEY_RETURNS_NON_NULL) + #undef JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) + #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ + #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else + #define JSON_HEDLEY_RETURNS_NON_NULL +#endif -template::value, int> = 0> -void from_json(const BasicJsonType& j, EnumType& e) -{ - typename std::underlying_type::type val; - get_arithmetic_value(j, val); - e = static_cast(val); -} +#if defined(JSON_HEDLEY_ARRAY_PARAM) + #undef JSON_HEDLEY_ARRAY_PARAM +#endif +#if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_ARRAY_PARAM(name) (name) +#else + #define JSON_HEDLEY_ARRAY_PARAM(name) +#endif -template -void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr) -{ - if (not j.is_array()) - { - JSON_THROW(type_error::create(302, "type must be array, but is " + j.type_name())); - } - arr = *j.template get_ptr(); -} +#if defined(JSON_HEDLEY_IS_CONSTANT) + #undef JSON_HEDLEY_IS_CONSTANT +#endif +#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) + #undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#endif +/* JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #undef JSON_HEDLEY_IS_CONSTEXPR_ +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) + #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +# if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) +#endif +# elif \ + ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) +#endif +# elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + defined(JSON_HEDLEY_INTEL_VERSION) || \ + defined(JSON_HEDLEY_TINYC_VERSION) || \ + defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ + defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ + defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ + defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ + defined(__clang__) +# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ +((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) +# endif +#endif +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) +#else + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) (0) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif -// forward_list doesn't have an insert method -template::value, int> = 0> -void from_json(const BasicJsonType& j, std::forward_list& l) -{ - if (not j.is_array()) - { - JSON_THROW(type_error::create(302, "type must be array, but is " + j.type_name())); - } +#if defined(JSON_HEDLEY_BEGIN_C_DECLS) + #undef JSON_HEDLEY_BEGIN_C_DECLS +#endif +#if defined(JSON_HEDLEY_END_C_DECLS) + #undef JSON_HEDLEY_END_C_DECLS +#endif +#if defined(JSON_HEDLEY_C_DECL) + #undef JSON_HEDLEY_C_DECL +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { + #define JSON_HEDLEY_END_C_DECLS } + #define JSON_HEDLEY_C_DECL extern "C" +#else + #define JSON_HEDLEY_BEGIN_C_DECLS + #define JSON_HEDLEY_END_C_DECLS + #define JSON_HEDLEY_C_DECL +#endif - for (auto it = j.rbegin(), end = j.rend(); it != end; ++it) - { - l.push_front(it->template get()); - } -} +#if defined(JSON_HEDLEY_STATIC_ASSERT) + #undef JSON_HEDLEY_STATIC_ASSERT +#endif +#if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + JSON_HEDLEY_HAS_FEATURE(c_static_assert) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) +#else +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) +#endif -template -void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<0>) -{ - using std::begin; - using std::end; +#if defined(JSON_HEDLEY_NULL) + #undef JSON_HEDLEY_NULL +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) + #elif defined(NULL) + #define JSON_HEDLEY_NULL NULL + #else + #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) + #endif +#elif defined(NULL) + #define JSON_HEDLEY_NULL NULL +#else + #define JSON_HEDLEY_NULL ((void*) 0) +#endif - std::transform(j.begin(), j.end(), - std::inserter(arr, end(arr)), [](const BasicJsonType & i) - { - // get() returns *this, this won't call a from_json - // method when value_type is BasicJsonType - return i.template get(); - }); -} +#if defined(JSON_HEDLEY_MESSAGE) + #undef JSON_HEDLEY_MESSAGE +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_MESSAGE(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(message msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) +#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_MESSAGE(msg) +#endif -template -auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<1>) --> decltype( - arr.reserve(std::declval()), - void()) -{ - using std::begin; - using std::end; +#if defined(JSON_HEDLEY_WARNING) + #undef JSON_HEDLEY_WARNING +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_WARNING(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(clang warning msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) +#endif - arr.reserve(j.size()); - std::transform(j.begin(), j.end(), - std::inserter(arr, end(arr)), [](const BasicJsonType & i) - { - // get() returns *this, this won't call a from_json - // method when value_type is BasicJsonType - return i.template get(); - }); -} +#if defined(JSON_HEDLEY_REQUIRE) + #undef JSON_HEDLEY_REQUIRE +#endif +#if defined(JSON_HEDLEY_REQUIRE_MSG) + #undef JSON_HEDLEY_REQUIRE_MSG +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) +# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") +# define JSON_HEDLEY_REQUIRE(expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), msg, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) +# endif +#else +# define JSON_HEDLEY_REQUIRE(expr) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) +#endif -template::value and - std::is_convertible::value and - not std::is_same::value, int> = 0> -void from_json(const BasicJsonType& j, CompatibleArrayType& arr) -{ - if (not j.is_array()) - { - JSON_THROW(type_error::create(302, "type must be array, but is " + j.type_name())); - } +#if defined(JSON_HEDLEY_FLAGS) + #undef JSON_HEDLEY_FLAGS +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) + #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) +#endif - from_json_array_impl(j, arr, priority_tag<1> {}); -} +#if defined(JSON_HEDLEY_FLAGS_CAST) + #undef JSON_HEDLEY_FLAGS_CAST +#endif +#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) +# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) +#endif -template::value, int> = 0> -void from_json(const BasicJsonType& j, CompatibleObjectType& obj) -{ - if (not j.is_object()) - { - JSON_THROW(type_error::create(302, "type must be object, but is " + j.type_name())); - } +#if defined(JSON_HEDLEY_EMPTY_BASES) + #undef JSON_HEDLEY_EMPTY_BASES +#endif +#if JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0) + #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) +#else + #define JSON_HEDLEY_EMPTY_BASES +#endif - auto inner_object = j.template get_ptr(); - using std::begin; - using std::end; - using value_type = typename CompatibleObjectType::value_type; - std::vector v; - v.reserve(j.size()); - std::transform( - inner_object->begin(), inner_object->end(), std::back_inserter(v), - [](typename BasicJsonType::object_t::value_type const & p) - { - return value_type - { - p.first, - p.second - .template get()}; - }); - // we could avoid the assignment, but this might require a for loop, which - // might be less efficient than the container constructor for some - // containers (would it?) - obj = CompatibleObjectType(std::make_move_iterator(begin(v)), - std::make_move_iterator(end(v))); -} +/* Remaining macros are deprecated. */ -// overload for arithmetic types, not chosen for basic_json template arguments -// (BooleanType, etc..); note: Is it really necessary to provide explicit -// overloads for boolean_t etc. in case of a custom BooleanType which is not -// an arithmetic type? -template::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value, - int> = 0> -void from_json(const BasicJsonType& j, ArithmeticType& val) -{ - switch (static_cast(j)) - { - case value_t::number_unsigned: - { - val = static_cast(*j.template get_ptr()); - break; - } - case value_t::number_integer: - { - val = static_cast(*j.template get_ptr()); - break; - } - case value_t::number_float: - { - val = static_cast(*j.template get_ptr()); - break; - } - case value_t::boolean: - { - val = static_cast(*j.template get_ptr()); - break; - } - default: - { - JSON_THROW(type_error::create(302, "type must be number, but is " + j.type_name())); - } - } -} +#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) +#else + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif -template ::value, int> = 0> -void from_json(const BasicJsonType& j, std::pair& p) -{ - if (not j.is_object()) - { - JSON_THROW(type_error::create(302, "type must be object, but is " + j.type_name())); - } +#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) - auto const inner_object = j.template get_ptr(); - auto const size = inner_object->size(); - if (size != 1) - { - JSON_THROW(other_error::create(502, "conversion to std::pair requires the object to have exactly one field, but it has " + std::to_string(size))); - } - auto const& obj = *inner_object->begin(); - // cannot use *inner_object, need to convert both members - p = std::make_pair(obj.first, obj.second.template get()); -} +#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) -struct to_json_fn -{ - private: - template - auto call(BasicJsonType& j, T&& val, priority_tag<1>) const noexcept(noexcept(to_json(j, std::forward(val)))) - -> decltype(to_json(j, std::forward(val)), void()) - { - return to_json(j, std::forward(val)); - } +#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) + #undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#endif +#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) - template - void call(BasicJsonType&, T&&, priority_tag<0>) const noexcept - { - static_assert(sizeof(BasicJsonType) == 0, - "could not find to_json() method in T's namespace"); - } +#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) + #undef JSON_HEDLEY_CLANG_HAS_FEATURE +#endif +#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) - public: - template - void operator()(BasicJsonType& j, T&& val) const - noexcept(noexcept(std::declval().call(j, std::forward(val), priority_tag<1> {}))) - { - return call(j, std::forward(val), priority_tag<1> {}); - } -}; +#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) + #undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#endif +#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) -struct from_json_fn -{ - private: - template - auto call(const BasicJsonType& j, T& val, priority_tag<1>) const - noexcept(noexcept(from_json(j, val))) - -> decltype(from_json(j, val), void()) - { - return from_json(j, val); - } +#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) - template - void call(const BasicJsonType&, T&, priority_tag<0>) const noexcept - { - static_assert(sizeof(BasicJsonType) == 0, - "could not find from_json() method in T's namespace"); - } +#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) + #undef JSON_HEDLEY_CLANG_HAS_WARNING +#endif +#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) - public: - template - void operator()(const BasicJsonType& j, T& val) const - noexcept(noexcept(std::declval().call(j, val, priority_tag<1> {}))) - { - return call(j, val, priority_tag<1> {}); - } -}; +#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ -// taken from ranges-v3 -template -struct static_const -{ - static constexpr T value{}; -}; -template -constexpr T static_const::value; -} // namespace detail +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif -/// namespace to hold default `to_json` / `from_json` functions -namespace -{ -constexpr const auto& to_json = detail::static_const::value; -constexpr const auto& from_json = detail::static_const::value; -} +// C++ language standard detection +#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 +#endif +// disable float-equal warnings on GCC/clang +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" +#endif -/*! -@brief default JSONSerializer template argument +// disable documentation warnings on clang +#if defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdocumentation" +#endif -This serializer ignores the template arguments and uses ADL -([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl)) -for serialization. -*/ -template -struct adl_serializer -{ - /*! - @brief convert a JSON value to any value type +// allow to disable exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif - This function is usually called by the `get()` function of the - @ref basic_json class (either explicit or via conversion operators). +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif - @param[in] j JSON value to read from - @param[in,out] val value to write to - */ - template - static void from_json(BasicJsonType&& j, ValueType& val) noexcept( - noexcept(::nlohmann::from_json(std::forward(j), val))) - { - ::nlohmann::from_json(std::forward(j), val); +// allow to override assert +#if !defined(JSON_ASSERT) + #include // assert + #define JSON_ASSERT(x) assert(x) +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ } - /*! - @brief convert any value type to a JSON value +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer, \ + class BinaryType> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +// Macros to simplify conversion from/to types + +#define NLOHMANN_JSON_EXPAND( x ) x +#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME +#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ + NLOHMANN_JSON_PASTE64, \ + NLOHMANN_JSON_PASTE63, \ + NLOHMANN_JSON_PASTE62, \ + NLOHMANN_JSON_PASTE61, \ + NLOHMANN_JSON_PASTE60, \ + NLOHMANN_JSON_PASTE59, \ + NLOHMANN_JSON_PASTE58, \ + NLOHMANN_JSON_PASTE57, \ + NLOHMANN_JSON_PASTE56, \ + NLOHMANN_JSON_PASTE55, \ + NLOHMANN_JSON_PASTE54, \ + NLOHMANN_JSON_PASTE53, \ + NLOHMANN_JSON_PASTE52, \ + NLOHMANN_JSON_PASTE51, \ + NLOHMANN_JSON_PASTE50, \ + NLOHMANN_JSON_PASTE49, \ + NLOHMANN_JSON_PASTE48, \ + NLOHMANN_JSON_PASTE47, \ + NLOHMANN_JSON_PASTE46, \ + NLOHMANN_JSON_PASTE45, \ + NLOHMANN_JSON_PASTE44, \ + NLOHMANN_JSON_PASTE43, \ + NLOHMANN_JSON_PASTE42, \ + NLOHMANN_JSON_PASTE41, \ + NLOHMANN_JSON_PASTE40, \ + NLOHMANN_JSON_PASTE39, \ + NLOHMANN_JSON_PASTE38, \ + NLOHMANN_JSON_PASTE37, \ + NLOHMANN_JSON_PASTE36, \ + NLOHMANN_JSON_PASTE35, \ + NLOHMANN_JSON_PASTE34, \ + NLOHMANN_JSON_PASTE33, \ + NLOHMANN_JSON_PASTE32, \ + NLOHMANN_JSON_PASTE31, \ + NLOHMANN_JSON_PASTE30, \ + NLOHMANN_JSON_PASTE29, \ + NLOHMANN_JSON_PASTE28, \ + NLOHMANN_JSON_PASTE27, \ + NLOHMANN_JSON_PASTE26, \ + NLOHMANN_JSON_PASTE25, \ + NLOHMANN_JSON_PASTE24, \ + NLOHMANN_JSON_PASTE23, \ + NLOHMANN_JSON_PASTE22, \ + NLOHMANN_JSON_PASTE21, \ + NLOHMANN_JSON_PASTE20, \ + NLOHMANN_JSON_PASTE19, \ + NLOHMANN_JSON_PASTE18, \ + NLOHMANN_JSON_PASTE17, \ + NLOHMANN_JSON_PASTE16, \ + NLOHMANN_JSON_PASTE15, \ + NLOHMANN_JSON_PASTE14, \ + NLOHMANN_JSON_PASTE13, \ + NLOHMANN_JSON_PASTE12, \ + NLOHMANN_JSON_PASTE11, \ + NLOHMANN_JSON_PASTE10, \ + NLOHMANN_JSON_PASTE9, \ + NLOHMANN_JSON_PASTE8, \ + NLOHMANN_JSON_PASTE7, \ + NLOHMANN_JSON_PASTE6, \ + NLOHMANN_JSON_PASTE5, \ + NLOHMANN_JSON_PASTE4, \ + NLOHMANN_JSON_PASTE3, \ + NLOHMANN_JSON_PASTE2, \ + NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) +#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) +#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) +#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) +#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) +#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) +#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) +#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) +#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) +#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) +#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) +#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) +#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) +#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) +#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) +#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) +#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) +#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) +#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) +#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) +#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) +#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) +#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) +#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) +#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) +#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) +#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) +#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) +#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) +#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) +#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) +#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) +#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) +#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) +#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) +#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) +#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) +#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) +#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) +#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) +#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) +#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) +#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) +#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) +#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) +#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) +#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) +#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) +#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) +#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) +#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) +#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) +#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) +#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) +#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) +#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) +#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) +#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) +#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) + +#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; +#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); - This function is usually called by the constructors of the @ref basic_json - class. +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } - @param[in,out] j JSON value to write to - @param[in] val value to read from - */ - template - static void to_json(BasicJsonType& j, ValueType&& val) noexcept( - noexcept(::nlohmann::to_json(j, std::forward(val)))) - { - ::nlohmann::to_json(j, std::forward(val)); - } -}; +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } +#ifndef JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_USE_IMPLICIT_CONVERSIONS 1 +#endif -/*! -@brief a class to store JSON values +#if JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_EXPLICIT +#else + #define JSON_EXPLICIT explicit +#endif -@tparam ObjectType type for JSON objects (`std::map` by default; will be used -in @ref object_t) -@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used -in @ref array_t) -@tparam StringType type for JSON strings and object keys (`std::string` by -default; will be used in @ref string_t) -@tparam BooleanType type for JSON booleans (`bool` by default; will be used -in @ref boolean_t) -@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by -default; will be used in @ref number_integer_t) -@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c -`uint64_t` by default; will be used in @ref number_unsigned_t) -@tparam NumberFloatType type for JSON floating-point numbers (`double` by -default; will be used in @ref number_float_t) -@tparam AllocatorType type of the allocator to use (`std::allocator` by -default) -@tparam JSONSerializer the serializer to resolve internal calls to `to_json()` -and `from_json()` (@ref adl_serializer by default) -@requirement The class satisfies the following concept requirements: -- Basic - - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible): - JSON values can be default constructed. The result will be a JSON null - value. - - [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible): - A JSON value can be constructed from an rvalue argument. - - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible): - A JSON value can be copy-constructed from an lvalue expression. - - [MoveAssignable](http://en.cppreference.com/w/cpp/concept/MoveAssignable): - A JSON value van be assigned from an rvalue argument. - - [CopyAssignable](http://en.cppreference.com/w/cpp/concept/CopyAssignable): - A JSON value can be copy-assigned from an lvalue expression. - - [Destructible](http://en.cppreference.com/w/cpp/concept/Destructible): - JSON values can be destructed. -- Layout - - [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType): - JSON values have - [standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout): - All non-static data members are private and standard layout types, the - class has no virtual functions or (virtual) base classes. -- Library-wide - - [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable): - JSON values can be compared with `==`, see @ref - operator==(const_reference,const_reference). - - [LessThanComparable](http://en.cppreference.com/w/cpp/concept/LessThanComparable): - JSON values can be compared with `<`, see @ref - operator<(const_reference,const_reference). - - [Swappable](http://en.cppreference.com/w/cpp/concept/Swappable): - Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of - other compatible types, using unqualified function call @ref swap(). - - [NullablePointer](http://en.cppreference.com/w/cpp/concept/NullablePointer): - JSON values can be compared against `std::nullptr_t` objects which are used - to model the `null` value. -- Container - - [Container](http://en.cppreference.com/w/cpp/concept/Container): - JSON values can be used like STL containers and provide iterator access. - - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer); - JSON values can be used like STL containers and provide reverse iterator - access. +namespace nlohmann +{ +namespace detail +{ +//////////////// +// exceptions // +//////////////// -@invariant The member variables @a m_value and @a m_type have the following -relationship: -- If `m_type == value_t::object`, then `m_value.object != nullptr`. -- If `m_type == value_t::array`, then `m_value.array != nullptr`. -- If `m_type == value_t::string`, then `m_value.string != nullptr`. -The invariants are checked by member function assert_invariant(). +/*! +@brief general exception of the @ref basic_json class + +This class is an extension of `std::exception` objects with a member @a id for +exception ids. It is used as the base class for all exceptions thrown by the +@ref basic_json class. This class can hence be used as "wildcard" to catch +exceptions. + +Subclasses: +- @ref parse_error for exceptions indicating a parse error +- @ref invalid_iterator for exceptions indicating errors with iterators +- @ref type_error for exceptions indicating executing a member function with + a wrong type +- @ref out_of_range for exceptions indicating access out of the defined range +- @ref other_error for exceptions indicating other library errors @internal -@note ObjectType trick from http://stackoverflow.com/a/9860911 +@note To have nothrow-copy-constructible exceptions, we internally use + `std::runtime_error` which can cope with arbitrary-length error messages. + Intermediate strings are built with static functions and then passed to + the actual constructor. @endinternal -@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange -Format](http://rfc7159.net/rfc7159) - -@since version 1.0.0 +@liveexample{The following code shows how arbitrary library exceptions can be +caught.,exception} -@nosubgrouping +@since version 3.0.0 */ -template < - template class ObjectType = std::map, - template class ArrayType = std::vector, - class StringType = std::string, - class BooleanType = bool, - class NumberIntegerType = std::int64_t, - class NumberUnsignedType = std::uint64_t, - class NumberFloatType = double, - template class AllocatorType = std::allocator, - template class JSONSerializer = adl_serializer - > -class basic_json +class exception : public std::exception { - private: - template friend struct detail::external_constructor; - /// workaround type for MSVC - using basic_json_t = basic_json; - public: - using value_t = detail::value_t; - // forward declarations - template class iter_impl; - template class json_reverse_iterator; - class json_pointer; - template - using json_serializer = JSONSerializer; + /// returns the explanatory string + JSON_HEDLEY_RETURNS_NON_NULL + const char* what() const noexcept override + { + return m.what(); + } + /// the id of the exception + const int id; - //////////////// - // exceptions // - //////////////// + protected: + JSON_HEDLEY_NON_NULL(3) + exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} - /// @name exceptions - /// Classes to implement user-defined exceptions. - /// @{ + static std::string name(const std::string& ename, int id_) + { + return "[json.exception." + ename + "." + std::to_string(id_) + "] "; + } - /// @copydoc detail::exception - using exception = detail::exception; - /// @copydoc detail::parse_error - using parse_error = detail::parse_error; - /// @copydoc detail::invalid_iterator - using invalid_iterator = detail::invalid_iterator; - /// @copydoc detail::type_error - using type_error = detail::type_error; - /// @copydoc detail::out_of_range - using out_of_range = detail::out_of_range; - /// @copydoc detail::other_error - using other_error = detail::other_error; + private: + /// an exception object as storage for error messages + std::runtime_error m; +}; - /// @} +/*! +@brief exception indicating a parse error +This exception is thrown by the library when a parse error occurs. Parse errors +can occur during the deserialization of JSON text, CBOR, MessagePack, as well +as when using JSON Patch. - ///////////////////// - // container types // - ///////////////////// +Member @a byte holds the byte index of the last read character in the input +file. - /// @name container types - /// The canonic container types to use @ref basic_json like any other STL - /// container. - /// @{ +Exceptions have ids 1xx. - /// the type of elements in a basic_json container - using value_type = basic_json; +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position. +json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. +json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid. +json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects. +json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors. +json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`. +json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character. +json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences. +json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. +json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. +json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. +json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. +json.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet). +json.exception.parse_error.115 | parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A | A UBJSON high-precision number could not be parsed. - /// the type of an element reference - using reference = value_type&; - /// the type of an element const reference - using const_reference = const value_type&; +@note For an input with n bytes, 1 is the index of the first character and n+1 + is the index of the terminating null byte or the end of file. This also + holds true when reading a byte vector (CBOR or MessagePack). - /// a type to represent differences between iterators - using difference_type = std::ptrdiff_t; - /// a type to represent container sizes - using size_type = std::size_t; - - /// the allocator type - using allocator_type = AllocatorType; - - /// the type of an element pointer - using pointer = typename std::allocator_traits::pointer; - /// the type of an element const pointer - using const_pointer = typename std::allocator_traits::const_pointer; - - /// an iterator for a basic_json container - using iterator = iter_impl; - /// a const iterator for a basic_json container - using const_iterator = iter_impl; - /// a reverse iterator for a basic_json container - using reverse_iterator = json_reverse_iterator; - /// a const reverse iterator for a basic_json container - using const_reverse_iterator = json_reverse_iterator; - - /// @} +@liveexample{The following code shows how a `parse_error` exception can be +caught.,parse_error} +@sa - @ref exception for the base class of the library exceptions +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors +@since version 3.0.0 +*/ +class parse_error : public exception +{ + public: /*! - @brief returns the allocator associated with the container + @brief create a parse error exception + @param[in] id_ the id of the exception + @param[in] pos the position where the error occurred (or with + chars_read_total=0 if the position cannot be + determined) + @param[in] what_arg the explanatory string + @return parse_error object */ - static allocator_type get_allocator() + static parse_error create(int id_, const position_t& pos, const std::string& what_arg) { - return allocator_type(); + std::string w = exception::name("parse_error", id_) + "parse error" + + position_string(pos) + ": " + what_arg; + return parse_error(id_, pos.chars_read_total, w.c_str()); } - /*! - @brief returns version information on the library + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + + ": " + what_arg; + return parse_error(id_, byte_, w.c_str()); + } - This function returns a JSON object with information about the library, - including the version number and information on the platform and compiler. + /*! + @brief byte index of the parse error - @return JSON object holding version information - key | description - ----------- | --------------- - `compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version). - `copyright` | The copyright line for the library as string. - `name` | The name of the library as string. - `platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`. - `url` | The URL of the project as string. - `version` | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string). + The byte index of the last read character in the input file. - @liveexample{The following code shows an example output of the `meta()` - function.,meta} + @note For an input with n bytes, 1 is the index of the first character and + n+1 is the index of the terminating null byte or the end of file. + This also holds true when reading a byte vector (CBOR or MessagePack). + */ + const std::size_t byte; - @complexity Constant. + private: + parse_error(int id_, std::size_t byte_, const char* what_arg) + : exception(id_, what_arg), byte(byte_) {} - @since 2.1.0 - */ - static basic_json meta() + static std::string position_string(const position_t& pos) { - basic_json result; + return " at line " + std::to_string(pos.lines_read + 1) + + ", column " + std::to_string(pos.chars_read_current_line); + } +}; - result["copyright"] = "(C) 2013-2017 Niels Lohmann"; - result["name"] = "JSON for Modern C++"; - result["url"] = "https://github.com/nlohmann/json"; - result["version"] = - { - {"string", "2.1.1"}, {"major", 2}, {"minor", 1}, {"patch", 1} - }; +/*! +@brief exception indicating errors with iterators -#ifdef _WIN32 - result["platform"] = "win32"; -#elif defined __linux__ - result["platform"] = "linux"; -#elif defined __APPLE__ - result["platform"] = "apple"; -#elif defined __unix__ - result["platform"] = "unix"; -#else - result["platform"] = "unknown"; -#endif +This exception is thrown if iterators passed to a library function do not match +the expected semantics. -#if defined(__clang__) - result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; -#elif defined(__ICC) || defined(__INTEL_COMPILER) - result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; -#elif defined(__GNUC__) || defined(__GNUG__) - result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}}; -#elif defined(__HP_cc) || defined(__HP_aCC) - result["compiler"] = "hp" -#elif defined(__IBMCPP__) - result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; -#elif defined(_MSC_VER) - result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; -#elif defined(__PGI) - result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; -#elif defined(__SUNPRO_CC) - result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; -#else - result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; -#endif +Exceptions have ids 2xx. -#ifdef __cplusplus - result["compiler"]["c++"] = std::to_string(__cplusplus); -#else - result["compiler"]["c++"] = "unknown"; -#endif - return result; - } +name / id | example message | description +----------------------------------- | --------------- | ------------------------- +json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion. +json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from. +json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid. +json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid. +json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range. +json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key. +json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to. +json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. +json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered. +json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin(). +@liveexample{The following code shows how an `invalid_iterator` exception can be +caught.,invalid_iterator} - /////////////////////////// - // JSON value data types // - /////////////////////////// +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors - /// @name JSON value data types - /// The data types to store a JSON value. These types are derived from - /// the template arguments passed to class @ref basic_json. - /// @{ +@since version 3.0.0 +*/ +class invalid_iterator : public exception +{ + public: + static invalid_iterator create(int id_, const std::string& what_arg) + { + std::string w = exception::name("invalid_iterator", id_) + what_arg; + return invalid_iterator(id_, w.c_str()); + } - /*! - @brief a type for an object + private: + JSON_HEDLEY_NON_NULL(3) + invalid_iterator(int id_, const char* what_arg) + : exception(id_, what_arg) {} +}; - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows: - > An object is an unordered collection of zero or more name/value pairs, - > where a name is a string and a value is a string, number, boolean, null, - > object, or array. +/*! +@brief exception indicating executing a member function with a wrong type - To store objects in C++, a type is defined by the template parameters - described below. +This exception is thrown in case of a type error; that is, a library function is +executed on a JSON value whose type does not match the expected semantics. - @tparam ObjectType the container to store objects (e.g., `std::map` or - `std::unordered_map`) - @tparam StringType the type of the keys or names (e.g., `std::string`). - The comparison function `std::less` is used to order elements - inside the container. - @tparam AllocatorType the allocator to use for objects (e.g., - `std::allocator`) +Exceptions have ids 3xx. - #### Default type +name / id | example message | description +----------------------------- | --------------- | ------------------------- +json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. +json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. +json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &. +json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. +json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. +json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. +json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. +json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types. +json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. +json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. +json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. +json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types. +json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined. +json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. +json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. +json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. | +json.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) | - With the default values for @a ObjectType (`std::map`), @a StringType - (`std::string`), and @a AllocatorType (`std::allocator`), the default - value for @a object_t is: +@liveexample{The following code shows how a `type_error` exception can be +caught.,type_error} - @code {.cpp} - std::map< - std::string, // key_type - basic_json, // value_type - std::less, // key_compare - std::allocator> // allocator_type - > - @endcode +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors - #### Behavior +@since version 3.0.0 +*/ +class type_error : public exception +{ + public: + static type_error create(int id_, const std::string& what_arg) + { + std::string w = exception::name("type_error", id_) + what_arg; + return type_error(id_, w.c_str()); + } - The choice of @a object_t influences the behavior of the JSON class. With - the default type, objects have the following behavior: + private: + JSON_HEDLEY_NON_NULL(3) + type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; - - When all names are unique, objects will be interoperable in the sense - that all software implementations receiving that object will agree on - the name-value mappings. - - When the names within an object are not unique, later stored name/value - pairs overwrite previously stored name/value pairs, leaving the used - names unique. For instance, `{"key": 1}` and `{"key": 2, "key": 1}` will - be treated as equal and both stored as `{"key": 1}`. - - Internally, name/value pairs are stored in lexicographical order of the - names. Objects will also be serialized (see @ref dump) in this order. - For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored - and serialized as `{"a": 2, "b": 1}`. - - When comparing objects, the order of the name/value pairs is irrelevant. - This makes objects interoperable in the sense that they will not be - affected by these differences. For instance, `{"b": 1, "a": 2}` and - `{"a": 2, "b": 1}` will be treated as equal. +/*! +@brief exception indicating access out of the defined range - #### Limits +This exception is thrown in case a library function is called on an input +parameter that exceeds the expected range, for instance in case of array +indices or nonexisting object keys. - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the maximum depth of nesting. +Exceptions have ids 4xx. - In this class, the object's limit of nesting is not constraint explicitly. - However, a maximum depth of nesting may be introduced by the compiler or - runtime environment. A theoretical limit can be queried by calling the - @ref max_size function of a JSON object. +name / id | example message | description +------------------------------- | --------------- | ------------------------- +json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. +json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it. +json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. +json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. +json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. +json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. +json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. (until version 3.8.0) | +json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | +json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string | - #### Storage +@liveexample{The following code shows how an `out_of_range` exception can be +caught.,out_of_range} - Objects are stored as pointers in a @ref basic_json type. That is, for any - access to object values, a pointer of type `object_t*` must be - dereferenced. +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref other_error for exceptions indicating other library errors - @sa @ref array_t -- type for an array value +@since version 3.0.0 +*/ +class out_of_range : public exception +{ + public: + static out_of_range create(int id_, const std::string& what_arg) + { + std::string w = exception::name("out_of_range", id_) + what_arg; + return out_of_range(id_, w.c_str()); + } - @since version 1.0.0 + private: + JSON_HEDLEY_NON_NULL(3) + out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; - @note The order name/value pairs are added to the object is *not* - preserved by the library. Therefore, iterating an object may return - name/value pairs in a different order than they were originally stored. In - fact, keys will be traversed in alphabetical order as `std::map` with - `std::less` is used by default. Please note this behavior conforms to [RFC - 7159](http://rfc7159.net/rfc7159), because any order implements the - specified "unordered" nature of JSON objects. - */ - using object_t = ObjectType, - AllocatorType>>; +/*! +@brief exception indicating other library errors - /*! - @brief a type for an array +This exception is thrown in case of errors that cannot be classified with the +other exception types. - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows: - > An array is an ordered sequence of zero or more values. +Exceptions have ids 5xx. - To store objects in C++, a type is defined by the template parameters - explained below. +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. - @tparam ArrayType container type to store arrays (e.g., `std::vector` or - `std::list`) - @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range - #### Default type +@liveexample{The following code shows how an `other_error` exception can be +caught.,other_error} - With the default values for @a ArrayType (`std::vector`) and @a - AllocatorType (`std::allocator`), the default value for @a array_t is: +@since version 3.0.0 +*/ +class other_error : public exception +{ + public: + static other_error create(int id_, const std::string& what_arg) + { + std::string w = exception::name("other_error", id_) + what_arg; + return other_error(id_, w.c_str()); + } - @code {.cpp} - std::vector< - basic_json, // value_type - std::allocator // allocator_type - > - @endcode + private: + JSON_HEDLEY_NON_NULL(3) + other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; +} // namespace detail +} // namespace nlohmann - #### Limits +// #include - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the maximum depth of nesting. +// #include - In this class, the array's limit of nesting is not constraint explicitly. - However, a maximum depth of nesting may be introduced by the compiler or - runtime environment. A theoretical limit can be queried by calling the - @ref max_size function of a JSON array. - #### Storage +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type - Arrays are stored as pointers in a @ref basic_json type. That is, for any - access to array values, a pointer of type `array_t*` must be dereferenced. +namespace nlohmann +{ +namespace detail +{ +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; - @sa @ref object_t -- type for an object value +template +using uncvref_t = typename std::remove_cv::type>::type; - @since version 1.0.0 - */ - using array_t = ArrayType>; +// implementation of C++14 index_sequence and affiliates +// source: https://stackoverflow.com/a/32223343 +template +struct index_sequence +{ + using type = index_sequence; + using value_type = std::size_t; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; - /*! - @brief a type for a string +template +struct merge_and_renumber; - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: - > A string is a sequence of zero or more Unicode characters. +template +struct merge_and_renumber, index_sequence> + : index_sequence < I1..., (sizeof...(I1) + I2)... > {}; - To store objects in C++, a type is defined by the template parameter - described below. Unicode values are split by the JSON class into - byte-sized characters during deserialization. +template +struct make_index_sequence + : merge_and_renumber < typename make_index_sequence < N / 2 >::type, + typename make_index_sequence < N - N / 2 >::type > {}; - @tparam StringType the container to store strings (e.g., `std::string`). - Note this container is used for keys/names in objects, see @ref object_t. +template<> struct make_index_sequence<0> : index_sequence<> {}; +template<> struct make_index_sequence<1> : index_sequence<0> {}; - #### Default type +template +using index_sequence_for = make_index_sequence; - With the default values for @a StringType (`std::string`), the default - value for @a string_t is: +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; - @code {.cpp} - std::string - @endcode +// taken from ranges-v3 +template +struct static_const +{ + static constexpr T value{}; +}; - #### Encoding +template +constexpr T static_const::value; +} // namespace detail +} // namespace nlohmann - Strings are stored in UTF-8 encoding. Therefore, functions like - `std::string::size()` or `std::string::length()` return the number of - bytes in the string rather than the number of characters or glyphs. +// #include - #### String comparison - [RFC 7159](http://rfc7159.net/rfc7159) states: - > Software implementations are typically required to test names of object - > members for equality. Implementations that transform the textual - > representation into sequences of Unicode code units and then perform the - > comparison numerically, code unit by code unit, are interoperable in the - > sense that implementations will agree in all cases on equality or - > inequality of two strings. For example, implementations that compare - > strings with escaped characters unconverted may incorrectly find that - > `"a\\b"` and `"a\u005Cb"` are not equal. +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval - This implementation is interoperable as it does compare strings code unit - by code unit. +// #include - #### Storage - String values are stored as pointers in a @ref basic_json type. That is, - for any access to string values, a pointer of type `string_t*` must be - dereferenced. +#include // random_access_iterator_tag - @since version 1.0.0 - */ - using string_t = StringType; +// #include - /*! - @brief a type for a boolean - [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a - type which differentiates the two literals `true` and `false`. +namespace nlohmann +{ +namespace detail +{ +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; +} // namespace detail +} // namespace nlohmann - To store objects in C++, a type is defined by the template parameter @a - BooleanType which chooses the type to use. +// #include - #### Default type - With the default values for @a BooleanType (`bool`), the default value for - @a boolean_t is: +namespace nlohmann +{ +namespace detail +{ +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; - @code {.cpp} - bool - @endcode +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; - #### Storage +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; - Boolean values are stored directly inside a @ref basic_json type. +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; +} // namespace detail +} // namespace nlohmann - @since version 1.0.0 - */ - using boolean_t = BooleanType; +// #include - /*! - @brief a type for a number (integer) +// #include - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most - > programming languages. A number is represented in base 10 using decimal - > digits. It contains an integer component that may be prefixed with an - > optional minus sign, which may be followed by a fraction part and/or an - > exponent part. Leading zeros are not allowed. (...) Numeric values that - > cannot be represented in the grammar below (such as Infinity and NaN) - > are not permitted. +// #include - This description includes both integer and floating-point numbers. - However, C++ allows more precise storage if it is known whether the number - is a signed integer, an unsigned integer or a floating-point number. - Therefore, three different types, @ref number_integer_t, @ref - number_unsigned_t and @ref number_float_t are used. - To store integer numbers in C++, a type is defined by the template - parameter @a NumberIntegerType which chooses the type to use. +#include - #### Default type +// #include - With the default values for @a NumberIntegerType (`int64_t`), the default - value for @a number_integer_t is: - @code {.cpp} - int64_t - @endcode +// https://en.cppreference.com/w/cpp/experimental/is_detected +namespace nlohmann +{ +namespace detail +{ +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; - #### Default behavior +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; - - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in integer literals lead to an interpretation as octal - number. Internally, the value will be stored as decimal number. For - instance, the C++ integer literal `010` will be serialized to `8`. - During deserialization, leading zeros yield an error. - - Not-a-number (NaN) values will be serialized to `null`. +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; - #### Limits +template class Op, class... Args> +using is_detected = typename detector::value_t; - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the range and precision of numbers. +template class Op, class... Args> +using detected_t = typename detector::type; - When the default type is used, the maximal integer number that can be - stored is `9223372036854775807` (INT64_MAX) and the minimal integer number - that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers - that are out of range will yield over/underflow when used in a - constructor. During deserialization, too large or small integer numbers - will be automatically be stored as @ref number_unsigned_t or @ref - number_float_t. +template class Op, class... Args> +using detected_or = detector; - [RFC 7159](http://rfc7159.net/rfc7159) further states: - > Note that when such software is used, numbers that are integers and are - > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense - > that implementations will agree exactly on their numeric values. +template class Op, class... Args> +using detected_or_t = typename detected_or::type; - As this range is a subrange of the exactly supported range [INT64_MIN, - INT64_MAX], this class's integer type is interoperable. +template class Op, class... Args> +using is_detected_exact = std::is_same>; - #### Storage +template class Op, class... Args> +using is_detected_convertible = + std::is_convertible, To>; +} // namespace detail +} // namespace nlohmann - Integer number values are stored directly inside a @ref basic_json type. +// #include +#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ +#define INCLUDE_NLOHMANN_JSON_FWD_HPP_ - @sa @ref number_float_t -- type for number values (floating-point) +#include // int64_t, uint64_t +#include // map +#include // allocator +#include // string +#include // vector - @sa @ref number_unsigned_t -- type for number values (unsigned integer) +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ +/*! +@brief default JSONSerializer template argument - @since version 1.0.0 - */ - using number_integer_t = NumberIntegerType; +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer; + +template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector> +class basic_json; - /*! - @brief a type for a number (unsigned) +/*! +@brief JSON Pointer - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most - > programming languages. A number is represented in base 10 using decimal - > digits. It contains an integer component that may be prefixed with an - > optional minus sign, which may be followed by a fraction part and/or an - > exponent part. Leading zeros are not allowed. (...) Numeric values that - > cannot be represented in the grammar below (such as Infinity and NaN) - > are not permitted. +A JSON pointer defines a string syntax for identifying a specific value +within a JSON document. It can be used with functions `at` and +`operator[]`. Furthermore, JSON pointers are the base for JSON patches. - This description includes both integer and floating-point numbers. - However, C++ allows more precise storage if it is known whether the number - is a signed integer, an unsigned integer or a floating-point number. - Therefore, three different types, @ref number_integer_t, @ref - number_unsigned_t and @ref number_float_t are used. +@sa [RFC 6901](https://tools.ietf.org/html/rfc6901) - To store unsigned integer numbers in C++, a type is defined by the - template parameter @a NumberUnsignedType which chooses the type to use. +@since version 2.0.0 +*/ +template +class json_pointer; - #### Default type +/*! +@brief default JSON class - With the default values for @a NumberUnsignedType (`uint64_t`), the - default value for @a number_unsigned_t is: +This type is the default specialization of the @ref basic_json class which +uses the standard template types. - @code {.cpp} - uint64_t - @endcode +@since version 1.0.0 +*/ +using json = basic_json<>; - #### Default behavior +template +struct ordered_map; - - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in integer literals lead to an interpretation as octal - number. Internally, the value will be stored as decimal number. For - instance, the C++ integer literal `010` will be serialized to `8`. - During deserialization, leading zeros yield an error. - - Not-a-number (NaN) values will be serialized to `null`. +/*! +@brief ordered JSON class - #### Limits +This type preserves the insertion order of object keys. - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the range and precision of numbers. +@since version 3.9.0 +*/ +using ordered_json = basic_json; - When the default type is used, the maximal integer number that can be - stored is `18446744073709551615` (UINT64_MAX) and the minimal integer - number that can be stored is `0`. Integer numbers that are out of range - will yield over/underflow when used in a constructor. During - deserialization, too large or small integer numbers will be automatically - be stored as @ref number_integer_t or @ref number_float_t. +} // namespace nlohmann - [RFC 7159](http://rfc7159.net/rfc7159) further states: - > Note that when such software is used, numbers that are integers and are - > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense - > that implementations will agree exactly on their numeric values. +#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ - As this range is a subrange (when considered in conjunction with the - number_integer_t type) of the exactly supported range [0, UINT64_MAX], - this class's integer type is interoperable. - #### Storage +namespace nlohmann +{ +/*! +@brief detail namespace with internal helper functions - Integer number values are stored directly inside a @ref basic_json type. +This namespace collects functions that should not be exposed, +implementations of some @ref basic_json methods, and meta-programming helpers. - @sa @ref number_float_t -- type for number values (floating-point) - @sa @ref number_integer_t -- type for number values (integer) +@since version 2.1.0 +*/ +namespace detail +{ +///////////// +// helpers // +///////////// - @since version 2.0.0 - */ - using number_unsigned_t = NumberUnsignedType; +// Note to maintainers: +// +// Every trait in this file expects a non CV-qualified type. +// The only exceptions are in the 'aliases for detected' section +// (i.e. those of the form: decltype(T::member_function(std::declval()))) +// +// In this case, T has to be properly CV-qualified to constraint the function arguments +// (e.g. to_json(BasicJsonType&, const T&)) - /*! - @brief a type for a number (floating-point) +template struct is_basic_json : std::false_type {}; - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most - > programming languages. A number is represented in base 10 using decimal - > digits. It contains an integer component that may be prefixed with an - > optional minus sign, which may be followed by a fraction part and/or an - > exponent part. Leading zeros are not allowed. (...) Numeric values that - > cannot be represented in the grammar below (such as Infinity and NaN) - > are not permitted. +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct is_basic_json : std::true_type {}; - This description includes both integer and floating-point numbers. - However, C++ allows more precise storage if it is known whether the number - is a signed integer, an unsigned integer or a floating-point number. - Therefore, three different types, @ref number_integer_t, @ref - number_unsigned_t and @ref number_float_t are used. +////////////////////// +// json_ref helpers // +////////////////////// - To store floating-point numbers in C++, a type is defined by the template - parameter @a NumberFloatType which chooses the type to use. +template +class json_ref; - #### Default type +template +struct is_json_ref : std::false_type {}; - With the default values for @a NumberFloatType (`double`), the default - value for @a number_float_t is: +template +struct is_json_ref> : std::true_type {}; - @code {.cpp} - double - @endcode +////////////////////////// +// aliases for detected // +////////////////////////// - #### Default behavior +template +using mapped_type_t = typename T::mapped_type; - - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in floating-point literals will be ignored. Internally, - the value will be stored as decimal number. For instance, the C++ - floating-point literal `01.2` will be serialized to `1.2`. During - deserialization, leading zeros yield an error. - - Not-a-number (NaN) values will be serialized to `null`. +template +using key_type_t = typename T::key_type; - #### Limits +template +using value_type_t = typename T::value_type; - [RFC 7159](http://rfc7159.net/rfc7159) states: - > This specification allows implementations to set limits on the range and - > precision of numbers accepted. Since software that implements IEEE - > 754-2008 binary64 (double precision) numbers is generally available and - > widely used, good interoperability can be achieved by implementations - > that expect no more precision or range than these provide, in the sense - > that implementations will approximate JSON numbers within the expected - > precision. +template +using difference_type_t = typename T::difference_type; - This implementation does exactly follow this approach, as it uses double - precision floating-point numbers. Note values smaller than - `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` - will be stored as NaN internally and be serialized to `null`. +template +using pointer_t = typename T::pointer; - #### Storage +template +using reference_t = typename T::reference; - Floating-point number values are stored directly inside a @ref basic_json - type. +template +using iterator_category_t = typename T::iterator_category; - @sa @ref number_integer_t -- type for number values (integer) +template +using iterator_t = typename T::iterator; - @sa @ref number_unsigned_t -- type for number values (unsigned integer) +template +using to_json_function = decltype(T::to_json(std::declval()...)); - @since version 1.0.0 - */ - using number_float_t = NumberFloatType; +template +using from_json_function = decltype(T::from_json(std::declval()...)); - /// @} +template +using get_template_function = decltype(std::declval().template get()); - private: +// trait checking if JSONSerializer::from_json(json const&, udt&) exists +template +struct has_from_json : std::false_type {}; + +// trait checking if j.get is valid +// use this trait instead of std::is_constructible or std::is_convertible, +// both rely on, or make use of implicit conversions, and thus fail when T +// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) +template +struct is_getable +{ + static constexpr bool value = is_detected::value; +}; - /// helper for exception-safe object creation - template - static T* create(Args&& ... args) - { - AllocatorType alloc; - auto deleter = [&](T * object) - { - alloc.deallocate(object, 1); - }; - std::unique_ptr object(alloc.allocate(1), deleter); - alloc.construct(object.get(), std::forward(args)...); - assert(object != nullptr); - return object.release(); - } +template +struct has_from_json < BasicJsonType, T, + enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; - //////////////////////// - // JSON value storage // - //////////////////////// + static constexpr bool value = + is_detected_exact::value; +}; - /*! - @brief a JSON value +// This trait checks if JSONSerializer::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template +struct has_non_default_from_json : std::false_type {}; - The actual storage for a JSON value of the @ref basic_json class. This - union combines the different storage types for the JSON value types - defined in @ref value_t. +template +struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; - JSON type | value_t type | used type - --------- | --------------- | ------------------------ - object | object | pointer to @ref object_t - array | array | pointer to @ref array_t - string | string | pointer to @ref string_t - boolean | boolean | @ref boolean_t - number | number_integer | @ref number_integer_t - number | number_unsigned | @ref number_unsigned_t - number | number_float | @ref number_float_t - null | null | *no value is stored* + static constexpr bool value = + is_detected_exact::value; +}; - @note Variable-length types (objects, arrays, and strings) are stored as - pointers. The size of the union should not exceed 64 bits if the default - value types are used. +// This trait checks if BasicJsonType::json_serializer::to_json exists +// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. +template +struct has_to_json : std::false_type {}; - @since version 1.0.0 - */ - union json_value - { - /// object (stored with pointer to save storage) - object_t* object; - /// array (stored with pointer to save storage) - array_t* array; - /// string (stored with pointer to save storage) - string_t* string; - /// boolean - boolean_t boolean; - /// number (integer) - number_integer_t number_integer; - /// number (unsigned integer) - number_unsigned_t number_unsigned; - /// number (floating-point) - number_float_t number_float; +template +struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; - /// default constructor (for null values) - json_value() = default; - /// constructor for booleans - json_value(boolean_t v) noexcept : boolean(v) {} - /// constructor for numbers (integer) - json_value(number_integer_t v) noexcept : number_integer(v) {} - /// constructor for numbers (unsigned) - json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} - /// constructor for numbers (floating-point) - json_value(number_float_t v) noexcept : number_float(v) {} - /// constructor for empty values of a given type - json_value(value_t t) - { - switch (t) - { - case value_t::object: - { - object = create(); - break; - } + static constexpr bool value = + is_detected_exact::value; +}; - case value_t::array: - { - array = create(); - break; - } - case value_t::string: - { - string = create(""); - break; - } +/////////////////// +// is_ functions // +/////////////////// - case value_t::boolean: - { - boolean = boolean_t(false); - break; - } +template +struct is_iterator_traits : std::false_type {}; - case value_t::number_integer: - { - number_integer = number_integer_t(0); - break; - } +template +struct is_iterator_traits> +{ + private: + using traits = iterator_traits; - case value_t::number_unsigned: - { - number_unsigned = number_unsigned_t(0); - break; - } + public: + static constexpr auto value = + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value; +}; - case value_t::number_float: - { - number_float = number_float_t(0.0); - break; - } +// source: https://stackoverflow.com/a/37193089/4116453 - case value_t::null: - { - break; - } +template +struct is_complete_type : std::false_type {}; - default: - { - if (JSON_UNLIKELY(t == value_t::null)) - { - JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE - } - break; - } - } - } +template +struct is_complete_type : std::true_type {}; - /// constructor for strings - json_value(const string_t& value) - { - string = create(value); - } +template +struct is_compatible_object_type_impl : std::false_type {}; - /// constructor for objects - json_value(const object_t& value) - { - object = create(value); - } +template +struct is_compatible_object_type_impl < + BasicJsonType, CompatibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ - /// constructor for arrays - json_value(const array_t& value) - { - array = create(value); - } - }; + using object_t = typename BasicJsonType::object_t; - /*! - @brief checks the class invariants + // macOS's is_constructible does not play well with nonesuch... + static constexpr bool value = + std::is_constructible::value && + std::is_constructible::value; +}; - This function asserts the class invariants. It needs to be called at the - end of every constructor to make sure that created objects respect the - invariant. Furthermore, it has to be called each time the type of a JSON - value is changed, because the invariant expresses a relationship between - @a m_type and @a m_value. - */ - void assert_invariant() const - { - assert(m_type != value_t::object or m_value.object != nullptr); - assert(m_type != value_t::array or m_value.array != nullptr); - assert(m_type != value_t::string or m_value.string != nullptr); - } +template +struct is_compatible_object_type + : is_compatible_object_type_impl {}; - public: - ////////////////////////// - // JSON parser callback // - ////////////////////////// - - /*! - @brief JSON callback events - - This enumeration lists the parser events that can trigger calling a - callback function of type @ref parser_callback_t during parsing. - - @image html callback_events.png "Example when certain parse events are triggered" +template +struct is_constructible_object_type_impl : std::false_type {}; - @since version 1.0.0 - */ - enum class parse_event_t : uint8_t - { - /// the parser read `{` and started to process a JSON object - object_start, - /// the parser read `}` and finished processing a JSON object - object_end, - /// the parser read `[` and started to process a JSON array - array_start, - /// the parser read `]` and finished processing a JSON array - array_end, - /// the parser read a key of a value in an object - key, - /// the parser finished reading a JSON value - value - }; +template +struct is_constructible_object_type_impl < + BasicJsonType, ConstructibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + static constexpr bool value = + (std::is_default_constructible::value && + (std::is_move_assignable::value || + std::is_copy_assignable::value) && + (std::is_constructible::value && + std::is_same < + typename object_t::mapped_type, + typename ConstructibleObjectType::mapped_type >::value)) || + (has_from_json::value || + has_non_default_from_json < + BasicJsonType, + typename ConstructibleObjectType::mapped_type >::value); +}; - /*! - @brief per-element parser callback type +template +struct is_constructible_object_type + : is_constructible_object_type_impl {}; - With a parser callback function, the result of parsing a JSON text can be - influenced. When passed to @ref parse(std::istream&, const - parser_callback_t) or @ref parse(const CharT, const parser_callback_t), - it is called on certain events (passed as @ref parse_event_t via parameter - @a event) with a set recursion depth @a depth and context JSON value - @a parsed. The return value of the callback function is a boolean - indicating whether the element that emitted the callback shall be kept or - not. +template +struct is_compatible_string_type_impl : std::false_type {}; - We distinguish six scenarios (determined by the event type) in which the - callback function can be called. The following table describes the values - of the parameters @a depth, @a event, and @a parsed. +template +struct is_compatible_string_type_impl < + BasicJsonType, CompatibleStringType, + enable_if_t::value >> +{ + static constexpr auto value = + std::is_constructible::value; +}; - parameter @a event | description | parameter @a depth | parameter @a parsed - ------------------ | ----------- | ------------------ | ------------------- - parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded - parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key - parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object - parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded - parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array - parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value +template +struct is_compatible_string_type + : is_compatible_string_type_impl {}; - @image html callback_events.png "Example when certain parse events are triggered" +template +struct is_constructible_string_type_impl : std::false_type {}; - Discarding a value (i.e., returning `false`) has different effects - depending on the context in which function was called: +template +struct is_constructible_string_type_impl < + BasicJsonType, ConstructibleStringType, + enable_if_t::value >> +{ + static constexpr auto value = + std::is_constructible::value; +}; - - Discarded values in structured types are skipped. That is, the parser - will behave as if the discarded value was never read. - - In case a value outside a structured type is skipped, it is replaced - with `null`. This case happens if the top-level element is skipped. +template +struct is_constructible_string_type + : is_constructible_string_type_impl {}; - @param[in] depth the depth of the recursion during parsing +template +struct is_compatible_array_type_impl : std::false_type {}; - @param[in] event an event of type parse_event_t indicating the context in - the callback function has been called +template +struct is_compatible_array_type_impl < + BasicJsonType, CompatibleArrayType, + enable_if_t < is_detected::value&& + is_detected::value&& +// This is needed because json_reverse_iterator has a ::iterator type... +// Therefore it is detected as a CompatibleArrayType. +// The real fix would be to have an Iterable concept. + !is_iterator_traits < + iterator_traits>::value >> +{ + static constexpr bool value = + std::is_constructible::value; +}; - @param[in,out] parsed the current intermediate parse result; note that - writing to this value has no effect for parse_event_t::key events +template +struct is_compatible_array_type + : is_compatible_array_type_impl {}; + +template +struct is_constructible_array_type_impl : std::false_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t::value >> + : std::true_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t < !std::is_same::value&& + std::is_default_constructible::value&& +(std::is_move_assignable::value || + std::is_copy_assignable::value)&& +is_detected::value&& +is_detected::value&& +is_complete_type < +detected_t>::value >> +{ + static constexpr bool value = + // This is needed because json_reverse_iterator has a ::iterator type, + // furthermore, std::back_insert_iterator (and other iterators) have a + // base class `iterator`... Therefore it is detected as a + // ConstructibleArrayType. The real fix would be to have an Iterable + // concept. + !is_iterator_traits>::value && + + (std::is_same::value || + has_from_json::value || + has_non_default_from_json < + BasicJsonType, typename ConstructibleArrayType::value_type >::value); +}; - @return Whether the JSON value which called the function during parsing - should be kept (`true`) or not (`false`). In the latter case, it is either - skipped completely or replaced by an empty discarded object. +template +struct is_constructible_array_type + : is_constructible_array_type_impl {}; - @sa @ref parse(std::istream&, parser_callback_t) or - @ref parse(const CharT, const parser_callback_t) for examples +template +struct is_compatible_integer_type_impl : std::false_type {}; - @since version 1.0.0 - */ - using parser_callback_t = std::function; +template +struct is_compatible_integer_type_impl < + RealIntegerType, CompatibleNumberIntegerType, + enable_if_t < std::is_integral::value&& + std::is_integral::value&& + !std::is_same::value >> +{ + // is there an assert somewhere on overflows? + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + static constexpr auto value = + std::is_constructible::value && + CompatibleLimits::is_integer && + RealLimits::is_signed == CompatibleLimits::is_signed; +}; - ////////////////// - // constructors // - ////////////////// +template +struct is_compatible_integer_type + : is_compatible_integer_type_impl {}; - /// @name constructors and destructors - /// Constructors of class @ref basic_json, copy/move constructor, copy - /// assignment, static functions creating objects, and the destructor. - /// @{ +template +struct is_compatible_type_impl: std::false_type {}; - /*! - @brief create an empty value with a given type +template +struct is_compatible_type_impl < + BasicJsonType, CompatibleType, + enable_if_t::value >> +{ + static constexpr bool value = + has_to_json::value; +}; - Create an empty JSON value with a given type. The value will be default - initialized with an empty value which depends on the type: +template +struct is_compatible_type + : is_compatible_type_impl {}; - Value type | initial value - ----------- | ------------- - null | `null` - boolean | `false` - string | `""` - number | `0` - object | `{}` - array | `[]` +// https://en.cppreference.com/w/cpp/types/conjunction +template struct conjunction : std::true_type { }; +template struct conjunction : B1 { }; +template +struct conjunction +: std::conditional, B1>::type {}; - @param[in] value_type the type of the value to create +template +struct is_constructible_tuple : std::false_type {}; - @complexity Constant. +template +struct is_constructible_tuple> : conjunction...> {}; +} // namespace detail +} // namespace nlohmann - @liveexample{The following code shows the constructor for different @ref - value_t values,basic_json__value_t} +// #include - @since version 1.0.0 - */ - basic_json(const value_t value_type) - : m_type(value_type), m_value(value_type) - { - assert_invariant(); - } - /*! - @brief create a null object +#include // array +#include // size_t +#include // uint8_t +#include // string - Create a `null` JSON value. It either takes a null pointer as parameter - (explicitly creating `null`) or no parameter (implicitly creating `null`). - The passed null pointer itself is not read -- it is only used to choose - the right constructor. +namespace nlohmann +{ +namespace detail +{ +/////////////////////////// +// JSON type enumeration // +/////////////////////////// - @complexity Constant. +/*! +@brief the JSON type enumeration - @exceptionsafety No-throw guarantee: this constructor never throws - exceptions. +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. - @liveexample{The following code shows the constructor with and without a - null pointer parameter.,basic_json__nullptr_t} +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. - @since version 1.0.0 - */ - basic_json(std::nullptr_t = nullptr) noexcept - : basic_json(value_t::null) - { - assert_invariant(); - } +@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type - /*! - @brief create a JSON value +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function +}; - This is a "catch all" constructor for all compatible JSON types; that is, - types for which a `to_json()` method exsits. The constructor forwards the - parameter @a val to that method (to `json_serializer::to_json` method - with `U = uncvref_t`, to be exact). +/*! +@brief comparison operator for JSON types - Template type @a CompatibleType includes, but is not limited to, the - following types: - - **arrays**: @ref array_t and all kinds of compatible containers such as - `std::vector`, `std::deque`, `std::list`, `std::forward_list`, - `std::array`, `std::set`, `std::unordered_set`, `std::multiset`, and - `unordered_multiset` with a `value_type` from which a @ref basic_json - value can be constructed. - - **objects**: @ref object_t and all kinds of compatible associative - containers such as `std::map`, `std::unordered_map`, `std::multimap`, - and `std::unordered_multimap` with a `key_type` compatible to - @ref string_t and a `value_type` from which a @ref basic_json value can - be constructed. - - **strings**: @ref string_t, string literals, and all compatible string - containers can be used. - - **numbers**: @ref number_integer_t, @ref number_unsigned_t, - @ref number_float_t, and all convertible number types such as `int`, - `size_t`, `int64_t`, `float` or `double` can be used. - - **boolean**: @ref boolean_t / `bool` can be used. +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string < binary +- furthermore, each type is not smaller than itself +- discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. - See the examples below. +@since version 1.0.0 +*/ +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; - @tparam CompatibleType a type such that: - - @a CompatibleType is not derived from `std::istream`, - - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move - constructors), - - @a CompatibleType is not a @ref basic_json nested type (e.g., - @ref json_pointer, @ref iterator, etc ...) - - @ref @ref json_serializer has a - `to_json(basic_json_t&, CompatibleType&&)` method + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +} +} // namespace detail +} // namespace nlohmann - @tparam U = `uncvref_t` - @param[in] val the value to be forwarded +namespace nlohmann +{ +namespace detail +{ +template +void from_json(const BasicJsonType& j, typename std::nullptr_t& n) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_null())) + { + JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()))); + } + n = nullptr; +} - @complexity Usually linear in the size of the passed @a val, also - depending on the implementation of the called `to_json()` - method. +// overloads for basic_json template parameters +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < std::is_arithmetic::value&& + !std::is_same::value, + int > = 0 > +void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } - @throw what `json_serializer::to_json()` throws + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + } +} - @liveexample{The following code shows the constructor with several - compatible types.,basic_json__CompatibleType} +template +void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) + { + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()))); + } + b = *j.template get_ptr(); +} - @since version 2.1.0 - */ - template, - detail::enable_if_t::value and - not std::is_same::value and - not detail::is_basic_json_nested_type< - basic_json_t, U>::value and - detail::has_to_json::value, - int> = 0> - basic_json(CompatibleType && val) noexcept(noexcept(JSONSerializer::to_json( - std::declval(), std::forward(val)))) +template +void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSONSerializer::to_json(*this, std::forward(val)); - assert_invariant(); + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); } + s = *j.template get_ptr(); +} - /*! - @brief create a container (array or object) from an initializer list +template < + typename BasicJsonType, typename ConstructibleStringType, + enable_if_t < + is_constructible_string_type::value&& + !std::is_same::value, + int > = 0 > +void from_json(const BasicJsonType& j, ConstructibleStringType& s) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + } - Creates a JSON value of type array or object from the passed initializer - list @a init. In case @a type_deduction is `true` (default), the type of - the JSON value to be created is deducted from the initializer list @a init - according to the following rules: + s = *j.template get_ptr(); +} - 1. If the list is empty, an empty JSON object value `{}` is created. - 2. If the list consists of pairs whose first element is a string, a JSON - object value is created where the first elements of the pairs are - treated as keys and the second elements are as values. - 3. In all other cases, an array is created. +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val) +{ + get_arithmetic_value(j, val); +} - The rules aim to create the best fit between a C++ initializer list and - JSON values. The rationale is as follows: +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val) +{ + get_arithmetic_value(j, val); +} - 1. The empty initializer list is written as `{}` which is exactly an empty - JSON object. - 2. C++ has now way of describing mapped types other than to list a list of - pairs. As JSON requires that keys must be of type string, rule 2 is the - weakest constraint one can pose on initializer lists to interpret them - as an object. - 3. In all other cases, the initializer list could not be interpreted as - JSON object type, so interpreting it as JSON array type is safe. +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val) +{ + get_arithmetic_value(j, val); +} - With the rules described above, the following JSON values cannot be - expressed by an initializer list: +template::value, int> = 0> +void from_json(const BasicJsonType& j, EnumType& e) +{ + typename std::underlying_type::type val; + get_arithmetic_value(j, val); + e = static_cast(val); +} - - the empty array (`[]`): use @ref array(std::initializer_list) - with an empty initializer list in this case - - arrays whose elements satisfy rule 2: use @ref - array(std::initializer_list) with the same initializer list - in this case +// forward_list doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::forward_list& l) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + l.clear(); + std::transform(j.rbegin(), j.rend(), + std::front_inserter(l), [](const BasicJsonType & i) + { + return i.template get(); + }); +} - @note When used without parentheses around an empty initializer list, @ref - basic_json() is called instead of this function, yielding the JSON null - value. +// valarray doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::valarray& l) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + l.resize(j.size()); + std::transform(j.begin(), j.end(), std::begin(l), + [](const BasicJsonType & elem) + { + return elem.template get(); + }); +} - @param[in] init initializer list with JSON values +template +auto from_json(const BasicJsonType& j, T (&arr)[N]) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} - @param[in] type_deduction internal parameter; when set to `true`, the type - of the JSON value is deducted from the initializer list @a init; when set - to `false`, the type provided via @a manual_type is forced. This mode is - used by the functions @ref array(std::initializer_list) and - @ref object(std::initializer_list). +template +void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/) +{ + arr = *j.template get_ptr(); +} - @param[in] manual_type internal parameter; when @a type_deduction is set - to `false`, the created JSON value will use the provided type (only @ref - value_t::array and @ref value_t::object are valid); when @a type_deduction - is set to `true`, this parameter has no effect +template +auto from_json_array_impl(const BasicJsonType& j, std::array& arr, + priority_tag<2> /*unused*/) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} - @throw type_error.301 if @a type_deduction is `false`, @a manual_type is - `value_t::object`, but @a init contains an element which is not a pair - whose first element is a string. In this case, the constructor could not - create an object. If @a type_deduction would have be `true`, an array - would have been created. See @ref object(std::initializer_list) - for an example. +template +auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/) +-> decltype( + arr.reserve(std::declval()), + j.template get(), + void()) +{ + using std::end; - @complexity Linear in the size of the initializer list @a init. + ConstructibleArrayType ret; + ret.reserve(j.size()); + std::transform(j.begin(), j.end(), + std::inserter(ret, end(ret)), [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} - @liveexample{The example below shows how JSON values are created from - initializer lists.,basic_json__list_init_t} +template +void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, + priority_tag<0> /*unused*/) +{ + using std::end; - @sa @ref array(std::initializer_list) -- create a JSON array - value from an initializer list - @sa @ref object(std::initializer_list) -- create a JSON object - value from an initializer list + ConstructibleArrayType ret; + std::transform( + j.begin(), j.end(), std::inserter(ret, end(ret)), + [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} - @since version 1.0.0 - */ - basic_json(std::initializer_list init, - bool type_deduction = true, - value_t manual_type = value_t::array) +template < typename BasicJsonType, typename ConstructibleArrayType, + enable_if_t < + is_constructible_array_type::value&& + !is_constructible_object_type::value&& + !is_constructible_string_type::value&& + !std::is_same::value&& + !is_basic_json::value, + int > = 0 > +auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) +-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), +j.template get(), +void()) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - // check if each element is an array with two elements whose first - // element is a string - bool is_an_object = std::all_of(init.begin(), init.end(), - [](const basic_json & element) - { - return element.is_array() and element.size() == 2 and element[0].is_string(); - }); + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } - // adjust type if type deduction is not wanted - if (not type_deduction) - { - // if array is wanted, do not create an object though possible - if (manual_type == value_t::array) - { - is_an_object = false; - } + from_json_array_impl(j, arr, priority_tag<3> {}); +} - // if object is wanted but impossible, throw an exception - if (manual_type == value_t::object and not is_an_object) - { - JSON_THROW(type_error::create(301, "cannot create object from initializer list")); - } - } +template +void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()))); + } - if (is_an_object) - { - // the initializer list is a list of pairs -> create object - m_type = value_t::object; - m_value = value_t::object; + bin = *j.template get_ptr(); +} - std::for_each(init.begin(), init.end(), [this](const basic_json & element) - { - m_value.object->emplace(*(element[0].m_value.string), element[1]); - }); +template::value, int> = 0> +void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_object())) + { + JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()))); + } + + ConstructibleObjectType ret; + auto inner_object = j.template get_ptr(); + using value_type = typename ConstructibleObjectType::value_type; + std::transform( + inner_object->begin(), inner_object->end(), + std::inserter(ret, ret.begin()), + [](typename BasicJsonType::object_t::value_type const & p) + { + return value_type(p.first, p.second.template get()); + }); + obj = std::move(ret); +} + +// overload for arithmetic types, not chosen for basic_json template arguments +// (BooleanType, etc..); note: Is it really necessary to provide explicit +// overloads for boolean_t etc. in case of a custom BooleanType which is not +// an arithmetic type? +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < + std::is_arithmetic::value&& + !std::is_same::value&& + !std::is_same::value&& + !std::is_same::value&& + !std::is_same::value, + int > = 0 > +void from_json(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; } - else + case value_t::number_integer: { - // the initializer list describes an array -> create array - m_type = value_t::array; - m_value.array = create(init); + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::boolean: + { + val = static_cast(*j.template get_ptr()); + break; } - assert_invariant(); + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); } +} - /*! - @brief explicitly create an array from an initializer list - - Creates a JSON array value from a given initializer list. That is, given a - list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the - initializer list is empty, the empty array `[]` is created. - - @note This function is only needed to express two edge cases that cannot - be realized with the initializer list constructor (@ref - basic_json(std::initializer_list, bool, value_t)). These cases - are: - 1. creating an array whose elements are all pairs whose first element is a - string -- in this case, the initializer list constructor would create an - object, taking the first elements as keys - 2. creating an empty array -- passing the empty initializer list to the - initializer list constructor yields an empty object - - @param[in] init initializer list with JSON values to create an array from - (optional) +template +void from_json(const BasicJsonType& j, std::pair& p) +{ + p = {j.at(0).template get(), j.at(1).template get()}; +} - @return JSON array value +template +void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence /*unused*/) +{ + t = std::make_tuple(j.at(Idx).template get::type>()...); +} - @complexity Linear in the size of @a init. +template +void from_json(const BasicJsonType& j, std::tuple& t) +{ + from_json_tuple_impl(j, t, index_sequence_for {}); +} - @liveexample{The following code shows an example for the `array` - function.,array} +template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, + typename = enable_if_t < !std::is_constructible < + typename BasicJsonType::string_t, Key >::value >> +void from_json(const BasicJsonType& j, std::map& m) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(!p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} - @sa @ref basic_json(std::initializer_list, bool, value_t) -- - create a JSON value from an initializer list - @sa @ref object(std::initializer_list) -- create a JSON object - value from an initializer list +template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator, + typename = enable_if_t < !std::is_constructible < + typename BasicJsonType::string_t, Key >::value >> +void from_json(const BasicJsonType& j, std::unordered_map& m) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(!p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} - @since version 1.0.0 - */ - static basic_json array(std::initializer_list init = - std::initializer_list()) +struct from_json_fn +{ + template + auto operator()(const BasicJsonType& j, T& val) const + noexcept(noexcept(from_json(j, val))) + -> decltype(from_json(j, val), void()) { - return basic_json(init, false, value_t::array); + return from_json(j, val); } +}; +} // namespace detail - /*! - @brief explicitly create an object from an initializer list +/// namespace to hold default `from_json` function +/// to see why this is required: +/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html +namespace +{ +constexpr const auto& from_json = detail::static_const::value; +} // namespace +} // namespace nlohmann - Creates a JSON object value from a given initializer list. The initializer - lists elements must be pairs, and their first elements must be strings. If - the initializer list is empty, the empty object `{}` is created. +// #include - @note This function is only added for symmetry reasons. In contrast to the - related function @ref array(std::initializer_list), there are - no cases which can only be expressed by this function. That is, any - initializer list @a init can also be passed to the initializer list - constructor @ref basic_json(std::initializer_list, bool, value_t). - @param[in] init initializer list to create an object from (optional) +#include // copy +#include // begin, end +#include // string +#include // tuple, get +#include // is_same, is_constructible, is_floating_point, is_enum, underlying_type +#include // move, forward, declval, pair +#include // valarray +#include // vector - @return JSON object value - - @throw type_error.301 if @a init is not a list of pairs whose first - elements are strings. In this case, no object can be created. When such a - value is passed to @ref basic_json(std::initializer_list, bool, value_t), - an array would have been created from the passed initializer list @a init. - See example below. - - @complexity Linear in the size of @a init. +// #include - @liveexample{The following code shows an example for the `object` - function.,object} - @sa @ref basic_json(std::initializer_list, bool, value_t) -- - create a JSON value from an initializer list - @sa @ref array(std::initializer_list) -- create a JSON array - value from an initializer list +#include // size_t +#include // input_iterator_tag +#include // string, to_string +#include // tuple_size, get, tuple_element - @since version 1.0.0 - */ - static basic_json object(std::initializer_list init = - std::initializer_list()) - { - return basic_json(init, false, value_t::object); - } +// #include - /*! - @brief construct an array with count copies of given value +// #include - Constructs a JSON array value by creating @a cnt copies of a passed value. - In case @a cnt is `0`, an empty array is created. As postcondition, - `std::distance(begin(),end()) == cnt` holds. - @param[in] cnt the number of JSON copies of @a val to create - @param[in] val the JSON value to copy +namespace nlohmann +{ +namespace detail +{ +template +void int_to_string( string_type& target, std::size_t value ) +{ + // For ADL + using std::to_string; + target = to_string(value); +} +template class iteration_proxy_value +{ + public: + using difference_type = std::ptrdiff_t; + using value_type = iteration_proxy_value; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::input_iterator_tag; + using string_type = typename std::remove_cv< typename std::remove_reference().key() ) >::type >::type; - @complexity Linear in @a cnt. + private: + /// the iterator + IteratorType anchor; + /// an index for arrays (used to create key names) + std::size_t array_index = 0; + /// last stringified array index + mutable std::size_t array_index_last = 0; + /// a string representation of the array index + mutable string_type array_index_str = "0"; + /// an empty string (to return a reference for primitive values) + const string_type empty_str = ""; - @liveexample{The following code shows examples for the @ref - basic_json(size_type\, const basic_json&) - constructor.,basic_json__size_type_basic_json} + public: + explicit iteration_proxy_value(IteratorType it) noexcept : anchor(it) {} - @since version 1.0.0 - */ - basic_json(size_type cnt, const basic_json& val) - : m_type(value_t::array) + /// dereference operator (needed for range-based for) + iteration_proxy_value& operator*() { - m_value.array = create(cnt, val); - assert_invariant(); + return *this; } - /*! - @brief construct a JSON container given an iterator range - - Constructs the JSON value with the contents of the range `[first, last)`. - The semantics depends on the different types a JSON value can have: - - In case of primitive types (number, boolean, or string), @a first must - be `begin()` and @a last must be `end()`. In this case, the value is - copied. Otherwise, invalid_iterator.204 is thrown. - - In case of structured types (array, object), the constructor behaves as - similar versions for `std::vector`. - - In case of a null type, invalid_iterator.206 is thrown. - - @tparam InputIT an input iterator type (@ref iterator or @ref - const_iterator) - - @param[in] first begin of the range to copy from (included) - @param[in] last end of the range to copy from (excluded) - - @pre Iterators @a first and @a last must be initialized. **This - precondition is enforced with an assertion.** - - @pre Range `[first, last)` is valid. Usually, this precondition cannot be - checked efficiently. Only certain edge cases are detected; see the - description of the exceptions below. - - @throw invalid_iterator.201 if iterators @a first and @a last are not - compatible (i.e., do not belong to the same JSON value). In this case, - the range `[first, last)` is undefined. - @throw invalid_iterator.204 if iterators @a first and @a last belong to a - primitive type (number, boolean, or string), but @a first does not point - to the first element any more. In this case, the range `[first, last)` is - undefined. See example code below. - @throw invalid_iterator.206 if iterators @a first and @a last belong to a - null value. In this case, the range `[first, last)` is undefined. - - @complexity Linear in distance between @a first and @a last. + /// increment operator (needed for range-based for) + iteration_proxy_value& operator++() + { + ++anchor; + ++array_index; - @liveexample{The example below shows several ways to create JSON values by - specifying a subrange with iterators.,basic_json__InputIt_InputIt} + return *this; + } - @since version 1.0.0 - */ - template::value or - std::is_same::value, int>::type = 0> - basic_json(InputIT first, InputIT last) + /// equality operator (needed for InputIterator) + bool operator==(const iteration_proxy_value& o) const { - assert(first.m_object != nullptr); - assert(last.m_object != nullptr); + return anchor == o.anchor; + } - // make sure iterator fits the current value - if (first.m_object != last.m_object) - { - JSON_THROW(invalid_iterator::create(201, "iterators are not compatible")); - } + /// inequality operator (needed for range-based for) + bool operator!=(const iteration_proxy_value& o) const + { + return anchor != o.anchor; + } - // copy type from first iterator - m_type = first.m_object->m_type; + /// return key of the iterator + const string_type& key() const + { + JSON_ASSERT(anchor.m_object != nullptr); - // check if iterator range is complete for primitive values - switch (m_type) + switch (anchor.m_object->type()) { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::string: + // use integer array index as key + case value_t::array: { - if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) + if (array_index != array_index_last) { - JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + int_to_string( array_index_str, array_index ); + array_index_last = array_index; } - break; + return array_index_str; } + // use key from the object + case value_t::object: + return anchor.key(); + + // use an empty key for all primitive types default: - { - break; - } + return empty_str; } + } - switch (m_type) - { - case value_t::number_integer: - { - m_value.number_integer = first.m_object->m_value.number_integer; - break; - } + /// return value of the iterator + typename IteratorType::reference value() const + { + return anchor.value(); + } +}; - case value_t::number_unsigned: - { - m_value.number_unsigned = first.m_object->m_value.number_unsigned; - break; - } +/// proxy class for the items() function +template class iteration_proxy +{ + private: + /// the container to iterate + typename IteratorType::reference container; - case value_t::number_float: - { - m_value.number_float = first.m_object->m_value.number_float; - break; - } + public: + /// construct iteration proxy from a container + explicit iteration_proxy(typename IteratorType::reference cont) noexcept + : container(cont) {} - case value_t::boolean: - { - m_value.boolean = first.m_object->m_value.boolean; - break; - } + /// return iterator begin (needed for range-based for) + iteration_proxy_value begin() noexcept + { + return iteration_proxy_value(container.begin()); + } - case value_t::string: - { - m_value = *first.m_object->m_value.string; - break; - } + /// return iterator end (needed for range-based for) + iteration_proxy_value end() noexcept + { + return iteration_proxy_value(container.end()); + } +}; +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.key()) +{ + return i.key(); +} +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.value()) +{ + return i.value(); +} +} // namespace detail +} // namespace nlohmann - case value_t::object: - { - m_value.object = create(first.m_it.object_iterator, - last.m_it.object_iterator); - break; - } +// The Addition to the STD Namespace is required to add +// Structured Bindings Support to the iteration_proxy_value class +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +namespace std +{ +#if defined(__clang__) + // Fix: https://github.com/nlohmann/json/issues/1401 + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +template +class tuple_size<::nlohmann::detail::iteration_proxy_value> + : public std::integral_constant {}; - case value_t::array: - { - m_value.array = create(first.m_it.array_iterator, - last.m_it.array_iterator); - break; - } +template +class tuple_element> +{ + public: + using type = decltype( + get(std::declval < + ::nlohmann::detail::iteration_proxy_value> ())); +}; +#if defined(__clang__) + #pragma clang diagnostic pop +#endif +} // namespace std - default: - { - JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + - first.m_object->type_name())); - } - } +// #include - assert_invariant(); - } +// #include +// #include - /////////////////////////////////////// - // other constructors and destructor // - /////////////////////////////////////// - /*! - @brief copy constructor +namespace nlohmann +{ +namespace detail +{ +////////////////// +// constructors // +////////////////// - Creates a copy of a given JSON value. +template struct external_constructor; - @param[in] other the JSON value to copy +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept + { + j.m_type = value_t::boolean; + j.m_value = b; + j.assert_invariant(); + } +}; - @complexity Linear in the size of @a other. +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) + { + j.m_type = value_t::string; + j.m_value = s; + j.assert_invariant(); + } - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is linear. - - As postcondition, it holds: `other == basic_json(other)`. + template + static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) + { + j.m_type = value_t::string; + j.m_value = std::move(s); + j.assert_invariant(); + } - @liveexample{The following code shows an example for the copy - constructor.,basic_json__basic_json} + template < typename BasicJsonType, typename CompatibleStringType, + enable_if_t < !std::is_same::value, + int > = 0 > + static void construct(BasicJsonType& j, const CompatibleStringType& str) + { + j.m_type = value_t::string; + j.m_value.string = j.template create(str); + j.assert_invariant(); + } +}; - @since version 1.0.0 - */ - basic_json(const basic_json& other) - : m_type(other.m_type) +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) { - // check of passed value is valid - other.assert_invariant(); + j.m_type = value_t::binary; + typename BasicJsonType::binary_t value{b}; + j.m_value = value; + j.assert_invariant(); + } - switch (m_type) - { - case value_t::object: - { - m_value = *other.m_value.object; - break; - } + template + static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) + { + j.m_type = value_t::binary; + typename BasicJsonType::binary_t value{std::move(b)}; + j.m_value = value; + j.assert_invariant(); + } +}; - case value_t::array: - { - m_value = *other.m_value.array; - break; - } +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept + { + j.m_type = value_t::number_float; + j.m_value = val; + j.assert_invariant(); + } +}; - case value_t::string: - { - m_value = *other.m_value.string; - break; - } +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept + { + j.m_type = value_t::number_unsigned; + j.m_value = val; + j.assert_invariant(); + } +}; - case value_t::boolean: - { - m_value = other.m_value.boolean; - break; - } +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept + { + j.m_type = value_t::number_integer; + j.m_value = val; + j.assert_invariant(); + } +}; - case value_t::number_integer: - { - m_value = other.m_value.number_integer; - break; - } +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) + { + j.m_type = value_t::array; + j.m_value = arr; + j.assert_invariant(); + } - case value_t::number_unsigned: - { - m_value = other.m_value.number_unsigned; - break; - } + template + static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) + { + j.m_type = value_t::array; + j.m_value = std::move(arr); + j.assert_invariant(); + } - case value_t::number_float: - { - m_value = other.m_value.number_float; - break; - } + template < typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < !std::is_same::value, + int > = 0 > + static void construct(BasicJsonType& j, const CompatibleArrayType& arr) + { + using std::begin; + using std::end; + j.m_type = value_t::array; + j.m_value.array = j.template create(begin(arr), end(arr)); + j.assert_invariant(); + } - default: - { - break; - } + template + static void construct(BasicJsonType& j, const std::vector& arr) + { + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->reserve(arr.size()); + for (const bool x : arr) + { + j.m_value.array->push_back(x); } + j.assert_invariant(); + } - assert_invariant(); + template::value, int> = 0> + static void construct(BasicJsonType& j, const std::valarray& arr) + { + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->resize(arr.size()); + if (arr.size() > 0) + { + std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); + } + j.assert_invariant(); } +}; - /*! - @brief move constructor +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) + { + j.m_type = value_t::object; + j.m_value = obj; + j.assert_invariant(); + } - Move constructor. Constructs a JSON value with the contents of the given - value @a other using move semantics. It "steals" the resources from @a - other and leaves it as JSON null value. + template + static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) + { + j.m_type = value_t::object; + j.m_value = std::move(obj); + j.assert_invariant(); + } - @param[in,out] other value to move to this object + template < typename BasicJsonType, typename CompatibleObjectType, + enable_if_t < !std::is_same::value, int > = 0 > + static void construct(BasicJsonType& j, const CompatibleObjectType& obj) + { + using std::begin; + using std::end; - @post @a other is a JSON null value + j.m_type = value_t::object; + j.m_value.object = j.template create(begin(obj), end(obj)); + j.assert_invariant(); + } +}; - @complexity Constant. +///////////// +// to_json // +///////////// - @liveexample{The code below shows the move constructor explicitly called - via std::move.,basic_json__moveconstructor} +template::value, int> = 0> +void to_json(BasicJsonType& j, T b) noexcept +{ + external_constructor::construct(j, b); +} - @since version 1.0.0 - */ - basic_json(basic_json&& other) noexcept - : m_type(std::move(other.m_type)), - m_value(std::move(other.m_value)) - { - // check that passed value is valid - other.assert_invariant(); +template::value, int> = 0> +void to_json(BasicJsonType& j, const CompatibleString& s) +{ + external_constructor::construct(j, s); +} - // invalidate payload - other.m_type = value_t::null; - other.m_value = {}; +template +void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s) +{ + external_constructor::construct(j, std::move(s)); +} - assert_invariant(); - } +template::value, int> = 0> +void to_json(BasicJsonType& j, FloatType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} - /*! - @brief copy assignment +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} - Copy assignment operator. Copies a JSON value via the "copy and swap" - strategy: It is expressed in terms of the copy constructor, destructor, - and the swap() member function. +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} - @param[in] other value to copy from +template::value, int> = 0> +void to_json(BasicJsonType& j, EnumType e) noexcept +{ + using underlying_type = typename std::underlying_type::type; + external_constructor::construct(j, static_cast(e)); +} - @complexity Linear. +template +void to_json(BasicJsonType& j, const std::vector& e) +{ + external_constructor::construct(j, e); +} - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is linear. +template < typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < is_compatible_array_type::value&& + !is_compatible_object_type::value&& + !is_compatible_string_type::value&& + !std::is_same::value&& + !is_basic_json::value, + int > = 0 > +void to_json(BasicJsonType& j, const CompatibleArrayType& arr) +{ + external_constructor::construct(j, arr); +} - @liveexample{The code below shows and example for the copy assignment. It - creates a copy of value `a` which is then swapped with `b`. Finally\, the - copy of `a` (which is the null value after the swap) is - destroyed.,basic_json__copyassignment} +template +void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin) +{ + external_constructor::construct(j, bin); +} - @since version 1.0.0 - */ - reference& operator=(basic_json other) noexcept ( - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value and - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value - ) - { - // check that passed value is valid - other.assert_invariant(); +template::value, int> = 0> +void to_json(BasicJsonType& j, const std::valarray& arr) +{ + external_constructor::construct(j, std::move(arr)); +} - using std::swap; - swap(m_type, other.m_type); - swap(m_value, other.m_value); +template +void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) +{ + external_constructor::construct(j, std::move(arr)); +} - assert_invariant(); - return *this; - } +template < typename BasicJsonType, typename CompatibleObjectType, + enable_if_t < is_compatible_object_type::value&& !is_basic_json::value, int > = 0 > +void to_json(BasicJsonType& j, const CompatibleObjectType& obj) +{ + external_constructor::construct(j, obj); +} - /*! - @brief destructor +template +void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) +{ + external_constructor::construct(j, std::move(obj)); +} - Destroys the JSON value and frees all allocated memory. +template < + typename BasicJsonType, typename T, std::size_t N, + enable_if_t < !std::is_constructible::value, + int > = 0 > +void to_json(BasicJsonType& j, const T(&arr)[N]) +{ + external_constructor::construct(j, arr); +} - @complexity Linear. +template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible::value&& std::is_constructible::value, int > = 0 > +void to_json(BasicJsonType& j, const std::pair& p) +{ + j = { p.first, p.second }; +} - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is linear. - - All stored elements are destroyed and all memory is freed. +// for https://github.com/nlohmann/json/pull/1134 +template>::value, int> = 0> +void to_json(BasicJsonType& j, const T& b) +{ + j = { {b.key(), b.value()} }; +} - @since version 1.0.0 - */ - ~basic_json() - { - assert_invariant(); - - switch (m_type) - { - case value_t::object: - { - AllocatorType alloc; - alloc.destroy(m_value.object); - alloc.deallocate(m_value.object, 1); - break; - } - - case value_t::array: - { - AllocatorType alloc; - alloc.destroy(m_value.array); - alloc.deallocate(m_value.array, 1); - break; - } +template +void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence /*unused*/) +{ + j = { std::get(t)... }; +} - case value_t::string: - { - AllocatorType alloc; - alloc.destroy(m_value.string); - alloc.deallocate(m_value.string, 1); - break; - } +template::value, int > = 0> +void to_json(BasicJsonType& j, const T& t) +{ + to_json_tuple_impl(j, t, make_index_sequence::value> {}); +} - default: - { - // all other types need no specific destructor - break; - } - } +struct to_json_fn +{ + template + auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward(val)))) + -> decltype(to_json(j, std::forward(val)), void()) + { + return to_json(j, std::forward(val)); } +}; +} // namespace detail - /// @} +/// namespace to hold default `to_json` function +namespace +{ +constexpr const auto& to_json = detail::static_const::value; +} // namespace +} // namespace nlohmann - public: - /////////////////////// - // object inspection // - /////////////////////// - /// @name object inspection - /// Functions to inspect the type of a JSON value. - /// @{ +namespace nlohmann +{ +template +struct adl_serializer +{ /*! - @brief serialization - - Serialization function for JSON values. The function tries to mimic - Python's `json.dumps()` function, and currently supports its @a indent - parameter. - - @param[in] indent If indent is nonnegative, then array elements and object - members will be pretty-printed with that indent level. An indent level of - `0` will only insert newlines. `-1` (the default) selects the most compact - representation. - @param[in] indent_char The character to use for indentation of @a indent is - greate than `0`. The default is ` ` (space). - - @return string containing the serialization of the JSON value - - @complexity Linear. - - @liveexample{The following example shows the effect of different @a indent - parameters to the result of the serialization.,dump} + @brief convert a JSON value to any value type - @see https://docs.python.org/2/library/json.html#json.dump + This function is usually called by the `get()` function of the + @ref basic_json class (either explicit or via conversion operators). - @since version 1.0.0; indentaction character added in version 3.0.0 + @param[in] j JSON value to read from + @param[in,out] val value to write to */ - string_t dump(const int indent = -1, const char indent_char = ' ') const + template + static auto from_json(BasicJsonType&& j, ValueType& val) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), val))) + -> decltype(::nlohmann::from_json(std::forward(j), val), void()) { - string_t result; - serializer s(output_adapter::create(result), indent_char); - - if (indent >= 0) - { - s.dump(*this, true, static_cast(indent)); - } - else - { - s.dump(*this, false, 0); - } - - return result; + ::nlohmann::from_json(std::forward(j), val); } /*! - @brief return the type of the JSON value (explicit) - - Return the type of the JSON value as a value from the @ref value_t - enumeration. - - @return the type of the JSON value - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + @brief convert any value type to a JSON value - @liveexample{The following code exemplifies `type()` for all JSON - types.,type} + This function is usually called by the constructors of the @ref basic_json + class. - @since version 1.0.0 + @param[in,out] j JSON value to write to + @param[in] val value to read from */ - constexpr value_t type() const noexcept + template + static auto to_json(BasicJsonType& j, ValueType&& val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) + -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) { - return m_type; + ::nlohmann::to_json(j, std::forward(val)); } +}; - /*! - @brief return whether type is primitive +} // namespace nlohmann - This function returns true iff the JSON type is primitive (string, number, - boolean, or null). +// #include - @return `true` if type is primitive (string, number, boolean, or null), - `false` otherwise. - @complexity Constant. +#include // uint8_t +#include // tie +#include // move - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. +namespace nlohmann +{ - @liveexample{The following code exemplifies `is_primitive()` for all JSON - types.,is_primitive} +/*! +@brief an internal type for a backed binary type - @sa @ref is_structured() -- returns whether JSON value is structured - @sa @ref is_null() -- returns whether JSON value is `null` - @sa @ref is_string() -- returns whether JSON value is a string - @sa @ref is_boolean() -- returns whether JSON value is a boolean - @sa @ref is_number() -- returns whether JSON value is a number +This type extends the template parameter @a BinaryType provided to `basic_json` +with a subtype used by BSON and MessagePack. This type exists so that the user +does not have to specify a type themselves with a specific naming scheme in +order to override the binary type. - @since version 1.0.0 - */ - constexpr bool is_primitive() const noexcept - { - return is_null() or is_string() or is_boolean() or is_number(); - } +@tparam BinaryType container to store bytes (`std::vector` by + default) - /*! - @brief return whether type is structured +@since version 3.8.0 +*/ +template +class byte_container_with_subtype : public BinaryType +{ + public: + /// the type of the underlying container + using container_type = BinaryType; - This function returns true iff the JSON type is structured (array or - object). + byte_container_with_subtype() noexcept(noexcept(container_type())) + : container_type() + {} - @return `true` if type is structured (array or object), `false` otherwise. + byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b))) + : container_type(b) + {} - @complexity Constant. + byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + {} - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + byte_container_with_subtype(const container_type& b, std::uint8_t subtype) noexcept(noexcept(container_type(b))) + : container_type(b) + , m_subtype(subtype) + , m_has_subtype(true) + {} - @liveexample{The following code exemplifies `is_structured()` for all JSON - types.,is_structured} + byte_container_with_subtype(container_type&& b, std::uint8_t subtype) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + , m_subtype(subtype) + , m_has_subtype(true) + {} - @sa @ref is_primitive() -- returns whether value is primitive - @sa @ref is_array() -- returns whether value is an array - @sa @ref is_object() -- returns whether value is an object + bool operator==(const byte_container_with_subtype& rhs) const + { + return std::tie(static_cast(*this), m_subtype, m_has_subtype) == + std::tie(static_cast(rhs), rhs.m_subtype, rhs.m_has_subtype); + } - @since version 1.0.0 - */ - constexpr bool is_structured() const noexcept + bool operator!=(const byte_container_with_subtype& rhs) const { - return is_array() or is_object(); + return !(rhs == *this); } /*! - @brief return whether value is null + @brief sets the binary subtype - This function returns true iff the JSON value is null. - - @return `true` if type is null, `false` otherwise. + Sets the binary subtype of the value, also flags a binary JSON value as + having a subtype, which has implications for serialization. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. - @liveexample{The following code exemplifies `is_null()` for all JSON - types.,is_null} + @sa @ref subtype() -- return the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype - @since version 1.0.0 + @since version 3.8.0 */ - constexpr bool is_null() const noexcept + void set_subtype(std::uint8_t subtype) noexcept { - return m_type == value_t::null; + m_subtype = subtype; + m_has_subtype = true; } /*! - @brief return whether value is a boolean + @brief return the binary subtype - This function returns true iff the JSON value is a boolean. + Returns the numerical subtype of the value if it has a subtype. If it does + not have a subtype, this function will return size_t(-1) as a sentinel + value. - @return `true` if type is boolean, `false` otherwise. + @return the numerical subtype of the binary value @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. - @liveexample{The following code exemplifies `is_boolean()` for all JSON - types.,is_boolean} + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype - @since version 1.0.0 + @since version 3.8.0 */ - constexpr bool is_boolean() const noexcept + constexpr std::uint8_t subtype() const noexcept { - return m_type == value_t::boolean; + return m_subtype; } /*! - @brief return whether value is a number + @brief return whether the value has a subtype - This function returns true iff the JSON value is a number. This includes - both integer and floating-point values. - - @return `true` if type is number (regardless whether integer, unsigned - integer or floating-type), `false` otherwise. + @return whether the value has a subtype @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. - @liveexample{The following code exemplifies `is_number()` for all JSON - types.,is_number} - - @sa @ref is_number_integer() -- check if value is an integer or unsigned - integer number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer - number - @sa @ref is_number_float() -- check if value is a floating-point number + @sa @ref subtype() -- return the binary subtype + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype - @since version 1.0.0 + @since version 3.8.0 */ - constexpr bool is_number() const noexcept + constexpr bool has_subtype() const noexcept { - return is_number_integer() or is_number_float(); + return m_has_subtype; } /*! - @brief return whether value is an integer number + @brief clears the binary subtype - This function returns true iff the JSON value is an integer or unsigned - integer number. This excludes floating-point values. - - @return `true` if type is an integer or unsigned integer number, `false` - otherwise. + Clears the binary subtype and flags the value as not having a subtype, which + has implications for serialization; for instance MessagePack will prefer the + bin family over the ext family. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. - @liveexample{The following code exemplifies `is_number_integer()` for all - JSON types.,is_number_integer} - - @sa @ref is_number() -- check if value is a number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer - number - @sa @ref is_number_float() -- check if value is a floating-point number + @sa @ref subtype() -- return the binary subtype + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype - @since version 1.0.0 + @since version 3.8.0 */ - constexpr bool is_number_integer() const noexcept + void clear_subtype() noexcept { - return m_type == value_t::number_integer or m_type == value_t::number_unsigned; + m_subtype = 0; + m_has_subtype = false; } - /*! - @brief return whether value is an unsigned integer number - - This function returns true iff the JSON value is an unsigned integer - number. This excludes floating-point and (signed) integer values. - - @return `true` if type is an unsigned integer number, `false` otherwise. + private: + std::uint8_t m_subtype = 0; + bool m_has_subtype = false; +}; - @complexity Constant. +} // namespace nlohmann - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. +// #include - @liveexample{The following code exemplifies `is_number_unsigned()` for all - JSON types.,is_number_unsigned} +// #include - @sa @ref is_number() -- check if value is a number - @sa @ref is_number_integer() -- check if value is an integer or unsigned - integer number - @sa @ref is_number_float() -- check if value is a floating-point number +// #include - @since version 2.0.0 - */ - constexpr bool is_number_unsigned() const noexcept - { - return m_type == value_t::number_unsigned; - } +// #include - /*! - @brief return whether value is a floating-point number - This function returns true iff the JSON value is a floating-point number. - This excludes integer and unsigned integer values. +#include // size_t, uint8_t +#include // hash - @return `true` if type is a floating-point number, `false` otherwise. +namespace nlohmann +{ +namespace detail +{ - @complexity Constant. +// boost::hash_combine +inline std::size_t combine(std::size_t seed, std::size_t h) noexcept +{ + seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U); + return seed; +} - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. +/*! +@brief hash a JSON value - @liveexample{The following code exemplifies `is_number_float()` for all - JSON types.,is_number_float} +The hash function tries to rely on std::hash where possible. Furthermore, the +type of the JSON value is taken into account to have different hash values for +null, 0, 0U, and false, etc. - @sa @ref is_number() -- check if value is number - @sa @ref is_number_integer() -- check if value is an integer number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer - number +@tparam BasicJsonType basic_json specialization +@param j JSON value to hash +@return hash value of j +*/ +template +std::size_t hash(const BasicJsonType& j) +{ + using string_t = typename BasicJsonType::string_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; - @since version 1.0.0 - */ - constexpr bool is_number_float() const noexcept + const auto type = static_cast(j.type()); + switch (j.type()) { - return m_type == value_t::number_float; - } + case BasicJsonType::value_t::null: + case BasicJsonType::value_t::discarded: + { + return combine(type, 0); + } - /*! - @brief return whether value is an object + case BasicJsonType::value_t::object: + { + auto seed = combine(type, j.size()); + for (const auto& element : j.items()) + { + const auto h = std::hash {}(element.key()); + seed = combine(seed, h); + seed = combine(seed, hash(element.value())); + } + return seed; + } - This function returns true iff the JSON value is an object. + case BasicJsonType::value_t::array: + { + auto seed = combine(type, j.size()); + for (const auto& element : j) + { + seed = combine(seed, hash(element)); + } + return seed; + } - @return `true` if type is object, `false` otherwise. + case BasicJsonType::value_t::string: + { + const auto h = std::hash {}(j.template get_ref()); + return combine(type, h); + } - @complexity Constant. + case BasicJsonType::value_t::boolean: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + case BasicJsonType::value_t::number_integer: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } - @liveexample{The following code exemplifies `is_object()` for all JSON - types.,is_object} + case nlohmann::detail::value_t::number_unsigned: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } - @since version 1.0.0 - */ - constexpr bool is_object() const noexcept - { - return m_type == value_t::object; + case nlohmann::detail::value_t::number_float: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case nlohmann::detail::value_t::binary: + { + auto seed = combine(type, j.get_binary().size()); + const auto h = std::hash {}(j.get_binary().has_subtype()); + seed = combine(seed, h); + seed = combine(seed, j.get_binary().subtype()); + for (const auto byte : j.get_binary()) + { + seed = combine(seed, std::hash {}(byte)); + } + return seed; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } +} - /*! - @brief return whether value is an array +} // namespace detail +} // namespace nlohmann - This function returns true iff the JSON value is an array. +// #include - @return `true` if type is array, `false` otherwise. - @complexity Constant. +#include // generate_n +#include // array +#include // ldexp +#include // size_t +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // snprintf +#include // memcpy +#include // back_inserter +#include // numeric_limits +#include // char_traits, string +#include // make_pair, move - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. +// #include - @liveexample{The following code exemplifies `is_array()` for all JSON - types.,is_array} +// #include - @since version 1.0.0 - */ - constexpr bool is_array() const noexcept - { - return m_type == value_t::array; - } - /*! - @brief return whether value is a string +#include // array +#include // size_t +#include //FILE * +#include // strlen +#include // istream +#include // begin, end, iterator_traits, random_access_iterator_tag, distance, next +#include // shared_ptr, make_shared, addressof +#include // accumulate +#include // string, char_traits +#include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer +#include // pair, declval - This function returns true iff the JSON value is a string. +// #include - @return `true` if type is string, `false` otherwise. +// #include - @complexity Constant. - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. +namespace nlohmann +{ +namespace detail +{ +/// the supported input formats +enum class input_format_t { json, cbor, msgpack, ubjson, bson }; - @liveexample{The following code exemplifies `is_string()` for all JSON - types.,is_string} +//////////////////// +// input adapters // +//////////////////// - @since version 1.0.0 - */ - constexpr bool is_string() const noexcept +/*! +Input adapter for stdio file access. This adapter read only 1 byte and do not use any + buffer. This adapter is a very low level adapter. +*/ +class file_input_adapter +{ + public: + using char_type = char; + + JSON_HEDLEY_NON_NULL(2) + explicit file_input_adapter(std::FILE* f) noexcept + : m_file(f) + {} + + // make class move-only + file_input_adapter(const file_input_adapter&) = delete; + file_input_adapter(file_input_adapter&&) = default; + file_input_adapter& operator=(const file_input_adapter&) = delete; + file_input_adapter& operator=(file_input_adapter&&) = delete; + + std::char_traits::int_type get_character() noexcept { - return m_type == value_t::string; + return std::fgetc(m_file); } - /*! - @brief return whether value is discarded + private: + /// the file pointer to read from + std::FILE* m_file; +}; - This function returns true iff the JSON value was discarded during parsing - with a callback function (see @ref parser_callback_t). - @note This function will always be `false` for JSON values after parsing. - That is, discarded values can only occur during parsing, but will be - removed when inside a structured value or replaced by null in other cases. +/*! +Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at +beginning of input. Does not support changing the underlying std::streambuf +in mid-input. Maintains underlying std::istream and std::streambuf to support +subsequent use of standard std::istream operations to process any input +characters following those used in parsing the JSON input. Clears the +std::istream flags; any input errors (e.g., EOF) will be detected by the first +subsequent call for input from the std::istream. +*/ +class input_stream_adapter +{ + public: + using char_type = char; - @return `true` if type is discarded, `false` otherwise. + ~input_stream_adapter() + { + // clear stream flags; we use underlying streambuf I/O, do not + // maintain ifstream flags, except eof + if (is != nullptr) + { + is->clear(is->rdstate() & std::ios::eofbit); + } + } - @complexity Constant. + explicit input_stream_adapter(std::istream& i) + : is(&i), sb(i.rdbuf()) + {} - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + // delete because of pointer members + input_stream_adapter(const input_stream_adapter&) = delete; + input_stream_adapter& operator=(input_stream_adapter&) = delete; + input_stream_adapter& operator=(input_stream_adapter&& rhs) = delete; - @liveexample{The following code exemplifies `is_discarded()` for all JSON - types.,is_discarded} + input_stream_adapter(input_stream_adapter&& rhs) noexcept : is(rhs.is), sb(rhs.sb) + { + rhs.is = nullptr; + rhs.sb = nullptr; + } - @since version 1.0.0 - */ - constexpr bool is_discarded() const noexcept + // std::istream/std::streambuf use std::char_traits::to_int_type, to + // ensure that std::char_traits::eof() and the character 0xFF do not + // end up as the same value, eg. 0xFFFFFFFF. + std::char_traits::int_type get_character() { - return m_type == value_t::discarded; + auto res = sb->sbumpc(); + // set eof manually, as we don't use the istream interface. + if (JSON_HEDLEY_UNLIKELY(res == EOF)) + { + is->clear(is->rdstate() | std::ios::eofbit); + } + return res; } - /*! - @brief return the type of the JSON value (implicit) + private: + /// the associated input stream + std::istream* is = nullptr; + std::streambuf* sb = nullptr; +}; - Implicitly return the type of the JSON value as a value from the @ref - value_t enumeration. +// General-purpose iterator-based adapter. It might not be as fast as +// theoretically possible for some containers, but it is extremely versatile. +template +class iterator_input_adapter +{ + public: + using char_type = typename std::iterator_traits::value_type; - @return the type of the JSON value + iterator_input_adapter(IteratorType first, IteratorType last) + : current(std::move(first)), end(std::move(last)) {} - @complexity Constant. + typename std::char_traits::int_type get_character() + { + if (JSON_HEDLEY_LIKELY(current != end)) + { + auto result = std::char_traits::to_int_type(*current); + std::advance(current, 1); + return result; + } + else + { + return std::char_traits::eof(); + } + } - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + private: + IteratorType current; + IteratorType end; - @liveexample{The following code exemplifies the @ref value_t operator for - all JSON types.,operator__value_t} + template + friend struct wide_string_input_helper; - @since version 1.0.0 - */ - constexpr operator value_t() const noexcept + bool empty() const { - return m_type; + return current == end; } - /// @} +}; - private: - ////////////////// - // value access // - ////////////////// - /// get a boolean (explicit) - boolean_t get_impl(boolean_t* /*unused*/) const +template +struct wide_string_input_helper; + +template +struct wide_string_input_helper +{ + // UTF-32 + static void fill_buffer(BaseInputAdapter& input, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) { - if (is_boolean()) + utf8_bytes_index = 0; + + if (JSON_HEDLEY_UNLIKELY(input.empty())) { - return m_value.boolean; + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; } + else + { + // get the current character + const auto wc = input.get_character(); - JSON_THROW(type_error::create(302, "type must be boolean, but is " + type_name())); + // UTF-32 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u) & 0x1Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (wc <= 0xFFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u) & 0x0Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 3; + } + else if (wc <= 0x10FFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xF0u | ((static_cast(wc) >> 18u) & 0x07u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + // unknown character + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } } +}; - /// get a pointer to the value (object) - object_t* get_impl_ptr(object_t* /*unused*/) noexcept +template +struct wide_string_input_helper +{ + // UTF-16 + static void fill_buffer(BaseInputAdapter& input, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) { - return is_object() ? m_value.object : nullptr; - } + utf8_bytes_index = 0; - /// get a pointer to the value (object) - constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept - { - return is_object() ? m_value.object : nullptr; - } + if (JSON_HEDLEY_UNLIKELY(input.empty())) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = input.get_character(); - /// get a pointer to the value (array) - array_t* get_impl_ptr(array_t* /*unused*/) noexcept - { - return is_array() ? m_value.array : nullptr; + // UTF-16 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (0xD800 > wc || wc >= 0xE000) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 3; + } + else + { + if (JSON_HEDLEY_UNLIKELY(!input.empty())) + { + const auto wc2 = static_cast(input.get_character()); + const auto charcode = 0x10000u + (((static_cast(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); + utf8_bytes[0] = static_cast::int_type>(0xF0u | (charcode >> 18u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (charcode & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } } +}; - /// get a pointer to the value (array) - constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept - { - return is_array() ? m_value.array : nullptr; - } +// Wraps another input apdater to convert wide character types into individual bytes. +template +class wide_string_input_adapter +{ + public: + using char_type = char; - /// get a pointer to the value (string) - string_t* get_impl_ptr(string_t* /*unused*/) noexcept - { - return is_string() ? m_value.string : nullptr; - } + wide_string_input_adapter(BaseInputAdapter base) + : base_adapter(base) {} - /// get a pointer to the value (string) - constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept + typename std::char_traits::int_type get_character() noexcept { - return is_string() ? m_value.string : nullptr; - } + // check if buffer needs to be filled + if (utf8_bytes_index == utf8_bytes_filled) + { + fill_buffer(); - /// get a pointer to the value (boolean) - boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept - { - return is_boolean() ? &m_value.boolean : nullptr; - } + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index == 0); + } - /// get a pointer to the value (boolean) - constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept - { - return is_boolean() ? &m_value.boolean : nullptr; + // use buffer + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled); + return utf8_bytes[utf8_bytes_index++]; } - /// get a pointer to the value (integer number) - number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept - { - return is_number_integer() ? &m_value.number_integer : nullptr; - } + private: + BaseInputAdapter base_adapter; - /// get a pointer to the value (integer number) - constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept + template + void fill_buffer() { - return is_number_integer() ? &m_value.number_integer : nullptr; + wide_string_input_helper::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); } - /// get a pointer to the value (unsigned number) - number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept - { - return is_number_unsigned() ? &m_value.number_unsigned : nullptr; - } + /// a buffer for UTF-8 bytes + std::array::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; - /// get a pointer to the value (unsigned number) - constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept + /// index to the utf8_codes array for the next valid byte + std::size_t utf8_bytes_index = 0; + /// number of valid bytes in the utf8_codes array + std::size_t utf8_bytes_filled = 0; +}; + + +template +struct iterator_input_adapter_factory +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using adapter_type = iterator_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) { - return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + return adapter_type(std::move(first), std::move(last)); } +}; - /// get a pointer to the value (floating-point number) - number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept +template +struct is_iterator_of_multibyte +{ + using value_type = typename std::iterator_traits::value_type; + enum { - return is_number_float() ? &m_value.number_float : nullptr; - } + value = sizeof(value_type) > 1 + }; +}; - /// get a pointer to the value (floating-point number) - constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept +template +struct iterator_input_adapter_factory::value>> +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using base_adapter_type = iterator_input_adapter; + using adapter_type = wide_string_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) { - return is_number_float() ? &m_value.number_float : nullptr; + return adapter_type(base_adapter_type(std::move(first), std::move(last))); } +}; - /*! - @brief helper function to implement get_ref() +// General purpose iterator-based input +template +typename iterator_input_adapter_factory::adapter_type input_adapter(IteratorType first, IteratorType last) +{ + using factory_type = iterator_input_adapter_factory; + return factory_type::create(first, last); +} - This funcion helps to implement get_ref() without code duplication for - const and non-const overloads +// Convenience shorthand from container to iterator +template +auto input_adapter(const ContainerType& container) -> decltype(input_adapter(begin(container), end(container))) +{ + // Enable ADL + using std::begin; + using std::end; - @tparam ThisType will be deduced as `basic_json` or `const basic_json` + return input_adapter(begin(container), end(container)); +} - @throw type_error.303 if ReferenceType does not match underlying value - type of the current JSON - */ - template - static ReferenceType get_ref_impl(ThisType& obj) - { - // helper type - using PointerType = typename std::add_pointer::type; +// Special cases with fast paths +inline file_input_adapter input_adapter(std::FILE* file) +{ + return file_input_adapter(file); +} - // delegate the call to get_ptr<>() - auto ptr = obj.template get_ptr(); +inline input_stream_adapter input_adapter(std::istream& stream) +{ + return input_stream_adapter(stream); +} - if (ptr != nullptr) - { - return *ptr; - } +inline input_stream_adapter input_adapter(std::istream&& stream) +{ + return input_stream_adapter(stream); +} - JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + obj.type_name())); - } +using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval(), std::declval())); + +// Null-delimited strings, and the like. +template < typename CharT, + typename std::enable_if < + std::is_pointer::value&& + !std::is_array::value&& + std::is_integral::type>::value&& + sizeof(typename std::remove_pointer::type) == 1, + int >::type = 0 > +contiguous_bytes_input_adapter input_adapter(CharT b) +{ + auto length = std::strlen(reinterpret_cast(b)); + const auto* ptr = reinterpret_cast(b); + return input_adapter(ptr, ptr + length); +} +template +auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) +{ + return input_adapter(array, array + N); +} + +// This class only handles inputs of input_buffer_adapter type. +// It's required so that expressions like {ptr, len} can be implicitely casted +// to the correct adapter. +class span_input_adapter +{ public: - /// @name value access - /// Direct access to the stored value of a JSON value. - /// @{ + template < typename CharT, + typename std::enable_if < + std::is_pointer::value&& + std::is_integral::type>::value&& + sizeof(typename std::remove_pointer::type) == 1, + int >::type = 0 > + span_input_adapter(CharT b, std::size_t l) + : ia(reinterpret_cast(b), reinterpret_cast(b) + l) {} + + template::iterator_category, std::random_access_iterator_tag>::value, + int>::type = 0> + span_input_adapter(IteratorType first, IteratorType last) + : ia(input_adapter(first, last)) {} + + contiguous_bytes_input_adapter&& get() + { + return std::move(ia); + } - /*! - @brief get special-case overload + private: + contiguous_bytes_input_adapter ia; +}; +} // namespace detail +} // namespace nlohmann - This overloads avoids a lot of template boilerplate, it can be seen as the - identity method +// #include - @tparam BasicJsonType == @ref basic_json - @return a copy of *this +#include +#include // string +#include // move +#include // vector - @complexity Constant. +// #include - @since version 2.1.0 +// #include + + +namespace nlohmann +{ + +/*! +@brief SAX interface + +This class describes the SAX interface used by @ref nlohmann::json::sax_parse. +Each function is called in different situations while the input is parsed. The +boolean return value informs the parser whether to continue processing the +input. +*/ +template +struct json_sax +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + /*! + @brief a null value was read + @return whether parsing should proceed */ - template < - typename BasicJsonType, - detail::enable_if_t::type, - basic_json_t>::value, - int> = 0 > - basic_json get() const - { - return *this; - } + virtual bool null() = 0; /*! - @brief get a value (explicit) + @brief a boolean value was read + @param[in] val boolean value + @return whether parsing should proceed + */ + virtual bool boolean(bool val) = 0; - Explicit type conversion between the JSON value and a compatible value - which is [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible) - and [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). - The value is converted by calling the @ref json_serializer - `from_json()` method. + /*! + @brief an integer number was read + @param[in] val integer value + @return whether parsing should proceed + */ + virtual bool number_integer(number_integer_t val) = 0; - The function is equivalent to executing - @code {.cpp} - ValueType ret; - JSONSerializer::from_json(*this, ret); - return ret; - @endcode + /*! + @brief an unsigned integer number was read + @param[in] val unsigned integer value + @return whether parsing should proceed + */ + virtual bool number_unsigned(number_unsigned_t val) = 0; - This overloads is chosen if: - - @a ValueType is not @ref basic_json, - - @ref json_serializer has a `from_json()` method of the form - `void from_json(const basic_json&, ValueType&)`, and - - @ref json_serializer does not have a `from_json()` method of - the form `ValueType from_json(const basic_json&)` + /*! + @brief an floating-point number was read + @param[in] val floating-point value + @param[in] s raw token value + @return whether parsing should proceed + */ + virtual bool number_float(number_float_t val, const string_t& s) = 0; - @tparam ValueTypeCV the provided value type - @tparam ValueType the returned value type + /*! + @brief a string was read + @param[in] val string value + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool string(string_t& val) = 0; - @return copy of the JSON value, converted to @a ValueType + /*! + @brief a binary string was read + @param[in] val binary value + @return whether parsing should proceed + @note It is safe to move the passed binary. + */ + virtual bool binary(binary_t& val) = 0; - @throw what @ref json_serializer `from_json()` method throws + /*! + @brief the beginning of an object was read + @param[in] elements number of object elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_object(std::size_t elements) = 0; - @liveexample{The example below shows several conversions from JSON values - to other types. There a few things to note: (1) Floating-point numbers can - be converted to integers\, (2) A JSON array can be converted to a standard - `std::vector`\, (3) A JSON object can be converted to C++ - associative containers such as `std::unordered_map`.,get__ValueType_const} + /*! + @brief an object key was read + @param[in] val object key + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool key(string_t& val) = 0; - @since version 2.1.0 + /*! + @brief the end of an object was read + @return whether parsing should proceed */ - template < - typename ValueTypeCV, - typename ValueType = detail::uncvref_t, - detail::enable_if_t < - not std::is_same::value and - detail::has_from_json::value and - not detail::has_non_default_from_json::value, - int > = 0 > - ValueType get() const noexcept(noexcept( - JSONSerializer::from_json(std::declval(), std::declval()))) - { - // we cannot static_assert on ValueTypeCV being non-const, because - // there is support for get(), which is why we - // still need the uncvref - static_assert(not std::is_reference::value, - "get() cannot be used with reference types, you might want to use get_ref()"); - static_assert(std::is_default_constructible::value, - "types must be DefaultConstructible when used with get()"); + virtual bool end_object() = 0; - ValueType ret; - JSONSerializer::from_json(*this, ret); - return ret; - } + /*! + @brief the beginning of an array was read + @param[in] elements number of array elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_array(std::size_t elements) = 0; /*! - @brief get a value (explicit); special case + @brief the end of an array was read + @return whether parsing should proceed + */ + virtual bool end_array() = 0; - Explicit type conversion between the JSON value and a compatible value - which is **not** [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible) - and **not** [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). - The value is converted by calling the @ref json_serializer - `from_json()` method. + /*! + @brief a parse error occurred + @param[in] position the position in the input where the error occurs + @param[in] last_token the last read token + @param[in] ex an exception object describing the error + @return whether parsing should proceed (must return false) + */ + virtual bool parse_error(std::size_t position, + const std::string& last_token, + const detail::exception& ex) = 0; - The function is equivalent to executing - @code {.cpp} - return JSONSerializer::from_json(*this); - @endcode + virtual ~json_sax() = default; +}; - This overloads is chosen if: - - @a ValueType is not @ref basic_json and - - @ref json_serializer has a `from_json()` method of the form - `ValueType from_json(const basic_json&)` - @note If @ref json_serializer has both overloads of - `from_json()`, this one is chosen. +namespace detail +{ +/*! +@brief SAX implementation to create a JSON value from SAX events - @tparam ValueTypeCV the provided value type - @tparam ValueType the returned value type +This class implements the @ref json_sax interface and processes the SAX events +to create a JSON value which makes it basically a DOM parser. The structure or +hierarchy of the JSON value is managed by the stack `ref_stack` which contains +a pointer to the respective array or object for each recursion depth. - @return copy of the JSON value, converted to @a ValueType +After successful parsing, the value that is passed by reference to the +constructor contains the parsed value. - @throw what @ref json_serializer `from_json()` method throws +@tparam BasicJsonType the JSON type +*/ +template +class json_sax_dom_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; - @since version 2.1.0 + /*! + @param[in, out] r reference to a JSON value that is manipulated while + parsing + @param[in] allow_exceptions_ whether parse errors yield exceptions */ - template < - typename ValueTypeCV, - typename ValueType = detail::uncvref_t, - detail::enable_if_t::value and - detail::has_non_default_from_json::value, int> = 0 > - ValueType get() const noexcept(noexcept( - JSONSerializer::from_json(std::declval()))) + explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true) + : root(r), allow_exceptions(allow_exceptions_) + {} + + // make class move-only + json_sax_dom_parser(const json_sax_dom_parser&) = delete; + json_sax_dom_parser(json_sax_dom_parser&&) = default; + json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete; + json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; + ~json_sax_dom_parser() = default; + + bool null() { - static_assert(not std::is_reference::value, - "get() cannot be used with reference types, you might want to use get_ref()"); - return JSONSerializer::from_json(*this); + handle_value(nullptr); + return true; } - /*! - @brief get a pointer value (explicit) + bool boolean(bool val) + { + handle_value(val); + return true; + } - Explicit pointer access to the internally stored JSON value. No copies are - made. + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } - @warning The pointer becomes invalid if the underlying JSON object - changes. + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } - @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, - @ref number_unsigned_t, or @ref number_float_t. + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } - @return pointer to the internally stored JSON value if the requested - pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + bool string(string_t& val) + { + handle_value(val); + return true; + } - @complexity Constant. + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } - @liveexample{The example below shows how pointers to internal values of a - JSON value can be requested. Note that no type conversions are made and a - `nullptr` is returned if the value and the requested pointer type does not - match.,get__PointerType} + bool start_object(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); - @sa @ref get_ptr() for explicit pointer-member access + if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, + "excessive object size: " + std::to_string(len))); + } - @since version 1.0.0 - */ - template::value, int>::type = 0> - PointerType get() noexcept - { - // delegate the call to get_ptr - return get_ptr(); + return true; } - /*! - @brief get a pointer value (explicit) - @copydoc get() - */ - template::value, int>::type = 0> - constexpr const PointerType get() const noexcept + bool key(string_t& val) { - // delegate the call to get_ptr - return get_ptr(); + // add null at given key and store the reference for later + object_element = &(ref_stack.back()->m_value.object->operator[](val)); + return true; } - /*! - @brief get a pointer value (implicit) - - Implicit pointer access to the internally stored JSON value. No copies are - made. - - @warning Writing data to the pointee of the result yields an undefined - state. + bool end_object() + { + ref_stack.pop_back(); + return true; + } - @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, - @ref number_unsigned_t, or @ref number_float_t. Enforced by a static - assertion. + bool start_array(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); - @return pointer to the internally stored JSON value if the requested - pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, + "excessive array size: " + std::to_string(len))); + } - @complexity Constant. + return true; + } - @liveexample{The example below shows how pointers to internal values of a - JSON value can be requested. Note that no type conversions are made and a - `nullptr` is returned if the value and the requested pointer type does not - match.,get_ptr} + bool end_array() + { + ref_stack.pop_back(); + return true; + } - @since version 1.0.0 - */ - template::value, int>::type = 0> - PointerType get_ptr() noexcept - { - // get the type of the PointerType (remove pointer and const) - using pointee_t = typename std::remove_const::type>::type>::type; - // make sure the type matches the allowed types - static_assert( - std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - , "incompatible pointer type"); + template + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) + { + errored = true; + static_cast(ex); + if (allow_exceptions) + { + JSON_THROW(ex); + } + return false; + } - // delegate the call to get_impl_ptr<>() - return get_impl_ptr(static_cast(nullptr)); + constexpr bool is_errored() const + { + return errored; } + private: /*! - @brief get a pointer value (implicit) - @copydoc get_ptr() + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements */ - template::value and - std::is_const::type>::value, int>::type = 0> - constexpr const PointerType get_ptr() const noexcept - { - // get the type of the PointerType (remove pointer and const) - using pointee_t = typename std::remove_const::type>::type>::type; - // make sure the type matches the allowed types - static_assert( - std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - , "incompatible pointer type"); + template + JSON_HEDLEY_RETURNS_NON_NULL + BasicJsonType* handle_value(Value&& v) + { + if (ref_stack.empty()) + { + root = BasicJsonType(std::forward(v)); + return &root; + } - // delegate the call to get_impl_ptr<>() const - return get_impl_ptr(static_cast(nullptr)); + JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); + + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->emplace_back(std::forward(v)); + return &(ref_stack.back()->m_value.array->back()); + } + + JSON_ASSERT(ref_stack.back()->is_object()); + JSON_ASSERT(object_element); + *object_element = BasicJsonType(std::forward(v)); + return object_element; } - /*! - @brief get a reference value (implicit) - - Implicit reference access to the internally stored JSON value. No copies - are made. - - @warning Writing data to the referee of the result yields an undefined - state. - - @tparam ReferenceType reference type; must be a reference to @ref array_t, - @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or - @ref number_float_t. Enforced by static assertion. - - @return reference to the internally stored JSON value if the requested - reference type @a ReferenceType fits to the JSON value; throws - type_error.303 otherwise + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; - @throw type_error.303 in case passed type @a ReferenceType is incompatible - with the stored JSON value; see example below +template +class json_sax_dom_callback_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using parser_callback_t = typename BasicJsonType::parser_callback_t; + using parse_event_t = typename BasicJsonType::parse_event_t; + + json_sax_dom_callback_parser(BasicJsonType& r, + const parser_callback_t cb, + const bool allow_exceptions_ = true) + : root(r), callback(cb), allow_exceptions(allow_exceptions_) + { + keep_stack.push_back(true); + } - @complexity Constant. + // make class move-only + json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; + json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; + ~json_sax_dom_callback_parser() = default; - @liveexample{The example shows several calls to `get_ref()`.,get_ref} + bool null() + { + handle_value(nullptr); + return true; + } - @since version 1.1.0 - */ - template::value, int>::type = 0> - ReferenceType get_ref() + bool boolean(bool val) { - // delegate call to get_ref_impl - return get_ref_impl(*this); + handle_value(val); + return true; } - /*! - @brief get a reference value (implicit) - @copydoc get_ref() - */ - template::value and - std::is_const::type>::value, int>::type = 0> - ReferenceType get_ref() const + bool number_integer(number_integer_t val) { - // delegate call to get_ref_impl - return get_ref_impl(*this); + handle_value(val); + return true; } - /*! - @brief get a value (implicit) + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } - Implicit type conversion between the JSON value and a compatible value. - The call is realized by calling @ref get() const. + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } - @tparam ValueType non-pointer type compatible to the JSON value, for - instance `int` for JSON integer numbers, `bool` for JSON booleans, or - `std::vector` types for JSON arrays. The character type of @ref string_t - as well as an initializer list of this type is excluded to avoid - ambiguities as these types implicitly convert to `std::string`. + bool string(string_t& val) + { + handle_value(val); + return true; + } - @return copy of the JSON value, converted to type @a ValueType + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } - @throw type_error.302 in case passed type @a ValueType is incompatible - to the JSON value type (e.g., the JSON value is of type boolean, but a - string is requested); see example below + bool start_object(std::size_t len) + { + // check callback for object start + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::object_start, discarded); + keep_stack.push_back(keep); - @complexity Linear in the size of the JSON value. + auto val = handle_value(BasicJsonType::value_t::object, true); + ref_stack.push_back(val.second); - @liveexample{The example below shows several conversions from JSON values - to other types. There a few things to note: (1) Floating-point numbers can - be converted to integers\, (2) A JSON array can be converted to a standard - `std::vector`\, (3) A JSON object can be converted to C++ - associative containers such as `std::unordered_map`.,operator__ValueType} + // check object limit + if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len))); + } - @since version 1.0.0 - */ - template < typename ValueType, typename std::enable_if < - not std::is_pointer::value and - not std::is_same::value -#ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015 - and not std::is_same>::value -#endif -#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_MSC_VER) && _MSC_VER >1900 && defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 - and not std::is_same::value -#endif - , int >::type = 0 > - operator ValueType() const - { - // delegate the call to get<>() const - return get(); + return true; } - /// @} + bool key(string_t& val) + { + BasicJsonType k = BasicJsonType(val); + // check callback for key + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::key, k); + key_keep_stack.push_back(keep); - //////////////////// - // element access // - //////////////////// + // add discarded value at given key and store the reference for later + if (keep && ref_stack.back()) + { + object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded); + } - /// @name element access - /// Access to the JSON value. - /// @{ + return true; + } - /*! - @brief access specified array element with bounds checking + bool end_object() + { + if (ref_stack.back() && !callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + { + // discard object + *ref_stack.back() = discarded; + } - Returns a reference to the element at specified location @a idx, with - bounds checking. + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(!keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); - @param[in] idx index of the element to access + if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured()) + { + // remove discarded value + for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) + { + if (it->is_discarded()) + { + ref_stack.back()->erase(it); + break; + } + } + } - @return reference to the element at index @a idx + return true; + } - @throw type_error.304 if the JSON value is not an array; in this case, - calling `at` with an index makes no sense. See example below. - @throw out_of_range.401 if the index @a idx is out of range of the array; - that is, `idx >= size()`. See example below. + bool start_array(std::size_t len) + { + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::array_start, discarded); + keep_stack.push_back(keep); - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. + auto val = handle_value(BasicJsonType::value_t::array, true); + ref_stack.push_back(val.second); - @complexity Constant. + // check array limit + if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len))); + } - @since version 1.0.0 + return true; + } - @liveexample{The example below shows how array elements can be read and - written using `at()`. It also demonstrates the different exceptions that - can be thrown.,at__size_type} - */ - reference at(size_type idx) + bool end_array() { - // at only works for arrays - if (is_array()) + bool keep = true; + + if (ref_stack.back()) { - JSON_TRY - { - return m_value.array->at(idx); - } - JSON_CATCH (std::out_of_range&) + keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); + if (!keep) { - // create better exception explanation - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + // discard array + *ref_stack.back() = discarded; } } - else + + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(!keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + // remove discarded value + if (!keep && !ref_stack.empty() && ref_stack.back()->is_array()) { - JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); + ref_stack.back()->m_value.array->pop_back(); } - } - /*! - @brief access specified array element with bounds checking + return true; + } - Returns a const reference to the element at specified location @a idx, - with bounds checking. + template + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) + { + errored = true; + static_cast(ex); + if (allow_exceptions) + { + JSON_THROW(ex); + } + return false; + } - @param[in] idx index of the element to access + constexpr bool is_errored() const + { + return errored; + } - @return const reference to the element at index @a idx + private: + /*! + @param[in] v value to add to the JSON value we build during parsing + @param[in] skip_callback whether we should skip calling the callback + function; this is required after start_array() and + start_object() SAX events, because otherwise we would call the + callback function with an empty array or object, respectively. + + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + + @return pair of boolean (whether value should be kept) and pointer (to the + passed value in the ref_stack hierarchy; nullptr if not kept) + */ + template + std::pair handle_value(Value&& v, const bool skip_callback = false) + { + JSON_ASSERT(!keep_stack.empty()); - @throw type_error.304 if the JSON value is not an array; in this case, - calling `at` with an index makes no sense. See example below. - @throw out_of_range.401 if the index @a idx is out of range of the array; - that is, `idx >= size()`. See example below. + // do not handle this value if we know it would be added to a discarded + // container + if (!keep_stack.back()) + { + return {false, nullptr}; + } - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. + // create value + auto value = BasicJsonType(std::forward(v)); - @complexity Constant. + // check callback + const bool keep = skip_callback || callback(static_cast(ref_stack.size()), parse_event_t::value, value); - @since version 1.0.0 + // do not handle this value if we just learnt it shall be discarded + if (!keep) + { + return {false, nullptr}; + } - @liveexample{The example below shows how array elements can be read using - `at()`. It also demonstrates the different exceptions that can be thrown., - at__size_type_const} - */ - const_reference at(size_type idx) const - { - // at only works for arrays - if (is_array()) + if (ref_stack.empty()) { - JSON_TRY - { - return m_value.array->at(idx); - } - JSON_CATCH (std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); - } + root = std::move(value); + return {true, &root}; } - else + + // skip this value if we already decided to skip the parent + // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360) + if (!ref_stack.back()) { - JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); + return {false, nullptr}; } - } - /*! - @brief access specified object element with bounds checking + // we now only expect arrays and objects + JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); - Returns a reference to the element at with specified key @a key, with - bounds checking. + // array + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->push_back(std::move(value)); + return {true, &(ref_stack.back()->m_value.array->back())}; + } - @param[in] key key of the element to access + // object + JSON_ASSERT(ref_stack.back()->is_object()); + // check if we should store an element for the current key + JSON_ASSERT(!key_keep_stack.empty()); + const bool store_element = key_keep_stack.back(); + key_keep_stack.pop_back(); - @return reference to the element at key @a key + if (!store_element) + { + return {false, nullptr}; + } - @throw type_error.304 if the JSON value is not an object; in this case, - calling `at` with a key makes no sense. See example below. - @throw out_of_range.403 if the key @a key is is not stored in the object; - that is, `find(key) == end()`. See example below. + JSON_ASSERT(object_element); + *object_element = std::move(value); + return {true, object_element}; + } - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// stack to manage which values to keep + std::vector keep_stack {}; + /// stack to manage which object keys to keep + std::vector key_keep_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// callback function + const parser_callback_t callback = nullptr; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; + /// a discarded value for the callback + BasicJsonType discarded = BasicJsonType::value_t::discarded; +}; - @complexity Logarithmic in the size of the container. +template +class json_sax_acceptor +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference - @sa @ref value() for access by value with a default value + bool null() + { + return true; + } - @since version 1.0.0 + bool boolean(bool /*unused*/) + { + return true; + } - @liveexample{The example below shows how object elements can be read and - written using `at()`. It also demonstrates the different exceptions that - can be thrown.,at__object_t_key_type} - */ - reference at(const typename object_t::key_type& key) + bool number_integer(number_integer_t /*unused*/) { - // at only works for objects - if (is_object()) - { - JSON_TRY - { - return m_value.object->at(key); - } - JSON_CATCH (std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); - } - } - else - { - JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); - } + return true; } - /*! - @brief access specified object element with bounds checking + bool number_unsigned(number_unsigned_t /*unused*/) + { + return true; + } - Returns a const reference to the element at with specified key @a key, - with bounds checking. + bool number_float(number_float_t /*unused*/, const string_t& /*unused*/) + { + return true; + } - @param[in] key key of the element to access + bool string(string_t& /*unused*/) + { + return true; + } - @return const reference to the element at key @a key + bool binary(binary_t& /*unused*/) + { + return true; + } - @throw type_error.304 if the JSON value is not an object; in this case, - calling `at` with a key makes no sense. See example below. - @throw out_of_range.403 if the key @a key is is not stored in the object; - that is, `find(key) == end()`. See example below. + bool start_object(std::size_t /*unused*/ = std::size_t(-1)) + { + return true; + } - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. + bool key(string_t& /*unused*/) + { + return true; + } - @complexity Logarithmic in the size of the container. + bool end_object() + { + return true; + } - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference - @sa @ref value() for access by value with a default value + bool start_array(std::size_t /*unused*/ = std::size_t(-1)) + { + return true; + } - @since version 1.0.0 + bool end_array() + { + return true; + } - @liveexample{The example below shows how object elements can be read using - `at()`. It also demonstrates the different exceptions that can be thrown., - at__object_t_key_type_const} - */ - const_reference at(const typename object_t::key_type& key) const + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/) { - // at only works for objects - if (is_object()) - { - JSON_TRY - { - return m_value.object->at(key); - } - JSON_CATCH (std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); - } - } - else - { - JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); - } + return false; } +}; +} // namespace detail - /*! - @brief access specified array element +} // namespace nlohmann - Returns a reference to the element at specified location @a idx. +// #include - @note If @a idx is beyond the range of the array (i.e., `idx >= size()`), - then the array is silently filled up with `null` values to make `idx` a - valid reference to the last stored element. - @param[in] idx index of the element to access +#include // array +#include // localeconv +#include // size_t +#include // snprintf +#include // strtof, strtod, strtold, strtoll, strtoull +#include // initializer_list +#include // char_traits, string +#include // move +#include // vector - @return reference to the element at index @a idx +// #include - @throw type_error.305 if the JSON value is not an array or null; in that - cases, using the [] operator with an index makes no sense. +// #include - @complexity Constant if @a idx is in the range of the array. Otherwise - linear in `idx - size()`. +// #include - @liveexample{The example below shows how array elements can be read and - written using `[]` operator. Note the addition of `null` - values.,operatorarray__size_type} - @since version 1.0.0 - */ - reference operator[](size_type idx) - { - // implicitly convert null value to an empty array - if (is_null()) - { - m_type = value_t::array; - m_value.array = create(); - assert_invariant(); - } +namespace nlohmann +{ +namespace detail +{ +/////////// +// lexer // +/////////// - // operator[] only works for arrays - if (is_array()) - { - // fill up array with null values if given idx is outside range - if (idx >= m_value.array->size()) - { - m_value.array->insert(m_value.array->end(), - idx - m_value.array->size() + 1, - basic_json()); - } +template +class lexer_base +{ + public: + /// token types for the parser + enum class token_type + { + uninitialized, ///< indicating the scanner is uninitialized + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal + value_string, ///< a string -- use get_string() for actual value + value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value + value_integer, ///< a signed integer -- use get_number_integer() for actual value + value_float, ///< an floating point number -- use get_number_float() for actual value + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` + name_separator, ///< the name separator `:` + value_separator, ///< the value separator `,` + parse_error, ///< indicating a parse error + end_of_input, ///< indicating the end of the input buffer + literal_or_value ///< a literal or the begin of a value (only for diagnostics) + }; - return m_value.array->operator[](idx); + /// return name of values of type token_type (only used for errors) + JSON_HEDLEY_RETURNS_NON_NULL + JSON_HEDLEY_CONST + static const char* token_type_name(const token_type t) noexcept + { + switch (t) + { + case token_type::uninitialized: + return ""; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case token_type::value_unsigned: + case token_type::value_integer: + case token_type::value_float: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return ""; + case token_type::end_of_input: + return "end of input"; + case token_type::literal_or_value: + return "'[', '{', or a literal"; + // LCOV_EXCL_START + default: // catch non-enum values + return "unknown token"; + // LCOV_EXCL_STOP } - - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } +}; +/*! +@brief lexical analysis - /*! - @brief access specified array element - - Returns a const reference to the element at specified location @a idx. - - @param[in] idx index of the element to access +This class organizes the lexical analysis during JSON deserialization. +*/ +template +class lexer : public lexer_base +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits::int_type; - @return const reference to the element at index @a idx + public: + using token_type = typename lexer_base::token_type; - @throw type_error.305 if the JSON value is not an array; in that cases, - using the [] operator with an index makes no sense. + explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) + : ia(std::move(adapter)) + , ignore_comments(ignore_comments_) + , decimal_point_char(static_cast(get_decimal_point())) + {} - @complexity Constant. + // delete because of pointer members + lexer(const lexer&) = delete; + lexer(lexer&&) = default; + lexer& operator=(lexer&) = delete; + lexer& operator=(lexer&&) = default; + ~lexer() = default; - @liveexample{The example below shows how array elements can be read using - the `[]` operator.,operatorarray__size_type_const} + private: + ///////////////////// + // locales + ///////////////////// - @since version 1.0.0 - */ - const_reference operator[](size_type idx) const + /// return the locale-dependent decimal point + JSON_HEDLEY_PURE + static char get_decimal_point() noexcept { - // const operator[] only works for arrays - if (is_array()) - { - return m_value.array->operator[](idx); - } - - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + const auto* loc = localeconv(); + JSON_ASSERT(loc != nullptr); + return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); } - /*! - @brief access specified object element - - Returns a reference to the element at with specified key @a key. - - @note If @a key is not found in the object, then it is silently added to - the object and filled with a `null` value to make `key` a valid reference. - In case the value was `null` before, it is converted to an object. - - @param[in] key key of the element to access - - @return reference to the element at key @a key - - @throw type_error.305 if the JSON value is not an object or null; in that - cases, using the [] operator with a key makes no sense. + ///////////////////// + // scan functions + ///////////////////// - @complexity Logarithmic in the size of the container. + /*! + @brief get codepoint from 4 hex characters following `\u` - @liveexample{The example below shows how object elements can be read and - written using the `[]` operator.,operatorarray__key_type} + For input "\u c1 c2 c3 c4" the codepoint is: + (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4 + = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0) - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value + Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f' + must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The + conversion is done by subtracting the offset (0x30, 0x37, and 0x57) + between the ASCII value of the character and the desired integer value. - @since version 1.0.0 + @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or + non-hex character) */ - reference operator[](const typename object_t::key_type& key) + int get_codepoint() { - // implicitly convert null value to an empty object - if (is_null()) - { - m_type = value_t::object; - m_value.object = create(); - assert_invariant(); - } + // this function only makes sense after reading `\u` + JSON_ASSERT(current == 'u'); + int codepoint = 0; - // operator[] only works for objects - if (is_object()) + const auto factors = { 12u, 8u, 4u, 0u }; + for (const auto factor : factors) { - return m_value.object->operator[](key); + get(); + + if (current >= '0' && current <= '9') + { + codepoint += static_cast((static_cast(current) - 0x30u) << factor); + } + else if (current >= 'A' && current <= 'F') + { + codepoint += static_cast((static_cast(current) - 0x37u) << factor); + } + else if (current >= 'a' && current <= 'f') + { + codepoint += static_cast((static_cast(current) - 0x57u) << factor); + } + else + { + return -1; + } } - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF); + return codepoint; } /*! - @brief read-only access specified object element - - Returns a const reference to the element at with specified key @a key. No - bounds checking is performed. - - @warning If the element with key @a key does not exist, the behavior is - undefined. - - @param[in] key key of the element to access + @brief check if the next byte(s) are inside a given range - @return const reference to the element at key @a key - - @pre The element with key @a key must exist. **This precondition is - enforced with an assertion.** - - @throw type_error.305 if the JSON value is not an object; in that cases, - using the [] operator with a key makes no sense. - - @complexity Logarithmic in the size of the container. + Adds the current byte and, for each passed range, reads a new byte and + checks if it is inside the range. If a violation was detected, set up an + error message and return false. Otherwise, return true. - @liveexample{The example below shows how object elements can be read using - the `[]` operator.,operatorarray__key_type_const} + @param[in] ranges list of integers; interpreted as list of pairs of + inclusive lower and upper bound, respectively - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value + @pre The passed list @a ranges must have 2, 4, or 6 elements; that is, + 1, 2, or 3 pairs. This precondition is enforced by an assertion. - @since version 1.0.0 + @return true if and only if no range violation was detected */ - const_reference operator[](const typename object_t::key_type& key) const + bool next_byte_in_range(std::initializer_list ranges) { - // const operator[] only works for objects - if (is_object()) + JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6); + add(current); + + for (auto range = ranges.begin(); range != ranges.end(); ++range) { - assert(m_value.object->find(key) != m_value.object->end()); - return m_value.object->find(key)->second; + get(); + if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) + { + add(current); + } + else + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return false; + } } - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + return true; } /*! - @brief access specified object element - - Returns a reference to the element at with specified key @a key. - - @note If @a key is not found in the object, then it is silently added to - the object and filled with a `null` value to make `key` a valid reference. - In case the value was `null` before, it is converted to an object. + @brief scan a string literal - @param[in] key key of the element to access - - @return reference to the element at key @a key - - @throw type_error.305 if the JSON value is not an object or null; in that - cases, using the [] operator with a key makes no sense. - - @complexity Logarithmic in the size of the container. + This function scans a string according to Sect. 7 of RFC 7159. While + scanning, bytes are escaped and copied into buffer token_buffer. Then the + function returns successfully, token_buffer is *not* null-terminated (as it + may contain \0 bytes), and token_buffer.size() is the number of bytes in the + string. - @liveexample{The example below shows how object elements can be read and - written using the `[]` operator.,operatorarray__key_type} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value + @return token_type::value_string if string could be successfully scanned, + token_type::parse_error otherwise - @since version 1.0.0 + @note In case of errors, variable error_message contains a textual + description. */ - template - reference operator[](T * (&key)[n]) + token_type scan_string() { - return operator[](static_cast(key)); - } + // reset token_buffer (ignore opening quote) + reset(); - /*! - @brief read-only access specified object element - - Returns a const reference to the element at with specified key @a key. No - bounds checking is performed. + // we entered the function by reading an open quote + JSON_ASSERT(current == '\"'); - @warning If the element with key @a key does not exist, the behavior is - undefined. + while (true) + { + // get next character + switch (get()) + { + // end of file while parsing string + case std::char_traits::eof(): + { + error_message = "invalid string: missing closing quote"; + return token_type::parse_error; + } - @note This function is required for compatibility reasons with Clang. + // closing quote + case '\"': + { + return token_type::value_string; + } - @param[in] key key of the element to access + // escapes + case '\\': + { + switch (get()) + { + // quotation mark + case '\"': + add('\"'); + break; + // reverse solidus + case '\\': + add('\\'); + break; + // solidus + case '/': + add('/'); + break; + // backspace + case 'b': + add('\b'); + break; + // form feed + case 'f': + add('\f'); + break; + // line feed + case 'n': + add('\n'); + break; + // carriage return + case 'r': + add('\r'); + break; + // tab + case 't': + add('\t'); + break; + + // unicode escapes + case 'u': + { + const int codepoint1 = get_codepoint(); + int codepoint = codepoint1; // start with codepoint1 - @return const reference to the element at key @a key + if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } - @throw type_error.305 if the JSON value is not an object; in that cases, - using the [] operator with a key makes no sense. + // check if code point is a high surrogate + if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF) + { + // expect next \uxxxx entry + if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u')) + { + const int codepoint2 = get_codepoint(); - @complexity Logarithmic in the size of the container. + if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } - @liveexample{The example below shows how object elements can be read using - the `[]` operator.,operatorarray__key_type_const} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value + // check if codepoint2 is a low surrogate + if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF)) + { + // overwrite codepoint + codepoint = static_cast( + // high surrogate occupies the most significant 22 bits + (static_cast(codepoint1) << 10u) + // low surrogate occupies the least significant 15 bits + + static_cast(codepoint2) + // there is still the 0xD800, 0xDC00 and 0x10000 noise + // in the result so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00u); + } + else + { + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF)) + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; + return token_type::parse_error; + } + } - @since version 1.0.0 - */ - template - const_reference operator[](T * (&key)[n]) const - { - return operator[](static_cast(key)); - } + // result of the above calculation yields a proper codepoint + JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF); - /*! - @brief access specified object element + // translate codepoint into bytes + if (codepoint < 0x80) + { + // 1-byte characters: 0xxxxxxx (ASCII) + add(static_cast(codepoint)); + } + else if (codepoint <= 0x7FF) + { + // 2-byte characters: 110xxxxx 10xxxxxx + add(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else if (codepoint <= 0xFFFF) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + add(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + add(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } - Returns a reference to the element at with specified key @a key. + break; + } - @note If @a key is not found in the object, then it is silently added to - the object and filled with a `null` value to make `key` a valid reference. - In case the value was `null` before, it is converted to an object. + // other characters after escape + default: + error_message = "invalid string: forbidden character after backslash"; + return token_type::parse_error; + } - @param[in] key key of the element to access + break; + } - @return reference to the element at key @a key + // invalid control characters + case 0x00: + { + error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000"; + return token_type::parse_error; + } - @throw type_error.305 if the JSON value is not an object or null; in that - cases, using the [] operator with a key makes no sense. + case 0x01: + { + error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; + return token_type::parse_error; + } - @complexity Logarithmic in the size of the container. + case 0x02: + { + error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; + return token_type::parse_error; + } - @liveexample{The example below shows how object elements can be read and - written using the `[]` operator.,operatorarray__key_type} + case 0x03: + { + error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; + return token_type::parse_error; + } - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value + case 0x04: + { + error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; + return token_type::parse_error; + } - @since version 1.1.0 - */ - template - reference operator[](T* key) - { - // implicitly convert null to object - if (is_null()) - { - m_type = value_t::object; - m_value = value_t::object; - assert_invariant(); - } + case 0x05: + { + error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; + return token_type::parse_error; + } - // at only works for objects - if (is_object()) - { - return m_value.object->operator[](key); - } + case 0x06: + { + error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; + return token_type::parse_error; + } - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); - } + case 0x07: + { + error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; + return token_type::parse_error; + } - /*! - @brief read-only access specified object element + case 0x08: + { + error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; + return token_type::parse_error; + } - Returns a const reference to the element at with specified key @a key. No - bounds checking is performed. + case 0x09: + { + error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; + return token_type::parse_error; + } - @warning If the element with key @a key does not exist, the behavior is - undefined. + case 0x0A: + { + error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; + return token_type::parse_error; + } - @param[in] key key of the element to access + case 0x0B: + { + error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; + return token_type::parse_error; + } - @return const reference to the element at key @a key + case 0x0C: + { + error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; + return token_type::parse_error; + } - @pre The element with key @a key must exist. **This precondition is - enforced with an assertion.** + case 0x0D: + { + error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; + return token_type::parse_error; + } - @throw type_error.305 if the JSON value is not an object; in that cases, - using the [] operator with a key makes no sense. + case 0x0E: + { + error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; + return token_type::parse_error; + } - @complexity Logarithmic in the size of the container. + case 0x0F: + { + error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; + return token_type::parse_error; + } - @liveexample{The example below shows how object elements can be read using - the `[]` operator.,operatorarray__key_type_const} + case 0x10: + { + error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; + return token_type::parse_error; + } - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value + case 0x11: + { + error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; + return token_type::parse_error; + } - @since version 1.1.0 - */ - template - const_reference operator[](T* key) const - { - // at only works for objects - if (is_object()) - { - assert(m_value.object->find(key) != m_value.object->end()); - return m_value.object->find(key)->second; - } + case 0x12: + { + error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; + return token_type::parse_error; + } - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); - } + case 0x13: + { + error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; + return token_type::parse_error; + } - /*! - @brief access specified object element with default value + case 0x14: + { + error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; + return token_type::parse_error; + } - Returns either a copy of an object's element at the specified key @a key - or a given default value if no element with key @a key exists. + case 0x15: + { + error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; + return token_type::parse_error; + } - The function is basically equivalent to executing - @code {.cpp} - try { - return at(key); - } catch(out_of_range) { - return default_value; - } - @endcode + case 0x16: + { + error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; + return token_type::parse_error; + } - @note Unlike @ref at(const typename object_t::key_type&), this function - does not throw if the given key @a key was not found. + case 0x17: + { + error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; + return token_type::parse_error; + } - @note Unlike @ref operator[](const typename object_t::key_type& key), this - function does not implicitly add an element to the position defined by @a - key. This function is furthermore also applicable to const objects. + case 0x18: + { + error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; + return token_type::parse_error; + } - @param[in] key key of the element to access - @param[in] default_value the value to return if @a key is not found + case 0x19: + { + error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; + return token_type::parse_error; + } - @tparam ValueType type compatible to JSON values, for instance `int` for - JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for - JSON arrays. Note the type of the expected value at @a key and the default - value @a default_value must be compatible. + case 0x1A: + { + error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; + return token_type::parse_error; + } - @return copy of the element at key @a key or @a default_value if @a key - is not found + case 0x1B: + { + error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; + return token_type::parse_error; + } - @throw type_error.306 if the JSON value is not an objec; in that cases, - using `value()` with a key makes no sense. + case 0x1C: + { + error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; + return token_type::parse_error; + } - @complexity Logarithmic in the size of the container. + case 0x1D: + { + error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; + return token_type::parse_error; + } - @liveexample{The example below shows how object elements can be queried - with a default value.,basic_json__value} + case 0x1E: + { + error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; + return token_type::parse_error; + } - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference + case 0x1F: + { + error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; + return token_type::parse_error; + } - @since version 1.0.0 - */ - template::value, int>::type = 0> - ValueType value(const typename object_t::key_type& key, ValueType default_value) const - { - // at only works for objects - if (is_object()) - { - // if key is found, return value and given default value otherwise - const auto it = find(key); - if (it != end()) - { - return *it; - } - - return default_value; - } - else - { - JSON_THROW(type_error::create(306, "cannot use value() with " + type_name())); - } - } + // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace)) + case 0x20: + case 0x21: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + { + add(current); + break; + } - /*! - @brief overload for a default value of type const char* - @copydoc basic_json::value(const typename object_t::key_type&, ValueType) const - */ - string_t value(const typename object_t::key_type& key, const char* default_value) const - { - return value(key, string_t(default_value)); - } + // U+0080..U+07FF: bytes C2..DF 80..BF + case 0xC2: + case 0xC3: + case 0xC4: + case 0xC5: + case 0xC6: + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD5: + case 0xD6: + case 0xD7: + case 0xD8: + case 0xD9: + case 0xDA: + case 0xDB: + case 0xDC: + case 0xDD: + case 0xDE: + case 0xDF: + { + if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF}))) + { + return token_type::parse_error; + } + break; + } - /*! - @brief access specified object element via JSON Pointer with default value + // U+0800..U+0FFF: bytes E0 A0..BF 80..BF + case 0xE0: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } - Returns either a copy of an object's element at the specified key @a key - or a given default value if no element with key @a key exists. + // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF + // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xEE: + case 0xEF: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } - The function is basically equivalent to executing - @code {.cpp} - try { - return at(ptr); - } catch(out_of_range) { - return default_value; - } - @endcode + // U+D000..U+D7FF: bytes ED 80..9F 80..BF + case 0xED: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } - @note Unlike @ref at(const json_pointer&), this function does not throw - if the given key @a key was not found. + // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + case 0xF0: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } - @param[in] ptr a JSON pointer to the element to access - @param[in] default_value the value to return if @a ptr found no value + // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + case 0xF1: + case 0xF2: + case 0xF3: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } - @tparam ValueType type compatible to JSON values, for instance `int` for - JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for - JSON arrays. Note the type of the expected value at @a key and the default - value @a default_value must be compatible. + // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF + case 0xF4: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } - @return copy of the element at key @a key or @a default_value if @a key - is not found + // remaining bytes (80..C1 and F5..FF) are ill-formed + default: + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } + } + } + } - @throw type_error.306 if the JSON value is not an objec; in that cases, - using `value()` with a key makes no sense. + /*! + * @brief scan a comment + * @return whether comment could be scanned successfully + */ + bool scan_comment() + { + switch (get()) + { + // single-line comments skip input until a newline or EOF is read + case '/': + { + while (true) + { + switch (get()) + { + case '\n': + case '\r': + case std::char_traits::eof(): + case '\0': + return true; + + default: + break; + } + } + } - @complexity Logarithmic in the size of the container. + // multi-line comments skip input until */ is read + case '*': + { + while (true) + { + switch (get()) + { + case std::char_traits::eof(): + case '\0': + { + error_message = "invalid comment; missing closing '*/'"; + return false; + } - @liveexample{The example below shows how object elements can be queried - with a default value.,basic_json__value_ptr} + case '*': + { + switch (get()) + { + case '/': + return true; - @sa @ref operator[](const json_pointer&) for unchecked access by reference + default: + { + unget(); + continue; + } + } + } - @since version 2.0.2 - */ - template::value, int>::type = 0> - ValueType value(const json_pointer& ptr, ValueType default_value) const - { - // at only works for objects - if (is_object()) - { - // if pointer resolves a value, return it or use default value - JSON_TRY - { - return ptr.get_checked(this); + default: + continue; + } + } } - JSON_CATCH (out_of_range&) + + // unexpected character after reading '/' + default: { - return default_value; + error_message = "invalid comment; expecting '/' or '*' after '/'"; + return false; } } - - JSON_THROW(type_error::create(306, "cannot use value() with " + type_name())); } - /*! - @brief overload for a default value of type const char* - @copydoc basic_json::value(const json_pointer&, ValueType) const - */ - string_t value(const json_pointer& ptr, const char* default_value) const + JSON_HEDLEY_NON_NULL(2) + static void strtof(float& f, const char* str, char** endptr) noexcept { - return value(ptr, string_t(default_value)); + f = std::strtof(str, endptr); } - /*! - @brief access the first element - - Returns a reference to the first element in the container. For a JSON - container `c`, the expression `c.front()` is equivalent to `*c.begin()`. - - @return In case of a structured type (array or object), a reference to the - first element is returned. In case of number, string, or boolean values, a - reference to the value is returned. - - @complexity Constant. - - @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, **guarded by - assertions**). - @post The JSON value remains unchanged. - - @throw invalid_iterator.214 when called on `null` value - - @liveexample{The following code shows an example for `front()`.,front} - - @sa @ref back() -- access the last element + JSON_HEDLEY_NON_NULL(2) + static void strtof(double& f, const char* str, char** endptr) noexcept + { + f = std::strtod(str, endptr); + } - @since version 1.0.0 - */ - reference front() + JSON_HEDLEY_NON_NULL(2) + static void strtof(long double& f, const char* str, char** endptr) noexcept { - return *begin(); + f = std::strtold(str, endptr); } /*! - @copydoc basic_json::front() + @brief scan a number literal + + This function scans a string according to Sect. 6 of RFC 7159. + + The function is realized with a deterministic finite state machine derived + from the grammar described in RFC 7159. Starting in state "init", the + input is read and used to determined the next state. Only state "done" + accepts the number. State "error" is a trap state to model errors. In the + table below, "anything" means any character but the ones listed before. + + state | 0 | 1-9 | e E | + | - | . | anything + ---------|----------|----------|----------|---------|---------|----------|----------- + init | zero | any1 | [error] | [error] | minus | [error] | [error] + minus | zero | any1 | [error] | [error] | [error] | [error] | [error] + zero | done | done | exponent | done | done | decimal1 | done + any1 | any1 | any1 | exponent | done | done | decimal1 | done + decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error] + decimal2 | decimal2 | decimal2 | exponent | done | done | done | done + exponent | any2 | any2 | [error] | sign | sign | [error] | [error] + sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] + any2 | any2 | any2 | done | done | done | done | done + + The state machine is realized with one label per state (prefixed with + "scan_number_") and `goto` statements between them. The state machine + contains cycles, but any cycle can be left when EOF is read. Therefore, + the function is guaranteed to terminate. + + During scanning, the read bytes are stored in token_buffer. This string is + then converted to a signed integer, an unsigned integer, or a + floating-point number. + + @return token_type::value_unsigned, token_type::value_integer, or + token_type::value_float if number could be successfully scanned, + token_type::parse_error otherwise + + @note The scanner is independent of the current locale. Internally, the + locale's decimal point is used instead of `.` to work with the + locale-dependent converters. */ - const_reference front() const + token_type scan_number() // lgtm [cpp/use-of-goto] { - return *cbegin(); - } + // reset token_buffer to store the number's bytes + reset(); - /*! - @brief access the last element + // the type of the parsed number; initially set to unsigned; will be + // changed if minus sign, decimal point or exponent is read + token_type number_type = token_type::value_unsigned; - Returns a reference to the last element in the container. For a JSON - container `c`, the expression `c.back()` is equivalent to - @code {.cpp} - auto tmp = c.end(); - --tmp; - return *tmp; - @endcode + // state (init): we just found out we need to scan a number + switch (current) + { + case '-': + { + add(current); + goto scan_number_minus; + } - @return In case of a structured type (array or object), a reference to the - last element is returned. In case of number, string, or boolean values, a - reference to the value is returned. + case '0': + { + add(current); + goto scan_number_zero; + } - @complexity Constant. + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } - @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, **guarded by - assertions**). - @post The JSON value remains unchanged. - - @throw invalid_iterator.214 when called on a `null` value. See example - below. + // all other characters are rejected outside scan_number() + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } - @liveexample{The following code shows an example for `back()`.,back} +scan_number_minus: + // state: we just parsed a leading minus sign + number_type = token_type::value_integer; + switch (get()) + { + case '0': + { + add(current); + goto scan_number_zero; + } - @sa @ref front() -- access the first element + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } - @since version 1.0.0 - */ - reference back() - { - auto tmp = end(); - --tmp; - return *tmp; - } + default: + { + error_message = "invalid number; expected digit after '-'"; + return token_type::parse_error; + } + } - /*! - @copydoc basic_json::back() - */ - const_reference back() const - { - auto tmp = cend(); - --tmp; - return *tmp; - } +scan_number_zero: + // state: we just parse a zero (maybe with a leading minus sign) + switch (get()) + { + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } - /*! - @brief remove element given an iterator + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } - Removes the element specified by iterator @a pos. The iterator @a pos must - be valid and dereferenceable. Thus the `end()` iterator (which is valid, - but is not dereferenceable) cannot be used as a value for @a pos. + default: + goto scan_number_done; + } - If called on a primitive type other than `null`, the resulting JSON value - will be `null`. +scan_number_any1: + // state: we just parsed a number 0-9 (maybe with a leading minus sign) + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } - @param[in] pos iterator to the element to remove - @return Iterator following the last removed element. If the iterator @a - pos refers to the last element, the `end()` iterator is returned. + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } - @tparam IteratorType an @ref iterator or @ref const_iterator + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } - @post Invalidates iterators and references at or after the point of the - erase, including the `end()` iterator. + default: + goto scan_number_done; + } - @throw type_error.307 if called on a `null` value; example: `"cannot use - erase() with null"` - @throw invalid_iterator.202 if called on an iterator which does not belong - to the current JSON value; example: `"iterator does not fit current - value"` - @throw invalid_iterator.205 if called on a primitive type with invalid - iterator (i.e., any iterator which is not `begin()`); example: `"iterator - out of range"` +scan_number_decimal1: + // state: we just parsed a decimal point + number_type = token_type::value_float; + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } - @complexity The complexity depends on the type: - - objects: amortized constant - - arrays: linear in distance between @a pos and the end of the container - - strings: linear in the length of the string - - other types: constant + default: + { + error_message = "invalid number; expected digit after '.'"; + return token_type::parse_error; + } + } - @liveexample{The example shows the result of `erase()` for different JSON - types.,erase__IteratorType} +scan_number_decimal2: + // we just parsed at least one number after a decimal point + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } - @sa @ref erase(IteratorType, IteratorType) -- removes the elements in - the given range - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key - @sa @ref erase(const size_type) -- removes the element from an array at - the given index + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } - @since version 1.0.0 - */ - template::value or - std::is_same::value, int>::type - = 0> - IteratorType erase(IteratorType pos) - { - // make sure iterator fits the current value - if (this != pos.m_object) - { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + default: + goto scan_number_done; } - IteratorType result = end(); - - switch (m_type) +scan_number_exponent: + // we just parsed an exponent + number_type = token_type::value_float; + switch (get()) { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::string: + case '+': + case '-': { - if (not pos.m_it.primitive_iterator.is_begin()) - { - JSON_THROW(invalid_iterator::create(205, "iterator out of range")); - } - - if (is_string()) - { - AllocatorType alloc; - alloc.destroy(m_value.string); - alloc.deallocate(m_value.string, 1); - m_value.string = nullptr; - } + add(current); + goto scan_number_sign; + } - m_type = value_t::null; - assert_invariant(); - break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; } - case value_t::object: + default: { - result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); - break; + error_message = + "invalid number; expected '+', '-', or digit after exponent"; + return token_type::parse_error; } + } - case value_t::array: +scan_number_sign: + // we just parsed an exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { - result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); - break; + add(current); + goto scan_number_any2; } default: { - JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); + error_message = "invalid number; expected digit after exponent sign"; + return token_type::parse_error; } } - return result; - } +scan_number_any2: + // we just parsed a number after the exponent or exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } - /*! - @brief remove elements given an iterator range + default: + goto scan_number_done; + } - Removes the element specified by the range `[first; last)`. The iterator - @a first does not need to be dereferenceable if `first == last`: erasing - an empty range is a no-op. +scan_number_done: + // unget the character after the number (we only read it to know that + // we are done scanning a number) + unget(); - If called on a primitive type other than `null`, the resulting JSON value - will be `null`. + char* endptr = nullptr; + errno = 0; - @param[in] first iterator to the beginning of the range to remove - @param[in] last iterator past the end of the range to remove - @return Iterator following the last removed element. If the iterator @a - second refers to the last element, the `end()` iterator is returned. + // try to parse integers first and fall back to floats + if (number_type == token_type::value_unsigned) + { + const auto x = std::strtoull(token_buffer.data(), &endptr, 10); - @tparam IteratorType an @ref iterator or @ref const_iterator + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); - @post Invalidates iterators and references at or after the point of the - erase, including the `end()` iterator. + if (errno == 0) + { + value_unsigned = static_cast(x); + if (value_unsigned == x) + { + return token_type::value_unsigned; + } + } + } + else if (number_type == token_type::value_integer) + { + const auto x = std::strtoll(token_buffer.data(), &endptr, 10); - @throw type_error.307 if called on a `null` value; example: `"cannot use - erase() with null"` - @throw invalid_iterator.203 if called on iterators which does not belong - to the current JSON value; example: `"iterators do not fit current value"` - @throw invalid_iterator.204 if called on a primitive type with invalid - iterators (i.e., if `first != begin()` and `last != end()`); example: - `"iterators out of range"` + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); - @complexity The complexity depends on the type: - - objects: `log(size()) + std::distance(first, last)` - - arrays: linear in the distance between @a first and @a last, plus linear - in the distance between @a last and end of the container - - strings: linear in the length of the string - - other types: constant + if (errno == 0) + { + value_integer = static_cast(x); + if (value_integer == x) + { + return token_type::value_integer; + } + } + } - @liveexample{The example shows the result of `erase()` for different JSON - types.,erase__IteratorType_IteratorType} + // this code is reached if we parse a floating-point number or if an + // integer conversion above failed + strtof(value_float, token_buffer.data(), &endptr); - @sa @ref erase(IteratorType) -- removes the element at a given position - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key - @sa @ref erase(const size_type) -- removes the element from an array at - the given index + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); - @since version 1.0.0 + return token_type::value_float; + } + + /*! + @param[in] literal_text the literal text to expect + @param[in] length the length of the passed literal text + @param[in] return_type the token type to return on success */ - template::value or - std::is_same::value, int>::type - = 0> - IteratorType erase(IteratorType first, IteratorType last) + JSON_HEDLEY_NON_NULL(2) + token_type scan_literal(const char_type* literal_text, const std::size_t length, + token_type return_type) { - // make sure iterator fits the current value - if (this != first.m_object or this != last.m_object) + JSON_ASSERT(std::char_traits::to_char_type(current) == literal_text[0]); + for (std::size_t i = 1; i < length; ++i) { - JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value")); + if (JSON_HEDLEY_UNLIKELY(std::char_traits::to_char_type(get()) != literal_text[i])) + { + error_message = "invalid literal"; + return token_type::parse_error; + } } + return return_type; + } - IteratorType result = end(); + ///////////////////// + // input management + ///////////////////// - switch (m_type) - { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::string: - { - if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) - { - JSON_THROW(invalid_iterator::create(204, "iterators out of range")); - } + /// reset token_buffer; current character is beginning of token + void reset() noexcept + { + token_buffer.clear(); + token_string.clear(); + token_string.push_back(std::char_traits::to_char_type(current)); + } - if (is_string()) - { - AllocatorType alloc; - alloc.destroy(m_value.string); - alloc.deallocate(m_value.string, 1); - m_value.string = nullptr; - } + /* + @brief get next character from the input - m_type = value_t::null; - assert_invariant(); - break; - } + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a + `std::char_traits::eof()` in that case. Stores the scanned characters + for use in error messages. - case value_t::object: - { - result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, - last.m_it.object_iterator); - break; - } + @return character read from the input + */ + char_int_type get() + { + ++position.chars_read_total; + ++position.chars_read_current_line; - case value_t::array: - { - result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, - last.m_it.array_iterator); - break; - } + if (next_unget) + { + // just reset the next_unget variable and work with current + next_unget = false; + } + else + { + current = ia.get_character(); + } - default: - { - JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); - } + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + token_string.push_back(std::char_traits::to_char_type(current)); } - return result; + if (current == '\n') + { + ++position.lines_read; + position.chars_read_current_line = 0; + } + + return current; } /*! - @brief remove element from a JSON object given a key + @brief unget current character (read it again on next get) - Removes elements from a JSON object with the key value @a key. + We implement unget by setting variable next_unget to true. The input is not + changed - we just simulate ungetting by modifying chars_read_total, + chars_read_current_line, and token_string. The next call to get() will + behave as if the unget character is read again. + */ + void unget() + { + next_unget = true; - @param[in] key value of the elements to remove + --position.chars_read_total; - @return Number of elements removed. If @a ObjectType is the default - `std::map` type, the return value will always be `0` (@a key was not - found) or `1` (@a key was found). + // in case we "unget" a newline, we have to also decrement the lines_read + if (position.chars_read_current_line == 0) + { + if (position.lines_read > 0) + { + --position.lines_read; + } + } + else + { + --position.chars_read_current_line; + } - @post References and iterators to the erased elements are invalidated. - Other references and iterators are not affected. + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + JSON_ASSERT(!token_string.empty()); + token_string.pop_back(); + } + } - @throw type_error.307 when called on a type other than JSON object; - example: `"cannot use erase() with null"` + /// add a character to token_buffer + void add(char_int_type c) + { + token_buffer.push_back(static_cast(c)); + } - @complexity `log(size()) + count(key)` + public: + ///////////////////// + // value getters + ///////////////////// - @liveexample{The example shows the effect of `erase()`.,erase__key_type} + /// return integer value + constexpr number_integer_t get_number_integer() const noexcept + { + return value_integer; + } - @sa @ref erase(IteratorType) -- removes the element at a given position - @sa @ref erase(IteratorType, IteratorType) -- removes the elements in - the given range - @sa @ref erase(const size_type) -- removes the element from an array at - the given index + /// return unsigned integer value + constexpr number_unsigned_t get_number_unsigned() const noexcept + { + return value_unsigned; + } - @since version 1.0.0 - */ - size_type erase(const typename object_t::key_type& key) + /// return floating-point value + constexpr number_float_t get_number_float() const noexcept { - // this erase only works for objects - if (is_object()) + return value_float; + } + + /// return current string value (implicitly resets the token; useful only once) + string_t& get_string() + { + return token_buffer; + } + + ///////////////////// + // diagnostics + ///////////////////// + + /// return position of last read token + constexpr position_t get_position() const noexcept + { + return position; + } + + /// return the last read token (for errors only). Will never contain EOF + /// (an arbitrary value that is not a valid char value, often -1), because + /// 255 may legitimately occur. May contain NUL, which should be escaped. + std::string get_token_string() const + { + // escape control characters + std::string result; + for (const auto c : token_string) { - return m_value.object->erase(key); + if (static_cast(c) <= '\x1F') + { + // escape control characters + std::array cs{{}}; + (std::snprintf)(cs.data(), cs.size(), "", static_cast(c)); + result += cs.data(); + } + else + { + // add character as is + result.push_back(static_cast(c)); + } } - JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); + return result; } - /*! - @brief remove element from a JSON array given an index + /// return syntax error message + JSON_HEDLEY_RETURNS_NON_NULL + constexpr const char* get_error_message() const noexcept + { + return error_message; + } - Removes element from a JSON array at the index @a idx. + ///////////////////// + // actual scanner + ///////////////////// - @param[in] idx index of the element to remove + /*! + @brief skip the UTF-8 byte order mark + @return true iff there is no BOM or the correct BOM has been skipped + */ + bool skip_bom() + { + if (get() == 0xEF) + { + // check if we completely parse the BOM + return get() == 0xBB && get() == 0xBF; + } - @throw type_error.307 when called on a type other than JSON object; - example: `"cannot use erase() with null"` - @throw out_of_range.401 when `idx >= size()`; example: `"array index 17 - is out of range"` + // the first character is not the beginning of the BOM; unget it to + // process is later + unget(); + return true; + } - @complexity Linear in distance between @a idx and the end of the container. + void skip_whitespace() + { + do + { + get(); + } + while (current == ' ' || current == '\t' || current == '\n' || current == '\r'); + } - @liveexample{The example shows the effect of `erase()`.,erase__size_type} + token_type scan() + { + // initially, skip the BOM + if (position.chars_read_total == 0 && !skip_bom()) + { + error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given"; + return token_type::parse_error; + } - @sa @ref erase(IteratorType) -- removes the element at a given position - @sa @ref erase(IteratorType, IteratorType) -- removes the elements in - the given range - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key + // read next character and ignore whitespace + skip_whitespace(); - @since version 1.0.0 - */ - void erase(const size_type idx) - { - // this erase only works for arrays - if (is_array()) + // ignore comments + while (ignore_comments && current == '/') { - if (idx >= size()) + if (!scan_comment()) { - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + return token_type::parse_error; } - m_value.array->erase(m_value.array->begin() + static_cast(idx)); + // skip following whitespace + skip_whitespace(); } - else + + switch (current) { - JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); + // structural characters + case '[': + return token_type::begin_array; + case ']': + return token_type::end_array; + case '{': + return token_type::begin_object; + case '}': + return token_type::end_object; + case ':': + return token_type::name_separator; + case ',': + return token_type::value_separator; + + // literals + case 't': + { + std::array true_literal = {{'t', 'r', 'u', 'e'}}; + return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true); + } + case 'f': + { + std::array false_literal = {{'f', 'a', 'l', 's', 'e'}}; + return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false); + } + case 'n': + { + std::array null_literal = {{'n', 'u', 'l', 'l'}}; + return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null); + } + + // string + case '\"': + return scan_string(); + + // number + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return scan_number(); + + // end of input (the null byte is needed when parsing from + // string literals) + case '\0': + case std::char_traits::eof(): + return token_type::end_of_input; + + // error + default: + error_message = "invalid literal"; + return token_type::parse_error; } } - /// @} + private: + /// input adapter + InputAdapterType ia; + /// whether comments should be ignored (true) or signaled as errors (false) + const bool ignore_comments = false; - //////////// - // lookup // - //////////// + /// the current character + char_int_type current = std::char_traits::eof(); - /// @name lookup - /// @{ + /// whether the next get() call should just return current + bool next_unget = false; - /*! - @brief find an element in a JSON object + /// the start position of the current token + position_t position {}; - Finds an element in a JSON object with key equivalent to @a key. If the - element is not found or the JSON value is not an object, end() is - returned. + /// raw input token string (for error messages) + std::vector token_string {}; - @note This method always returns @ref end() when executed on a JSON type - that is not an object. + /// buffer for variable-length tokens (numbers, strings) + string_t token_buffer {}; - @param[in] key key value of the element to search for + /// a description of occurred lexer errors + const char* error_message = ""; - @return Iterator to an element with key equivalent to @a key. If no such - element is found or the JSON value is not an object, past-the-end (see - @ref end()) iterator is returned. + // number values + number_integer_t value_integer = 0; + number_unsigned_t value_unsigned = 0; + number_float_t value_float = 0; - @complexity Logarithmic in the size of the JSON object. + /// the decimal point + const char_int_type decimal_point_char = '.'; +}; +} // namespace detail +} // namespace nlohmann - @liveexample{The example shows how `find()` is used.,find__key_type} +// #include - @since version 1.0.0 - */ - iterator find(typename object_t::key_type key) - { - auto result = end(); +// #include - if (is_object()) - { - result.m_it.object_iterator = m_value.object->find(key); - } - return result; - } +#include // size_t +#include // declval +#include // string - /*! - @brief find an element in a JSON object - @copydoc find(typename object_t::key_type) - */ - const_iterator find(typename object_t::key_type key) const - { - auto result = cend(); +// #include - if (is_object()) - { - result.m_it.object_iterator = m_value.object->find(key); - } +// #include - return result; - } - /*! - @brief returns the number of occurrences of a key in a JSON object +namespace nlohmann +{ +namespace detail +{ +template +using null_function_t = decltype(std::declval().null()); - Returns the number of elements with key @a key. If ObjectType is the - default `std::map` type, the return value will always be `0` (@a key was - not found) or `1` (@a key was found). +template +using boolean_function_t = + decltype(std::declval().boolean(std::declval())); - @note This method always returns `0` when executed on a JSON type that is - not an object. +template +using number_integer_function_t = + decltype(std::declval().number_integer(std::declval())); - @param[in] key key value of the element to count +template +using number_unsigned_function_t = + decltype(std::declval().number_unsigned(std::declval())); - @return Number of elements with key @a key. If the JSON value is not an - object, the return value will be `0`. +template +using number_float_function_t = decltype(std::declval().number_float( + std::declval(), std::declval())); - @complexity Logarithmic in the size of the JSON object. +template +using string_function_t = + decltype(std::declval().string(std::declval())); - @liveexample{The example shows how `count()` is used.,count} +template +using binary_function_t = + decltype(std::declval().binary(std::declval())); - @since version 1.0.0 - */ - size_type count(typename object_t::key_type key) const - { - // return 0 for all nonobject types - return is_object() ? m_value.object->count(key) : 0; - } +template +using start_object_function_t = + decltype(std::declval().start_object(std::declval())); - /// @} +template +using key_function_t = + decltype(std::declval().key(std::declval())); +template +using end_object_function_t = decltype(std::declval().end_object()); - /////////////// - // iterators // - /////////////// +template +using start_array_function_t = + decltype(std::declval().start_array(std::declval())); - /// @name iterators - /// @{ +template +using end_array_function_t = decltype(std::declval().end_array()); - /*! - @brief returns an iterator to the first element +template +using parse_error_function_t = decltype(std::declval().parse_error( + std::declval(), std::declval(), + std::declval())); - Returns an iterator to the first element. +template +struct is_sax +{ + private: + static_assert(is_basic_json::value, + "BasicJsonType must be of type basic_json<...>"); - @image html range-begin-end.svg "Illustration from cppreference.com" + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using exception_t = typename BasicJsonType::exception; - @return iterator to the first element + public: + static constexpr bool value = + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value; +}; - @complexity Constant. +template +struct is_sax_static_asserts +{ + private: + static_assert(is_basic_json::value, + "BasicJsonType must be of type basic_json<...>"); - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using exception_t = typename BasicJsonType::exception; - @liveexample{The following code shows an example for `begin()`.,begin} + public: + static_assert(is_detected_exact::value, + "Missing/invalid function: bool null()"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool boolean(bool)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool boolean(bool)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool number_integer(number_integer_t)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool number_unsigned(number_unsigned_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool number_float(number_float_t, const string_t&)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool string(string_t&)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool binary(binary_t&)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool start_object(std::size_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool key(string_t&)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool end_object()"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool start_array(std::size_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool end_array()"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool parse_error(std::size_t, const " + "std::string&, const exception&)"); +}; +} // namespace detail +} // namespace nlohmann - @sa @ref cbegin() -- returns a const iterator to the beginning - @sa @ref end() -- returns an iterator to the end - @sa @ref cend() -- returns a const iterator to the end +// #include - @since version 1.0.0 - */ - iterator begin() noexcept - { - iterator result(this); - result.set_begin(); - return result; - } - /*! - @copydoc basic_json::cbegin() - */ - const_iterator begin() const noexcept - { - return cbegin(); - } +namespace nlohmann +{ +namespace detail +{ - /*! - @brief returns a const iterator to the first element +/// how to treat CBOR tags +enum class cbor_tag_handler_t +{ + error, ///< throw a parse_error exception in case of a tag + ignore ///< ignore tags +}; - Returns a const iterator to the first element. +/*! +@brief determine system byte order - @image html range-begin-end.svg "Illustration from cppreference.com" +@return true if and only if system's byte order is little endian - @return const iterator to the first element +@note from https://stackoverflow.com/a/1001328/266378 +*/ +static inline bool little_endianess(int num = 1) noexcept +{ + return *reinterpret_cast(&num) == 1; +} - @complexity Constant. - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).begin()`. +/////////////////// +// binary reader // +/////////////////// - @liveexample{The following code shows an example for `cbegin()`.,cbegin} +/*! +@brief deserialization of CBOR, MessagePack, and UBJSON values +*/ +template> +class binary_reader +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using json_sax_t = SAX; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits::int_type; - @sa @ref begin() -- returns an iterator to the beginning - @sa @ref end() -- returns an iterator to the end - @sa @ref cend() -- returns a const iterator to the end + public: + /*! + @brief create a binary reader - @since version 1.0.0 + @param[in] adapter input adapter to read from */ - const_iterator cbegin() const noexcept + explicit binary_reader(InputAdapterType&& adapter) : ia(std::move(adapter)) { - const_iterator result(this); - result.set_begin(); - return result; + (void)detail::is_sax_static_asserts {}; } + // make class move-only + binary_reader(const binary_reader&) = delete; + binary_reader(binary_reader&&) = default; + binary_reader& operator=(const binary_reader&) = delete; + binary_reader& operator=(binary_reader&&) = default; + ~binary_reader() = default; + /*! - @brief returns an iterator to one past the last element + @param[in] format the binary format to parse + @param[in] sax_ a SAX event processor + @param[in] strict whether to expect the input to be consumed completed + @param[in] tag_handler how to treat CBOR tags - Returns an iterator to one past the last element. + @return + */ + JSON_HEDLEY_NON_NULL(3) + bool sax_parse(const input_format_t format, + json_sax_t* sax_, + const bool strict = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + sax = sax_; + bool result = false; - @image html range-begin-end.svg "Illustration from cppreference.com" + switch (format) + { + case input_format_t::bson: + result = parse_bson_internal(); + break; - @return iterator one past the last element + case input_format_t::cbor: + result = parse_cbor_internal(true, tag_handler); + break; - @complexity Constant. + case input_format_t::msgpack: + result = parse_msgpack_internal(); + break; - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. + case input_format_t::ubjson: + result = parse_ubjson_internal(); + break; - @liveexample{The following code shows an example for `end()`.,end} + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } - @sa @ref cend() -- returns a const iterator to the end - @sa @ref begin() -- returns an iterator to the beginning - @sa @ref cbegin() -- returns a const iterator to the beginning + // strict mode: next byte must be EOF + if (result && strict) + { + if (format == input_format_t::ubjson) + { + get_ignore_noop(); + } + else + { + get(); + } + + if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) + { + return sax->parse_error(chars_read, get_token_string(), + parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"))); + } + } - @since version 1.0.0 - */ - iterator end() noexcept - { - iterator result(this); - result.set_end(); return result; } + private: + ////////// + // BSON // + ////////// + /*! - @copydoc basic_json::cend() + @brief Reads in a BSON-object and passes it to the SAX-parser. + @return whether a valid BSON-value was passed to the SAX parser */ - const_iterator end() const noexcept + bool parse_bson_internal() { - return cend(); - } - - /*! - @brief returns a const iterator to one past the last element - - Returns a const iterator to one past the last element. - - @image html range-begin-end.svg "Illustration from cppreference.com" - - @return const iterator one past the last element + std::int32_t document_size{}; + get_number(input_format_t::bson, document_size); - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).end()`. - - @liveexample{The following code shows an example for `cend()`.,cend} + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1)))) + { + return false; + } - @sa @ref end() -- returns an iterator to the end - @sa @ref begin() -- returns an iterator to the beginning - @sa @ref cbegin() -- returns a const iterator to the beginning + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false))) + { + return false; + } - @since version 1.0.0 - */ - const_iterator cend() const noexcept - { - const_iterator result(this); - result.set_end(); - return result; + return sax->end_object(); } /*! - @brief returns an iterator to the reverse-beginning - - Returns an iterator to the reverse-beginning; that is, the last element. - - @image html range-rbegin-rend.svg "Illustration from cppreference.com" - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `reverse_iterator(end())`. - - @liveexample{The following code shows an example for `rbegin()`.,rbegin} - - @sa @ref crbegin() -- returns a const reverse iterator to the beginning - @sa @ref rend() -- returns a reverse iterator to the end - @sa @ref crend() -- returns a const reverse iterator to the end - - @since version 1.0.0 + @brief Parses a C-style string from the BSON input. + @param[in, out] result A reference to the string variable where the read + string is to be stored. + @return `true` if the \x00-byte indicating the end of the string was + encountered before the EOF; false` indicates an unexpected EOF. */ - reverse_iterator rbegin() noexcept + bool get_bson_cstr(string_t& result) { - return reverse_iterator(end()); + auto out = std::back_inserter(result); + while (true) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "cstring"))) + { + return false; + } + if (current == 0x00) + { + return true; + } + *out++ = static_cast(current); + } } /*! - @copydoc basic_json::crbegin() + @brief Parses a zero-terminated string of length @a len from the BSON + input. + @param[in] len The length (including the zero-byte at the end) of the + string to be read. + @param[in, out] result A reference to the string variable where the read + string is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 1 + @return `true` if the string was successfully parsed */ - const_reverse_iterator rbegin() const noexcept + template + bool get_bson_string(const NumberType len, string_t& result) { - return crbegin(); + if (JSON_HEDLEY_UNLIKELY(len < 1)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"))); + } + + return get_string(input_format_t::bson, len - static_cast(1), result) && get() != std::char_traits::eof(); } /*! - @brief returns an iterator to the reverse-end - - Returns an iterator to the reverse-end; that is, one before the first - element. - - @image html range-rbegin-rend.svg "Illustration from cppreference.com" - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `reverse_iterator(begin())`. - - @liveexample{The following code shows an example for `rend()`.,rend} - - @sa @ref crend() -- returns a const reverse iterator to the end - @sa @ref rbegin() -- returns a reverse iterator to the beginning - @sa @ref crbegin() -- returns a const reverse iterator to the beginning - - @since version 1.0.0 + @brief Parses a byte array input of length @a len from the BSON input. + @param[in] len The length of the byte array to be read. + @param[in, out] result A reference to the binary variable where the read + array is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 0 + @return `true` if the byte array was successfully parsed */ - reverse_iterator rend() noexcept + template + bool get_bson_binary(const NumberType len, binary_t& result) { - return reverse_iterator(begin()); + if (JSON_HEDLEY_UNLIKELY(len < 0)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"))); + } + + // All BSON binary values have a subtype + std::uint8_t subtype{}; + get_number(input_format_t::bson, subtype); + result.set_subtype(subtype); + + return get_binary(input_format_t::bson, len, result); } /*! - @copydoc basic_json::crend() + @brief Read a BSON document element of the given @a element_type. + @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html + @param[in] element_type_parse_position The position in the input stream, + where the `element_type` was read. + @warning Not all BSON element types are supported yet. An unsupported + @a element_type will give rise to a parse_error.114: + Unsupported BSON record type 0x... + @return whether a valid BSON-object/array was passed to the SAX parser */ - const_reverse_iterator rend() const noexcept + bool parse_bson_element_internal(const char_int_type element_type, + const std::size_t element_type_parse_position) { - return crend(); - } + switch (element_type) + { + case 0x01: // double + { + double number{}; + return get_number(input_format_t::bson, number) && sax->number_float(static_cast(number), ""); + } - /*! - @brief returns a const reverse iterator to the last element + case 0x02: // string + { + std::int32_t len{}; + string_t value; + return get_number(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value); + } - Returns a const iterator to the reverse-beginning; that is, the last - element. + case 0x03: // object + { + return parse_bson_internal(); + } - @image html range-rbegin-rend.svg "Illustration from cppreference.com" + case 0x04: // array + { + return parse_bson_array(); + } - @complexity Constant. + case 0x05: // binary + { + std::int32_t len{}; + binary_t value; + return get_number(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value); + } - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).rbegin()`. + case 0x08: // boolean + { + return sax->boolean(get() != 0); + } - @liveexample{The following code shows an example for `crbegin()`.,crbegin} + case 0x0A: // null + { + return sax->null(); + } - @sa @ref rbegin() -- returns a reverse iterator to the beginning - @sa @ref rend() -- returns a reverse iterator to the end - @sa @ref crend() -- returns a const reverse iterator to the end + case 0x10: // int32 + { + std::int32_t value{}; + return get_number(input_format_t::bson, value) && sax->number_integer(value); + } - @since version 1.0.0 - */ - const_reverse_iterator crbegin() const noexcept - { - return const_reverse_iterator(cend()); + case 0x12: // int64 + { + std::int64_t value{}; + return get_number(input_format_t::bson, value) && sax->number_integer(value); + } + + default: // anything else not supported (yet) + { + std::array cr{{}}; + (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type)); + return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()))); + } + } } /*! - @brief returns a const reverse iterator to one before the first + @brief Read a BSON element list (as specified in the BSON-spec) - Returns a const reverse iterator to the reverse-end; that is, one before - the first element. + The same binary layout is used for objects and arrays, hence it must be + indicated with the argument @a is_array which one is expected + (true --> array, false --> object). - @image html range-rbegin-rend.svg "Illustration from cppreference.com" + @param[in] is_array Determines if the element list being read is to be + treated as an object (@a is_array == false), or as an + array (@a is_array == true). + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_list(const bool is_array) + { + string_t key; - @complexity Constant. + while (auto element_type = get()) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "element list"))) + { + return false; + } - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).rend()`. + const std::size_t element_type_parse_position = chars_read; + if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key))) + { + return false; + } - @liveexample{The following code shows an example for `crend()`.,crend} + if (!is_array && !sax->key(key)) + { + return false; + } - @sa @ref rend() -- returns a reverse iterator to the end - @sa @ref rbegin() -- returns a reverse iterator to the beginning - @sa @ref crbegin() -- returns a const reverse iterator to the beginning + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position))) + { + return false; + } - @since version 1.0.0 - */ - const_reverse_iterator crend() const noexcept - { - return const_reverse_iterator(cbegin()); - } + // get_bson_cstr only appends + key.clear(); + } - private: - // forward declaration - template class iteration_proxy; + return true; + } - public: /*! - @brief wrapper to access iterator member functions in range-based for + @brief Reads an array from the BSON input and passes it to the SAX-parser. + @return whether a valid BSON-array was passed to the SAX parser + */ + bool parse_bson_array() + { + std::int32_t document_size{}; + get_number(input_format_t::bson, document_size); - This function allows to access @ref iterator::key() and @ref - iterator::value() during range-based for loops. In these loops, a - reference to the JSON values is returned, so there is no access to the - underlying iterator. + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1)))) + { + return false; + } - @liveexample{The following code shows how the wrapper is used,iterator_wrapper} + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true))) + { + return false; + } - @note The name of this function is not yet final and may change in the - future. - */ - static iteration_proxy iterator_wrapper(reference cont) - { - return iteration_proxy(cont); + return sax->end_array(); } + ////////// + // CBOR // + ////////// + /*! - @copydoc iterator_wrapper(reference) + @param[in] get_char whether a new character should be retrieved from the + input (true) or whether the last read character should + be considered instead (false) + @param[in] tag_handler how CBOR tags should be treated + + @return whether a valid CBOR value was passed to the SAX parser */ - static iteration_proxy iterator_wrapper(const_reference cont) + bool parse_cbor_internal(const bool get_char, + const cbor_tag_handler_t tag_handler) { - return iteration_proxy(cont); - } + switch (get_char ? get() : current) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::cbor, "value"); + + // Integer 0x00..0x17 (0..23) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + return sax->number_unsigned(static_cast(current)); + + case 0x18: // Unsigned integer (one-byte uint8_t follows) + { + std::uint8_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } - /// @} + case 0x19: // Unsigned integer (two-byte uint16_t follows) + { + std::uint16_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + case 0x1A: // Unsigned integer (four-byte uint32_t follows) + { + std::uint32_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } - ////////////// - // capacity // - ////////////// + case 0x1B: // Unsigned integer (eight-byte uint64_t follows) + { + std::uint64_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } - /// @name capacity - /// @{ + // Negative integer -1-0x00..-1-0x17 (-1..-24) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + return sax->number_integer(static_cast(0x20 - 1 - current)); + + case 0x38: // Negative integer (one-byte uint8_t follows) + { + std::uint8_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } - /*! - @brief checks whether the container is empty + case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + { + std::uint16_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } - Checks if a JSON value has no elements. - - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | `true` - boolean | `false` - string | `false` - number | `false` - object | result of function `object_t::empty()` - array | result of function `array_t::empty()` + case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) + { + std::uint32_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } - @note This function does not return whether a string stored as JSON value - is empty - it returns whether the JSON container itself is empty which is - false in the case of a string. + case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) + { + std::uint64_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) + - static_cast(number)); + } - @complexity Constant, as long as @ref array_t and @ref object_t satisfy - the Container concept; that is, their `empty()` functions have constant - complexity. + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: // Binary data (one-byte uint8_t for n follows) + case 0x59: // Binary data (two-byte uint16_t for n follow) + case 0x5A: // Binary data (four-byte uint32_t for n follow) + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + case 0x5F: // Binary data (indefinite length) + { + binary_t b; + return get_cbor_binary(b) && sax->binary(b); + } - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - Has the semantics of `begin() == end()`. + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + case 0x7F: // UTF-8 string (indefinite length) + { + string_t s; + return get_cbor_string(s) && sax->string(s); + } - @liveexample{The following code uses `empty()` to check if a JSON - object contains any elements.,empty} + // array (0x00..0x17 data items follow) + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + return get_cbor_array(static_cast(static_cast(current) & 0x1Fu), tag_handler); + + case 0x98: // array (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } - @sa @ref size() -- returns the number of elements + case 0x99: // array (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } - @since version 1.0.0 - */ - bool empty() const noexcept - { - switch (m_type) - { - case value_t::null: + case 0x9A: // array (four-byte uint32_t for n follow) { - // null values are empty - return true; + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); } - case value_t::array: + case 0x9B: // array (eight-byte uint64_t for n follow) { - // delegate call to array_t::empty() - return m_value.array->empty(); + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); } - case value_t::object: + case 0x9F: // array (indefinite length) + return get_cbor_array(std::size_t(-1), tag_handler); + + // map (0x00..0x17 pairs of data items follow) + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + return get_cbor_object(static_cast(static_cast(current) & 0x1Fu), tag_handler); + + case 0xB8: // map (one-byte uint8_t for n follows) { - // delegate call to object_t::empty() - return m_value.object->empty(); + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); } - default: + case 0xB9: // map (two-byte uint16_t for n follow) { - // all other types are nonempty - return false; + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); } - } - } - /*! - @brief returns the number of elements + case 0xBA: // map (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } - Returns the number of elements in a JSON value. + case 0xBB: // map (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | `0` - boolean | `1` - string | `1` - number | `1` - object | result of function object_t::size() - array | result of function array_t::size() + case 0xBF: // map (indefinite length) + return get_cbor_object(std::size_t(-1), tag_handler); + + case 0xC6: // tagged item + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD8: // tagged item (1 bytes follow) + case 0xD9: // tagged item (2 bytes follow) + case 0xDA: // tagged item (4 bytes follow) + case 0xDB: // tagged item (8 bytes follow) + { + switch (tag_handler) + { + case cbor_tag_handler_t::error: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"))); + } - @note This function does not return the length of a string stored as JSON - value - it returns the number of elements in the JSON value which is 1 in - the case of a string. + case cbor_tag_handler_t::ignore: + { + switch (current) + { + case 0xD8: + { + std::uint8_t len{}; + get_number(input_format_t::cbor, len); + break; + } + case 0xD9: + { + std::uint16_t len{}; + get_number(input_format_t::cbor, len); + break; + } + case 0xDA: + { + std::uint32_t len{}; + get_number(input_format_t::cbor, len); + break; + } + case 0xDB: + { + std::uint64_t len{}; + get_number(input_format_t::cbor, len); + break; + } + default: + break; + } + return parse_cbor_internal(true, tag_handler); + } - @complexity Constant, as long as @ref array_t and @ref object_t satisfy - the Container concept; that is, their size() functions have constant - complexity. + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } + } - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - Has the semantics of `std::distance(begin(), end())`. + case 0xF4: // false + return sax->boolean(false); - @liveexample{The following code calls `size()` on the different value - types.,size} + case 0xF5: // true + return sax->boolean(true); - @sa @ref empty() -- checks whether the container is empty - @sa @ref max_size() -- returns the maximal number of elements + case 0xF6: // null + return sax->null(); - @since version 1.0.0 - */ - size_type size() const noexcept - { - switch (m_type) - { - case value_t::null: + case 0xF9: // Half-Precision Float (two-byte IEEE 754) { - // null values are empty - return 0; + const auto byte1_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + const auto byte2_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + + const auto byte1 = static_cast(byte1_raw); + const auto byte2 = static_cast(byte2_raw); + + // code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added + // to IEEE 754 in 2008, today's programming platforms often + // still only have limited support for them. It is very + // easy to include at least decoding support for them even + // without such support. An example of a small decoder for + // half-precision floating-point numbers in the C language + // is shown in Fig. 3. + const auto half = static_cast((byte1 << 8u) + byte2); + const double val = [&half] + { + const int exp = (half >> 10u) & 0x1Fu; + const unsigned int mant = half & 0x3FFu; + JSON_ASSERT(0 <= exp&& exp <= 32); + JSON_ASSERT(mant <= 1024); + switch (exp) + { + case 0: + return std::ldexp(mant, -24); + case 31: + return (mant == 0) + ? std::numeric_limits::infinity() + : std::numeric_limits::quiet_NaN(); + default: + return std::ldexp(mant + 1024, exp - 25); + } + }(); + return sax->number_float((half & 0x8000u) != 0 + ? static_cast(-val) + : static_cast(val), ""); } - case value_t::array: + case 0xFA: // Single-Precision Float (four-byte IEEE 754) { - // delegate call to array_t::size() - return m_value.array->size(); + float number{}; + return get_number(input_format_t::cbor, number) && sax->number_float(static_cast(number), ""); } - case value_t::object: + case 0xFB: // Double-Precision Float (eight-byte IEEE 754) { - // delegate call to object_t::size() - return m_value.object->size(); + double number{}; + return get_number(input_format_t::cbor, number) && sax->number_float(static_cast(number), ""); } - default: + default: // anything else (0xFF is handled inside the other types) { - // all other types have size 1 - return 1; + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"))); } } } /*! - @brief returns the maximum possible number of elements + @brief reads a CBOR string - Returns the maximum number of elements a JSON value is able to hold due to - system or library implementation limitations, i.e. `std::distance(begin(), - end())` for the JSON value. + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + Additionally, CBOR's strings with indefinite lengths are supported. - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | `0` (same as `size()`) - boolean | `1` (same as `size()`) - string | `1` (same as `size()`) - number | `1` (same as `size()`) - object | result of function `object_t::max_size()` - array | result of function `array_t::max_size()` + @param[out] result created string - @complexity Constant, as long as @ref array_t and @ref object_t satisfy - the Container concept; that is, their `max_size()` functions have constant - complexity. + @return whether string creation completed + */ + bool get_cbor_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string"))) + { + return false; + } + + switch (current) + { + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + return get_string(input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - Has the semantics of returning `b.size()` where `b` is the largest - possible JSON value. + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } - @liveexample{The following code calls `max_size()` on the different value - types. Note the output is implementation specific.,max_size} + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } - @sa @ref size() -- returns the number of elements + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } - @since version 1.0.0 - */ - size_type max_size() const noexcept - { - switch (m_type) - { - case value_t::array: + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) { - // delegate call to array_t::max_size() - return m_value.array->max_size(); + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); } - case value_t::object: + case 0x7F: // UTF-8 string (indefinite length) { - // delegate call to object_t::max_size() - return m_value.object->max_size(); + while (get() != 0xFF) + { + string_t chunk; + if (!get_cbor_string(chunk)) + { + return false; + } + result.append(chunk); + } + return true; } default: { - // all other types have max_size() == size() - return size(); + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"))); } } } - /// @} - - - /////////////// - // modifiers // - /////////////// - - /// @name modifiers - /// @{ - /*! - @brief clears the contents - - Clears the content of a JSON value and resets it to the default value as - if @ref basic_json(value_t) would have been called: - - Value type | initial value - ----------- | ------------- - null | `null` - boolean | `false` - string | `""` - number | `0` - object | `{}` - array | `[]` + @brief reads a CBOR byte array - @complexity Linear in the size of the JSON value. + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into the byte array. + Additionally, CBOR's byte arrays with indefinite lengths are supported. - @liveexample{The example below shows the effect of `clear()` to different - JSON types.,clear} + @param[out] result created byte array - @since version 1.0.0 + @return whether byte array creation completed */ - void clear() noexcept + bool get_cbor_binary(binary_t& result) { - switch (m_type) - { - case value_t::number_integer: - { - m_value.number_integer = 0; - break; - } - - case value_t::number_unsigned: + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary"))) + { + return false; + } + + switch (current) + { + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: { - m_value.number_unsigned = 0; - break; + return get_binary(input_format_t::cbor, static_cast(current) & 0x1Fu, result); } - case value_t::number_float: + case 0x58: // Binary data (one-byte uint8_t for n follows) { - m_value.number_float = 0.0; - break; + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); } - case value_t::boolean: + case 0x59: // Binary data (two-byte uint16_t for n follow) { - m_value.boolean = false; - break; + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); } - case value_t::string: + case 0x5A: // Binary data (four-byte uint32_t for n follow) { - m_value.string->clear(); - break; + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); } - case value_t::array: + case 0x5B: // Binary data (eight-byte uint64_t for n follow) { - m_value.array->clear(); - break; + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); } - case value_t::object: + case 0x5F: // Binary data (indefinite length) { - m_value.object->clear(); - break; + while (get() != 0xFF) + { + binary_t chunk; + if (!get_cbor_binary(chunk)) + { + return false; + } + result.insert(result.end(), chunk.begin(), chunk.end()); + } + return true; } default: { - break; + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"))); } } } /*! - @brief add an object to an array - - Appends the given element @a val to the end of the JSON value. If the - function is called on a JSON null value, an empty array is created before - appending @a val. - - @param[in] val the value to add to the JSON array - - @throw type_error.308 when called on a type other than JSON array or - null; example: `"cannot use push_back() with number"` - - @complexity Amortized constant. - - @liveexample{The example shows how `push_back()` and `+=` can be used to - add elements to a JSON array. Note how the `null` value was silently - converted to a JSON array.,push_back} - - @since version 1.0.0 + @param[in] len the length of the array or std::size_t(-1) for an + array of indefinite size + @param[in] tag_handler how CBOR tags should be treated + @return whether array creation completed */ - void push_back(basic_json&& val) + bool get_cbor_array(const std::size_t len, + const cbor_tag_handler_t tag_handler) { - // push_back only works for null objects or arrays - if (not(is_null() or is_array())) + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); + return false; } - // transform null object into an array - if (is_null()) + if (len != std::size_t(-1)) { - m_type = value_t::array; - m_value = value_t::array; - assert_invariant(); + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler))) + { + return false; + } + } } - // add element to array (move semantics) - m_value.array->push_back(std::move(val)); - // invalidate object - val.m_type = value_t::null; - } - - /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) - */ - reference operator+=(basic_json&& val) - { - push_back(std::move(val)); - return *this; + return sax->end_array(); } /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) + @param[in] len the length of the object or std::size_t(-1) for an + object of indefinite size + @param[in] tag_handler how CBOR tags should be treated + @return whether object creation completed */ - void push_back(const basic_json& val) + bool get_cbor_object(const std::size_t len, + const cbor_tag_handler_t tag_handler) { - // push_back only works for null objects or arrays - if (not(is_null() or is_array())) + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); + return false; } - // transform null object into an array - if (is_null()) + string_t key; + if (len != std::size_t(-1)) { - m_type = value_t::array; - m_value = value_t::array; - assert_invariant(); + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + key.clear(); + } } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) + { + return false; + } - // add element to array - m_value.array->push_back(val); + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + key.clear(); + } + } + + return sax->end_object(); } + ///////////// + // MsgPack // + ///////////// + /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) + @return whether a valid MessagePack value was passed to the SAX parser */ - reference operator+=(const basic_json& val) + bool parse_msgpack_internal() { - push_back(val); - return *this; - } - - /*! - @brief add an object to an object - - Inserts the given element @a val to the JSON object. If the function is - called on a JSON null value, an empty object is created before inserting - @a val. + switch (get()) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::msgpack, "value"); + + // positive fixint + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + return sax->number_unsigned(static_cast(current)); + + // fixmap + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + return get_msgpack_object(static_cast(static_cast(current) & 0x0Fu)); + + // fixarray + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9A: + case 0x9B: + case 0x9C: + case 0x9D: + case 0x9E: + case 0x9F: + return get_msgpack_array(static_cast(static_cast(current) & 0x0Fu)); + + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + case 0xD9: // str 8 + case 0xDA: // str 16 + case 0xDB: // str 32 + { + string_t s; + return get_msgpack_string(s) && sax->string(s); + } - @param[in] val the value to add to the JSON object + case 0xC0: // nil + return sax->null(); + + case 0xC2: // false + return sax->boolean(false); + + case 0xC3: // true + return sax->boolean(true); + + case 0xC4: // bin 8 + case 0xC5: // bin 16 + case 0xC6: // bin 32 + case 0xC7: // ext 8 + case 0xC8: // ext 16 + case 0xC9: // ext 32 + case 0xD4: // fixext 1 + case 0xD5: // fixext 2 + case 0xD6: // fixext 4 + case 0xD7: // fixext 8 + case 0xD8: // fixext 16 + { + binary_t b; + return get_msgpack_binary(b) && sax->binary(b); + } - @throw type_error.308 when called on a type other than JSON object or - null; example: `"cannot use push_back() with number"` + case 0xCA: // float 32 + { + float number{}; + return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast(number), ""); + } - @complexity Logarithmic in the size of the container, O(log(`size()`)). + case 0xCB: // float 64 + { + double number{}; + return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast(number), ""); + } - @liveexample{The example shows how `push_back()` and `+=` can be used to - add elements to a JSON object. Note how the `null` value was silently - converted to a JSON object.,push_back__object_t__value} + case 0xCC: // uint 8 + { + std::uint8_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } - @since version 1.0.0 - */ - void push_back(const typename object_t::value_type& val) - { - // push_back only works for null objects or objects - if (not(is_null() or is_object())) - { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); - } + case 0xCD: // uint 16 + { + std::uint16_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } - // transform null object into an object - if (is_null()) - { - m_type = value_t::object; - m_value = value_t::object; - assert_invariant(); - } + case 0xCE: // uint 32 + { + std::uint32_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } - // add element to array - m_value.object->insert(val); - } + case 0xCF: // uint 64 + { + std::uint64_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } - /*! - @brief add an object to an object - @copydoc push_back(const typename object_t::value_type&) - */ - reference operator+=(const typename object_t::value_type& val) - { - push_back(val); - return *this; - } + case 0xD0: // int 8 + { + std::int8_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } - /*! - @brief add an object to an object + case 0xD1: // int 16 + { + std::int16_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } - This function allows to use `push_back` with an initializer list. In case + case 0xD2: // int 32 + { + std::int32_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } - 1. the current value is an object, - 2. the initializer list @a init contains only two elements, and - 3. the first element of @a init is a string, + case 0xD3: // int 64 + { + std::int64_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } - @a init is converted into an object element and added using - @ref push_back(const typename object_t::value_type&). Otherwise, @a init - is converted to a JSON value and added using @ref push_back(basic_json&&). + case 0xDC: // array 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast(len)); + } - @param[in] init an initializer list + case 0xDD: // array 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast(len)); + } - @complexity Linear in the size of the initializer list @a init. + case 0xDE: // map 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast(len)); + } - @note This function is required to resolve an ambiguous overload error, - because pairs like `{"key", "value"}` can be both interpreted as - `object_t::value_type` or `std::initializer_list`, see - https://github.com/nlohmann/json/issues/235 for more information. + case 0xDF: // map 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast(len)); + } - @liveexample{The example shows how initializer lists are treated as - objects when possible.,push_back__initializer_list} - */ - void push_back(std::initializer_list init) - { - if (is_object() and init.size() == 2 and init.begin()->is_string()) - { - const string_t key = *init.begin(); - push_back(typename object_t::value_type(key, *(init.begin() + 1))); - } - else - { - push_back(basic_json(init)); + // negative fixint + case 0xE0: + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xED: + case 0xEE: + case 0xEF: + case 0xF0: + case 0xF1: + case 0xF2: + case 0xF3: + case 0xF4: + case 0xF5: + case 0xF6: + case 0xF7: + case 0xF8: + case 0xF9: + case 0xFA: + case 0xFB: + case 0xFC: + case 0xFD: + case 0xFE: + case 0xFF: + return sax->number_integer(static_cast(current)); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"))); + } } } /*! - @brief add an object to an object - @copydoc push_back(std::initializer_list) + @brief reads a MessagePack string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + + @param[out] result created string + + @return whether string creation completed */ - reference operator+=(std::initializer_list init) + bool get_msgpack_string(string_t& result) { - push_back(init); - return *this; - } + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, "string"))) + { + return false; + } + + switch (current) + { + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + { + return get_string(input_format_t::msgpack, static_cast(current) & 0x1Fu, result); + } - /*! - @brief add an object to an array + case 0xD9: // str 8 + { + std::uint8_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } - Creates a JSON value from the passed parameters @a args to the end of the - JSON value. If the function is called on a JSON null value, an empty array - is created before appending the value created from @a args. + case 0xDA: // str 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } - @param[in] args arguments to forward to a constructor of @ref basic_json - @tparam Args compatible types to create a @ref basic_json object + case 0xDB: // str 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } - @throw type_error.311 when called on a type other than JSON array or - null; example: `"cannot use emplace_back() with number"` + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"))); + } + } + } - @complexity Amortized constant. + /*! + @brief reads a MessagePack byte array - @liveexample{The example shows how `push_back()` can be used to add - elements to a JSON array. Note how the `null` value was silently converted - to a JSON array.,emplace_back} + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into a byte array. - @since version 2.0.8 + @param[out] result created byte array + + @return whether byte array creation completed */ - template - void emplace_back(Args&& ... args) + bool get_msgpack_binary(binary_t& result) { - // emplace_back only works for null objects or arrays - if (not(is_null() or is_array())) + // helper function to set the subtype + auto assign_and_return_true = [&result](std::int8_t subtype) { - JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + type_name())); - } + result.set_subtype(static_cast(subtype)); + return true; + }; - // transform null object into an array - if (is_null()) + switch (current) { - m_type = value_t::array; - m_value = value_t::array; - assert_invariant(); - } - - // add element to array (perfect forwarding) - m_value.array->emplace_back(std::forward(args)...); - } + case 0xC4: // bin 8 + { + std::uint8_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } - /*! - @brief add an object to an object if key does not exist + case 0xC5: // bin 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } - Inserts a new element into a JSON object constructed in-place with the - given @a args if there is no element with the key in the container. If the - function is called on a JSON null value, an empty object is created before - appending the value created from @a args. + case 0xC6: // bin 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } - @param[in] args arguments to forward to a constructor of @ref basic_json - @tparam Args compatible types to create a @ref basic_json object + case 0xC7: // ext 8 + { + std::uint8_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } - @return a pair consisting of an iterator to the inserted element, or the - already-existing element if no insertion happened, and a bool - denoting whether the insertion took place. + case 0xC8: // ext 16 + { + std::uint16_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } - @throw type_error.311 when called on a type other than JSON object or - null; example: `"cannot use emplace() with number"` + case 0xC9: // ext 32 + { + std::uint32_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } - @complexity Logarithmic in the size of the container, O(log(`size()`)). + case 0xD4: // fixext 1 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 1, result) && + assign_and_return_true(subtype); + } - @liveexample{The example shows how `emplace()` can be used to add elements - to a JSON object. Note how the `null` value was silently converted to a - JSON object. Further note how no value is added if there was already one - value stored with the same key.,emplace} + case 0xD5: // fixext 2 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 2, result) && + assign_and_return_true(subtype); + } - @since version 2.0.8 + case 0xD6: // fixext 4 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 4, result) && + assign_and_return_true(subtype); + } + + case 0xD7: // fixext 8 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 8, result) && + assign_and_return_true(subtype); + } + + case 0xD8: // fixext 16 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 16, result) && + assign_and_return_true(subtype); + } + + default: // LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE + } + } + + /*! + @param[in] len the length of the array + @return whether array creation completed */ - template - std::pair emplace(Args&& ... args) + bool get_msgpack_array(const std::size_t len) { - // emplace only works for null objects or arrays - if (not(is_null() or is_object())) + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) { - JSON_THROW(type_error::create(311, "cannot use emplace() with " + type_name())); + return false; } - // transform null object into an object - if (is_null()) + for (std::size_t i = 0; i < len; ++i) { - m_type = value_t::object; - m_value = value_t::object; - assert_invariant(); + if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) + { + return false; + } } - // add element to array (perfect forwarding) - auto res = m_value.object->emplace(std::forward(args)...); - // create result iterator and set iterator to the result of emplace - auto it = begin(); - it.m_it.object_iterator = res.first; - - // return pair of iterator and boolean - return {it, res.second}; + return sax->end_array(); } /*! - @brief inserts element - - Inserts element @a val before iterator @a pos. - - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] val element to insert - @return iterator pointing to the inserted @a val. - - @throw type_error.309 if called on JSON values other than arrays; - example: `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` - - @complexity Constant plus linear in the distance between @a pos and end of - the container. - - @liveexample{The example shows how `insert()` is used.,insert} - - @since version 1.0.0 + @param[in] len the length of the object + @return whether object creation completed */ - iterator insert(const_iterator pos, const basic_json& val) + bool get_msgpack_object(const std::size_t len) { - // insert only works for arrays - if (is_array()) + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) { - // check if iterator pos fits to this JSON value - if (pos.m_object != this) + return false; + } + + string_t key; + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key))) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + return false; } - // insert to array and return iterator - iterator result(this); - result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val); - return result; + if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) + { + return false; + } + key.clear(); } - JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); + return sax->end_object(); } + //////////// + // UBJSON // + //////////// + /*! - @brief inserts element - @copydoc insert(const_iterator, const basic_json&) + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether a valid UBJSON value was passed to the SAX parser */ - iterator insert(const_iterator pos, basic_json&& val) + bool parse_ubjson_internal(const bool get_char = true) { - return insert(pos, val); + return get_ubjson_value(get_char ? get_ignore_noop() : current); } /*! - @brief inserts elements - - Inserts @a cnt copies of @a val before iterator @a pos. - - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] cnt number of copies of @a val to insert - @param[in] val element to insert - @return iterator pointing to the first element inserted, or @a pos if - `cnt==0` - - @throw type_error.309 if called on JSON values other than arrays; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` + @brief reads a UBJSON string - @complexity Linear in @a cnt plus linear in the distance between @a pos - and end of the container. + This function is either called after reading the 'S' byte explicitly + indicating a string, or in case of an object key where the 'S' byte can be + left out. - @liveexample{The example shows how `insert()` is used.,insert__count} + @param[out] result created string + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead - @since version 1.0.0 + @return whether string creation completed */ - iterator insert(const_iterator pos, size_type cnt, const basic_json& val) + bool get_ubjson_string(string_t& result, const bool get_char = true) { - // insert only works for arrays - if (is_array()) + if (get_char) { - // check if iterator pos fits to this JSON value - if (pos.m_object != this) - { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); - } - - // insert to array and return iterator - iterator result(this); - result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); - return result; + get(); // TODO(niels): may we ignore N here? } - JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); - } - - /*! - @brief inserts elements + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } - Inserts elements from range `[first, last)` before iterator @a pos. + switch (current) + { + case 'U': + { + std::uint8_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] first begin of the range of elements to insert - @param[in] last end of the range of elements to insert + case 'i': + { + std::int8_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } - @throw type_error.309 if called on JSON values other than arrays; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` - @throw invalid_iterator.210 if @a first and @a last do not belong to the - same JSON value; example: `"iterators do not fit"` - @throw invalid_iterator.211 if @a first or @a last are iterators into - container for which insert is called; example: `"passed iterators may not - belong to container"` + case 'I': + { + std::int16_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } - @return iterator pointing to the first element inserted, or @a pos if - `first==last` + case 'l': + { + std::int32_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } - @complexity Linear in `std::distance(first, last)` plus linear in the - distance between @a pos and end of the container. + case 'L': + { + std::int64_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } - @liveexample{The example shows how `insert()` is used.,insert__range} + default: + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"))); + } + } - @since version 1.0.0 + /*! + @param[out] result determined size + @return whether size determination completed */ - iterator insert(const_iterator pos, const_iterator first, const_iterator last) + bool get_ubjson_size_value(std::size_t& result) { - // insert only works for arrays - if (not is_array()) + switch (get_ignore_noop()) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); - } + case 'U': + { + std::uint8_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } - // check if iterator pos fits to this JSON value - if (pos.m_object != this) - { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); - } + case 'i': + { + std::int8_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } - // check if range iterators belong to the same JSON object - if (first.m_object != last.m_object) - { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); - } + case 'I': + { + std::int16_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } - if (first.m_object == this or last.m_object == this) - { - JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container")); - } + case 'l': + { + std::int32_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } - // insert to array and return iterator - iterator result(this); - result.m_it.array_iterator = m_value.array->insert( - pos.m_it.array_iterator, - first.m_it.array_iterator, - last.m_it.array_iterator); - return result; + case 'L': + { + std::int64_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"))); + } + } } /*! - @brief inserts elements + @brief determine the type and size for a container - Inserts elements from initializer list @a ilist before iterator @a pos. + In the optimized UBJSON format, a type and a size can be provided to allow + for a more compact representation. - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] ilist initializer list to insert the values from + @param[out] result pair of the size and the type - @throw type_error.309 if called on JSON values other than arrays; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` + @return whether pair creation completed + */ + bool get_ubjson_size_type(std::pair& result) + { + result.first = string_t::npos; // size + result.second = 0; // type - @return iterator pointing to the first element inserted, or @a pos if - `ilist` is empty + get_ignore_noop(); - @complexity Linear in `ilist.size()` plus linear in the distance between - @a pos and end of the container. + if (current == '$') + { + result.second = get(); // must not ignore 'N', because 'N' maybe the type + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "type"))) + { + return false; + } - @liveexample{The example shows how `insert()` is used.,insert__ilist} + get_ignore_noop(); + if (JSON_HEDLEY_UNLIKELY(current != '#')) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"))); + } - @since version 1.0.0 - */ - iterator insert(const_iterator pos, std::initializer_list ilist) - { - // insert only works for arrays - if (not is_array()) - { - JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); + return get_ubjson_size_value(result.first); } - // check if iterator pos fits to this JSON value - if (pos.m_object != this) + if (current == '#') { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + return get_ubjson_size_value(result.first); } - // insert to array and return iterator - iterator result(this); - result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist); - return result; + return true; } /*! - @brief inserts elements + @param prefix the previously read or set type prefix + @return whether value creation completed + */ + bool get_ubjson_value(const char_int_type prefix) + { + switch (prefix) + { + case std::char_traits::eof(): // EOF + return unexpect_eof(input_format_t::ubjson, "value"); - Inserts elements from range `[first, last)`. + case 'T': // true + return sax->boolean(true); + case 'F': // false + return sax->boolean(false); - @param[in] first begin of the range of elements to insert - @param[in] last end of the range of elements to insert + case 'Z': // null + return sax->null(); - @throw type_error.309 if called on JSON values other than objects; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if iterator @a first or @a last does does not - point to an object; example: `"iterators first and last must point to - objects"` - @throw invalid_iterator.210 if @a first and @a last do not belong to the - same JSON value; example: `"iterators do not fit"` + case 'U': + { + std::uint8_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_unsigned(number); + } - @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number - of elements to insert. + case 'i': + { + std::int8_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } - @liveexample{The example shows how `insert()` is used.,insert__range_object} + case 'I': + { + std::int16_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } - @since version 3.0.0 + case 'l': + { + std::int32_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'L': + { + std::int64_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'd': + { + float number{}; + return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast(number), ""); + } + + case 'D': + { + double number{}; + return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast(number), ""); + } + + case 'H': + { + return get_ubjson_high_precision_number(); + } + + case 'C': // char + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "char"))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(current > 127)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"))); + } + string_t s(1, static_cast(current)); + return sax->string(s); + } + + case 'S': // string + { + string_t s; + return get_ubjson_string(s) && sax->string(s); + } + + case '[': // array + return get_ubjson_array(); + + case '{': // object + return get_ubjson_object(); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"))); + } + } + } + + /*! + @return whether array creation completed */ - void insert(const_iterator first, const_iterator last) + bool get_ubjson_array() { - // insert only works for objects - if (not is_object()) + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); + return false; } - // check if range iterators belong to the same JSON object - if (first.m_object != last.m_object) + if (size_and_type.first != string_t::npos) { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); - } + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first))) + { + return false; + } - // passed iterators must belong to objects - if (not first.m_object->is_object() or not first.m_object->is_object()) + if (size_and_type.second != 0) + { + if (size_and_type.second != 'N') + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + } + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + } + } + } + else { - JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1)))) + { + return false; + } + + while (current != ']') + { + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false))) + { + return false; + } + get_ignore_noop(); + } } - m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); + return sax->end_array(); } /*! - @brief exchanges the values - - Exchanges the contents of the JSON value with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. + @return whether object creation completed + */ + bool get_ubjson_object() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) + { + return false; + } - @param[in,out] other JSON value to exchange the contents with + string_t key; + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first))) + { + return false; + } - @complexity Constant. + if (size_and_type.second != 0) + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + key.clear(); + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + key.clear(); + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1)))) + { + return false; + } - @liveexample{The example below shows how JSON values can be swapped with - `swap()`.,swap__reference} + while (current != '}') + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + get_ignore_noop(); + key.clear(); + } + } - @since version 1.0.0 - */ - void swap(reference other) noexcept ( - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value and - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value - ) - { - std::swap(m_type, other.m_type); - std::swap(m_value, other.m_value); - assert_invariant(); + return sax->end_object(); } - /*! - @brief exchanges the values - - Exchanges the contents of a JSON array with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. + // Note, no reader for UBJSON binary types is implemented because they do + // not exist - @param[in,out] other array to exchange the contents with + bool get_ubjson_high_precision_number() + { + // get size of following number string + std::size_t size{}; + auto res = get_ubjson_size_value(size); + if (JSON_HEDLEY_UNLIKELY(!res)) + { + return res; + } - @throw type_error.310 when JSON value is not an array; example: `"cannot - use swap() with string"` + // get number string + std::vector number_vector; + for (std::size_t i = 0; i < size; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "number"))) + { + return false; + } + number_vector.push_back(static_cast(current)); + } - @complexity Constant. + // parse number string + auto number_ia = detail::input_adapter(std::forward(number_vector)); + auto number_lexer = detail::lexer(std::move(number_ia), false); + const auto result_number = number_lexer.scan(); + const auto number_string = number_lexer.get_token_string(); + const auto result_remainder = number_lexer.scan(); - @liveexample{The example below shows how arrays can be swapped with - `swap()`.,swap__array_t} + using token_type = typename detail::lexer_base::token_type; - @since version 1.0.0 - */ - void swap(array_t& other) - { - // swap only works for arrays - if (is_array()) + if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) { - std::swap(*(m_value.array), other); + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"))); } - else + + switch (result_number) { - JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); + case token_type::value_integer: + return sax->number_integer(number_lexer.get_number_integer()); + case token_type::value_unsigned: + return sax->number_unsigned(number_lexer.get_number_unsigned()); + case token_type::value_float: + return sax->number_float(number_lexer.get_number_float(), std::move(number_string)); + default: + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"))); } } + /////////////////////// + // Utility functions // + /////////////////////// + /*! - @brief exchanges the values + @brief get next character from the input - Exchanges the contents of a JSON object with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a -'ve valued + `std::char_traits::eof()` in that case. - @param[in,out] other object to exchange the contents with + @return character read from the input + */ + char_int_type get() + { + ++chars_read; + return current = ia.get_character(); + } - @throw type_error.310 when JSON value is not an object; example: - `"cannot use swap() with string"` + /*! + @return character read from the input after ignoring all 'N' entries + */ + char_int_type get_ignore_noop() + { + do + { + get(); + } + while (current == 'N'); - @complexity Constant. + return current; + } - @liveexample{The example below shows how objects can be swapped with - `swap()`.,swap__object_t} + /* + @brief read a number from the input - @since version 1.0.0 + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[out] result number of type @a NumberType + + @return whether conversion completed + + @note This function needs to respect the system's endianess, because + bytes in CBOR, MessagePack, and UBJSON are stored in network order + (big endian) and therefore need reordering on little endian systems. */ - void swap(object_t& other) + template + bool get_number(const input_format_t format, NumberType& result) { - // swap only works for objects - if (is_object()) - { - std::swap(*(m_value.object), other); - } - else + // step 1: read input into array with system's byte order + std::array vec; + for (std::size_t i = 0; i < sizeof(NumberType); ++i) { - JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number"))) + { + return false; + } + + // reverse byte order prior to conversion if necessary + if (is_little_endian != InputIsLittleEndian) + { + vec[sizeof(NumberType) - i - 1] = static_cast(current); + } + else + { + vec[i] = static_cast(current); // LCOV_EXCL_LINE + } } + + // step 2: convert array into number of type T and return + std::memcpy(&result, vec.data(), sizeof(NumberType)); + return true; } /*! - @brief exchanges the values + @brief create a string by reading characters from the input - Exchanges the contents of a JSON string with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of characters to read + @param[out] result string created by reading @a len bytes - @param[in,out] other string to exchange the contents with + @return whether string creation completed - @throw type_error.310 when JSON value is not a string; example: `"cannot - use swap() with boolean"` + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of string memory. + */ + template + bool get_string(const input_format_t format, + const NumberType len, + string_t& result) + { + bool success = true; + for (NumberType i = 0; i < len; i++) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "string"))) + { + success = false; + break; + } + result.push_back(static_cast(current)); + }; + return success; + } - @complexity Constant. + /*! + @brief create a byte array by reading bytes from the input - @liveexample{The example below shows how strings can be swapped with - `swap()`.,swap__string_t} + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of bytes to read + @param[out] result byte array created by reading @a len bytes - @since version 1.0.0 + @return whether byte array creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of memory. */ - void swap(string_t& other) + template + bool get_binary(const input_format_t format, + const NumberType len, + binary_t& result) { - // swap only works for strings - if (is_string()) + bool success = true; + for (NumberType i = 0; i < len; i++) { - std::swap(*(m_value.string), other); + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "binary"))) + { + success = false; + break; + } + result.push_back(static_cast(current)); } - else + return success; + } + + /*! + @param[in] format the current format (for diagnostics) + @param[in] context further context information (for diagnostics) + @return whether the last read character is not EOF + */ + JSON_HEDLEY_NON_NULL(3) + bool unexpect_eof(const input_format_t format, const char* context) const + { + if (JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) { - JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); + return sax->parse_error(chars_read, "", + parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context))); } + return true; } - /// @} + /*! + @return a string representation of the last read byte + */ + std::string get_token_string() const + { + std::array cr{{}}; + (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(current)); + return std::string{cr.data()}; + } - public: - ////////////////////////////////////////// - // lexicographical comparison operators // - ////////////////////////////////////////// + /*! + @param[in] format the current format + @param[in] detail a detailed error message + @param[in] context further context information + @return a message string to use in the parse_error exceptions + */ + std::string exception_message(const input_format_t format, + const std::string& detail, + const std::string& context) const + { + std::string error_msg = "syntax error while parsing "; - /// @name lexicographical comparison operators - /// @{ + switch (format) + { + case input_format_t::cbor: + error_msg += "CBOR"; + break; - /*! - @brief comparison: equal + case input_format_t::msgpack: + error_msg += "MessagePack"; + break; - Compares two JSON values for equality according to the following rules: - - Two JSON values are equal if (1) they are from the same type and (2) - their stored values are the same according to their respective - `operator==`. - - Integer and floating-point numbers are automatically converted before - comparison. Floating-point numbers are compared indirectly: two - floating-point numbers `f1` and `f2` are considered equal if neither - `f1 > f2` nor `f2 > f1` holds. Note than two NaN values are always - treated as unequal. - - Two JSON null values are equal. + case input_format_t::ubjson: + error_msg += "UBJSON"; + break; - @note NaN values never compare equal to themselves or to other NaN values. + case input_format_t::bson: + error_msg += "BSON"; + break; - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether the values @a lhs and @a rhs are equal + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } - @complexity Linear. + return error_msg + " " + context + ": " + detail; + } - @liveexample{The example demonstrates comparing several JSON - types.,operator__equal} + private: + /// input adapter + InputAdapterType ia; - @since version 1.0.0 - */ - friend bool operator==(const_reference lhs, const_reference rhs) noexcept - { - const auto lhs_type = lhs.type(); - const auto rhs_type = rhs.type(); + /// the current character + char_int_type current = std::char_traits::eof(); - if (lhs_type == rhs_type) - { - switch (lhs_type) - { - case value_t::array: - { - return *lhs.m_value.array == *rhs.m_value.array; - } - case value_t::object: - { - return *lhs.m_value.object == *rhs.m_value.object; - } - case value_t::null: - { - return true; - } - case value_t::string: - { - return *lhs.m_value.string == *rhs.m_value.string; - } - case value_t::boolean: - { - return lhs.m_value.boolean == rhs.m_value.boolean; - } - case value_t::number_integer: - { - return lhs.m_value.number_integer == rhs.m_value.number_integer; - } - case value_t::number_unsigned: - { - return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; - } - case value_t::number_float: - { - return lhs.m_value.number_float == rhs.m_value.number_float; - } - default: - { - return false; - } - } - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) - { - return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); - } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) - { - return lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned); - } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) - { - return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) - { - return lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned); - } + /// the number of characters read + std::size_t chars_read = 0; - return false; - } + /// whether we can assume little endianess + const bool is_little_endian = little_endianess(); - /*! - @brief comparison: equal - @copydoc operator==(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept - { - return (lhs == basic_json(rhs)); - } + /// the SAX parser + json_sax_t* sax = nullptr; +}; +} // namespace detail +} // namespace nlohmann - /*! - @brief comparison: equal - @copydoc operator==(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept - { - return (basic_json(lhs) == rhs); - } +// #include - /*! - @brief comparison: not equal +// #include - Compares two JSON values for inequality by calculating `not (lhs == rhs)`. +// #include - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether the values @a lhs and @a rhs are not equal - @complexity Linear. +#include // isfinite +#include // uint8_t +#include // function +#include // string +#include // move +#include // vector - @liveexample{The example demonstrates comparing several JSON - types.,operator__notequal} +// #include - @since version 1.0.0 - */ - friend bool operator!=(const_reference lhs, const_reference rhs) noexcept +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +//////////// +// parser // +//////////// + +enum class parse_event_t : uint8_t +{ + /// the parser read `{` and started to process a JSON object + object_start, + /// the parser read `}` and finished processing a JSON object + object_end, + /// the parser read `[` and started to process a JSON array + array_start, + /// the parser read `]` and finished processing a JSON array + array_end, + /// the parser read a key of a value in an object + key, + /// the parser finished reading a JSON value + value +}; + +template +using parser_callback_t = + std::function; + +/*! +@brief syntax analysis + +This class implements a recursive descent parser. +*/ +template +class parser +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using lexer_t = lexer; + using token_type = typename lexer_t::token_type; + + public: + /// a parser reading from an input adapter + explicit parser(InputAdapterType&& adapter, + const parser_callback_t cb = nullptr, + const bool allow_exceptions_ = true, + const bool skip_comments = false) + : callback(cb) + , m_lexer(std::move(adapter), skip_comments) + , allow_exceptions(allow_exceptions_) { - return not (lhs == rhs); + // read first token + get_token(); } /*! - @brief comparison: not equal - @copydoc operator!=(const_reference, const_reference) + @brief public parser interface + + @param[in] strict whether to expect the last token to be EOF + @param[in,out] result parsed JSON value + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails */ - template::value, int>::type = 0> - friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept + void parse(const bool strict, BasicJsonType& result) { - return (lhs != basic_json(rhs)); + if (callback) + { + json_sax_dom_callback_parser sdp(result, callback, allow_exceptions); + sax_parse_internal(&sdp); + result.assert_invariant(); + + // in strict mode, input must be completely read + if (strict && (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"))); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + + // set top-level value to null if it was discarded by the callback + // function + if (result.is_discarded()) + { + result = nullptr; + } + } + else + { + json_sax_dom_parser sdp(result, allow_exceptions); + sax_parse_internal(&sdp); + result.assert_invariant(); + + // in strict mode, input must be completely read + if (strict && (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"))); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + } } /*! - @brief comparison: not equal - @copydoc operator!=(const_reference, const_reference) + @brief public accept interface + + @param[in] strict whether to expect the last token to be EOF + @return whether the input is a proper JSON text */ - template::value, int>::type = 0> - friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept + bool accept(const bool strict = true) { - return (basic_json(lhs) != rhs); + json_sax_acceptor sax_acceptor; + return sax_parse(&sax_acceptor, strict); } - /*! - @brief comparison: less than - - Compares whether one JSON value @a lhs is less than another JSON value @a - rhs according to the following rules: - - If @a lhs and @a rhs have the same type, the values are compared using - the default `<` operator. - - Integer and floating-point numbers are automatically converted before - comparison - - In case @a lhs and @a rhs have different types, the values are ignored - and the order of the types is considered, see - @ref operator<(const value_t, const value_t). - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is less than @a rhs + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse(SAX* sax, const bool strict = true) + { + (void)detail::is_sax_static_asserts {}; + const bool result = sax_parse_internal(sax); - @complexity Linear. + // strict mode: next byte must be EOF + if (result && strict && (get_token() != token_type::end_of_input)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"))); + } - @liveexample{The example demonstrates comparing several JSON - types.,operator__less} + return result; + } - @since version 1.0.0 - */ - friend bool operator<(const_reference lhs, const_reference rhs) noexcept + private: + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse_internal(SAX* sax) { - const auto lhs_type = lhs.type(); - const auto rhs_type = rhs.type(); + // stack to remember the hierarchy of structured values we are parsing + // true = array; false = object + std::vector states; + // value to avoid a goto (see comment where set to true) + bool skip_to_state_evaluation = false; - if (lhs_type == rhs_type) + while (true) { - switch (lhs_type) + if (!skip_to_state_evaluation) { - case value_t::array: - { - return (*lhs.m_value.array) < (*rhs.m_value.array); - } - case value_t::object: - { - return *lhs.m_value.object < *rhs.m_value.object; - } - case value_t::null: - { - return false; - } - case value_t::string: - { - return *lhs.m_value.string < *rhs.m_value.string; - } - case value_t::boolean: - { - return lhs.m_value.boolean < rhs.m_value.boolean; - } - case value_t::number_integer: - { - return lhs.m_value.number_integer < rhs.m_value.number_integer; - } - case value_t::number_unsigned: - { - return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned; - } - case value_t::number_float: - { - return lhs.m_value.number_float < rhs.m_value.number_float; - } - default: + // invariant: get_token() was called before each iteration + switch (last_token) { - return false; - } - } - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) - { - return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); + case token_type::begin_object: + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1)))) + { + return false; + } + + // closing } -> we are done + if (get_token() == token_type::end_object) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) + { + return false; + } + break; + } + + // parse key + if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::value_string, "object key"))); + } + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::name_separator, "object separator"))); + } + + // remember we are now inside an object + states.push_back(false); + + // parse values + get_token(); + continue; + } + + case token_type::begin_array: + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1)))) + { + return false; + } + + // closing ] -> we are done + if (get_token() == token_type::end_array) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) + { + return false; + } + break; + } + + // remember we are now inside an array + states.push_back(true); + + // parse values (no need to call get_token) + continue; + } + + case token_type::value_float: + { + const auto res = m_lexer.get_number_float(); + + if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res))) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'")); + } + + if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string()))) + { + return false; + } + + break; + } + + case token_type::literal_false: + { + if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false))) + { + return false; + } + break; + } + + case token_type::literal_null: + { + if (JSON_HEDLEY_UNLIKELY(!sax->null())) + { + return false; + } + break; + } + + case token_type::literal_true: + { + if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true))) + { + return false; + } + break; + } + + case token_type::value_integer: + { + if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer()))) + { + return false; + } + break; + } + + case token_type::value_string: + { + if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string()))) + { + return false; + } + break; + } + + case token_type::value_unsigned: + { + if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned()))) + { + return false; + } + break; + } + + case token_type::parse_error: + { + // using "uninitialized" to avoid "expected" message + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::uninitialized, "value"))); + } + + default: // the last token was unexpected + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::literal_or_value, "value"))); + } + } + } + else + { + skip_to_state_evaluation = false; + } + + // we reached this line after we successfully parsed a value + if (states.empty()) + { + // empty stack: we reached the end of the hierarchy: done + return true; + } + + if (states.back()) // array + { + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse a new value + get_token(); + continue; + } + + // closing ] + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array)) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) + { + return false; + } + + // We are done with this array. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + JSON_ASSERT(!states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_array, "array"))); + } + else // object + { + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse key + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::value_string, "object key"))); + } + + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::name_separator, "object separator"))); + } + + // parse values + get_token(); + continue; + } + + // closing } + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object)) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) + { + return false; + } + + // We are done with this object. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + JSON_ASSERT(!states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_object, "object"))); + } } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + } + + /// get next token from lexer + token_type get_token() + { + return last_token = m_lexer.scan(); + } + + std::string exception_message(const token_type expected, const std::string& context) + { + std::string error_msg = "syntax error "; + + if (!context.empty()) { - return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; + error_msg += "while parsing " + context + " "; } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + + error_msg += "- "; + + if (last_token == token_type::parse_error) { - return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); + error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" + + m_lexer.get_token_string() + "'"; } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + else { - return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); + error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token)); } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + + if (expected != token_type::uninitialized) { - return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; + error_msg += "; expected " + std::string(lexer_t::token_type_name(expected)); } - // We only reach this line if we cannot compare values. In that case, - // we compare types. Note we have to call the operator explicitly, - // because MSVC has problems otherwise. - return operator<(lhs_type, rhs_type); + return error_msg; } - /*! - @brief comparison: less than - @copydoc operator<(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept - { - return (lhs < basic_json(rhs)); - } + private: + /// callback function + const parser_callback_t callback = nullptr; + /// the type of the last read token + token_type last_token = token_type::uninitialized; + /// the lexer + lexer_t m_lexer; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; +} // namespace detail +} // namespace nlohmann - /*! - @brief comparison: less than - @copydoc operator<(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept - { - return (basic_json(lhs) < rhs); - } +// #include - /*! - @brief comparison: less than or equal - Compares whether one JSON value @a lhs is less than or equal to another - JSON value by calculating `not (rhs < lhs)`. +// #include - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is less than or equal to @a rhs - @complexity Linear. +#include // ptrdiff_t +#include // numeric_limits - @liveexample{The example demonstrates comparing several JSON - types.,operator__greater} +namespace nlohmann +{ +namespace detail +{ +/* +@brief an iterator for primitive JSON types - @since version 1.0.0 - */ - friend bool operator<=(const_reference lhs, const_reference rhs) noexcept +This class models an iterator for primitive JSON types (boolean, number, +string). It's only purpose is to allow the iterator/const_iterator classes +to "iterate" over primitive values. Internally, the iterator is modeled by +a `difference_type` variable. Value begin_value (`0`) models the begin, +end_value (`1`) models past the end. +*/ +class primitive_iterator_t +{ + private: + using difference_type = std::ptrdiff_t; + static constexpr difference_type begin_value = 0; + static constexpr difference_type end_value = begin_value + 1; + + /// iterator as signed integer type + difference_type m_it = (std::numeric_limits::min)(); + + public: + constexpr difference_type get_value() const noexcept { - return not (rhs < lhs); + return m_it; } - /*! - @brief comparison: less than or equal - @copydoc operator<=(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept + /// set iterator to a defined beginning + void set_begin() noexcept { - return (lhs <= basic_json(rhs)); + m_it = begin_value; } - /*! - @brief comparison: less than or equal - @copydoc operator<=(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept + /// set iterator to a defined past the end + void set_end() noexcept { - return (basic_json(lhs) <= rhs); + m_it = end_value; } - /*! - @brief comparison: greater than - - Compares whether one JSON value @a lhs is greater than another - JSON value by calculating `not (lhs <= rhs)`. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is greater than to @a rhs - - @complexity Linear. + /// return whether the iterator can be dereferenced + constexpr bool is_begin() const noexcept + { + return m_it == begin_value; + } - @liveexample{The example demonstrates comparing several JSON - types.,operator__lessequal} + /// return whether the iterator is at end + constexpr bool is_end() const noexcept + { + return m_it == end_value; + } - @since version 1.0.0 - */ - friend bool operator>(const_reference lhs, const_reference rhs) noexcept + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { - return not (lhs <= rhs); + return lhs.m_it == rhs.m_it; } - /*! - @brief comparison: greater than - @copydoc operator>(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { - return (lhs > basic_json(rhs)); + return lhs.m_it < rhs.m_it; } - /*! - @brief comparison: greater than - @copydoc operator>(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept + primitive_iterator_t operator+(difference_type n) noexcept { - return (basic_json(lhs) > rhs); + auto result = *this; + result += n; + return result; } - /*! - @brief comparison: greater than or equal - - Compares whether one JSON value @a lhs is greater than or equal to another - JSON value by calculating `not (lhs < rhs)`. + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it - rhs.m_it; + } - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is greater than or equal to @a rhs + primitive_iterator_t& operator++() noexcept + { + ++m_it; + return *this; + } - @complexity Linear. + primitive_iterator_t const operator++(int) noexcept + { + auto result = *this; + ++m_it; + return result; + } - @liveexample{The example demonstrates comparing several JSON - types.,operator__greaterequal} + primitive_iterator_t& operator--() noexcept + { + --m_it; + return *this; + } - @since version 1.0.0 - */ - friend bool operator>=(const_reference lhs, const_reference rhs) noexcept + primitive_iterator_t const operator--(int) noexcept { - return not (lhs < rhs); + auto result = *this; + --m_it; + return result; } - /*! - @brief comparison: greater than or equal - @copydoc operator>=(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept + primitive_iterator_t& operator+=(difference_type n) noexcept { - return (lhs >= basic_json(rhs)); + m_it += n; + return *this; } - /*! - @brief comparison: greater than or equal - @copydoc operator>=(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept + primitive_iterator_t& operator-=(difference_type n) noexcept { - return (basic_json(lhs) >= rhs); + m_it -= n; + return *this; } +}; +} // namespace detail +} // namespace nlohmann - /// @} - private: - ///////////////////// - // output adapters // - ///////////////////// +namespace nlohmann +{ +namespace detail +{ +/*! +@brief an iterator value - /// abstract output adapter interface - template - class output_adapter - { - public: - virtual void write_character(CharType c) = 0; - virtual void write_characters(const CharType* s, size_t length) = 0; - virtual ~output_adapter() {} +@note This structure could easily be a union, but MSVC currently does not allow +unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. +*/ +template struct internal_iterator +{ + /// iterator for JSON objects + typename BasicJsonType::object_t::iterator object_iterator {}; + /// iterator for JSON arrays + typename BasicJsonType::array_t::iterator array_iterator {}; + /// generic iterator for all other types + primitive_iterator_t primitive_iterator {}; +}; +} // namespace detail +} // namespace nlohmann - static std::shared_ptr> create(std::vector& vec) - { - return std::shared_ptr(new output_vector_adapter(vec)); - } +// #include - static std::shared_ptr> create(std::ostream& s) - { - return std::shared_ptr(new output_stream_adapter(s)); - } - static std::shared_ptr> create(std::string& s) - { - return std::shared_ptr(new output_string_adapter(s)); - } - }; +#include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next +#include // conditional, is_const, remove_const - /// a type to simplify interfaces - template - using output_adapter_t = std::shared_ptr>; +// #include - /// output adapter for byte vectors - template - class output_vector_adapter : public output_adapter - { - public: - output_vector_adapter(std::vector& vec) - : v(vec) - {} +// #include - void write_character(CharType c) override - { - v.push_back(c); - } +// #include - void write_characters(const CharType* s, size_t length) override - { - std::copy(s, s + length, std::back_inserter(v)); - } +// #include - private: - std::vector& v; - }; +// #include - /// putput adatpter for output streams - template - class output_stream_adapter : public output_adapter - { - public: - output_stream_adapter(std::basic_ostream& s) - : stream(s) - {} +// #include - void write_character(CharType c) override - { - stream.put(c); - } +// #include - void write_characters(const CharType* s, size_t length) override - { - stream.write(s, static_cast(length)); - } - private: - std::basic_ostream& stream; - }; +namespace nlohmann +{ +namespace detail +{ +// forward declare, to be able to friend it later on +template class iteration_proxy; +template class iteration_proxy_value; - /// output adapter for basic_string - template - class output_string_adapter : public output_adapter +/*! +@brief a template for a bidirectional iterator for the @ref basic_json class +This class implements a both iterators (iterator and const_iterator) for the +@ref basic_json class. +@note An iterator is called *initialized* when a pointer to a JSON value has + been set (e.g., by a constructor or a copy assignment). If the iterator is + default-constructed, it is *uninitialized* and most methods are undefined. + **The library uses assertions to detect calls on uninitialized iterators.** +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +@since version 1.0.0, simplified in version 2.0.9, change to bidirectional + iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) +*/ +template +class iter_impl +{ + /// allow basic_json to access private members + friend iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; + friend BasicJsonType; + friend iteration_proxy; + friend iteration_proxy_value; + + using object_t = typename BasicJsonType::object_t; + using array_t = typename BasicJsonType::array_t; + // make sure BasicJsonType is basic_json or const basic_json + static_assert(is_basic_json::type>::value, + "iter_impl only accepts (const) basic_json"); + + public: + + /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. + /// The C++ Standard has never required user-defined iterators to derive from std::iterator. + /// A user-defined iterator should provide publicly accessible typedefs named + /// iterator_category, value_type, difference_type, pointer, and reference. + /// Note that value_type is required to be non-const, even for constant iterators. + using iterator_category = std::bidirectional_iterator_tag; + + /// the type of the values when the iterator is dereferenced + using value_type = typename BasicJsonType::value_type; + /// a type to represent differences between iterators + using difference_type = typename BasicJsonType::difference_type; + /// defines a pointer to the type iterated over (value_type) + using pointer = typename std::conditional::value, + typename BasicJsonType::const_pointer, + typename BasicJsonType::pointer>::type; + /// defines a reference to the type iterated over (value_type) + using reference = + typename std::conditional::value, + typename BasicJsonType::const_reference, + typename BasicJsonType::reference>::type; + + /// default constructor + iter_impl() = default; + + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ + explicit iter_impl(pointer object) noexcept : m_object(object) { - public: - output_string_adapter(std::string& s) - : str(s) - {} + JSON_ASSERT(m_object != nullptr); - void write_character(CharType c) override + switch (m_object->m_type) { - str.push_back(c); - } + case value_t::object: + { + m_it.object_iterator = typename object_t::iterator(); + break; + } - void write_characters(const CharType* s, size_t length) override - { - str.append(s, length); + case value_t::array: + { + m_it.array_iterator = typename array_t::iterator(); + break; + } + + default: + { + m_it.primitive_iterator = primitive_iterator_t(); + break; + } } + } - private: - std::basic_string& str; - }; + /*! + @note The conventional copy constructor and copy assignment are implicitly + defined. Combined with the following converting constructor and + assignment, they support: (1) copy from iterator to iterator, (2) + copy from const iterator to const iterator, and (3) conversion from + iterator to const iterator. However conversion from const iterator + to iterator is not defined. + */ + /*! + @brief const copy constructor + @param[in] other const iterator to copy from + @note This copy constructor had to be defined explicitly to circumvent a bug + occurring on msvc v19.0 compiler (VS 2015) debug build. For more + information refer to: https://github.com/nlohmann/json/issues/1608 + */ + iter_impl(const iter_impl& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} - /////////////////// - // serialization // - /////////////////// + /*! + @brief converting assignment + @param[in] other const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl& other) noexcept + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } - /// @name serialization - /// @{ + /*! + @brief converting constructor + @param[in] other non-const iterator to copy from + @note It is not checked whether @a other is initialized. + */ + iter_impl(const iter_impl::type>& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} - private: /*! - @brief wrapper around the serialization functions + @brief converting assignment + @param[in] other non-const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. */ - class serializer + iter_impl& operator=(const iter_impl::type>& other) noexcept { - private: - serializer(const serializer&) = delete; - serializer& operator=(const serializer&) = delete; + m_object = other.m_object; + m_it = other.m_it; + return *this; + } - public: - /*! - @param[in] s output stream to serialize to - @param[in] ichar indentation character to use - */ - serializer(output_adapter_t s, const char ichar) - : o(s), loc(std::localeconv()), - thousands_sep(!loc->thousands_sep ? '\0' : loc->thousands_sep[0]), - decimal_point(!loc->decimal_point ? '\0' : loc->decimal_point[0]), - indent_char(ichar), indent_string(512, indent_char) - {} + private: + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_begin() noexcept + { + JSON_ASSERT(m_object != nullptr); - /*! - @brief internal implementation of the serialization function + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } - This function is called by the public member function dump and - organizes the serialization internally. The indentation level is - propagated as additional parameter. In case of arrays and objects, the - function is called recursively. + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->begin(); + break; + } - - strings and object keys are escaped using `escape_string()` - - integer numbers are converted implicitly via `operator<<` - - floating-point numbers are converted to a string using `"%g"` format + case value_t::null: + { + // set to end so begin()==end() is true: null is empty + m_it.primitive_iterator.set_end(); + break; + } - @param[in] val value to serialize - @param[in] pretty_print whether the output shall be pretty-printed - @param[in] indent_step the indent level - @param[in] current_indent the current indent level (only used internally) - */ - void dump(const basic_json& val, - const bool pretty_print, - const unsigned int indent_step, - const unsigned int current_indent = 0) - { - switch (val.m_type) + default: { - case value_t::object: - { - if (val.m_value.object->empty()) - { - o->write_characters("{}", 2); - return; - } + m_it.primitive_iterator.set_begin(); + break; + } + } + } - if (pretty_print) - { - o->write_characters("{\n", 2); + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_end() noexcept + { + JSON_ASSERT(m_object != nullptr); - // variable to hold indentation for recursive calls - const auto new_indent = current_indent + indent_step; - if (indent_string.size() < new_indent) - { - indent_string.resize(new_indent, ' '); - } + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->end(); + break; + } - // first n-1 elements - auto i = val.m_value.object->cbegin(); - for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) - { - o->write_characters(indent_string.c_str(), new_indent); - o->write_character('\"'); - dump_escaped(i->first); - o->write_characters("\": ", 3); - dump(i->second, true, indent_step, new_indent); - o->write_characters(",\n", 2); - } + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->end(); + break; + } - // last element - assert(i != val.m_value.object->cend()); - o->write_characters(indent_string.c_str(), new_indent); - o->write_character('\"'); - dump_escaped(i->first); - o->write_characters("\": ", 3); - dump(i->second, true, indent_step, new_indent); + default: + { + m_it.primitive_iterator.set_end(); + break; + } + } + } - o->write_character('\n'); - o->write_characters(indent_string.c_str(), current_indent); - o->write_character('}'); - } - else - { - o->write_character('{'); + public: + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator*() const + { + JSON_ASSERT(m_object != nullptr); - // first n-1 elements - auto i = val.m_value.object->cbegin(); - for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) - { - o->write_character('\"'); - dump_escaped(i->first); - o->write_characters("\":", 2); - dump(i->second, false, indent_step, current_indent); - o->write_character(','); - } + switch (m_object->m_type) + { + case value_t::object: + { + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); + return m_it.object_iterator->second; + } - // last element - assert(i != val.m_value.object->cend()); - o->write_character('\"'); - dump_escaped(i->first); - o->write_characters("\":", 2); - dump(i->second, false, indent_step, current_indent); + case value_t::array: + { + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); + return *m_it.array_iterator; + } - o->write_character('}'); - } + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value")); - return; + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return *m_object; } - case value_t::array: - { - if (val.m_value.array->empty()) - { - o->write_characters("[]", 2); - return; - } + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } - if (pretty_print) - { - o->write_characters("[\n", 2); + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const + { + JSON_ASSERT(m_object != nullptr); - // variable to hold indentation for recursive calls - const auto new_indent = current_indent + indent_step; - if (indent_string.size() < new_indent) - { - indent_string.resize(new_indent, ' '); - } + switch (m_object->m_type) + { + case value_t::object: + { + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } - // first n-1 elements - for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) - { - o->write_characters(indent_string.c_str(), new_indent); - dump(*i, true, indent_step, new_indent); - o->write_characters(",\n", 2); - } + case value_t::array: + { + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } - // last element - assert(not val.m_value.array->empty()); - o->write_characters(indent_string.c_str(), new_indent); - dump(val.m_value.array->back(), true, indent_step, new_indent); + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return m_object; + } - o->write_character('\n'); - o->write_characters(indent_string.c_str(), current_indent); - o->write_character(']'); - } - else - { - o->write_character('['); - - // first n-1 elements - for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) - { - dump(*i, false, indent_step, current_indent); - o->write_character(','); - } - - // last element - assert(not val.m_value.array->empty()); - dump(val.m_value.array->back(), false, indent_step, current_indent); - - o->write_character(']'); - } - - return; - } - - case value_t::string: - { - o->write_character('\"'); - dump_escaped(*val.m_value.string); - o->write_character('\"'); - return; - } - - case value_t::boolean: - { - if (val.m_value.boolean) - { - o->write_characters("true", 4); - } - else - { - o->write_characters("false", 5); - } - return; - } - - case value_t::number_integer: - { - dump_integer(val.m_value.number_integer); - return; - } - - case value_t::number_unsigned: - { - dump_integer(val.m_value.number_unsigned); - return; - } - - case value_t::number_float: - { - dump_float(val.m_value.number_float); - return; - } - - case value_t::discarded: - { - o->write_characters("", 11); - return; - } - - case value_t::null: - { - o->write_characters("null", 4); - return; - } + JSON_THROW(invalid_iterator::create(214, "cannot get value")); } } + } - private: - /*! - @brief calculates the extra space to escape a JSON string + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator++(int) + { + auto result = *this; + ++(*this); + return result; + } - @param[in] s the string to escape - @return the number of characters required to escape string @a s + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator++() + { + JSON_ASSERT(m_object != nullptr); - @complexity Linear in the length of string @a s. - */ - static std::size_t extra_space(const string_t& s) noexcept + switch (m_object->m_type) { - return std::accumulate(s.begin(), s.end(), size_t{}, - [](size_t res, typename string_t::value_type c) + case value_t::object: { - switch (c) - { - case '"': - case '\\': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - { - // from c (1 byte) to \x (2 bytes) - return res + 1; - } + std::advance(m_it.object_iterator, 1); + break; + } - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x0b: - case 0x0e: - case 0x0f: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: - { - // from c (1 byte) to \uxxxx (6 bytes) - return res + 5; - } + case value_t::array: + { + std::advance(m_it.array_iterator, 1); + break; + } - default: - { - return res; - } - } - }); + default: + { + ++m_it.primitive_iterator; + break; + } } - /*! - @brief dump escaped string + return *this; + } - Escape a string by replacing certain special characters by a sequence - of an escape character (backslash) and another character and other - control characters by a sequence of "\u" followed by a four-digit hex - representation. The escaped string is written to output stream @a o. + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator--(int) + { + auto result = *this; + --(*this); + return result; + } - @param[in] s the string to escape + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator--() + { + JSON_ASSERT(m_object != nullptr); - @complexity Linear in the length of string @a s. - */ - void dump_escaped(const string_t& s) const + switch (m_object->m_type) { - const auto space = extra_space(s); - if (space == 0) + case value_t::object: { - o->write_characters(s.c_str(), s.size()); - return; + std::advance(m_it.object_iterator, -1); + break; } - // create a result string of necessary size - string_t result(s.size() + space, '\\'); - std::size_t pos = 0; - - for (const auto& c : s) + case value_t::array: { - switch (c) - { - // quotation mark (0x22) - case '"': - { - result[pos + 1] = '"'; - pos += 2; - break; - } - - // reverse solidus (0x5c) - case '\\': - { - // nothing to change - pos += 2; - break; - } + std::advance(m_it.array_iterator, -1); + break; + } - // backspace (0x08) - case '\b': - { - result[pos + 1] = 'b'; - pos += 2; - break; - } + default: + { + --m_it.primitive_iterator; + break; + } + } - // formfeed (0x0c) - case '\f': - { - result[pos + 1] = 'f'; - pos += 2; - break; - } + return *this; + } - // newline (0x0a) - case '\n': - { - result[pos + 1] = 'n'; - pos += 2; - break; - } + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator==(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + } - // carriage return (0x0d) - case '\r': - { - result[pos + 1] = 'r'; - pos += 2; - break; - } + JSON_ASSERT(m_object != nullptr); - // horizontal tab (0x09) - case '\t': - { - result[pos + 1] = 't'; - pos += 2; - break; - } + switch (m_object->m_type) + { + case value_t::object: + return (m_it.object_iterator == other.m_it.object_iterator); - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x0b: - case 0x0e: - case 0x0f: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: - { - // convert a number 0..15 to its hex representation - // (0..f) - static const char hexify[16] = - { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' - }; - - // print character c as \uxxxx - for (const char m : - { 'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f] - }) - { - result[++pos] = m; - } + case value_t::array: + return (m_it.array_iterator == other.m_it.array_iterator); - ++pos; - break; - } + default: + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } + } - default: - { - // all other characters are added as-is - result[pos++] = c; - break; - } - } - } + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator!=(const iter_impl& other) const + { + return !operator==(other); + } - assert(pos == s.size() + space); - o->write_characters(result.c_str(), result.size()); + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); } - /*! - @brief dump an integer + JSON_ASSERT(m_object != nullptr); - Dump a given integer to output stream @a o. Works internally with - @a number_buffer. - - @param[in] x integer number (signed or unsigned) to dump - @tparam NumberType either @a number_integer_t or @a number_unsigned_t - */ - template::value or - std::is_same::value, int> = 0> - void dump_integer(NumberType x) + switch (m_object->m_type) { - // special case for "0" - if (x == 0) - { - o->write_character('0'); - return; - } - - const bool is_negative = x < 0; - size_t i = 0; + case value_t::object: + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); - // spare 1 byte for '\0' - while (x != 0 and i < number_buffer.size() - 1) - { - const auto digit = std::labs(static_cast(x % 10)); - number_buffer[i++] = static_cast('0' + digit); - x /= 10; - } + case value_t::array: + return (m_it.array_iterator < other.m_it.array_iterator); - // make sure the number has been processed completely - assert(x == 0); + default: + return (m_it.primitive_iterator < other.m_it.primitive_iterator); + } + } - if (is_negative) - { - // make sure there is capacity for the '-' - assert(i < number_buffer.size() - 2); - number_buffer[i++] = '-'; - } + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const iter_impl& other) const + { + return !other.operator < (*this); + } - std::reverse(number_buffer.begin(), number_buffer.begin() + i); - o->write_characters(number_buffer.data(), i); - } + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>(const iter_impl& other) const + { + return !operator<=(other); + } - /*! - @brief dump a floating-point number + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>=(const iter_impl& other) const + { + return !operator<(other); + } - Dump a given floating-point number to output stream @a o. Works - internally with @a number_buffer. + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator+=(difference_type i) + { + JSON_ASSERT(m_object != nullptr); - @param[in] x floating-point number to dump - */ - void dump_float(number_float_t x) + switch (m_object->m_type) { - // NaN / inf - if (not std::isfinite(x) or std::isnan(x)) + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + + case value_t::array: { - o->write_characters("null", 4); - return; + std::advance(m_it.array_iterator, i); + break; } - // special case for 0.0 and -0.0 - if (x == 0) + default: { - if (std::signbit(x)) - { - o->write_characters("-0.0", 4); - } - else - { - o->write_characters("0.0", 3); - } - return; + m_it.primitive_iterator += i; + break; } + } - // get number of digits for a text -> float -> text round-trip - static constexpr auto d = std::numeric_limits::digits10; + return *this; + } - // the actual conversion - std::ptrdiff_t len = snprintf(number_buffer.data(), number_buffer.size(), - "%.*g", d, x); + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator-=(difference_type i) + { + return operator+=(-i); + } - // negative value indicates an error - assert(len > 0); - // check if buffer was large enough - assert(static_cast(len) < number_buffer.size()); + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator+(difference_type i) const + { + auto result = *this; + result += i; + return result; + } - // erase thousands separator - if (thousands_sep != '\0') - { - const auto end = std::remove(number_buffer.begin(), - number_buffer.begin() + len, - thousands_sep); - std::fill(end, number_buffer.end(), '\0'); - assert((end - number_buffer.begin()) <= len); - len = (end - number_buffer.begin()); - } + /*! + @brief addition of distance and iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + friend iter_impl operator+(difference_type i, const iter_impl& it) + { + auto result = it; + result += i; + return result; + } - // convert decimal point to '.' - if (decimal_point != '\0' and decimal_point != '.') - { - for (auto& c : number_buffer) - { - if (c == decimal_point) - { - c = '.'; - break; - } - } - } + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator-(difference_type i) const + { + auto result = *this; + result -= i; + return result; + } - o->write_characters(number_buffer.data(), static_cast(len)); + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + difference_type operator-(const iter_impl& other) const + { + JSON_ASSERT(m_object != nullptr); - // determine if need to append ".0" - const bool value_is_int_like = std::none_of(number_buffer.begin(), - number_buffer.begin() + len + 1, - [](char c) - { - return c == '.' or c == 'e'; - }); + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); - if (value_is_int_like) - { - o->write_characters(".0", 2); - } + case value_t::array: + return m_it.array_iterator - other.m_it.array_iterator; + + default: + return m_it.primitive_iterator - other.m_it.primitive_iterator; } + } - private: - /// the output of the serializer - output_adapter_t o = nullptr; + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator[](difference_type n) const + { + JSON_ASSERT(m_object != nullptr); - /// a (hopefully) large enough character buffer - std::array number_buffer{{}}; + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); - /// the locale - const std::lconv* loc = nullptr; - /// the locale's thousand separator character - const char thousands_sep = '\0'; - /// the locale's decimal point character - const char decimal_point = '\0'; + case value_t::array: + return *std::next(m_it.array_iterator, n); - /// the indentation character - const char indent_char; + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value")); - /// the indentation string - string_t indent_string; - }; - - public: - /*! - @brief serialize to stream - - Serialize the given JSON value @a j to the output stream @a o. The JSON - value will be serialized using the @ref dump member function. - - - The indentation of the output can be controlled with the member variable - `width` of the output stream @a o. For instance, using the manipulator - `std::setw(4)` on @a o sets the indentation level to `4` and the - serialization result is the same as calling `dump(4)`. - - - The indentation characrer can be controlled with the member variable - `fill` of the output stream @a o. For instance, the manipulator - `std::setfill('\\t')` sets indentation to use a tab character rather than - the default space character. - - @param[in,out] o stream to serialize to - @param[in] j JSON value to serialize - - @return the stream @a o - - @complexity Linear. + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n)) + { + return *m_object; + } - @liveexample{The example below shows the serialization with different - parameters to `width` to adjust the indentation level.,operator_serialize} + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } - @since version 1.0.0; indentaction character added in version 3.0.0 + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - friend std::ostream& operator<<(std::ostream& o, const basic_json& j) + const typename object_t::key_type& key() const { - // read width member and use it as indentation parameter if nonzero - const bool pretty_print = (o.width() > 0); - const auto indentation = (pretty_print ? o.width() : 0); + JSON_ASSERT(m_object != nullptr); - // reset width to 0 for subsequent calls to this stream - o.width(0); + if (JSON_HEDLEY_LIKELY(m_object->is_object())) + { + return m_it.object_iterator->first; + } - // do the actual serialization - serializer s(output_adapter::create(o), o.fill()); - s.dump(j, pretty_print, static_cast(indentation)); - return o; + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); } /*! - @brief serialize to stream - @deprecated This stream operator is deprecated and will be removed in a - future version of the library. Please use - @ref std::ostream& operator<<(std::ostream&, const basic_json&) - instead; that is, replace calls like `j >> o;` with `o << j;`. + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - JSON_DEPRECATED - friend std::ostream& operator>>(const basic_json& j, std::ostream& o) + reference value() const { - return o << j; + return operator*(); } - /// @} + private: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + internal_iterator::type> m_it {}; +}; +} // namespace detail +} // namespace nlohmann +// #include - ///////////////////// - // deserialization // - ///////////////////// +// #include - /// @name deserialization - /// @{ - /*! - @brief deserialize from an array +#include // ptrdiff_t +#include // reverse_iterator +#include // declval - This function reads from an array of 1-byte values. +namespace nlohmann +{ +namespace detail +{ +////////////////////// +// reverse_iterator // +////////////////////// - @pre Each element of the container has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** +/*! +@brief a template for a reverse iterator class - @param[in] array array to read from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) +@tparam Base the base iterator type to reverse. Valid types are @ref +iterator (to create @ref reverse_iterator) and @ref const_iterator (to +create @ref const_reverse_iterator). - @return result of the deserialization +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator): + It is possible to write to the pointed-to element (only if @a Base is + @ref iterator). - @throw parse_error.101 if a parse error occurs; example: `""unexpected end - of input; expected string literal""` - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails +@since version 1.0.0 +*/ +template +class json_reverse_iterator : public std::reverse_iterator +{ + public: + using difference_type = std::ptrdiff_t; + /// shortcut to the reverse iterator adapter + using base_iterator = std::reverse_iterator; + /// the reference type for the pointed-to element + using reference = typename Base::reference; - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. + /// create reverse iterator from iterator + explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept + : base_iterator(it) {} - @note A UTF-8 byte order mark is silently ignored. + /// create reverse iterator from base class + explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} - @liveexample{The example below demonstrates the `parse()` function reading - from an array.,parse__array__parser_callback_t} + /// post-increment (it++) + json_reverse_iterator const operator++(int) + { + return static_cast(base_iterator::operator++(1)); + } - @since version 2.0.3 - */ - template - static basic_json parse(T (&array)[N], - const parser_callback_t cb = nullptr) + /// pre-increment (++it) + json_reverse_iterator& operator++() { - // delegate the call to the iterator-range parse overload - return parse(std::begin(array), std::end(array), cb); + return static_cast(base_iterator::operator++()); } - /*! - @brief deserialize from string literal + /// post-decrement (it--) + json_reverse_iterator const operator--(int) + { + return static_cast(base_iterator::operator--(1)); + } - @tparam CharT character/literal type with size of 1 byte - @param[in] s string literal to read a serialized JSON value from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) + /// pre-decrement (--it) + json_reverse_iterator& operator--() + { + return static_cast(base_iterator::operator--()); + } - @return result of the deserialization + /// add to iterator + json_reverse_iterator& operator+=(difference_type i) + { + return static_cast(base_iterator::operator+=(i)); + } - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails + /// add to iterator + json_reverse_iterator operator+(difference_type i) const + { + return static_cast(base_iterator::operator+(i)); + } - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. + /// subtract from iterator + json_reverse_iterator operator-(difference_type i) const + { + return static_cast(base_iterator::operator-(i)); + } - @note A UTF-8 byte order mark is silently ignored. - @note String containers like `std::string` or @ref string_t can be parsed - with @ref parse(const ContiguousContainer&, const parser_callback_t) + /// return difference + difference_type operator-(const json_reverse_iterator& other) const + { + return base_iterator(*this) - base_iterator(other); + } - @liveexample{The example below demonstrates the `parse()` function with - and without callback function.,parse__string__parser_callback_t} + /// access to successor + reference operator[](difference_type n) const + { + return *(this->operator+(n)); + } - @sa @ref parse(std::istream&, const parser_callback_t) for a version that - reads from an input stream + /// return the key of an object iterator + auto key() const -> decltype(std::declval().key()) + { + auto it = --this->base(); + return it.key(); + } - @since version 1.0.0 (originally for @ref string_t) - */ - template::value and - std::is_integral::type>::value and - sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> - static basic_json parse(const CharT s, - const parser_callback_t cb = nullptr) + /// return the value of an iterator + reference value() const { - return parser(input_adapter::create(s), cb).parse(true); + auto it = --this->base(); + return it.operator * (); } +}; +} // namespace detail +} // namespace nlohmann + +// #include +// #include + + +#include // all_of +#include // isdigit +#include // max +#include // accumulate +#include // string +#include // move +#include // vector + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +template +class json_pointer +{ + // allow basic_json to access private members + NLOHMANN_BASIC_JSON_TPL_DECLARATION + friend class basic_json; + + public: /*! - @brief deserialize from stream + @brief create JSON pointer - @param[in,out] i stream to read a serialized JSON value from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) + Create a JSON pointer according to the syntax described in + [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). - @return result of the deserialization + @param[in] s string representing the JSON pointer; if omitted, the empty + string is assumed which references the whole JSON value - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - @throw parse_error.111 if input stream is in a bad state + @throw parse_error.107 if the given JSON pointer @a s is nonempty and does + not begin with a slash (`/`); see example below - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. + @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is + not followed by `0` (representing `~`) or `1` (representing `/`); see + example below - @note A UTF-8 byte order mark is silently ignored. + @liveexample{The example shows the construction several valid JSON pointers + as well as the exceptional behavior.,json_pointer} - @liveexample{The example below demonstrates the `parse()` function with - and without callback function.,parse__istream__parser_callback_t} + @since version 2.0.0 + */ + explicit json_pointer(const std::string& s = "") + : reference_tokens(split(s)) + {} - @sa @ref parse(const CharT, const parser_callback_t) for a version - that reads from a string + /*! + @brief return a string representation of the JSON pointer - @since version 1.0.0 + @invariant For each JSON pointer `ptr`, it holds: + @code {.cpp} + ptr == json_pointer(ptr.to_string()); + @endcode + + @return a string representation of the JSON pointer + + @liveexample{The example shows the result of `to_string`.,json_pointer__to_string} + + @since version 2.0.0 */ - static basic_json parse(std::istream& i, - const parser_callback_t cb = nullptr) + std::string to_string() const { - return parser(input_adapter::create(i), cb).parse(true); + return std::accumulate(reference_tokens.begin(), reference_tokens.end(), + std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + escape(b); + }); } - /*! - @copydoc parse(std::istream&, const parser_callback_t) - */ - static basic_json parse(std::istream&& i, - const parser_callback_t cb = nullptr) + /// @copydoc to_string() + operator std::string() const { - return parser(input_adapter::create(i), cb).parse(true); + return to_string(); } /*! - @brief deserialize from an iterator range with contiguous storage + @brief append another JSON pointer at the end of this JSON pointer - This function reads from an iterator range of a container with contiguous - storage of 1-byte values. Compatible container types include - `std::vector`, `std::string`, `std::array`, `std::valarray`, and - `std::initializer_list`. Furthermore, C-style arrays can be used with - `std::begin()`/`std::end()`. User-defined containers can be used as long - as they implement random-access iterators and a contiguous storage. + @param[in] ptr JSON pointer to append + @return JSON pointer with @a ptr appended - @pre The iterator range is contiguous. Violating this precondition yields - undefined behavior. **This precondition is enforced with an assertion.** - @pre Each element in the range has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} - @warning There is no way to enforce all preconditions at compile-time. If - the function is called with noncompliant iterators and with - assertions switched off, the behavior is undefined and will most - likely yield segmentation violation. + @complexity Linear in the length of @a ptr. - @tparam IteratorType iterator of container with contiguous storage - @param[in] first begin of the range to parse (included) - @param[in] last end of the range to parse (excluded) - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) + @sa @ref operator/=(std::string) to append a reference token + @sa @ref operator/=(std::size_t) to append an array index + @sa @ref operator/(const json_pointer&, const json_pointer&) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(const json_pointer& ptr) + { + reference_tokens.insert(reference_tokens.end(), + ptr.reference_tokens.begin(), + ptr.reference_tokens.end()); + return *this; + } - @return result of the deserialization + /*! + @brief append an unescaped reference token at the end of this JSON pointer - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails + @param[in] token reference token to append + @return JSON pointer with @a token appended without escaping @a token - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} - @note A UTF-8 byte order mark is silently ignored. + @complexity Amortized constant. - @liveexample{The example below demonstrates the `parse()` function reading - from an iterator range.,parse__iteratortype__parser_callback_t} + @sa @ref operator/=(const json_pointer&) to append a JSON pointer + @sa @ref operator/=(std::size_t) to append an array index + @sa @ref operator/(const json_pointer&, std::size_t) for a binary operator - @since version 2.0.3 + @since version 3.6.0 */ - template::iterator_category>::value, int>::type = 0> - static basic_json parse(IteratorType first, IteratorType last, - const parser_callback_t cb = nullptr) + json_pointer& operator/=(std::string token) { - return parser(input_adapter::create(first, last), cb).parse(true); + push_back(std::move(token)); + return *this; } /*! - @brief deserialize from a container with contiguous storage + @brief append an array index at the end of this JSON pointer + + @param[in] array_idx array index to append + @return JSON pointer with @a array_idx appended - This function reads from a container with contiguous storage of 1-byte - values. Compatible container types include `std::vector`, `std::string`, - `std::array`, and `std::initializer_list`. User-defined containers can be - used as long as they implement random-access iterators and a contiguous - storage. + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} - @pre The container storage is contiguous. Violating this precondition - yields undefined behavior. **This precondition is enforced with an - assertion.** - @pre Each element of the container has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** + @complexity Amortized constant. - @warning There is no way to enforce all preconditions at compile-time. If - the function is called with a noncompliant container and with - assertions switched off, the behavior is undefined and will most - likely yield segmentation violation. + @sa @ref operator/=(const json_pointer&) to append a JSON pointer + @sa @ref operator/=(std::string) to append a reference token + @sa @ref operator/(const json_pointer&, std::string) for a binary operator - @tparam ContiguousContainer container type with contiguous storage - @param[in] c container to read from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) + @since version 3.6.0 + */ + json_pointer& operator/=(std::size_t array_idx) + { + return *this /= std::to_string(array_idx); + } - @return result of the deserialization + /*! + @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails + @param[in] lhs JSON pointer + @param[in] rhs JSON pointer + @return a new JSON pointer with @a rhs appended to @a lhs - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} - @note A UTF-8 byte order mark is silently ignored. + @complexity Linear in the length of @a lhs and @a rhs. - @liveexample{The example below demonstrates the `parse()` function reading - from a contiguous container.,parse__contiguouscontainer__parser_callback_t} + @sa @ref operator/=(const json_pointer&) to append a JSON pointer - @since version 2.0.3 + @since version 3.6.0 */ - template::value and - std::is_base_of< - std::random_access_iterator_tag, - typename std::iterator_traits()))>::iterator_category>::value - , int>::type = 0> - static basic_json parse(const ContiguousContainer& c, - const parser_callback_t cb = nullptr) + friend json_pointer operator/(const json_pointer& lhs, + const json_pointer& rhs) { - // delegate the call to the iterator-range parse overload - return parse(std::begin(c), std::end(c), cb); + return json_pointer(lhs) /= rhs; } /*! - @brief deserialize from stream - @deprecated This stream operator is deprecated and will be removed in a - future version of the library. Please use - @ref std::istream& operator>>(std::istream&, basic_json&) - instead; that is, replace calls like `j << i;` with `i >> j;`. + @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer + + @param[in] ptr JSON pointer + @param[in] token reference token + @return a new JSON pointer with unescaped @a token appended to @a ptr + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a ptr. + + @sa @ref operator/=(std::string) to append a reference token + + @since version 3.6.0 */ - JSON_DEPRECATED - friend std::istream& operator<<(basic_json& j, std::istream& i) + friend json_pointer operator/(const json_pointer& ptr, std::string token) { - j = parser(input_adapter::create(i)).parse(false); - return i; + return json_pointer(ptr) /= std::move(token); } /*! - @brief deserialize from stream + @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer - Deserializes an input stream to a JSON value. + @param[in] ptr JSON pointer + @param[in] array_idx array index + @return a new JSON pointer with @a array_idx appended to @a ptr - @param[in,out] i input stream to read a serialized JSON value from - @param[in,out] j JSON value to write the deserialized input to + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - @throw parse_error.111 if input stream is in a bad state + @complexity Linear in the length of @a ptr. - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. + @sa @ref operator/=(std::size_t) to append an array index - @note A UTF-8 byte order mark is silently ignored. + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& ptr, std::size_t array_idx) + { + return json_pointer(ptr) /= array_idx; + } - @liveexample{The example below shows how a JSON value is constructed by - reading a serialization from a stream.,operator_deserialize} + /*! + @brief returns the parent of this JSON pointer - @sa parse(std::istream&, const parser_callback_t) for a variant with a - parser callback function to filter values while parsing + @return parent of this JSON pointer; in case this JSON pointer is the root, + the root itself is returned - @since version 1.0.0 + @complexity Linear in the length of the JSON pointer. + + @liveexample{The example shows the result of `parent_pointer` for different + JSON Pointers.,json_pointer__parent_pointer} + + @since version 3.6.0 */ - friend std::istream& operator>>(std::istream& i, basic_json& j) + json_pointer parent_pointer() const { - j = parser(input_adapter::create(i)).parse(false); - return i; - } - - /// @} + if (empty()) + { + return *this; + } - /////////////////////////// - // convenience functions // - /////////////////////////// + json_pointer res = *this; + res.pop_back(); + return res; + } /*! - @brief return the type as string + @brief remove last reference token - Returns the type name as string to be used in error messages - usually to - indicate that a function was called on a wrong JSON type. + @pre not `empty()` - @return basically a string representation of a the @a m_type member + @liveexample{The example shows the usage of `pop_back`.,json_pointer__pop_back} @complexity Constant. - @liveexample{The following code exemplifies `type_name()` for all JSON - types.,type_name} + @throw out_of_range.405 if JSON pointer has no parent - @since version 1.0.0, public since 2.1.0 + @since version 3.6.0 */ - std::string type_name() const + void pop_back() { + if (JSON_HEDLEY_UNLIKELY(empty())) { - switch (m_type) - { - case value_t::null: - return "null"; - case value_t::object: - return "object"; - case value_t::array: - return "array"; - case value_t::string: - return "string"; - case value_t::boolean: - return "boolean"; - case value_t::discarded: - return "discarded"; - default: - return "number"; - } + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); } + + reference_tokens.pop_back(); } + /*! + @brief return last reference token - private: - ////////////////////// - // member variables // - ////////////////////// + @pre not `empty()` + @return last reference token - /// the type of the current element - value_t m_type = value_t::null; + @liveexample{The example shows the usage of `back`.,json_pointer__back} - /// the value of the current element - json_value m_value = {}; + @complexity Constant. + @throw out_of_range.405 if JSON pointer has no parent - private: - /////////////// - // iterators // - /////////////// + @since version 3.6.0 + */ + const std::string& back() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + return reference_tokens.back(); + } /*! - @brief an iterator for primitive JSON types + @brief append an unescaped token at the end of the reference pointer + + @param[in] token token to add + + @complexity Amortized constant. - This class models an iterator for primitive JSON types (boolean, number, - string). It's only purpose is to allow the iterator/const_iterator classes - to "iterate" over primitive values. Internally, the iterator is modeled by - a `difference_type` variable. Value begin_value (`0`) models the begin, - end_value (`1`) models past the end. + @liveexample{The example shows the result of `push_back` for different + JSON Pointers.,json_pointer__push_back} + + @since version 3.6.0 */ - class primitive_iterator_t + void push_back(const std::string& token) { - public: - - difference_type get_value() const noexcept - { - return m_it; - } - /// set iterator to a defined beginning - void set_begin() noexcept - { - m_it = begin_value; - } + reference_tokens.push_back(token); + } - /// set iterator to a defined past the end - void set_end() noexcept - { - m_it = end_value; - } + /// @copydoc push_back(const std::string&) + void push_back(std::string&& token) + { + reference_tokens.push_back(std::move(token)); + } - /// return whether the iterator can be dereferenced - constexpr bool is_begin() const noexcept - { - return (m_it == begin_value); - } + /*! + @brief return whether pointer points to the root document - /// return whether the iterator is at end - constexpr bool is_end() const noexcept - { - return (m_it == end_value); - } + @return true iff the JSON pointer points to the root document - friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it == rhs.m_it; - } + @complexity Constant. - friend constexpr bool operator!=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return !(lhs == rhs); - } + @exceptionsafety No-throw guarantee: this function never throws exceptions. - friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it < rhs.m_it; - } + @liveexample{The example shows the result of `empty` for different JSON + Pointers.,json_pointer__empty} - friend constexpr bool operator<=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it <= rhs.m_it; - } + @since version 3.6.0 + */ + bool empty() const noexcept + { + return reference_tokens.empty(); + } - friend constexpr bool operator>(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it > rhs.m_it; - } + private: + /*! + @param[in] s reference token to be converted into an array index - friend constexpr bool operator>=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it >= rhs.m_it; - } + @return integer representation of @a s - primitive_iterator_t operator+(difference_type i) - { - auto result = *this; - result += i; - return result; - } + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index begins not with a digit + @throw out_of_range.404 if string @a s could not be converted to an integer + @throw out_of_range.410 if an array index exceeds size_type + */ + static typename BasicJsonType::size_type array_index(const std::string& s) + { + using size_type = typename BasicJsonType::size_type; - friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) { - return lhs.m_it - rhs.m_it; + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + s + + "' must not begin with '0'")); } - friend std::ostream& operator<<(std::ostream& os, primitive_iterator_t it) + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) { - return os << it.m_it; + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number")); } - primitive_iterator_t& operator++() + std::size_t processed_chars = 0; + unsigned long long res = 0; + JSON_TRY { - ++m_it; - return *this; + res = std::stoull(s, &processed_chars); } - - primitive_iterator_t operator++(int) + JSON_CATCH(std::out_of_range&) { - auto result = *this; - m_it++; - return result; + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); } - primitive_iterator_t& operator--() + // check if the string was completely read + if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) { - --m_it; - return *this; + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); } - primitive_iterator_t operator--(int) + // only triggered on special platforms (like 32bit), see also + // https://github.com/nlohmann/json/pull/2203 + if (res >= static_cast((std::numeric_limits::max)())) { - auto result = *this; - m_it--; - return result; + JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type")); // LCOV_EXCL_LINE } - primitive_iterator_t& operator+=(difference_type n) - { - m_it += n; - return *this; - } + return static_cast(res); + } - primitive_iterator_t& operator-=(difference_type n) + json_pointer top() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) { - m_it -= n; - return *this; + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); } - private: - static constexpr difference_type begin_value = 0; - static constexpr difference_type end_value = begin_value + 1; - - /// iterator as signed integer type - difference_type m_it = std::numeric_limits::denorm_min(); - }; + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } /*! - @brief an iterator value + @brief create and return a reference to the pointed to value + + @complexity Linear in the number of reference tokens. - @note This structure could easily be a union, but MSVC currently does not - allow unions members with complex constructors, see - https://github.com/nlohmann/json/pull/105. + @throw parse_error.109 if array index is not a number + @throw type_error.313 if value cannot be unflattened */ - struct internal_iterator + BasicJsonType& get_and_create(BasicJsonType& j) const { - /// iterator for JSON objects - typename object_t::iterator object_iterator; - /// iterator for JSON arrays - typename array_t::iterator array_iterator; - /// generic iterator for all other types - primitive_iterator_t primitive_iterator; + auto result = &j; - /// create an uninitialized internal_iterator - internal_iterator() noexcept - : object_iterator(), array_iterator(), primitive_iterator() - {} - }; - - /// proxy class for the iterator_wrapper functions - template - class iteration_proxy - { - private: - /// helper class for iteration - class iteration_proxy_internal + // in case no reference tokens exist, return a reference to the JSON value + // j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) { - private: - /// the iterator - IteratorType anchor; - /// an index for arrays (used to create key names) - size_t array_index = 0; - - public: - explicit iteration_proxy_internal(IteratorType it) noexcept - : anchor(it) - {} - - /// dereference operator (needed for range-based for) - iteration_proxy_internal& operator*() - { - return *this; - } - - /// increment operator (needed for range-based for) - iteration_proxy_internal& operator++() - { - ++anchor; - ++array_index; - - return *this; - } - - /// inequality operator (needed for range-based for) - bool operator!= (const iteration_proxy_internal& o) const - { - return anchor != o.anchor; - } - - /// return key of the iterator - typename basic_json::string_t key() const + switch (result->type()) { - assert(anchor.m_object != nullptr); - - switch (anchor.m_object->type()) + case detail::value_t::null: { - // use integer array index as key - case value_t::array: - { - return std::to_string(array_index); - } - - // use key from the object - case value_t::object: + if (reference_token == "0") { - return anchor.key(); + // start a new array if reference token is 0 + result = &result->operator[](0); } - - // use an empty key for all primitive types - default: + else { - return ""; + // start a new object otherwise + result = &result->operator[](reference_token); } - } - } - - /// return value of the iterator - typename IteratorType::reference value() const - { - return anchor.value(); - } - }; - - /// the container to iterate - typename IteratorType::reference container; - - public: - /// construct iteration proxy from a container - explicit iteration_proxy(typename IteratorType::reference cont) - : container(cont) - {} - - /// return iterator begin (needed for range-based for) - iteration_proxy_internal begin() noexcept - { - return iteration_proxy_internal(container.begin()); - } - - /// return iterator end (needed for range-based for) - iteration_proxy_internal end() noexcept - { - return iteration_proxy_internal(container.end()); - } - }; - - public: - /*! - @brief a template for a random access iterator for the @ref basic_json class - - This class implements a both iterators (iterator and const_iterator) for the - @ref basic_json class. - - @note An iterator is called *initialized* when a pointer to a JSON value - has been set (e.g., by a constructor or a copy assignment). If the - iterator is default-constructed, it is *uninitialized* and most - methods are undefined. **The library uses assertions to detect calls - on uninitialized iterators.** - - @requirement The class satisfies the following concept requirements: - - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): - The iterator that can be moved to point (forward and backward) to any - element in constant time. - - @since version 1.0.0, simplified in version 2.0.9 - */ - template - class iter_impl : public std::iterator - { - /// allow basic_json to access private members - friend class basic_json; - - // make sure U is basic_json or const basic_json - static_assert(std::is_same::value - or std::is_same::value, - "iter_impl only accepts (const) basic_json"); - - public: - /// the type of the values when the iterator is dereferenced - using value_type = typename basic_json::value_type; - /// a type to represent differences between iterators - using difference_type = typename basic_json::difference_type; - /// defines a pointer to the type iterated over (value_type) - using pointer = typename std::conditional::value, - typename basic_json::const_pointer, - typename basic_json::pointer>::type; - /// defines a reference to the type iterated over (value_type) - using reference = typename std::conditional::value, - typename basic_json::const_reference, - typename basic_json::reference>::type; - /// the category of the iterator - using iterator_category = std::bidirectional_iterator_tag; - - /// default constructor - iter_impl() = default; - - /*! - @brief constructor for a given JSON instance - @param[in] object pointer to a JSON object for this iterator - @pre object != nullptr - @post The iterator is initialized; i.e. `m_object != nullptr`. - */ - explicit iter_impl(pointer object) noexcept - : m_object(object) - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - m_it.object_iterator = typename object_t::iterator(); break; } - case basic_json::value_t::array: + case detail::value_t::object: { - m_it.array_iterator = typename array_t::iterator(); + // create an entry in the object + result = &result->operator[](reference_token); break; } - default: + case detail::value_t::array: { - m_it.primitive_iterator = primitive_iterator_t(); + // create an entry in the array + result = &result->operator[](array_index(reference_token)); break; } + + /* + The following code is only reached if there exists a reference + token _and_ the current value is primitive. In this case, we have + an error situation, because primitive values may only occur as + single value; that is, with an empty list of reference tokens. + */ + default: + JSON_THROW(detail::type_error::create(313, "invalid value to unflatten")); } } - /*! - @note The conventional copy constructor and copy assignment are - implicitly defined. - Combined with the following converting constructor and assigment, - they support: copy from iterator to iterator, - copy from const iterator to const iterator, - and conversion from iterator to const iterator. - However conversion from const iterator to iterator is not defined. - */ + return *result; + } - /*! - @brief converting constructor - @param[in] other non-const iterator to copy from - @note It is not checked whether @a other is initialized. - */ - iter_impl(const iter_impl& other) noexcept - : m_object(other.m_object), m_it(other.m_it) - {} + /*! + @brief return a reference to the pointed to value - /*! - @brief converting assignment - @param[in,out] other non-const iterator to copy from - @return const/non-const iterator - @note It is not checked whether @a other is initialized. - */ - iter_impl& operator=(const iter_impl& other) noexcept - { - m_object = other.m_object; - m_it = other.m_it; - return *this; - } + @note This version does not throw if a value is not present, but tries to + create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. - private: - /*! - @brief set the iterator to the first value - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - void set_begin() noexcept - { - assert(m_object != nullptr); + @param[in] ptr a JSON value + + @return reference to the JSON value pointed to by the JSON pointer - switch (m_object->m_type) + @complexity Linear in the length of the JSON pointer. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_unchecked(BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + // convert null values to arrays or objects before continuing + if (ptr->is_null()) { - case basic_json::value_t::object: + // check if reference token is a number + const bool nums = + std::all_of(reference_token.begin(), reference_token.end(), + [](const unsigned char x) { - m_it.object_iterator = m_object->m_value.object->begin(); - break; - } + return std::isdigit(x); + }); + + // change value to array for numbers or "-" or to object otherwise + *ptr = (nums || reference_token == "-") + ? detail::value_t::array + : detail::value_t::object; + } - case basic_json::value_t::array: + switch (ptr->type()) + { + case detail::value_t::object: { - m_it.array_iterator = m_object->m_value.array->begin(); + // use unchecked object access + ptr = &ptr->operator[](reference_token); break; } - case basic_json::value_t::null: + case detail::value_t::array: { - // set to end so begin()==end() is true: null is empty - m_it.primitive_iterator.set_end(); + if (reference_token == "-") + { + // explicitly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); + } + else + { + // convert array index to number; unchecked access + ptr = &ptr->operator[](array_index(reference_token)); + } break; } default: - { - m_it.primitive_iterator.set_begin(); - break; - } + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); } } - /*! - @brief set the iterator past the last value - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - void set_end() noexcept - { - assert(m_object != nullptr); + return *ptr; + } - switch (m_object->m_type) + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_checked(BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) { - case basic_json::value_t::object: + case detail::value_t::object: { - m_it.object_iterator = m_object->m_value.object->end(); + // note: at performs range check + ptr = &ptr->at(reference_token); break; } - case basic_json::value_t::array: + case detail::value_t::array: { - m_it.array_iterator = m_object->m_value.array->end(); + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // note: at performs range check + ptr = &ptr->at(array_index(reference_token)); break; } default: - { - m_it.primitive_iterator.set_end(); - break; - } + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); } } - public: - /*! - @brief return a reference to the value pointed to by the iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - reference operator*() const - { - assert(m_object != nullptr); + return *ptr; + } - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - assert(m_it.object_iterator != m_object->m_value.object->end()); - return m_it.object_iterator->second; - } + /*! + @brief return a const reference to the pointed to value - case basic_json::value_t::array: - { - assert(m_it.array_iterator != m_object->m_value.array->end()); - return *m_it.array_iterator; - } + @param[in] ptr a JSON value - case basic_json::value_t::null: + @return const reference to the JSON value pointed to by the JSON + pointer + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: { - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; } - default: + case detail::value_t::array: { - if (m_it.primitive_iterator.is_begin()) + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) { - return *m_object; + // "-" cannot be used for const access + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); } - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + // use unchecked array access + ptr = &ptr->operator[](array_index(reference_token)); + break; } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); } } - /*! - @brief dereference the iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - pointer operator->() const - { - assert(m_object != nullptr); + return *ptr; + } - switch (m_object->m_type) + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_checked(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) { - case basic_json::value_t::object: - { - assert(m_it.object_iterator != m_object->m_value.object->end()); - return &(m_it.object_iterator->second); - } - - case basic_json::value_t::array: + case detail::value_t::object: { - assert(m_it.array_iterator != m_object->m_value.array->end()); - return &*m_it.array_iterator; + // note: at performs range check + ptr = &ptr->at(reference_token); + break; } - default: + case detail::value_t::array: { - if (m_it.primitive_iterator.is_begin()) + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) { - return m_object; + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); } - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + // note: at performs range check + ptr = &ptr->at(array_index(reference_token)); + break; } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); } } - /*! - @brief post-increment (it++) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator++(int) - { - auto result = *this; - ++(*this); - return result; - } + return *ptr; + } - /*! - @brief pre-increment (++it) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator++() + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + */ + bool contains(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) { - assert(m_object != nullptr); - - switch (m_object->m_type) + switch (ptr->type()) { - case basic_json::value_t::object: + case detail::value_t::object: { - std::advance(m_it.object_iterator, 1); + if (!ptr->contains(reference_token)) + { + // we did not find the key in the object + return false; + } + + ptr = &ptr->operator[](reference_token); break; } - case basic_json::value_t::array: + case detail::value_t::array: { - std::advance(m_it.array_iterator, 1); + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + return false; + } + if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9"))) + { + // invalid char + return false; + } + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1)) + { + if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9'))) + { + // first char should be between '1' and '9' + return false; + } + for (std::size_t i = 1; i < reference_token.size(); i++) + { + if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9'))) + { + // other char should be between '0' and '9' + return false; + } + } + } + + const auto idx = array_index(reference_token); + if (idx >= ptr->size()) + { + // index out of range + return false; + } + + ptr = &ptr->operator[](idx); break; } default: { - ++m_it.primitive_iterator; - break; + // we do not expect primitive values if there is still a + // reference token to process + return false; } } - - return *this; } - /*! - @brief post-decrement (it--) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator--(int) + // no reference token left means we found a primitive value + return true; + } + + /*! + @brief split the string input to reference tokens + + @note This function is only called by the json_pointer constructor. + All exceptions below are documented there. + + @throw parse_error.107 if the pointer is not empty or begins with '/' + @throw parse_error.108 if character '~' is not followed by '0' or '1' + */ + static std::vector split(const std::string& reference_string) + { + std::vector result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) { - auto result = *this; - --(*this); return result; } - /*! - @brief pre-decrement (--it) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator--() - { - assert(m_object != nullptr); - - switch (m_object->m_type) + // check if nonempty reference string begins with slash + if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) + { + JSON_THROW(detail::parse_error::create(107, 1, + "JSON pointer must be empty or begin with '/' - was: '" + + reference_string + "'")); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + std::size_t slash = reference_string.find_first_of('/', 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == 0 (if slash == std::string::npos) + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = (slash == std::string::npos) ? 0 : slash + 1, + // find next slash + slash = reference_string.find_first_of('/', start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); + + // check reference tokens are properly escaped + for (std::size_t pos = reference_token.find_first_of('~'); + pos != std::string::npos; + pos = reference_token.find_first_of('~', pos + 1)) { - case basic_json::value_t::object: - { - std::advance(m_it.object_iterator, -1); - break; - } + JSON_ASSERT(reference_token[pos] == '~'); - case basic_json::value_t::array: + // ~ must be followed by 0 or 1 + if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 || + (reference_token[pos + 1] != '0' && + reference_token[pos + 1] != '1'))) { - std::advance(m_it.array_iterator, -1); - break; + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'")); } + } - default: - { - --m_it.primitive_iterator; - break; - } - } - - return *this; + // finally, store the reference token + unescape(reference_token); + result.push_back(reference_token); } - /*! - @brief comparison: equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator==(const iter_impl& other) const - { - // if objects are not the same, the comparison is undefined - if (m_object != other.m_object) - { - JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); - } + return result; + } - assert(m_object != nullptr); + /*! + @brief replace all occurrences of a substring by another string - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - return (m_it.object_iterator == other.m_it.object_iterator); - } + @param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t + @param[in] f the substring to replace with @a t + @param[in] t the string to replace @a f - case basic_json::value_t::array: - { - return (m_it.array_iterator == other.m_it.array_iterator); - } + @pre The search string @a f must not be empty. **This precondition is + enforced with an assertion.** - default: - { - return (m_it.primitive_iterator == other.m_it.primitive_iterator); - } - } - } + @since version 2.0.0 + */ + static void replace_substring(std::string& s, const std::string& f, + const std::string& t) + { + JSON_ASSERT(!f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} + } - /*! - @brief comparison: not equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator!=(const iter_impl& other) const - { - return not operator==(other); - } + /// escape "~" to "~0" and "/" to "~1" + static std::string escape(std::string s) + { + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; + } - /*! - @brief comparison: smaller - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator<(const iter_impl& other) const - { - // if objects are not the same, the comparison is undefined - if (m_object != other.m_object) - { - JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); - } + /// unescape "~1" to tilde and "~0" to slash (order is important!) + static void unescape(std::string& s) + { + replace_substring(s, "~1", "/"); + replace_substring(s, "~0", "~"); + } - assert(m_object != nullptr); + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to - switch (m_object->m_type) + @note Empty objects or arrays are flattened to `null`. + */ + static void flatten(const std::string& reference_string, + const BasicJsonType& value, + BasicJsonType& result) + { + switch (value.type()) + { + case detail::value_t::array: { - case basic_json::value_t::object: + if (value.m_value.array->empty()) { - JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); + // flatten empty array as null + result[reference_string] = nullptr; } - - case basic_json::value_t::array: - { - return (m_it.array_iterator < other.m_it.array_iterator); - } - - default: + else { - return (m_it.primitive_iterator < other.m_it.primitive_iterator); + // iterate array and use index as reference string + for (std::size_t i = 0; i < value.m_value.array->size(); ++i) + { + flatten(reference_string + "/" + std::to_string(i), + value.m_value.array->operator[](i), result); + } } + break; } - } - - /*! - @brief comparison: less than or equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator<=(const iter_impl& other) const - { - return not other.operator < (*this); - } - - /*! - @brief comparison: greater than - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator>(const iter_impl& other) const - { - return not operator<=(other); - } - /*! - @brief comparison: greater than or equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator>=(const iter_impl& other) const - { - return not operator<(other); - } - - /*! - @brief add to iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator+=(difference_type i) - { - assert(m_object != nullptr); - - switch (m_object->m_type) + case detail::value_t::object: { - case basic_json::value_t::object: - { - JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); - } - - case basic_json::value_t::array: + if (value.m_value.object->empty()) { - std::advance(m_it.array_iterator, i); - break; + // flatten empty object as null + result[reference_string] = nullptr; } - - default: + else { - m_it.primitive_iterator += i; - break; + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + escape(element.first), element.second, result); + } } + break; } - return *this; + default: + { + // add primitive value with its reference string + result[reference_string] = value; + break; + } } + } - /*! - @brief subtract from iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator-=(difference_type i) - { - return operator+=(-i); - } + /*! + @param[in] value flattened JSON - /*! - @brief add to iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator+(difference_type i) - { - auto result = *this; - result += i; - return result; - } + @return unflattened JSON - /*! - @brief subtract from iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator-(difference_type i) + @throw parse_error.109 if array index is not a number + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + @throw type_error.313 if value cannot be unflattened + */ + static BasicJsonType + unflatten(const BasicJsonType& value) + { + if (JSON_HEDLEY_UNLIKELY(!value.is_object())) { - auto result = *this; - result -= i; - return result; + JSON_THROW(detail::type_error::create(314, "only objects can be unflattened")); } - /*! - @brief return difference - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - difference_type operator-(const iter_impl& other) const - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); - } - - case basic_json::value_t::array: - { - return m_it.array_iterator - other.m_it.array_iterator; - } - - default: - { - return m_it.primitive_iterator - other.m_it.primitive_iterator; - } - } - } + BasicJsonType result; - /*! - @brief access to successor - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - reference operator[](difference_type n) const + // iterate the JSON object values + for (const auto& element : *value.m_value.object) { - assert(m_object != nullptr); - - switch (m_object->m_type) + if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) { - case basic_json::value_t::object: - { - JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); - } - - case basic_json::value_t::array: - { - return *std::next(m_it.array_iterator, n); - } - - case basic_json::value_t::null: - { - JSON_THROW(invalid_iterator::create(214, "cannot get value")); - } - - default: - { - if (m_it.primitive_iterator.get_value() == -n) - { - return *m_object; - } - - JSON_THROW(invalid_iterator::create(214, "cannot get value")); - } + JSON_THROW(detail::type_error::create(315, "values in object must be primitive")); } + + // assign value to reference pointed to by JSON pointer; Note that if + // the JSON pointer is "" (i.e., points to the whole value), function + // get_and_create returns a reference to result itself. An assignment + // will then create a primitive value. + json_pointer(element.first).get_and_create(result) = element.second; } - /*! - @brief return the key of an object iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - typename object_t::key_type key() const - { - assert(m_object != nullptr); + return result; + } - if (m_object->is_object()) - { - return m_it.object_iterator->first; - } + /*! + @brief compares two JSON pointers for equality - JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); - } + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is equal to @a rhs - /*! - @brief return the value of an iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - reference value() const - { - return operator*(); - } + @complexity Linear in the length of the JSON pointer - private: - /// associated JSON instance - pointer m_object = nullptr; - /// the actual iterator of the associated instance - struct internal_iterator m_it = internal_iterator(); - }; + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return lhs.reference_tokens == rhs.reference_tokens; + } /*! - @brief a template for a reverse iterator class + @brief compares two JSON pointers for inequality - @tparam Base the base iterator type to reverse. Valid types are @ref - iterator (to create @ref reverse_iterator) and @ref const_iterator (to - create @ref const_reverse_iterator). + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is not equal @a rhs - @requirement The class satisfies the following concept requirements: - - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): - The iterator that can be moved to point (forward and backward) to any - element in constant time. - - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator): - It is possible to write to the pointed-to element (only if @a Base is - @ref iterator). + @complexity Linear in the length of the JSON pointer - @since version 1.0.0 + @exceptionsafety No-throw guarantee: this function never throws exceptions. */ - template - class json_reverse_iterator : public std::reverse_iterator + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept { - public: - /// shortcut to the reverse iterator adaptor - using base_iterator = std::reverse_iterator; - /// the reference type for the pointed-to element - using reference = typename Base::reference; + return !(lhs == rhs); + } - /// create reverse iterator from iterator - json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept - : base_iterator(it) - {} + /// the reference tokens + std::vector reference_tokens; +}; +} // namespace nlohmann - /// create reverse iterator from base class - json_reverse_iterator(const base_iterator& it) noexcept - : base_iterator(it) - {} +// #include - /// post-increment (it++) - json_reverse_iterator operator++(int) - { - return base_iterator::operator++(1); - } - /// pre-increment (++it) - json_reverse_iterator& operator++() - { - base_iterator::operator++(); - return *this; - } +#include +#include - /// post-decrement (it--) - json_reverse_iterator operator--(int) - { - return base_iterator::operator--(1); - } +// #include - /// pre-decrement (--it) - json_reverse_iterator& operator--() - { - base_iterator::operator--(); - return *this; - } - /// add to iterator - json_reverse_iterator& operator+=(difference_type i) - { - base_iterator::operator+=(i); - return *this; - } +namespace nlohmann +{ +namespace detail +{ +template +class json_ref +{ + public: + using value_type = BasicJsonType; - /// add to iterator - json_reverse_iterator operator+(difference_type i) const - { - auto result = *this; - result += i; - return result; - } + json_ref(value_type&& value) + : owned_value(std::move(value)) + , value_ref(&owned_value) + , is_rvalue(true) + {} - /// subtract from iterator - json_reverse_iterator operator-(difference_type i) const - { - auto result = *this; - result -= i; - return result; - } + json_ref(const value_type& value) + : value_ref(const_cast(&value)) + , is_rvalue(false) + {} - /// return difference - difference_type operator-(const json_reverse_iterator& other) const - { - return this->base() - other.base(); - } + json_ref(std::initializer_list init) + : owned_value(init) + , value_ref(&owned_value) + , is_rvalue(true) + {} - /// access to successor - reference operator[](difference_type n) const - { - return *(this->operator+(n)); - } + template < + class... Args, + enable_if_t::value, int> = 0 > + json_ref(Args && ... args) + : owned_value(std::forward(args)...) + , value_ref(&owned_value) + , is_rvalue(true) + {} - /// return the key of an object iterator - typename object_t::key_type key() const - { - auto it = --this->base(); - return it.key(); - } + // class should be movable only + json_ref(json_ref&&) = default; + json_ref(const json_ref&) = delete; + json_ref& operator=(const json_ref&) = delete; + json_ref& operator=(json_ref&&) = delete; + ~json_ref() = default; - /// return the value of an iterator - reference value() const + value_type moved_or_copied() const + { + if (is_rvalue) { - auto it = --this->base(); - return it.operator * (); + return std::move(*value_ref); } - }; - + return *value_ref; + } - private: - //////////////////// - // input adapters // - //////////////////// + value_type const& operator*() const + { + return *static_cast(value_ref); + } - /// abstract input adapter interface - class input_adapter + value_type const* operator->() const { - public: - virtual int get_character() = 0; - virtual std::string read(size_t offset, size_t length) = 0; - virtual ~input_adapter() {} + return static_cast(value_ref); + } - // native support + private: + mutable value_type owned_value = nullptr; + value_type* value_ref = nullptr; + const bool is_rvalue = true; +}; +} // namespace detail +} // namespace nlohmann - /// input adapter for input stream - static std::shared_ptr create(std::istream& i, const size_t buffer_size = 16384) - { - return std::shared_ptr(new cached_input_stream_adapter(i, buffer_size)); - } +// #include - /// input adapter for input stream - static std::shared_ptr create(std::istream&& i, const size_t buffer_size = 16384) - { - return std::shared_ptr(new cached_input_stream_adapter(i, buffer_size)); - } +// #include - /// input adapter for buffer - static std::shared_ptr create(const char* b, size_t l) - { - return std::shared_ptr(new input_buffer_adapter(b, l)); - } +// #include - // derived support +// #include - /// input adapter for string literal - template::value and - std::is_integral::type>::value and - sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> - static std::shared_ptr create(CharT b) - { - return create(reinterpret_cast(b), - std::strlen(reinterpret_cast(b))); - } - /// input adapter for iterator range with contiguous storage - template::iterator_category, std::random_access_iterator_tag>::value - , int>::type - = 0> - static std::shared_ptr create(IteratorType first, IteratorType last) - { - // assertion to check that the iterator range is indeed contiguous, - // see http://stackoverflow.com/a/35008842/266378 for more discussion - assert(std::accumulate(first, last, std::pair(true, 0), - [&first](std::pair res, decltype(*first) val) - { - res.first &= (val == *(std::next(std::addressof(*first), res.second++))); - return res; - }).first); +#include // reverse +#include // array +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // memcpy +#include // numeric_limits +#include // string +#include // isnan, isinf - // assertion to check that each element is 1 byte long - static_assert(sizeof(typename std::iterator_traits::value_type) == 1, - "each element in the iterator range must have the size of 1 byte"); +// #include - return create(reinterpret_cast(&(*first)), - static_cast(std::distance(first, last))); - } +// #include - /// input adapter for array - template - static std::shared_ptr create(T (&array)[N]) - { - // delegate the call to the iterator-range overload - return create(std::begin(array), std::end(array)); - } +// #include - /// input adapter for contiguous container - template::value and - std::is_base_of< - std::random_access_iterator_tag, - typename std::iterator_traits()))>::iterator_category>::value - , int>::type = 0> - static std::shared_ptr create(const ContiguousContainer& c) - { - // delegate the call to the iterator-range overload - return create(std::begin(c), std::end(c)); - } - }; - /// a type to simplify interfaces - using input_adapter_t = std::shared_ptr; +#include // copy +#include // size_t +#include // streamsize +#include // back_inserter +#include // shared_ptr, make_shared +#include // basic_ostream +#include // basic_string +#include // vector +// #include - /// input adapter for cached stream input - class cached_input_stream_adapter : public input_adapter - { - public: - cached_input_stream_adapter(std::istream& i, const size_t buffer_size) - : is(i), start_position(is.tellg()), buffer(buffer_size, '\0') - { - // immediately abort if stream is erroneous - if (JSON_UNLIKELY(i.fail())) - { - JSON_THROW(parse_error::create(111, 0, "bad input stream")); - } - // initial fill - is.read(buffer.data(), static_cast(buffer.size())); - // store number of bytes in the buffer - fill_size = static_cast(is.gcount()); +namespace nlohmann +{ +namespace detail +{ +/// abstract output adapter interface +template struct output_adapter_protocol +{ + virtual void write_character(CharType c) = 0; + virtual void write_characters(const CharType* s, std::size_t length) = 0; + virtual ~output_adapter_protocol() = default; +}; - // skip byte order mark - if (fill_size >= 3 and buffer[0] == '\xEF' and buffer[1] == '\xBB' and buffer[2] == '\xBF') - { - buffer_pos += 3; - processed_chars += 3; - } - } +/// a type to simplify interfaces +template +using output_adapter_t = std::shared_ptr>; - ~cached_input_stream_adapter() override - { - // clear stream flags - is.clear(); - // We initially read a lot of characters into the buffer, and we - // may not have processed all of them. Therefore, we need to - // "rewind" the stream after the last processed char. - is.seekg(start_position + static_cast(processed_chars)); - // clear stream flags - is.clear(); - } +/// output adapter for byte vectors +template +class output_vector_adapter : public output_adapter_protocol +{ + public: + explicit output_vector_adapter(std::vector& vec) noexcept + : v(vec) + {} - int get_character() override - { - // check if refilling is necessary and possible - if (buffer_pos == fill_size and not eof) - { - // refill - is.read(buffer.data(), static_cast(buffer.size())); - // store number of bytes in the buffer - fill_size = static_cast(is.gcount()); + void write_character(CharType c) override + { + v.push_back(c); + } - // the buffer is ready - buffer_pos = 0; + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + std::copy(s, s + length, std::back_inserter(v)); + } - // remember that filling did not yield new input - if (fill_size == 0) - { - eof = true; - return std::char_traits::eof(); - } - } + private: + std::vector& v; +}; - ++processed_chars; - return buffer[buffer_pos++] & 0xFF;; - } +/// output adapter for output streams +template +class output_stream_adapter : public output_adapter_protocol +{ + public: + explicit output_stream_adapter(std::basic_ostream& s) noexcept + : stream(s) + {} - std::string read(size_t offset, size_t length) override - { - // create buffer - std::string result(length, '\0'); + void write_character(CharType c) override + { + stream.put(c); + } - // save stream position - auto current_pos = is.tellg(); - // save stream flags - auto flags = is.rdstate(); + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + stream.write(s, static_cast(length)); + } - // clear stream flags - is.clear(); - // set stream position - is.seekg(static_cast(offset)); - // read bytes - is.read(&result[0], static_cast(length)); + private: + std::basic_ostream& stream; +}; - // reset stream position - is.seekg(current_pos); - // reset stream flags - is.setstate(flags); +/// output adapter for basic_string +template> +class output_string_adapter : public output_adapter_protocol +{ + public: + explicit output_string_adapter(StringType& s) noexcept + : str(s) + {} - return result; - } + void write_character(CharType c) override + { + str.push_back(c); + } - private: - /// the associated input stream - std::istream& is; + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + str.append(s, length); + } - /// chars returned via get_character() - size_t processed_chars = 0; - /// chars processed in the current buffer - size_t buffer_pos = 0; + private: + StringType& str; +}; - /// whether stream reached eof - bool eof = false; - /// how many chars have been copied to the buffer by last (re)fill - size_t fill_size = 0; +template> +class output_adapter +{ + public: + output_adapter(std::vector& vec) + : oa(std::make_shared>(vec)) {} - /// position of the stream when we started - const std::streampos start_position; + output_adapter(std::basic_ostream& s) + : oa(std::make_shared>(s)) {} - /// internal buffer - std::vector buffer; - }; + output_adapter(StringType& s) + : oa(std::make_shared>(s)) {} - /// input adapter for buffer input - class input_buffer_adapter : public input_adapter + operator output_adapter_t() { - public: - input_buffer_adapter(const char* b, size_t l) - : input_adapter(), cursor(b), limit(b + l), start(b) - { - // skip byte order mark - if (l >= 3 and b[0] == '\xEF' and b[1] == '\xBB' and b[2] == '\xBF') - { - cursor += 3; - } - } + return oa; + } - // delete because of pointer members - input_buffer_adapter(const input_buffer_adapter&) = delete; - input_buffer_adapter& operator=(input_buffer_adapter&) = delete; + private: + output_adapter_t oa = nullptr; +}; +} // namespace detail +} // namespace nlohmann - int get_character() override - { - if (JSON_LIKELY(cursor < limit)) - { - return *(cursor++) & 0xFF; - } - else - { - return std::char_traits::eof(); - } - } - std::string read(size_t offset, size_t length) override - { - // avoid reading too many characters - const size_t max_length = static_cast(limit - start); - return std::string(start + offset, std::min(length, max_length - offset)); - } +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// binary writer // +/////////////////// - private: - /// pointer to the current character - const char* cursor; - /// pointer past the last character - const char* limit; - /// pointer to the first character - const char* start; - }; +/*! +@brief serialization to CBOR and MessagePack values +*/ +template +class binary_writer +{ + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using number_float_t = typename BasicJsonType::number_float_t; - ////////////////////////////////////////// - // binary serialization/deserialization // - ////////////////////////////////////////// + public: + /*! + @brief create a binary writer - /// @name binary serialization/deserialization support - /// @{ + @param[in] adapter output adapter to write to + */ + explicit binary_writer(output_adapter_t adapter) : oa(adapter) + { + JSON_ASSERT(oa); + } - private: /*! - @brief deserialization of CBOR and MessagePack values + @param[in] j JSON value to serialize + @pre j.type() == value_t::object */ - class binary_reader + void write_bson(const BasicJsonType& j) { - public: - /*! - @brief create a binary reader - - @param[in] adapter input adapter to read from - */ - explicit binary_reader(input_adapter_t adapter) - : ia(adapter), is_little_endian(little_endianess()) + switch (j.type()) { - assert(ia); - } + case value_t::object: + { + write_bson_object(*j.m_value.object); + break; + } - /*! - @brief create a JSON value from CBOR input + default: + { + JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()))); + } + } + } - @param[in] get_char whether a new character should be retrieved from - the input (true, default) or whether the last - read character should be considered instead + /*! + @param[in] j JSON value to serialize + */ + void write_cbor(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: + { + oa->write_character(to_char_type(0xF6)); + break; + } - @return JSON value created from CBOR input + case value_t::boolean: + { + oa->write_character(j.m_value.boolean + ? to_char_type(0xF5) + : to_char_type(0xF4)); + break; + } - @throw parse_error.110 if input ended unexpectedly - @throw parse_error.112 if unsupported byte was read - */ - basic_json parse_cbor(const bool get_char = true) - { - switch (get_char ? get() : current) + case value_t::number_integer: { - // EOF - case std::char_traits::eof(): + if (j.m_value.number_integer >= 0) { - JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); + // CBOR does not differentiate between positive signed + // integers and unsigned integers. Therefore, we used the + // code from the value_t::number_unsigned case here. + if (j.m_value.number_integer <= 0x17) + { + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x18)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x19)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x1A)); + write_number(static_cast(j.m_value.number_integer)); + } + else + { + oa->write_character(to_char_type(0x1B)); + write_number(static_cast(j.m_value.number_integer)); + } } - - // Integer 0x00..0x17 (0..23) - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x08: - case 0x09: - case 0x0a: - case 0x0b: - case 0x0c: - case 0x0d: - case 0x0e: - case 0x0f: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: + else { - return static_cast(current); + // The conversions below encode the sign in the first + // byte, and the value is converted to a positive number. + const auto positive_number = -1 - j.m_value.number_integer; + if (j.m_value.number_integer >= -24) + { + write_number(static_cast(0x20 + positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x38)); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x39)); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x3A)); + write_number(static_cast(positive_number)); + } + else + { + oa->write_character(to_char_type(0x3B)); + write_number(static_cast(positive_number)); + } } + break; + } - case 0x18: // Unsigned integer (one-byte uint8_t follows) + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= 0x17) { - return get_number(); + write_number(static_cast(j.m_value.number_unsigned)); } - - case 0x19: // Unsigned integer (two-byte uint16_t follows) + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { - return get_number(); + oa->write_character(to_char_type(0x18)); + write_number(static_cast(j.m_value.number_unsigned)); } - - case 0x1a: // Unsigned integer (four-byte uint32_t follows) + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { - return get_number(); + oa->write_character(to_char_type(0x19)); + write_number(static_cast(j.m_value.number_unsigned)); } - - case 0x1b: // Unsigned integer (eight-byte uint64_t follows) + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { - return get_number(); + oa->write_character(to_char_type(0x1A)); + write_number(static_cast(j.m_value.number_unsigned)); } - - // Negative integer -1-0x00..-1-0x17 (-1..-24) - case 0x20: - case 0x21: - case 0x22: - case 0x23: - case 0x24: - case 0x25: - case 0x26: - case 0x27: - case 0x28: - case 0x29: - case 0x2a: - case 0x2b: - case 0x2c: - case 0x2d: - case 0x2e: - case 0x2f: - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: + else { - return static_cast(0x20 - 1 - current); + oa->write_character(to_char_type(0x1B)); + write_number(static_cast(j.m_value.number_unsigned)); } + break; + } - case 0x38: // Negative integer (one-byte uint8_t follows) + case value_t::number_float: + { + if (std::isnan(j.m_value.number_float)) { - // must be uint8_t ! - return static_cast(-1) - get_number(); + // NaN is 0xf97e00 in CBOR + oa->write_character(to_char_type(0xF9)); + oa->write_character(to_char_type(0x7E)); + oa->write_character(to_char_type(0x00)); } - - case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + else if (std::isinf(j.m_value.number_float)) { - return static_cast(-1) - get_number(); + // Infinity is 0xf97c00, -Infinity is 0xf9fc00 + oa->write_character(to_char_type(0xf9)); + oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC)); + oa->write_character(to_char_type(0x00)); } - - case 0x3a: // Negative integer -1-n (four-byte uint32_t follows) + else { - return static_cast(-1) - get_number(); + write_compact_float(j.m_value.number_float, detail::input_format_t::cbor); } + break; + } - case 0x3b: // Negative integer -1-n (eight-byte uint64_t follows) + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 0x17) { - return static_cast(-1) - static_cast(get_number()); + write_number(static_cast(0x60 + N)); } - - // UTF-8 string (0x00..0x17 bytes follow) - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6a: - case 0x6b: - case 0x6c: - case 0x6d: - case 0x6e: - case 0x6f: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: - case 0x78: // UTF-8 string (one-byte uint8_t for n follows) - case 0x79: // UTF-8 string (two-byte uint16_t for n follow) - case 0x7a: // UTF-8 string (four-byte uint32_t for n follow) - case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow) - case 0x7f: // UTF-8 string (indefinite length) - { - return get_cbor_string(); - } - - // array (0x00..0x17 data items follow) - case 0x80: - case 0x81: - case 0x82: - case 0x83: - case 0x84: - case 0x85: - case 0x86: - case 0x87: - case 0x88: - case 0x89: - case 0x8a: - case 0x8b: - case 0x8c: - case 0x8d: - case 0x8e: - case 0x8f: - case 0x90: - case 0x91: - case 0x92: - case 0x93: - case 0x94: - case 0x95: - case 0x96: - case 0x97: - { - basic_json result = value_t::array; - const auto len = static_cast(current & 0x1f); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_cbor()); - } - return result; + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x78)); + write_number(static_cast(N)); } - - case 0x98: // array (one-byte uint8_t for n follows) + else if (N <= (std::numeric_limits::max)()) { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_cbor()); - } - return result; + oa->write_character(to_char_type(0x79)); + write_number(static_cast(N)); } - - case 0x99: // array (two-byte uint16_t for n follow) + else if (N <= (std::numeric_limits::max)()) { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_cbor()); - } - return result; + oa->write_character(to_char_type(0x7A)); + write_number(static_cast(N)); } - - case 0x9a: // array (four-byte uint32_t for n follow) + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_cbor()); - } - return result; + oa->write_character(to_char_type(0x7B)); + write_number(static_cast(N)); } + // LCOV_EXCL_STOP + + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } - case 0x9b: // array (eight-byte uint64_t for n follow) + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 0x17) { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_cbor()); - } - return result; + write_number(static_cast(0x80 + N)); } - - case 0x9f: // array (indefinite length) + else if (N <= (std::numeric_limits::max)()) { - basic_json result = value_t::array; - while (get() != 0xff) - { - result.push_back(parse_cbor(false)); - } - return result; - } - - // map (0x00..0x17 pairs of data items follow) - case 0xa0: - case 0xa1: - case 0xa2: - case 0xa3: - case 0xa4: - case 0xa5: - case 0xa6: - case 0xa7: - case 0xa8: - case 0xa9: - case 0xaa: - case 0xab: - case 0xac: - case 0xad: - case 0xae: - case 0xaf: - case 0xb0: - case 0xb1: - case 0xb2: - case 0xb3: - case 0xb4: - case 0xb5: - case 0xb6: - case 0xb7: - { - basic_json result = value_t::object; - const auto len = static_cast(current & 0x1f); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_cbor_string(); - result[key] = parse_cbor(); - } - return result; + oa->write_character(to_char_type(0x98)); + write_number(static_cast(N)); } - - case 0xb8: // map (one-byte uint8_t for n follows) + else if (N <= (std::numeric_limits::max)()) { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_cbor_string(); - result[key] = parse_cbor(); - } - return result; + oa->write_character(to_char_type(0x99)); + write_number(static_cast(N)); } - - case 0xb9: // map (two-byte uint16_t for n follow) + else if (N <= (std::numeric_limits::max)()) { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_cbor_string(); - result[key] = parse_cbor(); - } - return result; + oa->write_character(to_char_type(0x9A)); + write_number(static_cast(N)); } - - case 0xba: // map (four-byte uint32_t for n follow) + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_cbor_string(); - result[key] = parse_cbor(); - } - return result; + oa->write_character(to_char_type(0x9B)); + write_number(static_cast(N)); } + // LCOV_EXCL_STOP - case 0xbb: // map (eight-byte uint64_t for n follow) + // step 2: write each element + for (const auto& el : *j.m_value.array) { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_cbor_string(); - result[key] = parse_cbor(); - } - return result; + write_cbor(el); } + break; + } - case 0xbf: // map (indefinite length) + case value_t::binary: + { + if (j.m_value.binary->has_subtype()) { - basic_json result = value_t::object; - while (get() != 0xff) - { - auto key = get_cbor_string(); - result[key] = parse_cbor(); - } - return result; + write_number(static_cast(0xd8)); + write_number(j.m_value.binary->subtype()); } - case 0xf4: // false + // step 1: write control byte and the binary array size + const auto N = j.m_value.binary->size(); + if (N <= 0x17) { - return false; + write_number(static_cast(0x40 + N)); } - - case 0xf5: // true + else if (N <= (std::numeric_limits::max)()) { - return true; + oa->write_character(to_char_type(0x58)); + write_number(static_cast(N)); } - - case 0xf6: // null + else if (N <= (std::numeric_limits::max)()) { - return value_t::null; + oa->write_character(to_char_type(0x59)); + write_number(static_cast(N)); } - - case 0xf9: // Half-Precision Float (two-byte IEEE 754) + else if (N <= (std::numeric_limits::max)()) { - const int byte1 = get(); - check_eof(); - const int byte2 = get(); - check_eof(); - - // code from RFC 7049, Appendix D, Figure 3: - // As half-precision floating-point numbers were only added - // to IEEE 754 in 2008, today's programming platforms often - // still only have limited support for them. It is very - // easy to include at least decoding support for them even - // without such support. An example of a small decoder for - // half-precision floating-point numbers in the C language - // is shown in Fig. 3. - const int half = (byte1 << 8) + byte2; - const int exp = (half >> 10) & 0x1f; - const int mant = half & 0x3ff; - double val; - if (exp == 0) - { - val = std::ldexp(mant, -24); - } - else if (exp != 31) - { - val = std::ldexp(mant + 1024, exp - 25); - } - else - { - val = mant == 0 - ? std::numeric_limits::infinity() - : std::numeric_limits::quiet_NaN(); - } - return (half & 0x8000) != 0 ? -val : val; + oa->write_character(to_char_type(0x5A)); + write_number(static_cast(N)); } - - case 0xfa: // Single-Precision Float (four-byte IEEE 754) + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) { - return get_number(); + oa->write_character(to_char_type(0x5B)); + write_number(static_cast(N)); } + // LCOV_EXCL_STOP + + // step 2: write each element + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + N); + + break; + } - case 0xfb: // Double-Precision Float (eight-byte IEEE 754) + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 0x17) + { + write_number(static_cast(0xA0 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xB8)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xB9)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xBA)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) { - return get_number(); + oa->write_character(to_char_type(0xBB)); + write_number(static_cast(N)); } + // LCOV_EXCL_STOP - default: // anything else (0xFF is handled inside the other types) + // step 2: write each element + for (const auto& el : *j.m_value.object) { - std::stringstream ss; - ss << std::setw(2) << std::setfill('0') << std::hex << current; - JSON_THROW(parse_error::create(112, chars_read, "error reading CBOR; last byte: 0x" + ss.str())); + write_cbor(el.first); + write_cbor(el.second); } + break; } + + default: + break; } + } - /*! - @brief create a JSON value from MessagePack input + /*! + @param[in] j JSON value to serialize + */ + void write_msgpack(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: // nil + { + oa->write_character(to_char_type(0xC0)); + break; + } - @return JSON value created from MessagePack input + case value_t::boolean: // true and false + { + oa->write_character(j.m_value.boolean + ? to_char_type(0xC3) + : to_char_type(0xC2)); + break; + } - @throw parse_error.110 if input ended unexpectedly - @throw parse_error.112 if unsupported byte was read - */ - basic_json parse_msgpack() - { - switch (get()) + case value_t::number_integer: { - // EOF - case std::char_traits::eof(): + if (j.m_value.number_integer >= 0) { - JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); - } - - // positive fixint - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x08: - case 0x09: - case 0x0a: - case 0x0b: - case 0x0c: - case 0x0d: - case 0x0e: - case 0x0f: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: - case 0x20: - case 0x21: - case 0x22: - case 0x23: - case 0x24: - case 0x25: - case 0x26: - case 0x27: - case 0x28: - case 0x29: - case 0x2a: - case 0x2b: - case 0x2c: - case 0x2d: - case 0x2e: - case 0x2f: - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: - case 0x38: - case 0x39: - case 0x3a: - case 0x3b: - case 0x3c: - case 0x3d: - case 0x3e: - case 0x3f: - case 0x40: - case 0x41: - case 0x42: - case 0x43: - case 0x44: - case 0x45: - case 0x46: - case 0x47: - case 0x48: - case 0x49: - case 0x4a: - case 0x4b: - case 0x4c: - case 0x4d: - case 0x4e: - case 0x4f: - case 0x50: - case 0x51: - case 0x52: - case 0x53: - case 0x54: - case 0x55: - case 0x56: - case 0x57: - case 0x58: - case 0x59: - case 0x5a: - case 0x5b: - case 0x5c: - case 0x5d: - case 0x5e: - case 0x5f: - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6a: - case 0x6b: - case 0x6c: - case 0x6d: - case 0x6e: - case 0x6f: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: - case 0x78: - case 0x79: - case 0x7a: - case 0x7b: - case 0x7c: - case 0x7d: - case 0x7e: - case 0x7f: - { - return static_cast(current); - } - - // fixmap - case 0x80: - case 0x81: - case 0x82: - case 0x83: - case 0x84: - case 0x85: - case 0x86: - case 0x87: - case 0x88: - case 0x89: - case 0x8a: - case 0x8b: - case 0x8c: - case 0x8d: - case 0x8e: - case 0x8f: - { - basic_json result = value_t::object; - const auto len = static_cast(current & 0x0f); - for (size_t i = 0; i < len; ++i) + // MessagePack does not differentiate between positive + // signed integers and unsigned integers. Therefore, we used + // the code from the value_t::number_unsigned case here. + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(to_char_type(0xCC)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(to_char_type(0xCD)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { - get(); - auto key = get_msgpack_string(); - result[key] = parse_msgpack(); + // uint 32 + oa->write_character(to_char_type(0xCE)); + write_number(static_cast(j.m_value.number_integer)); } - return result; - } - - // fixarray - case 0x90: - case 0x91: - case 0x92: - case 0x93: - case 0x94: - case 0x95: - case 0x96: - case 0x97: - case 0x98: - case 0x99: - case 0x9a: - case 0x9b: - case 0x9c: - case 0x9d: - case 0x9e: - case 0x9f: - { - basic_json result = value_t::array; - const auto len = static_cast(current & 0x0f); - for (size_t i = 0; i < len; ++i) + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { - result.push_back(parse_msgpack()); + // uint 64 + oa->write_character(to_char_type(0xCF)); + write_number(static_cast(j.m_value.number_integer)); } - return result; - } - - // fixstr - case 0xa0: - case 0xa1: - case 0xa2: - case 0xa3: - case 0xa4: - case 0xa5: - case 0xa6: - case 0xa7: - case 0xa8: - case 0xa9: - case 0xaa: - case 0xab: - case 0xac: - case 0xad: - case 0xae: - case 0xaf: - case 0xb0: - case 0xb1: - case 0xb2: - case 0xb3: - case 0xb4: - case 0xb5: - case 0xb6: - case 0xb7: - case 0xb8: - case 0xb9: - case 0xba: - case 0xbb: - case 0xbc: - case 0xbd: - case 0xbe: - case 0xbf: - { - return get_msgpack_string(); - } - - case 0xc0: // nil - { - return value_t::null; - } - - case 0xc2: // false + } + else { - return false; + if (j.m_value.number_integer >= -32) + { + // negative fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 8 + oa->write_character(to_char_type(0xD0)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 16 + oa->write_character(to_char_type(0xD1)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 32 + oa->write_character(to_char_type(0xD2)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 64 + oa->write_character(to_char_type(0xD3)); + write_number(static_cast(j.m_value.number_integer)); + } } + break; + } - case 0xc3: // true + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned < 128) { - return true; + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); } - - case 0xca: // float 32 + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { - return get_number(); + // uint 8 + oa->write_character(to_char_type(0xCC)); + write_number(static_cast(j.m_value.number_integer)); } - - case 0xcb: // float 64 + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { - return get_number(); + // uint 16 + oa->write_character(to_char_type(0xCD)); + write_number(static_cast(j.m_value.number_integer)); } - - case 0xcc: // uint 8 + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { - return get_number(); + // uint 32 + oa->write_character(to_char_type(0xCE)); + write_number(static_cast(j.m_value.number_integer)); } - - case 0xcd: // uint 16 + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { - return get_number(); + // uint 64 + oa->write_character(to_char_type(0xCF)); + write_number(static_cast(j.m_value.number_integer)); } + break; + } + + case value_t::number_float: + { + write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack); + break; + } - case 0xce: // uint 32 + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 31) { - return get_number(); + // fixstr + write_number(static_cast(0xA0 | N)); } - - case 0xcf: // uint 64 + else if (N <= (std::numeric_limits::max)()) { - return get_number(); + // str 8 + oa->write_character(to_char_type(0xD9)); + write_number(static_cast(N)); } - - case 0xd0: // int 8 + else if (N <= (std::numeric_limits::max)()) { - return get_number(); + // str 16 + oa->write_character(to_char_type(0xDA)); + write_number(static_cast(N)); } - - case 0xd1: // int 16 + else if (N <= (std::numeric_limits::max)()) { - return get_number(); + // str 32 + oa->write_character(to_char_type(0xDB)); + write_number(static_cast(N)); } - case 0xd2: // int 32 + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 15) { - return get_number(); + // fixarray + write_number(static_cast(0x90 | N)); } - - case 0xd3: // int 64 + else if (N <= (std::numeric_limits::max)()) { - return get_number(); + // array 16 + oa->write_character(to_char_type(0xDC)); + write_number(static_cast(N)); } - - case 0xd9: // str 8 - case 0xda: // str 16 - case 0xdb: // str 32 + else if (N <= (std::numeric_limits::max)()) { - return get_msgpack_string(); + // array 32 + oa->write_character(to_char_type(0xDD)); + write_number(static_cast(N)); } - case 0xdc: // array 16 + // step 2: write each element + for (const auto& el : *j.m_value.array) { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_msgpack()); - } - return result; + write_msgpack(el); } + break; + } - case 0xdd: // array 32 + case value_t::binary: + { + // step 0: determine if the binary type has a set subtype to + // determine whether or not to use the ext or fixext types + const bool use_ext = j.m_value.binary->has_subtype(); + + // step 1: write control byte and the byte string length + const auto N = j.m_value.binary->size(); + if (N <= (std::numeric_limits::max)()) { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) + std::uint8_t output_type{}; + bool fixed = true; + if (use_ext) { - result.push_back(parse_msgpack()); - } - return result; - } + switch (N) + { + case 1: + output_type = 0xD4; // fixext 1 + break; + case 2: + output_type = 0xD5; // fixext 2 + break; + case 4: + output_type = 0xD6; // fixext 4 + break; + case 8: + output_type = 0xD7; // fixext 8 + break; + case 16: + output_type = 0xD8; // fixext 16 + break; + default: + output_type = 0xC7; // ext 8 + fixed = false; + break; + } - case 0xde: // map 16 - { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) + } + else { - get(); - auto key = get_msgpack_string(); - result[key] = parse_msgpack(); + output_type = 0xC4; // bin 8 + fixed = false; } - return result; - } - case 0xdf: // map 32 - { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) + oa->write_character(to_char_type(output_type)); + if (!fixed) { - get(); - auto key = get_msgpack_string(); - result[key] = parse_msgpack(); + write_number(static_cast(N)); } - return result; } + else if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type = use_ext + ? 0xC8 // ext 16 + : 0xC5; // bin 16 - // positive fixint - case 0xe0: - case 0xe1: - case 0xe2: - case 0xe3: - case 0xe4: - case 0xe5: - case 0xe6: - case 0xe7: - case 0xe8: - case 0xe9: - case 0xea: - case 0xeb: - case 0xec: - case 0xed: - case 0xee: - case 0xef: - case 0xf0: - case 0xf1: - case 0xf2: - case 0xf3: - case 0xf4: - case 0xf5: - case 0xf6: - case 0xf7: - case 0xf8: - case 0xf9: - case 0xfa: - case 0xfb: - case 0xfc: - case 0xfd: - case 0xfe: - case 0xff: + oa->write_character(to_char_type(output_type)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) { - return static_cast(current); + std::uint8_t output_type = use_ext + ? 0xC9 // ext 32 + : 0xC6; // bin 32 + + oa->write_character(to_char_type(output_type)); + write_number(static_cast(N)); } - default: // anything else + // step 1.5: if this is an ext type, write the subtype + if (use_ext) { - std::stringstream ss; - ss << std::setw(2) << std::setfill('0') << std::hex << current; - JSON_THROW(parse_error::create(112, chars_read, "error reading MessagePack; last byte: 0x" + ss.str())); + write_number(static_cast(j.m_value.binary->subtype())); } - } - } - - /*! - @brief determine system byte order - @return true iff system's byte order is little endian + // step 2: write the byte string + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + N); - @note from http://stackoverflow.com/a/1001328/266378 - */ - static bool little_endianess() noexcept - { - int num = 1; - return (*reinterpret_cast(&num) == 1); - } + break; + } - private: - /*! - @brief get next character from the input + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 15) + { + // fixmap + write_number(static_cast(0x80 | (N & 0xF))); + } + else if (N <= (std::numeric_limits::max)()) + { + // map 16 + oa->write_character(to_char_type(0xDE)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // map 32 + oa->write_character(to_char_type(0xDF)); + write_number(static_cast(N)); + } - This function provides the interface to the used input adapter. It does - not throw in case the input reached EOF, but returns - `std::char_traits::eof()` in that case. + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_msgpack(el.first); + write_msgpack(el.second); + } + break; + } - @return character read from the input - */ - int get() - { - ++chars_read; - return (current = ia->get_character()); + default: + break; } + } - /* - @brief read a number from the input - - @tparam T the type of the number - - @return number of type @a T - - @note This function needs to respect the system's endianess, because - bytes in CBOR and MessagePack are stored in network order (big - endian) and therefore need reordering on little endian systems. - - @throw parse_error.110 if input has less than `sizeof(T)` bytes - */ - template - T get_number() + /*! + @param[in] j JSON value to serialize + @param[in] use_count whether to use '#' prefixes (optimized format) + @param[in] use_type whether to use '$' prefixes (optimized format) + @param[in] add_prefix whether prefixes need to be used for this value + */ + void write_ubjson(const BasicJsonType& j, const bool use_count, + const bool use_type, const bool add_prefix = true) + { + switch (j.type()) { - // step 1: read input into array with system's byte order - std::array vec; - for (size_t i = 0; i < sizeof(T); ++i) + case value_t::null: { - get(); - check_eof(); - - // reverse byte order prior to conversion if necessary - if (is_little_endian) + if (add_prefix) { - vec[sizeof(T) - i - 1] = static_cast(current); + oa->write_character(to_char_type('Z')); } - else + break; + } + + case value_t::boolean: + { + if (add_prefix) { - vec[i] = static_cast(current); + oa->write_character(j.m_value.boolean + ? to_char_type('T') + : to_char_type('F')); } + break; } - // step 2: convert array into number of type T and return - T result; - std::memcpy(&result, vec.data(), sizeof(T)); - return result; - } - - /*! - @brief create a string by reading characters from the input + case value_t::number_integer: + { + write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix); + break; + } - @param[in] len number of bytes to read + case value_t::number_unsigned: + { + write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix); + break; + } - @return string created by reading @a len bytes + case value_t::number_float: + { + write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix); + break; + } - @throw parse_error.110 if input has less than @a len bytes - */ - std::string get_string(const size_t len) - { - std::string result; - for (size_t i = 0; i < len; ++i) + case value_t::string: { - get(); - check_eof(); - result.append(1, static_cast(current)); + if (add_prefix) + { + oa->write_character(to_char_type('S')); + } + write_number_with_ubjson_prefix(j.m_value.string->size(), true); + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; } - return result; - } - /*! - @brief reads a CBOR string + case value_t::array: + { + if (add_prefix) + { + oa->write_character(to_char_type('[')); + } - This function first reads starting bytes to determine the expected - string length and then copies this number of bytes into a string. - Additionally, CBOR's strings with indefinite lengths are supported. + bool prefix_required = true; + if (use_type && !j.m_value.array->empty()) + { + JSON_ASSERT(use_count); + const CharType first_prefix = ubjson_prefix(j.front()); + const bool same_prefix = std::all_of(j.begin() + 1, j.end(), + [this, first_prefix](const BasicJsonType & v) + { + return ubjson_prefix(v) == first_prefix; + }); - @return string + if (same_prefix) + { + prefix_required = false; + oa->write_character(to_char_type('$')); + oa->write_character(first_prefix); + } + } - @throw parse_error.110 if input ended - @throw parse_error.113 if an unexpexted byte is read - */ - std::string get_cbor_string() - { - check_eof(); + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.array->size(), true); + } - switch (current) - { - // UTF-8 string (0x00..0x17 bytes follow) - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6a: - case 0x6b: - case 0x6c: - case 0x6d: - case 0x6e: - case 0x6f: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: + for (const auto& el : *j.m_value.array) { - const auto len = static_cast(current & 0x1f); - return get_string(len); + write_ubjson(el, use_count, use_type, prefix_required); } - case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + if (!use_count) { - const auto len = static_cast(get_number()); - return get_string(len); + oa->write_character(to_char_type(']')); } - case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + break; + } + + case value_t::binary: + { + if (add_prefix) { - const auto len = static_cast(get_number()); - return get_string(len); + oa->write_character(to_char_type('[')); } - case 0x7a: // UTF-8 string (four-byte uint32_t for n follow) + if (use_type && !j.m_value.binary->empty()) { - const auto len = static_cast(get_number()); - return get_string(len); + JSON_ASSERT(use_count); + oa->write_character(to_char_type('$')); + oa->write_character('U'); } - case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow) + if (use_count) { - const auto len = static_cast(get_number()); - return get_string(len); + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.binary->size(), true); } - case 0x7f: // UTF-8 string (indefinite length) + if (use_type) + { + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + j.m_value.binary->size()); + } + else { - std::string result; - while (get() != 0xff) + for (size_t i = 0; i < j.m_value.binary->size(); ++i) { - check_eof(); - result.append(1, static_cast(current)); + oa->write_character(to_char_type('U')); + oa->write_character(j.m_value.binary->data()[i]); } - return result; } - default: + if (!use_count) { - std::stringstream ss; - ss << std::setw(2) << std::setfill('0') << std::hex << current; - JSON_THROW(parse_error::create(113, chars_read, "expected a CBOR string; last byte: 0x" + ss.str())); + oa->write_character(to_char_type(']')); } - } - } - - /*! - @brief reads a MessagePack string - This function first reads starting bytes to determine the expected - string length and then copies this number of bytes into a string. - - @return string - - @throw parse_error.110 if input ended - @throw parse_error.113 if an unexpexted byte is read - */ - std::string get_msgpack_string() - { - check_eof(); + break; + } - switch (current) + case value_t::object: { - // fixstr - case 0xa0: - case 0xa1: - case 0xa2: - case 0xa3: - case 0xa4: - case 0xa5: - case 0xa6: - case 0xa7: - case 0xa8: - case 0xa9: - case 0xaa: - case 0xab: - case 0xac: - case 0xad: - case 0xae: - case 0xaf: - case 0xb0: - case 0xb1: - case 0xb2: - case 0xb3: - case 0xb4: - case 0xb5: - case 0xb6: - case 0xb7: - case 0xb8: - case 0xb9: - case 0xba: - case 0xbb: - case 0xbc: - case 0xbd: - case 0xbe: - case 0xbf: + if (add_prefix) { - const auto len = static_cast(current & 0x1f); - return get_string(len); + oa->write_character(to_char_type('{')); } - case 0xd9: // str 8 + bool prefix_required = true; + if (use_type && !j.m_value.object->empty()) { - const auto len = static_cast(get_number()); - return get_string(len); + JSON_ASSERT(use_count); + const CharType first_prefix = ubjson_prefix(j.front()); + const bool same_prefix = std::all_of(j.begin(), j.end(), + [this, first_prefix](const BasicJsonType & v) + { + return ubjson_prefix(v) == first_prefix; + }); + + if (same_prefix) + { + prefix_required = false; + oa->write_character(to_char_type('$')); + oa->write_character(first_prefix); + } } - case 0xda: // str 16 + if (use_count) { - const auto len = static_cast(get_number()); - return get_string(len); + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.object->size(), true); } - case 0xdb: // str 32 + for (const auto& el : *j.m_value.object) { - const auto len = static_cast(get_number()); - return get_string(len); + write_number_with_ubjson_prefix(el.first.size(), true); + oa->write_characters( + reinterpret_cast(el.first.c_str()), + el.first.size()); + write_ubjson(el.second, use_count, use_type, prefix_required); } - default: + if (!use_count) { - std::stringstream ss; - ss << std::setw(2) << std::setfill('0') << std::hex << current; - JSON_THROW(parse_error::create(113, chars_read, "expected a MessagePack string; last byte: 0x" + ss.str())); + oa->write_character(to_char_type('}')); } + + break; } + + default: + break; } + } + + private: + ////////// + // BSON // + ////////// - /*! - @brief check if input ended - @throw parse_error.110 if input ended - */ - void check_eof() const + /*! + @return The size of a BSON document entry header, including the id marker + and the entry name size (and its null-terminator). + */ + static std::size_t calc_bson_entry_header_size(const string_t& name) + { + const auto it = name.find(static_cast(0)); + if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) { - if (JSON_UNLIKELY(current == std::char_traits::eof())) - { - JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); - } + JSON_THROW(out_of_range::create(409, + "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")")); } - private: - /// input adapter - input_adapter_t ia = nullptr; + return /*id*/ 1ul + name.size() + /*zero-terminator*/1u; + } + + /*! + @brief Writes the given @a element_type and @a name to the output adapter + */ + void write_bson_entry_header(const string_t& name, + const std::uint8_t element_type) + { + oa->write_character(to_char_type(element_type)); // boolean + oa->write_characters( + reinterpret_cast(name.c_str()), + name.size() + 1u); + } + + /*! + @brief Writes a BSON element with key @a name and boolean value @a value + */ + void write_bson_boolean(const string_t& name, + const bool value) + { + write_bson_entry_header(name, 0x08); + oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00)); + } + + /*! + @brief Writes a BSON element with key @a name and double value @a value + */ + void write_bson_double(const string_t& name, + const double value) + { + write_bson_entry_header(name, 0x01); + write_number(value); + } + + /*! + @return The size of the BSON-encoded string in @a value + */ + static std::size_t calc_bson_string_size(const string_t& value) + { + return sizeof(std::int32_t) + value.size() + 1ul; + } - /// the current character - int current = std::char_traits::eof(); + /*! + @brief Writes a BSON element with key @a name and string value @a value + */ + void write_bson_string(const string_t& name, + const string_t& value) + { + write_bson_entry_header(name, 0x02); - /// the number of characters read - size_t chars_read = 0; + write_number(static_cast(value.size() + 1ul)); + oa->write_characters( + reinterpret_cast(value.c_str()), + value.size() + 1); + } - /// whether we can assume little endianess - const bool is_little_endian = true; - }; + /*! + @brief Writes a BSON element with key @a name and null value + */ + void write_bson_null(const string_t& name) + { + write_bson_entry_header(name, 0x0A); + } /*! - @brief serialization to CBOR and MessagePack values + @return The size of the BSON-encoded integer @a value */ - class binary_writer + static std::size_t calc_bson_integer_size(const std::int64_t value) { - public: - /*! - @brief create a binary writer + return (std::numeric_limits::min)() <= value && value <= (std::numeric_limits::max)() + ? sizeof(std::int32_t) + : sizeof(std::int64_t); + } - @param[in] adapter output adapter to write to - */ - explicit binary_writer(output_adapter_t adapter) - : is_little_endian(binary_reader::little_endianess()), oa(adapter) + /*! + @brief Writes a BSON element with key @a name and integer @a value + */ + void write_bson_integer(const string_t& name, + const std::int64_t value) + { + if ((std::numeric_limits::min)() <= value && value <= (std::numeric_limits::max)()) + { + write_bson_entry_header(name, 0x10); // int32 + write_number(static_cast(value)); + } + else { - assert(oa); + write_bson_entry_header(name, 0x12); // int64 + write_number(static_cast(value)); } + } + + /*! + @return The size of the BSON-encoded unsigned integer in @a j + */ + static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept + { + return (value <= static_cast((std::numeric_limits::max)())) + ? sizeof(std::int32_t) + : sizeof(std::int64_t); + } - /*! - @brief[in] j JSON value to serialize - */ - void write_cbor(const basic_json& j) + /*! + @brief Writes a BSON element with key @a name and unsigned @a value + */ + void write_bson_unsigned(const string_t& name, + const std::uint64_t value) + { + if (value <= static_cast((std::numeric_limits::max)())) { - switch (j.type()) - { - case value_t::null: - { - oa->write_character(0xf6); - break; - } + write_bson_entry_header(name, 0x10 /* int32 */); + write_number(static_cast(value)); + } + else if (value <= static_cast((std::numeric_limits::max)())) + { + write_bson_entry_header(name, 0x12 /* int64 */); + write_number(static_cast(value)); + } + else + { + JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(value) + " cannot be represented by BSON as it does not fit int64")); + } + } - case value_t::boolean: - { - oa->write_character(j.m_value.boolean ? 0xf5 : 0xf4); - break; - } + /*! + @brief Writes a BSON element with key @a name and object @a value + */ + void write_bson_object_entry(const string_t& name, + const typename BasicJsonType::object_t& value) + { + write_bson_entry_header(name, 0x03); // object + write_bson_object(value); + } - case value_t::number_integer: - { - if (j.m_value.number_integer >= 0) - { - // CBOR does not differentiate between positive signed - // integers and unsigned integers. Therefore, we used the - // code from the value_t::number_unsigned case here. - if (j.m_value.number_integer <= 0x17) - { - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer <= (std::numeric_limits::max)()) - { - oa->write_character(0x18); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer <= (std::numeric_limits::max)()) - { - oa->write_character(0x19); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer <= (std::numeric_limits::max)()) - { - oa->write_character(0x1a); - write_number(static_cast(j.m_value.number_integer)); - } - else - { - oa->write_character(0x1b); - write_number(static_cast(j.m_value.number_integer)); - } - } - else - { - // The conversions below encode the sign in the first - // byte, and the value is converted to a positive number. - const auto positive_number = -1 - j.m_value.number_integer; - if (j.m_value.number_integer >= -24) - { - write_number(static_cast(0x20 + positive_number)); - } - else if (positive_number <= (std::numeric_limits::max)()) - { - oa->write_character(0x38); - write_number(static_cast(positive_number)); - } - else if (positive_number <= (std::numeric_limits::max)()) - { - oa->write_character(0x39); - write_number(static_cast(positive_number)); - } - else if (positive_number <= (std::numeric_limits::max)()) - { - oa->write_character(0x3a); - write_number(static_cast(positive_number)); - } - else - { - oa->write_character(0x3b); - write_number(static_cast(positive_number)); - } - } - break; - } + /*! + @return The size of the BSON-encoded array @a value + */ + static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value) + { + std::size_t array_index = 0ul; - case value_t::number_unsigned: - { - if (j.m_value.number_unsigned <= 0x17) - { - write_number(static_cast(j.m_value.number_unsigned)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - oa->write_character(0x18); - write_number(static_cast(j.m_value.number_unsigned)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - oa->write_character(0x19); - write_number(static_cast(j.m_value.number_unsigned)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - oa->write_character(0x1a); - write_number(static_cast(j.m_value.number_unsigned)); - } - else - { - oa->write_character(0x1b); - write_number(static_cast(j.m_value.number_unsigned)); - } - break; - } + const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), std::size_t(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el) + { + return result + calc_bson_element_size(std::to_string(array_index++), el); + }); - case value_t::number_float: - { - // Double-Precision Float - oa->write_character(0xfb); - write_number(j.m_value.number_float); - break; - } + return sizeof(std::int32_t) + embedded_document_size + 1ul; + } - case value_t::string: - { - // step 1: write control byte and the string length - const auto N = j.m_value.string->size(); - if (N <= 0x17) - { - write_number(static_cast(0x60 + N)); - } - else if (N <= 0xff) - { - oa->write_character(0x78); - write_number(static_cast(N)); - } - else if (N <= 0xffff) - { - oa->write_character(0x79); - write_number(static_cast(N)); - } - else if (N <= 0xffffffff) - { - oa->write_character(0x7a); - write_number(static_cast(N)); - } - // LCOV_EXCL_START - else if (N <= 0xffffffffffffffff) - { - oa->write_character(0x7b); - write_number(static_cast(N)); - } - // LCOV_EXCL_STOP + /*! + @return The size of the BSON-encoded binary array @a value + */ + static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value) + { + return sizeof(std::int32_t) + value.size() + 1ul; + } - // step 2: write the string - oa->write_characters(reinterpret_cast(j.m_value.string->c_str()), - j.m_value.string->size()); - break; - } + /*! + @brief Writes a BSON element with key @a name and array @a value + */ + void write_bson_array(const string_t& name, + const typename BasicJsonType::array_t& value) + { + write_bson_entry_header(name, 0x04); // array + write_number(static_cast(calc_bson_array_size(value))); - case value_t::array: - { - // step 1: write control byte and the array size - const auto N = j.m_value.array->size(); - if (N <= 0x17) - { - write_number(static_cast(0x80 + N)); - } - else if (N <= 0xff) - { - oa->write_character(0x98); - write_number(static_cast(N)); - } - else if (N <= 0xffff) - { - oa->write_character(0x99); - write_number(static_cast(N)); - } - else if (N <= 0xffffffff) - { - oa->write_character(0x9a); - write_number(static_cast(N)); - } - // LCOV_EXCL_START - else if (N <= 0xffffffffffffffff) - { - oa->write_character(0x9b); - write_number(static_cast(N)); - } - // LCOV_EXCL_STOP + std::size_t array_index = 0ul; - // step 2: write each element - for (const auto& el : *j.m_value.array) - { - write_cbor(el); - } - break; - } + for (const auto& el : value) + { + write_bson_element(std::to_string(array_index++), el); + } - case value_t::object: - { - // step 1: write control byte and the object size - const auto N = j.m_value.object->size(); - if (N <= 0x17) - { - write_number(static_cast(0xa0 + N)); - } - else if (N <= 0xff) - { - oa->write_character(0xb8); - write_number(static_cast(N)); - } - else if (N <= 0xffff) - { - oa->write_character(0xb9); - write_number(static_cast(N)); - } - else if (N <= 0xffffffff) - { - oa->write_character(0xba); - write_number(static_cast(N)); - } - // LCOV_EXCL_START - else if (N <= 0xffffffffffffffff) - { - oa->write_character(0xbb); - write_number(static_cast(N)); - } - // LCOV_EXCL_STOP + oa->write_character(to_char_type(0x00)); + } - // step 2: write each element - for (const auto& el : *j.m_value.object) - { - write_cbor(el.first); - write_cbor(el.second); - } - break; - } + /*! + @brief Writes a BSON element with key @a name and binary value @a value + */ + void write_bson_binary(const string_t& name, + const binary_t& value) + { + write_bson_entry_header(name, 0x05); - default: - { - break; - } - } + write_number(static_cast(value.size())); + write_number(value.has_subtype() ? value.subtype() : std::uint8_t(0x00)); + + oa->write_characters(reinterpret_cast(value.data()), value.size()); + } + + /*! + @brief Calculates the size necessary to serialize the JSON value @a j with its @a name + @return The calculated size for the BSON document entry for @a j with the given @a name. + */ + static std::size_t calc_bson_element_size(const string_t& name, + const BasicJsonType& j) + { + const auto header_size = calc_bson_entry_header_size(name); + switch (j.type()) + { + case value_t::object: + return header_size + calc_bson_object_size(*j.m_value.object); + + case value_t::array: + return header_size + calc_bson_array_size(*j.m_value.array); + + case value_t::binary: + return header_size + calc_bson_binary_size(*j.m_value.binary); + + case value_t::boolean: + return header_size + 1ul; + + case value_t::number_float: + return header_size + 8ul; + + case value_t::number_integer: + return header_size + calc_bson_integer_size(j.m_value.number_integer); + + case value_t::number_unsigned: + return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned); + + case value_t::string: + return header_size + calc_bson_string_size(*j.m_value.string); + + case value_t::null: + return header_size + 0ul; + + // LCOV_EXCL_START + default: + JSON_ASSERT(false); + return 0ul; + // LCOV_EXCL_STOP } + } - /*! - @brief[in] j JSON value to serialize - */ - void write_msgpack(const basic_json& j) + /*! + @brief Serializes the JSON value @a j to BSON and associates it with the + key @a name. + @param name The name to associate with the JSON entity @a j within the + current BSON document + @return The size of the BSON entry + */ + void write_bson_element(const string_t& name, + const BasicJsonType& j) + { + switch (j.type()) { - switch (j.type()) - { - case value_t::null: - { - // nil - oa->write_character(0xc0); - break; - } + case value_t::object: + return write_bson_object_entry(name, *j.m_value.object); - case value_t::boolean: - { - // true and false - oa->write_character(j.m_value.boolean ? 0xc3 : 0xc2); - break; - } + case value_t::array: + return write_bson_array(name, *j.m_value.array); - case value_t::number_integer: - { - if (j.m_value.number_integer >= 0) - { - // MessagePack does not differentiate between positive - // signed integers and unsigned integers. Therefore, we - // used the code from the value_t::number_unsigned case - // here. - if (j.m_value.number_unsigned < 128) - { - // positive fixnum - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 8 - oa->write_character(0xcc); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 16 - oa->write_character(0xcd); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 32 - oa->write_character(0xce); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 64 - oa->write_character(0xcf); - write_number(static_cast(j.m_value.number_integer)); - } - } - else - { - if (j.m_value.number_integer >= -32) - { - // negative fixnum - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) - { - // int 8 - oa->write_character(0xd0); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) - { - // int 16 - oa->write_character(0xd1); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) - { - // int 32 - oa->write_character(0xd2); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) - { - // int 64 - oa->write_character(0xd3); - write_number(static_cast(j.m_value.number_integer)); - } - } - break; - } + case value_t::binary: + return write_bson_binary(name, *j.m_value.binary); - case value_t::number_unsigned: + case value_t::boolean: + return write_bson_boolean(name, j.m_value.boolean); + + case value_t::number_float: + return write_bson_double(name, j.m_value.number_float); + + case value_t::number_integer: + return write_bson_integer(name, j.m_value.number_integer); + + case value_t::number_unsigned: + return write_bson_unsigned(name, j.m_value.number_unsigned); + + case value_t::string: + return write_bson_string(name, *j.m_value.string); + + case value_t::null: + return write_bson_null(name); + + // LCOV_EXCL_START + default: + JSON_ASSERT(false); + return; + // LCOV_EXCL_STOP + } + } + + /*! + @brief Calculates the size of the BSON serialization of the given + JSON-object @a j. + @param[in] j JSON value to serialize + @pre j.type() == value_t::object + */ + static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value) + { + std::size_t document_size = std::accumulate(value.begin(), value.end(), std::size_t(0), + [](size_t result, const typename BasicJsonType::object_t::value_type & el) + { + return result += calc_bson_element_size(el.first, el.second); + }); + + return sizeof(std::int32_t) + document_size + 1ul; + } + + /*! + @param[in] j JSON value to serialize + @pre j.type() == value_t::object + */ + void write_bson_object(const typename BasicJsonType::object_t& value) + { + write_number(static_cast(calc_bson_object_size(value))); + + for (const auto& el : value) + { + write_bson_element(el.first, el.second); + } + + oa->write_character(to_char_type(0x00)); + } + + ////////// + // CBOR // + ////////// + + static constexpr CharType get_cbor_float_prefix(float /*unused*/) + { + return to_char_type(0xFA); // Single-Precision Float + } + + static constexpr CharType get_cbor_float_prefix(double /*unused*/) + { + return to_char_type(0xFB); // Double-Precision Float + } + + ///////////// + // MsgPack // + ///////////// + + static constexpr CharType get_msgpack_float_prefix(float /*unused*/) + { + return to_char_type(0xCA); // float 32 + } + + static constexpr CharType get_msgpack_float_prefix(double /*unused*/) + { + return to_char_type(0xCB); // float 64 + } + + //////////// + // UBJSON // + //////////// + + // UBJSON: write number (floating point) + template::value, int>::type = 0> + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if (add_prefix) + { + oa->write_character(get_ubjson_float_prefix(n)); + } + write_number(n); + } + + // UBJSON: write number (unsigned integer) + template::value, int>::type = 0> + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('i')); // int8 + } + write_number(static_cast(n)); + } + else if (n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('U')); // uint8 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('I')); // int16 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('l')); // int32 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('L')); // int64 + } + write_number(static_cast(n)); + } + else + { + if (add_prefix) + { + oa->write_character(to_char_type('H')); // high-precision number + } + + const auto number = BasicJsonType(n).dump(); + write_number_with_ubjson_prefix(number.size(), true); + for (std::size_t i = 0; i < number.size(); ++i) + { + oa->write_character(to_char_type(static_cast(number[i]))); + } + } + } + + // UBJSON: write number (signed integer) + template < typename NumberType, typename std::enable_if < + std::is_signed::value&& + !std::is_floating_point::value, int >::type = 0 > + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('i')); // int8 + } + write_number(static_cast(n)); + } + else if (static_cast((std::numeric_limits::min)()) <= n && n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('U')); // uint8 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('I')); // int16 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('l')); // int32 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('L')); // int64 + } + write_number(static_cast(n)); + } + // LCOV_EXCL_START + else + { + if (add_prefix) + { + oa->write_character(to_char_type('H')); // high-precision number + } + + const auto number = BasicJsonType(n).dump(); + write_number_with_ubjson_prefix(number.size(), true); + for (std::size_t i = 0; i < number.size(); ++i) + { + oa->write_character(to_char_type(static_cast(number[i]))); + } + } + // LCOV_EXCL_STOP + } + + /*! + @brief determine the type prefix of container values + */ + CharType ubjson_prefix(const BasicJsonType& j) const noexcept + { + switch (j.type()) + { + case value_t::null: + return 'Z'; + + case value_t::boolean: + return j.m_value.boolean ? 'T' : 'F'; + + case value_t::number_integer: + { + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) { - if (j.m_value.number_unsigned < 128) - { - // positive fixnum - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 8 - oa->write_character(0xcc); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 16 - oa->write_character(0xcd); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 32 - oa->write_character(0xce); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 64 - oa->write_character(0xcf); - write_number(static_cast(j.m_value.number_integer)); - } - break; + return 'i'; } - - case value_t::number_float: + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) { - // float 64 - oa->write_character(0xcb); - write_number(j.m_value.number_float); - break; + return 'U'; } - - case value_t::string: + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) { - // step 1: write control byte and the string length - const auto N = j.m_value.string->size(); - if (N <= 31) - { - // fixstr - write_number(static_cast(0xa0 | N)); - } - else if (N <= 255) - { - // str 8 - oa->write_character(0xd9); - write_number(static_cast(N)); - } - else if (N <= 65535) - { - // str 16 - oa->write_character(0xda); - write_number(static_cast(N)); - } - else if (N <= 4294967295) - { - // str 32 - oa->write_character(0xdb); - write_number(static_cast(N)); - } - - // step 2: write the string - oa->write_characters(reinterpret_cast(j.m_value.string->c_str()), - j.m_value.string->size()); - break; + return 'I'; } - - case value_t::array: + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) { - // step 1: write control byte and the array size - const auto N = j.m_value.array->size(); - if (N <= 15) - { - // fixarray - write_number(static_cast(0x90 | N)); - } - else if (N <= 0xffff) - { - // array 16 - oa->write_character(0xdc); - write_number(static_cast(N)); - } - else if (N <= 0xffffffff) - { - // array 32 - oa->write_character(0xdd); - write_number(static_cast(N)); - } - - // step 2: write each element - for (const auto& el : *j.m_value.array) - { - write_msgpack(el); - } - break; + return 'l'; } - - case value_t::object: + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) { - // step 1: write control byte and the object size - const auto N = j.m_value.object->size(); - if (N <= 15) - { - // fixmap - write_number(static_cast(0x80 | (N & 0xf))); - } - else if (N <= 65535) - { - // map 16 - oa->write_character(0xde); - write_number(static_cast(N)); - } - else if (N <= 4294967295) - { - // map 32 - oa->write_character(0xdf); - write_number(static_cast(N)); - } - - // step 2: write each element - for (const auto& el : *j.m_value.object) - { - write_msgpack(el.first); - write_msgpack(el.second); - } - break; + return 'L'; } + // anything else is treated as high-precision number + return 'H'; // LCOV_EXCL_LINE + } - default: + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { - break; + return 'i'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'U'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'I'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'l'; } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'L'; + } + // anything else is treated as high-precision number + return 'H'; // LCOV_EXCL_LINE } + + case value_t::number_float: + return get_ubjson_float_prefix(j.m_value.number_float); + + case value_t::string: + return 'S'; + + case value_t::array: // fallthrough + case value_t::binary: + return '['; + + case value_t::object: + return '{'; + + default: // discarded values + return 'N'; + } + } + + static constexpr CharType get_ubjson_float_prefix(float /*unused*/) + { + return 'd'; // float 32 + } + + static constexpr CharType get_ubjson_float_prefix(double /*unused*/) + { + return 'D'; // float 64 + } + + /////////////////////// + // Utility functions // + /////////////////////// + + /* + @brief write a number to output input + @param[in] n number of type @a NumberType + @tparam NumberType the type of the number + @tparam OutputIsLittleEndian Set to true if output data is + required to be little endian + + @note This function needs to respect the system's endianess, because bytes + in CBOR, MessagePack, and UBJSON are stored in network order (big + endian) and therefore need reordering on little endian systems. + */ + template + void write_number(const NumberType n) + { + // step 1: write number to array of length NumberType + std::array vec; + std::memcpy(vec.data(), &n, sizeof(NumberType)); + + // step 2: write array to output (with possible reordering) + if (is_little_endian != OutputIsLittleEndian) + { + // reverse byte order prior to conversion if necessary + std::reverse(vec.begin(), vec.end()); + } + + oa->write_characters(vec.data(), sizeof(NumberType)); + } + + void write_compact_float(const number_float_t n, detail::input_format_t format) + { + if (static_cast(n) >= static_cast(std::numeric_limits::lowest()) && + static_cast(n) <= static_cast((std::numeric_limits::max)()) && + static_cast(static_cast(n)) == static_cast(n)) + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(static_cast(n)) + : get_msgpack_float_prefix(static_cast(n))); + write_number(static_cast(n)); + } + else + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(n) + : get_msgpack_float_prefix(n)); + write_number(n); + } + } + + public: + // The following to_char_type functions are implement the conversion + // between uint8_t and CharType. In case CharType is not unsigned, + // such a conversion is required to allow values greater than 128. + // See for a discussion. + template < typename C = CharType, + enable_if_t < std::is_signed::value && std::is_signed::value > * = nullptr > + static constexpr CharType to_char_type(std::uint8_t x) noexcept + { + return *reinterpret_cast(&x); + } + + template < typename C = CharType, + enable_if_t < std::is_signed::value && std::is_unsigned::value > * = nullptr > + static CharType to_char_type(std::uint8_t x) noexcept + { + static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t"); + static_assert(std::is_trivial::value, "CharType must be trivial"); + CharType result; + std::memcpy(&result, &x, sizeof(x)); + return result; + } + + template::value>* = nullptr> + static constexpr CharType to_char_type(std::uint8_t x) noexcept + { + return x; + } + + template < typename InputCharType, typename C = CharType, + enable_if_t < + std::is_signed::value && + std::is_signed::value && + std::is_same::type>::value + > * = nullptr > + static constexpr CharType to_char_type(InputCharType x) noexcept + { + return x; + } + + private: + /// whether we can assume little endianess + const bool is_little_endian = little_endianess(); + + /// the output + output_adapter_t oa = nullptr; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // reverse, remove, fill, find, none_of +#include // array +#include // localeconv, lconv +#include // labs, isfinite, isnan, signbit +#include // size_t, ptrdiff_t +#include // uint8_t +#include // snprintf +#include // numeric_limits +#include // string, char_traits +#include // is_same +#include // move + +// #include + + +#include // array +#include // signbit, isfinite +#include // intN_t, uintN_t +#include // memcpy, memmove +#include // numeric_limits +#include // conditional + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief implements the Grisu2 algorithm for binary to decimal floating-point +conversion. + +This implementation is a slightly modified version of the reference +implementation which may be obtained from +http://florian.loitsch.com/publications (bench.tar.gz). + +The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch. + +For a detailed description of the algorithm see: + +[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with + Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming + Language Design and Implementation, PLDI 2010 +[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately", + Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language + Design and Implementation, PLDI 1996 +*/ +namespace dtoa_impl +{ + +template +Target reinterpret_bits(const Source source) +{ + static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); + + Target target; + std::memcpy(&target, &source, sizeof(Source)); + return target; +} + +struct diyfp // f * 2^e +{ + static constexpr int kPrecision = 64; // = q + + std::uint64_t f = 0; + int e = 0; + + constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {} + + /*! + @brief returns x - y + @pre x.e == y.e and x.f >= y.f + */ + static diyfp sub(const diyfp& x, const diyfp& y) noexcept + { + JSON_ASSERT(x.e == y.e); + JSON_ASSERT(x.f >= y.f); + + return {x.f - y.f, x.e}; + } + + /*! + @brief returns x * y + @note The result is rounded. (Only the upper q bits are returned.) + */ + static diyfp mul(const diyfp& x, const diyfp& y) noexcept + { + static_assert(kPrecision == 64, "internal error"); + + // Computes: + // f = round((x.f * y.f) / 2^q) + // e = x.e + y.e + q + + // Emulate the 64-bit * 64-bit multiplication: + // + // p = u * v + // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi) + // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi ) + // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 ) + // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 ) + // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3) + // = (p0_lo ) + 2^32 (Q ) + 2^64 (H ) + // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H ) + // + // (Since Q might be larger than 2^32 - 1) + // + // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H) + // + // (Q_hi + H does not overflow a 64-bit int) + // + // = p_lo + 2^64 p_hi + + const std::uint64_t u_lo = x.f & 0xFFFFFFFFu; + const std::uint64_t u_hi = x.f >> 32u; + const std::uint64_t v_lo = y.f & 0xFFFFFFFFu; + const std::uint64_t v_hi = y.f >> 32u; + + const std::uint64_t p0 = u_lo * v_lo; + const std::uint64_t p1 = u_lo * v_hi; + const std::uint64_t p2 = u_hi * v_lo; + const std::uint64_t p3 = u_hi * v_hi; + + const std::uint64_t p0_hi = p0 >> 32u; + const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu; + const std::uint64_t p1_hi = p1 >> 32u; + const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu; + const std::uint64_t p2_hi = p2 >> 32u; + + std::uint64_t Q = p0_hi + p1_lo + p2_lo; + + // The full product might now be computed as + // + // p_hi = p3 + p2_hi + p1_hi + (Q >> 32) + // p_lo = p0_lo + (Q << 32) + // + // But in this particular case here, the full p_lo is not required. + // Effectively we only need to add the highest bit in p_lo to p_hi (and + // Q_hi + 1 does not overflow). + + Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up + + const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); + + return {h, x.e + y.e + 64}; + } + + /*! + @brief normalize x such that the significand is >= 2^(q-1) + @pre x.f != 0 + */ + static diyfp normalize(diyfp x) noexcept + { + JSON_ASSERT(x.f != 0); + + while ((x.f >> 63u) == 0) + { + x.f <<= 1u; + x.e--; + } + + return x; + } + + /*! + @brief normalize x such that the result has the exponent E + @pre e >= x.e and the upper e - x.e bits of x.f must be zero. + */ + static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept + { + const int delta = x.e - target_exponent; + + JSON_ASSERT(delta >= 0); + JSON_ASSERT(((x.f << delta) >> delta) == x.f); + + return {x.f << delta, target_exponent}; + } +}; + +struct boundaries +{ + diyfp w; + diyfp minus; + diyfp plus; +}; + +/*! +Compute the (normalized) diyfp representing the input number 'value' and its +boundaries. + +@pre value must be finite and positive +*/ +template +boundaries compute_boundaries(FloatType value) +{ + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); + + // Convert the IEEE representation into a diyfp. + // + // If v is denormal: + // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1)) + // If v is normalized: + // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1)) + + static_assert(std::numeric_limits::is_iec559, + "internal error: dtoa_short requires an IEEE-754 floating-point implementation"); + + constexpr int kPrecision = std::numeric_limits::digits; // = p (includes the hidden bit) + constexpr int kBias = std::numeric_limits::max_exponent - 1 + (kPrecision - 1); + constexpr int kMinExp = 1 - kBias; + constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1) + + using bits_type = typename std::conditional::type; + + const std::uint64_t bits = reinterpret_bits(value); + const std::uint64_t E = bits >> (kPrecision - 1); + const std::uint64_t F = bits & (kHiddenBit - 1); + + const bool is_denormal = E == 0; + const diyfp v = is_denormal + ? diyfp(F, kMinExp) + : diyfp(F + kHiddenBit, static_cast(E) - kBias); + + // Compute the boundaries m- and m+ of the floating-point value + // v = f * 2^e. + // + // Determine v- and v+, the floating-point predecessor and successor if v, + // respectively. + // + // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) + // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B) + // + // v+ = v + 2^e + // + // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_ + // between m- and m+ round to v, regardless of how the input rounding + // algorithm breaks ties. + // + // ---+-------------+-------------+-------------+-------------+--- (A) + // v- m- v m+ v+ + // + // -----------------+------+------+-------------+-------------+--- (B) + // v- m- v m+ v+ + + const bool lower_boundary_is_closer = F == 0 && E > 1; + const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); + const diyfp m_minus = lower_boundary_is_closer + ? diyfp(4 * v.f - 1, v.e - 2) // (B) + : diyfp(2 * v.f - 1, v.e - 1); // (A) + + // Determine the normalized w+ = m+. + const diyfp w_plus = diyfp::normalize(m_plus); + + // Determine w- = m- such that e_(w-) = e_(w+). + const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); + + return {diyfp::normalize(v), w_minus, w_plus}; +} + +// Given normalized diyfp w, Grisu needs to find a (normalized) cached +// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies +// within a certain range [alpha, gamma] (Definition 3.2 from [1]) +// +// alpha <= e = e_c + e_w + q <= gamma +// +// or +// +// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q +// <= f_c * f_w * 2^gamma +// +// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies +// +// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma +// +// or +// +// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma) +// +// The choice of (alpha,gamma) determines the size of the table and the form of +// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well +// in practice: +// +// The idea is to cut the number c * w = f * 2^e into two parts, which can be +// processed independently: An integral part p1, and a fractional part p2: +// +// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e +// = (f div 2^-e) + (f mod 2^-e) * 2^e +// = p1 + p2 * 2^e +// +// The conversion of p1 into decimal form requires a series of divisions and +// modulos by (a power of) 10. These operations are faster for 32-bit than for +// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be +// achieved by choosing +// +// -e >= 32 or e <= -32 := gamma +// +// In order to convert the fractional part +// +// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ... +// +// into decimal form, the fraction is repeatedly multiplied by 10 and the digits +// d[-i] are extracted in order: +// +// (10 * p2) div 2^-e = d[-1] +// (10 * p2) mod 2^-e = d[-2] / 10^1 + ... +// +// The multiplication by 10 must not overflow. It is sufficient to choose +// +// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64. +// +// Since p2 = f mod 2^-e < 2^-e, +// +// -e <= 60 or e >= -60 := alpha + +constexpr int kAlpha = -60; +constexpr int kGamma = -32; + +struct cached_power // c = f * 2^e ~= 10^k +{ + std::uint64_t f; + int e; + int k; +}; + +/*! +For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached +power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c +satisfies (Definition 3.2 from [1]) + + alpha <= e_c + e + q <= gamma. +*/ +inline cached_power get_cached_power_for_binary_exponent(int e) +{ + // Now + // + // alpha <= e_c + e + q <= gamma (1) + // ==> f_c * 2^alpha <= c * 2^e * 2^q + // + // and since the c's are normalized, 2^(q-1) <= f_c, + // + // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) + // ==> 2^(alpha - e - 1) <= c + // + // If c were an exact power of ten, i.e. c = 10^k, one may determine k as + // + // k = ceil( log_10( 2^(alpha - e - 1) ) ) + // = ceil( (alpha - e - 1) * log_10(2) ) + // + // From the paper: + // "In theory the result of the procedure could be wrong since c is rounded, + // and the computation itself is approximated [...]. In practice, however, + // this simple function is sufficient." + // + // For IEEE double precision floating-point numbers converted into + // normalized diyfp's w = f * 2^e, with q = 64, + // + // e >= -1022 (min IEEE exponent) + // -52 (p - 1) + // -52 (p - 1, possibly normalize denormal IEEE numbers) + // -11 (normalize the diyfp) + // = -1137 + // + // and + // + // e <= +1023 (max IEEE exponent) + // -52 (p - 1) + // -11 (normalize the diyfp) + // = 960 + // + // This binary exponent range [-1137,960] results in a decimal exponent + // range [-307,324]. One does not need to store a cached power for each + // k in this range. For each such k it suffices to find a cached power + // such that the exponent of the product lies in [alpha,gamma]. + // This implies that the difference of the decimal exponents of adjacent + // table entries must be less than or equal to + // + // floor( (gamma - alpha) * log_10(2) ) = 8. + // + // (A smaller distance gamma-alpha would require a larger table.) + + // NB: + // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. + + constexpr int kCachedPowersMinDecExp = -300; + constexpr int kCachedPowersDecStep = 8; + + static constexpr std::array kCachedPowers = + { + { + { 0xAB70FE17C79AC6CA, -1060, -300 }, + { 0xFF77B1FCBEBCDC4F, -1034, -292 }, + { 0xBE5691EF416BD60C, -1007, -284 }, + { 0x8DD01FAD907FFC3C, -980, -276 }, + { 0xD3515C2831559A83, -954, -268 }, + { 0x9D71AC8FADA6C9B5, -927, -260 }, + { 0xEA9C227723EE8BCB, -901, -252 }, + { 0xAECC49914078536D, -874, -244 }, + { 0x823C12795DB6CE57, -847, -236 }, + { 0xC21094364DFB5637, -821, -228 }, + { 0x9096EA6F3848984F, -794, -220 }, + { 0xD77485CB25823AC7, -768, -212 }, + { 0xA086CFCD97BF97F4, -741, -204 }, + { 0xEF340A98172AACE5, -715, -196 }, + { 0xB23867FB2A35B28E, -688, -188 }, + { 0x84C8D4DFD2C63F3B, -661, -180 }, + { 0xC5DD44271AD3CDBA, -635, -172 }, + { 0x936B9FCEBB25C996, -608, -164 }, + { 0xDBAC6C247D62A584, -582, -156 }, + { 0xA3AB66580D5FDAF6, -555, -148 }, + { 0xF3E2F893DEC3F126, -529, -140 }, + { 0xB5B5ADA8AAFF80B8, -502, -132 }, + { 0x87625F056C7C4A8B, -475, -124 }, + { 0xC9BCFF6034C13053, -449, -116 }, + { 0x964E858C91BA2655, -422, -108 }, + { 0xDFF9772470297EBD, -396, -100 }, + { 0xA6DFBD9FB8E5B88F, -369, -92 }, + { 0xF8A95FCF88747D94, -343, -84 }, + { 0xB94470938FA89BCF, -316, -76 }, + { 0x8A08F0F8BF0F156B, -289, -68 }, + { 0xCDB02555653131B6, -263, -60 }, + { 0x993FE2C6D07B7FAC, -236, -52 }, + { 0xE45C10C42A2B3B06, -210, -44 }, + { 0xAA242499697392D3, -183, -36 }, + { 0xFD87B5F28300CA0E, -157, -28 }, + { 0xBCE5086492111AEB, -130, -20 }, + { 0x8CBCCC096F5088CC, -103, -12 }, + { 0xD1B71758E219652C, -77, -4 }, + { 0x9C40000000000000, -50, 4 }, + { 0xE8D4A51000000000, -24, 12 }, + { 0xAD78EBC5AC620000, 3, 20 }, + { 0x813F3978F8940984, 30, 28 }, + { 0xC097CE7BC90715B3, 56, 36 }, + { 0x8F7E32CE7BEA5C70, 83, 44 }, + { 0xD5D238A4ABE98068, 109, 52 }, + { 0x9F4F2726179A2245, 136, 60 }, + { 0xED63A231D4C4FB27, 162, 68 }, + { 0xB0DE65388CC8ADA8, 189, 76 }, + { 0x83C7088E1AAB65DB, 216, 84 }, + { 0xC45D1DF942711D9A, 242, 92 }, + { 0x924D692CA61BE758, 269, 100 }, + { 0xDA01EE641A708DEA, 295, 108 }, + { 0xA26DA3999AEF774A, 322, 116 }, + { 0xF209787BB47D6B85, 348, 124 }, + { 0xB454E4A179DD1877, 375, 132 }, + { 0x865B86925B9BC5C2, 402, 140 }, + { 0xC83553C5C8965D3D, 428, 148 }, + { 0x952AB45CFA97A0B3, 455, 156 }, + { 0xDE469FBD99A05FE3, 481, 164 }, + { 0xA59BC234DB398C25, 508, 172 }, + { 0xF6C69A72A3989F5C, 534, 180 }, + { 0xB7DCBF5354E9BECE, 561, 188 }, + { 0x88FCF317F22241E2, 588, 196 }, + { 0xCC20CE9BD35C78A5, 614, 204 }, + { 0x98165AF37B2153DF, 641, 212 }, + { 0xE2A0B5DC971F303A, 667, 220 }, + { 0xA8D9D1535CE3B396, 694, 228 }, + { 0xFB9B7CD9A4A7443C, 720, 236 }, + { 0xBB764C4CA7A44410, 747, 244 }, + { 0x8BAB8EEFB6409C1A, 774, 252 }, + { 0xD01FEF10A657842C, 800, 260 }, + { 0x9B10A4E5E9913129, 827, 268 }, + { 0xE7109BFBA19C0C9D, 853, 276 }, + { 0xAC2820D9623BF429, 880, 284 }, + { 0x80444B5E7AA7CF85, 907, 292 }, + { 0xBF21E44003ACDD2D, 933, 300 }, + { 0x8E679C2F5E44FF8F, 960, 308 }, + { 0xD433179D9C8CB841, 986, 316 }, + { 0x9E19DB92B4E31BA9, 1013, 324 }, + } + }; + + // This computation gives exactly the same results for k as + // k = ceil((kAlpha - e - 1) * 0.30102999566398114) + // for |e| <= 1500, but doesn't require floating-point operations. + // NB: log_10(2) ~= 78913 / 2^18 + JSON_ASSERT(e >= -1500); + JSON_ASSERT(e <= 1500); + const int f = kAlpha - e - 1; + const int k = (f * 78913) / (1 << 18) + static_cast(f > 0); + + const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; + JSON_ASSERT(index >= 0); + JSON_ASSERT(static_cast(index) < kCachedPowers.size()); + + const cached_power cached = kCachedPowers[static_cast(index)]; + JSON_ASSERT(kAlpha <= cached.e + e + 64); + JSON_ASSERT(kGamma >= cached.e + e + 64); + + return cached; +} + +/*! +For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. +For n == 0, returns 1 and sets pow10 := 1. +*/ +inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10) +{ + // LCOV_EXCL_START + if (n >= 1000000000) + { + pow10 = 1000000000; + return 10; + } + // LCOV_EXCL_STOP + else if (n >= 100000000) + { + pow10 = 100000000; + return 9; + } + else if (n >= 10000000) + { + pow10 = 10000000; + return 8; + } + else if (n >= 1000000) + { + pow10 = 1000000; + return 7; + } + else if (n >= 100000) + { + pow10 = 100000; + return 6; + } + else if (n >= 10000) + { + pow10 = 10000; + return 5; + } + else if (n >= 1000) + { + pow10 = 1000; + return 4; + } + else if (n >= 100) + { + pow10 = 100; + return 3; + } + else if (n >= 10) + { + pow10 = 10; + return 2; + } + else + { + pow10 = 1; + return 1; + } +} + +inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta, + std::uint64_t rest, std::uint64_t ten_k) +{ + JSON_ASSERT(len >= 1); + JSON_ASSERT(dist <= delta); + JSON_ASSERT(rest <= delta); + JSON_ASSERT(ten_k > 0); + + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // ten_k + // <------> + // <---- rest ----> + // --------------[------------------+----+--------------]-------------- + // w V + // = buf * 10^k + // + // ten_k represents a unit-in-the-last-place in the decimal representation + // stored in buf. + // Decrement buf by ten_k while this takes buf closer to w. + + // The tests are written in this order to avoid overflow in unsigned + // integer arithmetic. + + while (rest < dist + && delta - rest >= ten_k + && (rest + ten_k < dist || dist - rest > rest + ten_k - dist)) + { + JSON_ASSERT(buf[len - 1] != '0'); + buf[len - 1]--; + rest += ten_k; + } +} + +/*! +Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. +M- and M+ must be normalized and share the same exponent -60 <= e <= -32. +*/ +inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, + diyfp M_minus, diyfp w, diyfp M_plus) +{ + static_assert(kAlpha >= -60, "internal error"); + static_assert(kGamma <= -32, "internal error"); + + // Generates the digits (and the exponent) of a decimal floating-point + // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's + // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma. + // + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // Grisu2 generates the digits of M+ from left to right and stops as soon as + // V is in [M-,M+]. + + JSON_ASSERT(M_plus.e >= kAlpha); + JSON_ASSERT(M_plus.e <= kGamma); + + std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) + std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) + + // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0): + // + // M+ = f * 2^e + // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e + // = ((p1 ) * 2^-e + (p2 )) * 2^e + // = p1 + p2 * 2^e + + const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e); + + auto p1 = static_cast(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) + std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e + + // 1) + // + // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] + + JSON_ASSERT(p1 > 0); + + std::uint32_t pow10; + const int k = find_largest_pow10(p1, pow10); + + // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1) + // + // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1)) + // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1)) + // + // M+ = p1 + p2 * 2^e + // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e + // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e + // = d[k-1] * 10^(k-1) + ( rest) * 2^e + // + // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0) + // + // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0] + // + // but stop as soon as + // + // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e + + int n = k; + while (n > 0) + { + // Invariants: + // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k) + // pow10 = 10^(n-1) <= p1 < 10^n + // + const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) + const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) + // + // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e + // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) + // + JSON_ASSERT(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) + // + p1 = r; + n--; + // + // M+ = buffer * 10^n + (p1 + p2 * 2^e) + // pow10 = 10^n + // + + // Now check if enough digits have been generated. + // Compute + // + // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e + // + // Note: + // Since rest and delta share the same exponent e, it suffices to + // compare the significands. + const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2; + if (rest <= delta) + { + // V = buffer * 10^n, with M- <= V <= M+. + + decimal_exponent += n; + + // We may now just stop. But instead look if the buffer could be + // decremented to bring V closer to w. + // + // pow10 = 10^n is now 1 ulp in the decimal representation V. + // The rounding procedure works with diyfp's with an implicit + // exponent of e. + // + // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e + // + const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e; + grisu2_round(buffer, length, dist, delta, rest, ten_n); + + return; + } + + pow10 /= 10; + // + // pow10 = 10^(n-1) <= p1 < 10^n + // Invariants restored. + } + + // 2) + // + // The digits of the integral part have been generated: + // + // M+ = d[k-1]...d[1]d[0] + p2 * 2^e + // = buffer + p2 * 2^e + // + // Now generate the digits of the fractional part p2 * 2^e. + // + // Note: + // No decimal point is generated: the exponent is adjusted instead. + // + // p2 actually represents the fraction + // + // p2 * 2^e + // = p2 / 2^-e + // = d[-1] / 10^1 + d[-2] / 10^2 + ... + // + // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...) + // + // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m + // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...) + // + // using + // + // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e) + // = ( d) * 2^-e + ( r) + // + // or + // 10^m * p2 * 2^e = d + r * 2^e + // + // i.e. + // + // M+ = buffer + p2 * 2^e + // = buffer + 10^-m * (d + r * 2^e) + // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e + // + // and stop as soon as 10^-m * r * 2^e <= delta * 2^e + + JSON_ASSERT(p2 > delta); + + int m = 0; + for (;;) + { + // Invariant: + // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e + // = buffer * 10^-m + 10^-m * (p2 ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e + // + JSON_ASSERT(p2 <= (std::numeric_limits::max)() / 10); + p2 *= 10; + const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e + const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e + // + // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) + // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + JSON_ASSERT(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + p2 = r; + m++; + // + // M+ = buffer * 10^-m + 10^-m * p2 * 2^e + // Invariant restored. + + // Check if enough digits have been generated. + // + // 10^-m * p2 * 2^e <= delta * 2^e + // p2 * 2^e <= 10^m * delta * 2^e + // p2 <= 10^m * delta + delta *= 10; + dist *= 10; + if (p2 <= delta) + { + break; + } + } + + // V = buffer * 10^-m, with M- <= V <= M+. + + decimal_exponent -= m; + + // 1 ulp in the decimal representation is now 10^-m. + // Since delta and dist are now scaled by 10^m, we need to do the + // same with ulp in order to keep the units in sync. + // + // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e + // + const std::uint64_t ten_m = one.f; + grisu2_round(buffer, length, dist, delta, p2, ten_m); + + // By construction this algorithm generates the shortest possible decimal + // number (Loitsch, Theorem 6.2) which rounds back to w. + // For an input number of precision p, at least + // + // N = 1 + ceil(p * log_10(2)) + // + // decimal digits are sufficient to identify all binary floating-point + // numbers (Matula, "In-and-Out conversions"). + // This implies that the algorithm does not produce more than N decimal + // digits. + // + // N = 17 for p = 53 (IEEE double precision) + // N = 9 for p = 24 (IEEE single precision) +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +JSON_HEDLEY_NON_NULL(1) +inline void grisu2(char* buf, int& len, int& decimal_exponent, + diyfp m_minus, diyfp v, diyfp m_plus) +{ + JSON_ASSERT(m_plus.e == m_minus.e); + JSON_ASSERT(m_plus.e == v.e); + + // --------(-----------------------+-----------------------)-------- (A) + // m- v m+ + // + // --------------------(-----------+-----------------------)-------- (B) + // m- v m+ + // + // First scale v (and m- and m+) such that the exponent is in the range + // [alpha, gamma]. + + const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); + + const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k + + // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma] + const diyfp w = diyfp::mul(v, c_minus_k); + const diyfp w_minus = diyfp::mul(m_minus, c_minus_k); + const diyfp w_plus = diyfp::mul(m_plus, c_minus_k); + + // ----(---+---)---------------(---+---)---------------(---+---)---- + // w- w w+ + // = c*m- = c*v = c*m+ + // + // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and + // w+ are now off by a small amount. + // In fact: + // + // w - v * 10^k < 1 ulp + // + // To account for this inaccuracy, add resp. subtract 1 ulp. + // + // --------+---[---------------(---+---)---------------]---+-------- + // w- M- w M+ w+ + // + // Now any number in [M-, M+] (bounds included) will round to w when input, + // regardless of how the input rounding algorithm breaks ties. + // + // And digit_gen generates the shortest possible such number in [M-, M+]. + // Note that this does not mean that Grisu2 always generates the shortest + // possible number in the interval (m-, m+). + const diyfp M_minus(w_minus.f + 1, w_minus.e); + const diyfp M_plus (w_plus.f - 1, w_plus.e ); + + decimal_exponent = -cached.k; // = -(-k) = k + + grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +template +JSON_HEDLEY_NON_NULL(1) +void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) +{ + static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, + "internal error: not enough precision"); + + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); + + // If the neighbors (and boundaries) of 'value' are always computed for double-precision + // numbers, all float's can be recovered using strtod (and strtof). However, the resulting + // decimal representations are not exactly "short". + // + // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars) + // says "value is converted to a string as if by std::sprintf in the default ("C") locale" + // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars' + // does. + // On the other hand, the documentation for 'std::to_chars' requires that "parsing the + // representation using the corresponding std::from_chars function recovers value exactly". That + // indicates that single precision floating-point numbers should be recovered using + // 'std::strtof'. + // + // NB: If the neighbors are computed for single-precision numbers, there is a single float + // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision + // value is off by 1 ulp. +#if 0 + const boundaries w = compute_boundaries(static_cast(value)); +#else + const boundaries w = compute_boundaries(value); +#endif + + grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); +} + +/*! +@brief appends a decimal representation of e to buf +@return a pointer to the element following the exponent. +@pre -1000 < e < 1000 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* append_exponent(char* buf, int e) +{ + JSON_ASSERT(e > -1000); + JSON_ASSERT(e < 1000); + + if (e < 0) + { + e = -e; + *buf++ = '-'; + } + else + { + *buf++ = '+'; + } + + auto k = static_cast(e); + if (k < 10) + { + // Always print at least two digits in the exponent. + // This is for compatibility with printf("%g"). + *buf++ = '0'; + *buf++ = static_cast('0' + k); + } + else if (k < 100) + { + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + else + { + *buf++ = static_cast('0' + k / 100); + k %= 100; + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + + return buf; +} + +/*! +@brief prettify v = buf * 10^decimal_exponent + +If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point +notation. Otherwise it will be printed in exponential notation. + +@pre min_exp < 0 +@pre max_exp > 0 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* format_buffer(char* buf, int len, int decimal_exponent, + int min_exp, int max_exp) +{ + JSON_ASSERT(min_exp < 0); + JSON_ASSERT(max_exp > 0); + + const int k = len; + const int n = len + decimal_exponent; + + // v = buf * 10^(n-k) + // k is the length of the buffer (number of decimal digits) + // n is the position of the decimal point relative to the start of the buffer. + + if (k <= n && n <= max_exp) + { + // digits[000] + // len <= max_exp + 2 + + std::memset(buf + k, '0', static_cast(n) - static_cast(k)); + // Make it look like a floating-point number (#362, #378) + buf[n + 0] = '.'; + buf[n + 1] = '0'; + return buf + (static_cast(n) + 2); + } + + if (0 < n && n <= max_exp) + { + // dig.its + // len <= max_digits10 + 1 + + JSON_ASSERT(k > n); + + std::memmove(buf + (static_cast(n) + 1), buf + n, static_cast(k) - static_cast(n)); + buf[n] = '.'; + return buf + (static_cast(k) + 1U); + } + + if (min_exp < n && n <= 0) + { + // 0.[000]digits + // len <= 2 + (-min_exp - 1) + max_digits10 + + std::memmove(buf + (2 + static_cast(-n)), buf, static_cast(k)); + buf[0] = '0'; + buf[1] = '.'; + std::memset(buf + 2, '0', static_cast(-n)); + return buf + (2U + static_cast(-n) + static_cast(k)); + } + + if (k == 1) + { + // dE+123 + // len <= 1 + 5 + + buf += 1; + } + else + { + // d.igitsE+123 + // len <= max_digits10 + 1 + 5 + + std::memmove(buf + 2, buf + 1, static_cast(k) - 1); + buf[1] = '.'; + buf += 1 + static_cast(k); + } + + *buf++ = 'e'; + return append_exponent(buf, n - 1); +} + +} // namespace dtoa_impl + +/*! +@brief generates a decimal representation of the floating-point number value in [first, last). + +The format of the resulting decimal representation is similar to printf's %g +format. Returns an iterator pointing past-the-end of the decimal representation. + +@note The input number must be finite, i.e. NaN's and Inf's are not supported. +@note The buffer must be large enough. +@note The result is NOT null-terminated. +*/ +template +JSON_HEDLEY_NON_NULL(1, 2) +JSON_HEDLEY_RETURNS_NON_NULL +char* to_chars(char* first, const char* last, FloatType value) +{ + static_cast(last); // maybe unused - fix warning + JSON_ASSERT(std::isfinite(value)); + + // Use signbit(value) instead of (value < 0) since signbit works for -0. + if (std::signbit(value)) + { + value = -value; + *first++ = '-'; + } + + if (value == 0) // +-0 + { + *first++ = '0'; + // Make it look like a floating-point number (#362, #378) + *first++ = '.'; + *first++ = '0'; + return first; + } + + JSON_ASSERT(last - first >= std::numeric_limits::max_digits10); + + // Compute v = buffer * 10^decimal_exponent. + // The decimal digits are stored in the buffer, which needs to be interpreted + // as an unsigned decimal integer. + // len is the length of the buffer, i.e. the number of decimal digits. + int len = 0; + int decimal_exponent = 0; + dtoa_impl::grisu2(first, len, decimal_exponent, value); + + JSON_ASSERT(len <= std::numeric_limits::max_digits10); + + // Format the buffer like printf("%.*g", prec, value) + constexpr int kMinExp = -4; + // Use digits10 here to increase compatibility with version 2. + constexpr int kMaxExp = std::numeric_limits::digits10; + + JSON_ASSERT(last - first >= kMaxExp + 2); + JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); + JSON_ASSERT(last - first >= std::numeric_limits::max_digits10 + 6); + + return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); +} + +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// serialization // +/////////////////// + +/// how to treat decoding errors +enum class error_handler_t +{ + strict, ///< throw a type_error exception in case of invalid UTF-8 + replace, ///< replace invalid UTF-8 sequences with U+FFFD + ignore ///< ignore invalid UTF-8 sequences +}; + +template +class serializer +{ + using string_t = typename BasicJsonType::string_t; + using number_float_t = typename BasicJsonType::number_float_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using binary_char_t = typename BasicJsonType::binary_t::value_type; + static constexpr std::uint8_t UTF8_ACCEPT = 0; + static constexpr std::uint8_t UTF8_REJECT = 1; + + public: + /*! + @param[in] s output stream to serialize to + @param[in] ichar indentation character to use + @param[in] error_handler_ how to react on decoding errors + */ + serializer(output_adapter_t s, const char ichar, + error_handler_t error_handler_ = error_handler_t::strict) + : o(std::move(s)) + , loc(std::localeconv()) + , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->thousands_sep))) + , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->decimal_point))) + , indent_char(ichar) + , indent_string(512, indent_char) + , error_handler(error_handler_) + {} + + // delete because of pointer members + serializer(const serializer&) = delete; + serializer& operator=(const serializer&) = delete; + serializer(serializer&&) = delete; + serializer& operator=(serializer&&) = delete; + ~serializer() = default; + + /*! + @brief internal implementation of the serialization function + + This function is called by the public member function dump and organizes + the serialization internally. The indentation level is propagated as + additional parameter. In case of arrays and objects, the function is + called recursively. + + - strings and object keys are escaped using `escape_string()` + - integer numbers are converted implicitly via `operator<<` + - floating-point numbers are converted to a string using `"%g"` format + - binary values are serialized as objects containing the subtype and the + byte array + + @param[in] val value to serialize + @param[in] pretty_print whether the output shall be pretty-printed + @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters + in the output are escaped with `\uXXXX` sequences, and the result consists + of ASCII characters only. + @param[in] indent_step the indent level + @param[in] current_indent the current indent level (only used internally) + */ + void dump(const BasicJsonType& val, + const bool pretty_print, + const bool ensure_ascii, + const unsigned int indent_step, + const unsigned int current_indent = 0) + { + switch (val.m_type) + { + case value_t::object: + { + if (val.m_value.object->empty()) + { + o->write_characters("{}", 2); + return; + } + + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + // first n-1 elements + auto i = val.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + // last element + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_character('{'); + + // first n-1 elements + auto i = val.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\":", 2); + dump(i->second, false, ensure_ascii, indent_step, current_indent); + o->write_character(','); + } + + // last element + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\":", 2); + dump(i->second, false, ensure_ascii, indent_step, current_indent); + + o->write_character('}'); + } + + return; + } + + case value_t::array: + { + if (val.m_value.array->empty()) + { + o->write_characters("[]", 2); + return; + } + + if (pretty_print) + { + o->write_characters("[\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + // first n-1 elements + for (auto i = val.m_value.array->cbegin(); + i != val.m_value.array->cend() - 1; ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + dump(*i, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + // last element + JSON_ASSERT(!val.m_value.array->empty()); + o->write_characters(indent_string.c_str(), new_indent); + dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character(']'); + } + else + { + o->write_character('['); + + // first n-1 elements + for (auto i = val.m_value.array->cbegin(); + i != val.m_value.array->cend() - 1; ++i) + { + dump(*i, false, ensure_ascii, indent_step, current_indent); + o->write_character(','); + } + + // last element + JSON_ASSERT(!val.m_value.array->empty()); + dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); + + o->write_character(']'); + } + + return; + } + + case value_t::string: + { + o->write_character('\"'); + dump_escaped(*val.m_value.string, ensure_ascii); + o->write_character('\"'); + return; + } + + case value_t::binary: + { + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"bytes\": [", 10); + + if (!val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_characters(", ", 2); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\n", 3); + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"subtype\": ", 11); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + } + else + { + o->write_characters("null", 4); + } + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_characters("{\"bytes\":[", 10); + + if (!val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_character(','); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\"subtype\":", 12); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + o->write_character('}'); + } + else + { + o->write_characters("null}", 5); + } + } + return; + } + + case value_t::boolean: + { + if (val.m_value.boolean) + { + o->write_characters("true", 4); + } + else + { + o->write_characters("false", 5); + } + return; + } + + case value_t::number_integer: + { + dump_integer(val.m_value.number_integer); + return; + } + + case value_t::number_unsigned: + { + dump_integer(val.m_value.number_unsigned); + return; + } + + case value_t::number_float: + { + dump_float(val.m_value.number_float); + return; + } + + case value_t::discarded: + { + o->write_characters("", 11); + return; + } + + case value_t::null: + { + o->write_characters("null", 4); + return; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } + } + + private: + /*! + @brief dump escaped string + + Escape a string by replacing certain special characters by a sequence of an + escape character (backslash) and another character and other control + characters by a sequence of "\u" followed by a four-digit hex + representation. The escaped string is written to output stream @a o. + + @param[in] s the string to escape + @param[in] ensure_ascii whether to escape non-ASCII characters with + \uXXXX sequences + + @complexity Linear in the length of string @a s. + */ + void dump_escaped(const string_t& s, const bool ensure_ascii) + { + std::uint32_t codepoint; + std::uint8_t state = UTF8_ACCEPT; + std::size_t bytes = 0; // number of bytes written to string_buffer + + // number of bytes written at the point of the last valid byte + std::size_t bytes_after_last_accept = 0; + std::size_t undumped_chars = 0; + + for (std::size_t i = 0; i < s.size(); ++i) + { + const auto byte = static_cast(s[i]); + + switch (decode(state, codepoint, byte)) + { + case UTF8_ACCEPT: // decode found a new code point + { + switch (codepoint) + { + case 0x08: // backspace + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'b'; + break; + } + + case 0x09: // horizontal tab + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 't'; + break; + } + + case 0x0A: // newline + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'n'; + break; + } + + case 0x0C: // formfeed + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'f'; + break; + } + + case 0x0D: // carriage return + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'r'; + break; + } + + case 0x22: // quotation mark + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = '\"'; + break; + } + + case 0x5C: // reverse solidus + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = '\\'; + break; + } + + default: + { + // escape control characters (0x00..0x1F) or, if + // ensure_ascii parameter is used, non-ASCII characters + if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F))) + { + if (codepoint <= 0xFFFF) + { + (std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x", + static_cast(codepoint)); + bytes += 6; + } + else + { + (std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x", + static_cast(0xD7C0u + (codepoint >> 10u)), + static_cast(0xDC00u + (codepoint & 0x3FFu))); + bytes += 12; + } + } + else + { + // copy byte to buffer (all previous bytes + // been copied have in default case above) + string_buffer[bytes++] = s[i]; + } + break; + } + } + + // write buffer and reset index; there must be 13 bytes + // left, as this is the maximal number of bytes to be + // written ("\uxxxx\uxxxx\0") for one code point + if (string_buffer.size() - bytes < 13) + { + o->write_characters(string_buffer.data(), bytes); + bytes = 0; + } + + // remember the byte position of this accept + bytes_after_last_accept = bytes; + undumped_chars = 0; + break; + } + + case UTF8_REJECT: // decode found invalid UTF-8 byte + { + switch (error_handler) + { + case error_handler_t::strict: + { + std::string sn(3, '\0'); + (std::snprintf)(&sn[0], sn.size(), "%.2X", byte); + JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn)); + } + + case error_handler_t::ignore: + case error_handler_t::replace: + { + // in case we saw this character the first time, we + // would like to read it again, because the byte + // may be OK for itself, but just not OK for the + // previous sequence + if (undumped_chars > 0) + { + --i; + } + + // reset length buffer to the last accepted index; + // thus removing/ignoring the invalid characters + bytes = bytes_after_last_accept; + + if (error_handler == error_handler_t::replace) + { + // add a replacement character + if (ensure_ascii) + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'u'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'd'; + } + else + { + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xEF'); + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xBF'); + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xBD'); + } + + // write buffer and reset index; there must be 13 bytes + // left, as this is the maximal number of bytes to be + // written ("\uxxxx\uxxxx\0") for one code point + if (string_buffer.size() - bytes < 13) + { + o->write_characters(string_buffer.data(), bytes); + bytes = 0; + } + + bytes_after_last_accept = bytes; + } + + undumped_chars = 0; + + // continue processing the string + state = UTF8_ACCEPT; + break; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } + break; + } + + default: // decode found yet incomplete multi-byte code point + { + if (!ensure_ascii) + { + // code point will not be escaped - copy byte to buffer + string_buffer[bytes++] = s[i]; + } + ++undumped_chars; + break; + } + } + } + + // we finished processing the string + if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT)) + { + // write buffer + if (bytes > 0) + { + o->write_characters(string_buffer.data(), bytes); + } + } + else + { + // we finish reading, but do not accept: string was incomplete + switch (error_handler) + { + case error_handler_t::strict: + { + std::string sn(3, '\0'); + (std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast(s.back())); + JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn)); + } + + case error_handler_t::ignore: + { + // write all accepted bytes + o->write_characters(string_buffer.data(), bytes_after_last_accept); + break; + } + + case error_handler_t::replace: + { + // write all accepted bytes + o->write_characters(string_buffer.data(), bytes_after_last_accept); + // add a replacement character + if (ensure_ascii) + { + o->write_characters("\\ufffd", 6); + } + else + { + o->write_characters("\xEF\xBF\xBD", 3); + } + break; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } + } + } + + /*! + @brief count digits + + Count the number of decimal (base 10) digits for an input unsigned integer. + + @param[in] x unsigned integer number to count its digits + @return number of decimal digits + */ + inline unsigned int count_digits(number_unsigned_t x) noexcept + { + unsigned int n_digits = 1; + for (;;) + { + if (x < 10) + { + return n_digits; + } + if (x < 100) + { + return n_digits + 1; + } + if (x < 1000) + { + return n_digits + 2; + } + if (x < 10000) + { + return n_digits + 3; + } + x = x / 10000u; + n_digits += 4; + } + } + + /*! + @brief dump an integer + + Dump a given integer to output stream @a o. Works internally with + @a number_buffer. + + @param[in] x integer number (signed or unsigned) to dump + @tparam NumberType either @a number_integer_t or @a number_unsigned_t + */ + template < typename NumberType, detail::enable_if_t < + std::is_same::value || + std::is_same::value || + std::is_same::value, + int > = 0 > + void dump_integer(NumberType x) + { + static constexpr std::array, 100> digits_to_99 + { + { + {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}}, + {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}}, + {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}}, + {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}}, + {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}}, + {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}}, + {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}}, + {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}}, + {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}}, + {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}}, + } + }; + + // special case for "0" + if (x == 0) + { + o->write_character('0'); + return; + } + + // use a pointer to fill the buffer + auto buffer_ptr = number_buffer.begin(); + + const bool is_negative = std::is_same::value && !(x >= 0); // see issue #755 + number_unsigned_t abs_value; + + unsigned int n_chars; + + if (is_negative) + { + *buffer_ptr = '-'; + abs_value = remove_sign(static_cast(x)); + + // account one more byte for the minus sign + n_chars = 1 + count_digits(abs_value); + } + else + { + abs_value = static_cast(x); + n_chars = count_digits(abs_value); + } + + // spare 1 byte for '\0' + JSON_ASSERT(n_chars < number_buffer.size() - 1); + + // jump to the end to generate the string from backward + // so we later avoid reversing the result + buffer_ptr += n_chars; + + // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu + // See: https://www.youtube.com/watch?v=o4-CwDo2zpg + while (abs_value >= 100) + { + const auto digits_index = static_cast((abs_value % 100)); + abs_value /= 100; + *(--buffer_ptr) = digits_to_99[digits_index][1]; + *(--buffer_ptr) = digits_to_99[digits_index][0]; + } + + if (abs_value >= 10) + { + const auto digits_index = static_cast(abs_value); + *(--buffer_ptr) = digits_to_99[digits_index][1]; + *(--buffer_ptr) = digits_to_99[digits_index][0]; + } + else + { + *(--buffer_ptr) = static_cast('0' + abs_value); + } + + o->write_characters(number_buffer.data(), n_chars); + } + + /*! + @brief dump a floating-point number + + Dump a given floating-point number to output stream @a o. Works internally + with @a number_buffer. + + @param[in] x floating-point number to dump + */ + void dump_float(number_float_t x) + { + // NaN / inf + if (!std::isfinite(x)) + { + o->write_characters("null", 4); + return; + } + + // If number_float_t is an IEEE-754 single or double precision number, + // use the Grisu2 algorithm to produce short numbers which are + // guaranteed to round-trip, using strtof and strtod, resp. + // + // NB: The test below works if == . + static constexpr bool is_ieee_single_or_double + = (std::numeric_limits::is_iec559 && std::numeric_limits::digits == 24 && std::numeric_limits::max_exponent == 128) || + (std::numeric_limits::is_iec559 && std::numeric_limits::digits == 53 && std::numeric_limits::max_exponent == 1024); + + dump_float(x, std::integral_constant()); + } + + void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/) + { + char* begin = number_buffer.data(); + char* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x); + + o->write_characters(begin, static_cast(end - begin)); + } + + void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/) + { + // get number of digits for a float -> text -> float round-trip + static constexpr auto d = std::numeric_limits::max_digits10; + + // the actual conversion + std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x); + + // negative value indicates an error + JSON_ASSERT(len > 0); + // check if buffer was large enough + JSON_ASSERT(static_cast(len) < number_buffer.size()); + + // erase thousands separator + if (thousands_sep != '\0') + { + const auto end = std::remove(number_buffer.begin(), + number_buffer.begin() + len, thousands_sep); + std::fill(end, number_buffer.end(), '\0'); + JSON_ASSERT((end - number_buffer.begin()) <= len); + len = (end - number_buffer.begin()); + } + + // convert decimal point to '.' + if (decimal_point != '\0' && decimal_point != '.') + { + const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point); + if (dec_pos != number_buffer.end()) + { + *dec_pos = '.'; + } + } + + o->write_characters(number_buffer.data(), static_cast(len)); + + // determine if need to append ".0" + const bool value_is_int_like = + std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1, + [](char c) + { + return c == '.' || c == 'e'; + }); + + if (value_is_int_like) + { + o->write_characters(".0", 2); + } + } + + /*! + @brief check whether a string is UTF-8 encoded + + The function checks each byte of a string whether it is UTF-8 encoded. The + result of the check is stored in the @a state parameter. The function must + be called initially with state 0 (accept). State 1 means the string must + be rejected, because the current byte is not allowed. If the string is + completely processed, but the state is non-zero, the string ended + prematurely; that is, the last byte indicated more bytes should have + followed. + + @param[in,out] state the state of the decoding + @param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT) + @param[in] byte next byte to decode + @return new state + + @note The function has been edited: a std::array is used. + + @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann + @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + */ + static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept + { + static const std::array utf8d = + { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF + 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF + 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF + 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF + 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2 + 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4 + 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6 + 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8 + } + }; + + const std::uint8_t type = utf8d[byte]; + + codep = (state != UTF8_ACCEPT) + ? (byte & 0x3fu) | (codep << 6u) + : (0xFFu >> type) & (byte); + + std::size_t index = 256u + static_cast(state) * 16u + static_cast(type); + JSON_ASSERT(index < 400); + state = utf8d[index]; + return state; + } + + /* + * Overload to make the compiler happy while it is instantiating + * dump_integer for number_unsigned_t. + * Must never be called. + */ + number_unsigned_t remove_sign(number_unsigned_t x) + { + JSON_ASSERT(false); // LCOV_EXCL_LINE + return x; // LCOV_EXCL_LINE + } + + /* + * Helper function for dump_integer + * + * This function takes a negative signed integer and returns its absolute + * value as unsigned integer. The plus/minus shuffling is necessary as we can + * not directly remove the sign of an arbitrary signed integer as the + * absolute values of INT_MIN and INT_MAX are usually not the same. See + * #1708 for details. + */ + inline number_unsigned_t remove_sign(number_integer_t x) noexcept + { + JSON_ASSERT(x < 0 && x < (std::numeric_limits::max)()); + return static_cast(-(x + 1)) + 1; + } + + private: + /// the output of the serializer + output_adapter_t o = nullptr; + + /// a (hopefully) large enough character buffer + std::array number_buffer{{}}; + + /// the locale + const std::lconv* loc = nullptr; + /// the locale's thousand separator character + const char thousands_sep = '\0'; + /// the locale's decimal point character + const char decimal_point = '\0'; + + /// string buffer + std::array string_buffer{{}}; + + /// the indentation character + const char indent_char; + /// the indentation string + string_t indent_string; + + /// error_handler how to react on decoding errors + const error_handler_t error_handler; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + + +#include // less +#include // allocator +#include // pair +#include // vector + +namespace nlohmann +{ + +/// ordered_map: a minimal map-like container that preserves insertion order +/// for use within nlohmann::basic_json +template , + class Allocator = std::allocator>> + struct ordered_map : std::vector, Allocator> +{ + using key_type = Key; + using mapped_type = T; + using Container = std::vector, Allocator>; + using typename Container::iterator; + using typename Container::const_iterator; + using typename Container::size_type; + using typename Container::value_type; + + // Explicit constructors instead of `using Container::Container` + // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4) + ordered_map(const Allocator& alloc = Allocator()) : Container{alloc} {} + template + ordered_map(It first, It last, const Allocator& alloc = Allocator()) + : Container{first, last, alloc} {} + ordered_map(std::initializer_list init, const Allocator& alloc = Allocator() ) + : Container{init, alloc} {} + + std::pair emplace(const key_type& key, T&& t) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return {it, false}; + } + } + Container::emplace_back(key, t); + return {--this->end(), true}; + } + + T& operator[](const Key& key) + { + return emplace(key, T{}).first->second; + } + + const T& operator[](const Key& key) const + { + return at(key); + } + + T& at(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it->second; + } + } + + throw std::out_of_range("key not found"); + } + + const T& at(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it->second; + } + } + + throw std::out_of_range("key not found"); + } + + size_type erase(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + // Since we cannot move const Keys, re-construct them in place + for (auto next = it; ++next != this->end(); ++it) + { + it->~value_type(); // Destroy but keep allocation + new (&*it) value_type{std::move(*next)}; + } + Container::pop_back(); + return 1; + } + } + return 0; + } + + iterator erase(iterator pos) + { + auto it = pos; + + // Since we cannot move const Keys, re-construct them in place + for (auto next = it; ++next != this->end(); ++it) + { + it->~value_type(); // Destroy but keep allocation + new (&*it) value_type{std::move(*next)}; + } + Container::pop_back(); + return pos; + } + + size_type count(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return 1; + } + } + return 0; + } + + iterator find(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it; + } + } + return Container::end(); + } + + const_iterator find(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it; + } + } + return Container::end(); + } + + std::pair insert( value_type&& value ) + { + return emplace(value.first, std::move(value.second)); + } + + std::pair insert( const value_type& value ) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == value.first) + { + return {it, false}; + } + } + Container::push_back(value); + return {--this->end(), true}; + } +}; + +} // namespace nlohmann + + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ + +/*! +@brief a class to store JSON values + +@tparam ObjectType type for JSON objects (`std::map` by default; will be used +in @ref object_t) +@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used +in @ref array_t) +@tparam StringType type for JSON strings and object keys (`std::string` by +default; will be used in @ref string_t) +@tparam BooleanType type for JSON booleans (`bool` by default; will be used +in @ref boolean_t) +@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by +default; will be used in @ref number_integer_t) +@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c +`uint64_t` by default; will be used in @ref number_unsigned_t) +@tparam NumberFloatType type for JSON floating-point numbers (`double` by +default; will be used in @ref number_float_t) +@tparam BinaryType type for packed binary data for compatibility with binary +serialization formats (`std::vector` by default; will be used in +@ref binary_t) +@tparam AllocatorType type of the allocator to use (`std::allocator` by +default) +@tparam JSONSerializer the serializer to resolve internal calls to `to_json()` +and `from_json()` (@ref adl_serializer by default) + +@requirement The class satisfies the following concept requirements: +- Basic + - [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible): + JSON values can be default constructed. The result will be a JSON null + value. + - [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible): + A JSON value can be constructed from an rvalue argument. + - [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible): + A JSON value can be copy-constructed from an lvalue expression. + - [MoveAssignable](https://en.cppreference.com/w/cpp/named_req/MoveAssignable): + A JSON value van be assigned from an rvalue argument. + - [CopyAssignable](https://en.cppreference.com/w/cpp/named_req/CopyAssignable): + A JSON value can be copy-assigned from an lvalue expression. + - [Destructible](https://en.cppreference.com/w/cpp/named_req/Destructible): + JSON values can be destructed. +- Layout + - [StandardLayoutType](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType): + JSON values have + [standard layout](https://en.cppreference.com/w/cpp/language/data_members#Standard_layout): + All non-static data members are private and standard layout types, the + class has no virtual functions or (virtual) base classes. +- Library-wide + - [EqualityComparable](https://en.cppreference.com/w/cpp/named_req/EqualityComparable): + JSON values can be compared with `==`, see @ref + operator==(const_reference,const_reference). + - [LessThanComparable](https://en.cppreference.com/w/cpp/named_req/LessThanComparable): + JSON values can be compared with `<`, see @ref + operator<(const_reference,const_reference). + - [Swappable](https://en.cppreference.com/w/cpp/named_req/Swappable): + Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of + other compatible types, using unqualified function call @ref swap(). + - [NullablePointer](https://en.cppreference.com/w/cpp/named_req/NullablePointer): + JSON values can be compared against `std::nullptr_t` objects which are used + to model the `null` value. +- Container + - [Container](https://en.cppreference.com/w/cpp/named_req/Container): + JSON values can be used like STL containers and provide iterator access. + - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer); + JSON values can be used like STL containers and provide reverse iterator + access. + +@invariant The member variables @a m_value and @a m_type have the following +relationship: +- If `m_type == value_t::object`, then `m_value.object != nullptr`. +- If `m_type == value_t::array`, then `m_value.array != nullptr`. +- If `m_type == value_t::string`, then `m_value.string != nullptr`. +The invariants are checked by member function assert_invariant(). + +@internal +@note ObjectType trick from https://stackoverflow.com/a/9860911 +@endinternal + +@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange +Format](http://rfc7159.net/rfc7159) + +@since version 1.0.0 + +@nosubgrouping +*/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +class basic_json +{ + private: + template friend struct detail::external_constructor; + friend ::nlohmann::json_pointer; + + template + friend class ::nlohmann::detail::parser; + friend ::nlohmann::detail::serializer; + template + friend class ::nlohmann::detail::iter_impl; + template + friend class ::nlohmann::detail::binary_writer; + template + friend class ::nlohmann::detail::binary_reader; + template + friend class ::nlohmann::detail::json_sax_dom_parser; + template + friend class ::nlohmann::detail::json_sax_dom_callback_parser; + + /// workaround type for MSVC + using basic_json_t = NLOHMANN_BASIC_JSON_TPL; + + // convenience aliases for types residing in namespace detail; + using lexer = ::nlohmann::detail::lexer_base; + + template + static ::nlohmann::detail::parser parser( + InputAdapterType adapter, + detail::parser_callback_tcb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false + ) + { + return ::nlohmann::detail::parser(std::move(adapter), + std::move(cb), allow_exceptions, ignore_comments); + } + + using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; + template + using internal_iterator = ::nlohmann::detail::internal_iterator; + template + using iter_impl = ::nlohmann::detail::iter_impl; + template + using iteration_proxy = ::nlohmann::detail::iteration_proxy; + template using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator; + + template + using output_adapter_t = ::nlohmann::detail::output_adapter_t; + + template + using binary_reader = ::nlohmann::detail::binary_reader; + template using binary_writer = ::nlohmann::detail::binary_writer; + + using serializer = ::nlohmann::detail::serializer; + + public: + using value_t = detail::value_t; + /// JSON Pointer, see @ref nlohmann::json_pointer + using json_pointer = ::nlohmann::json_pointer; + template + using json_serializer = JSONSerializer; + /// how to treat decoding errors + using error_handler_t = detail::error_handler_t; + /// how to treat CBOR tags + using cbor_tag_handler_t = detail::cbor_tag_handler_t; + /// helper type for initializer lists of basic_json values + using initializer_list_t = std::initializer_list>; + + using input_format_t = detail::input_format_t; + /// SAX interface type, see @ref nlohmann::json_sax + using json_sax_t = json_sax; + + //////////////// + // exceptions // + //////////////// + + /// @name exceptions + /// Classes to implement user-defined exceptions. + /// @{ + + /// @copydoc detail::exception + using exception = detail::exception; + /// @copydoc detail::parse_error + using parse_error = detail::parse_error; + /// @copydoc detail::invalid_iterator + using invalid_iterator = detail::invalid_iterator; + /// @copydoc detail::type_error + using type_error = detail::type_error; + /// @copydoc detail::out_of_range + using out_of_range = detail::out_of_range; + /// @copydoc detail::other_error + using other_error = detail::other_error; + + /// @} + + + ///////////////////// + // container types // + ///////////////////// + + /// @name container types + /// The canonic container types to use @ref basic_json like any other STL + /// container. + /// @{ + + /// the type of elements in a basic_json container + using value_type = basic_json; + + /// the type of an element reference + using reference = value_type&; + /// the type of an element const reference + using const_reference = const value_type&; + + /// a type to represent differences between iterators + using difference_type = std::ptrdiff_t; + /// a type to represent container sizes + using size_type = std::size_t; + + /// the allocator type + using allocator_type = AllocatorType; + + /// the type of an element pointer + using pointer = typename std::allocator_traits::pointer; + /// the type of an element const pointer + using const_pointer = typename std::allocator_traits::const_pointer; + + /// an iterator for a basic_json container + using iterator = iter_impl; + /// a const iterator for a basic_json container + using const_iterator = iter_impl; + /// a reverse iterator for a basic_json container + using reverse_iterator = json_reverse_iterator; + /// a const reverse iterator for a basic_json container + using const_reverse_iterator = json_reverse_iterator; + + /// @} + + + /*! + @brief returns the allocator associated with the container + */ + static allocator_type get_allocator() + { + return allocator_type(); + } + + /*! + @brief returns version information on the library + + This function returns a JSON object with information about the library, + including the version number and information on the platform and compiler. + + @return JSON object holding version information + key | description + ----------- | --------------- + `compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version). + `copyright` | The copyright line for the library as string. + `name` | The name of the library as string. + `platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`. + `url` | The URL of the project as string. + `version` | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string). + + @liveexample{The following code shows an example output of the `meta()` + function.,meta} + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @complexity Constant. + + @since 2.1.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json meta() + { + basic_json result; + + result["copyright"] = "(C) 2013-2020 Niels Lohmann"; + result["name"] = "JSON for Modern C++"; + result["url"] = "https://github.com/nlohmann/json"; + result["version"]["string"] = + std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." + + std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." + + std::to_string(NLOHMANN_JSON_VERSION_PATCH); + result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR; + result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR; + result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH; + +#ifdef _WIN32 + result["platform"] = "win32"; +#elif defined __linux__ + result["platform"] = "linux"; +#elif defined __APPLE__ + result["platform"] = "apple"; +#elif defined __unix__ + result["platform"] = "unix"; +#else + result["platform"] = "unknown"; +#endif + +#if defined(__ICC) || defined(__INTEL_COMPILER) + result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; +#elif defined(__clang__) + result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; +#elif defined(__GNUC__) || defined(__GNUG__) + result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}}; +#elif defined(__HP_cc) || defined(__HP_aCC) + result["compiler"] = "hp" +#elif defined(__IBMCPP__) + result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; +#elif defined(_MSC_VER) + result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; +#elif defined(__PGI) + result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; +#elif defined(__SUNPRO_CC) + result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; +#else + result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; +#endif + +#ifdef __cplusplus + result["compiler"]["c++"] = std::to_string(__cplusplus); +#else + result["compiler"]["c++"] = "unknown"; +#endif + return result; + } + + + /////////////////////////// + // JSON value data types // + /////////////////////////// + + /// @name JSON value data types + /// The data types to store a JSON value. These types are derived from + /// the template arguments passed to class @ref basic_json. + /// @{ + +#if defined(JSON_HAS_CPP_14) + // Use transparent comparator if possible, combined with perfect forwarding + // on find() and count() calls prevents unnecessary string construction. + using object_comparator_t = std::less<>; +#else + using object_comparator_t = std::less; +#endif + + /*! + @brief a type for an object + + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows: + > An object is an unordered collection of zero or more name/value pairs, + > where a name is a string and a value is a string, number, boolean, null, + > object, or array. + + To store objects in C++, a type is defined by the template parameters + described below. + + @tparam ObjectType the container to store objects (e.g., `std::map` or + `std::unordered_map`) + @tparam StringType the type of the keys or names (e.g., `std::string`). + The comparison function `std::less` is used to order elements + inside the container. + @tparam AllocatorType the allocator to use for objects (e.g., + `std::allocator`) + + #### Default type + + With the default values for @a ObjectType (`std::map`), @a StringType + (`std::string`), and @a AllocatorType (`std::allocator`), the default + value for @a object_t is: + + @code {.cpp} + std::map< + std::string, // key_type + basic_json, // value_type + std::less, // key_compare + std::allocator> // allocator_type + > + @endcode + + #### Behavior + + The choice of @a object_t influences the behavior of the JSON class. With + the default type, objects have the following behavior: + + - When all names are unique, objects will be interoperable in the sense + that all software implementations receiving that object will agree on + the name-value mappings. + - When the names within an object are not unique, it is unspecified which + one of the values for a given key will be chosen. For instance, + `{"key": 2, "key": 1}` could be equal to either `{"key": 1}` or + `{"key": 2}`. + - Internally, name/value pairs are stored in lexicographical order of the + names. Objects will also be serialized (see @ref dump) in this order. + For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored + and serialized as `{"a": 2, "b": 1}`. + - When comparing objects, the order of the name/value pairs is irrelevant. + This makes objects interoperable in the sense that they will not be + affected by these differences. For instance, `{"b": 1, "a": 2}` and + `{"a": 2, "b": 1}` will be treated as equal. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the maximum depth of nesting. + + In this class, the object's limit of nesting is not explicitly constrained. + However, a maximum depth of nesting may be introduced by the compiler or + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON object. + + #### Storage + + Objects are stored as pointers in a @ref basic_json type. That is, for any + access to object values, a pointer of type `object_t*` must be + dereferenced. + + @sa @ref array_t -- type for an array value + + @since version 1.0.0 + + @note The order name/value pairs are added to the object is *not* + preserved by the library. Therefore, iterating an object may return + name/value pairs in a different order than they were originally stored. In + fact, keys will be traversed in alphabetical order as `std::map` with + `std::less` is used by default. Please note this behavior conforms to [RFC + 7159](http://rfc7159.net/rfc7159), because any order implements the + specified "unordered" nature of JSON objects. + */ + using object_t = ObjectType>>; + + /*! + @brief a type for an array + + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows: + > An array is an ordered sequence of zero or more values. + + To store objects in C++, a type is defined by the template parameters + explained below. + + @tparam ArrayType container type to store arrays (e.g., `std::vector` or + `std::list`) + @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) + + #### Default type + + With the default values for @a ArrayType (`std::vector`) and @a + AllocatorType (`std::allocator`), the default value for @a array_t is: + + @code {.cpp} + std::vector< + basic_json, // value_type + std::allocator // allocator_type + > + @endcode + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the maximum depth of nesting. + + In this class, the array's limit of nesting is not explicitly constrained. + However, a maximum depth of nesting may be introduced by the compiler or + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON array. + + #### Storage + + Arrays are stored as pointers in a @ref basic_json type. That is, for any + access to array values, a pointer of type `array_t*` must be dereferenced. + + @sa @ref object_t -- type for an object value + + @since version 1.0.0 + */ + using array_t = ArrayType>; + + /*! + @brief a type for a string + + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: + > A string is a sequence of zero or more Unicode characters. + + To store objects in C++, a type is defined by the template parameter + described below. Unicode values are split by the JSON class into + byte-sized characters during deserialization. + + @tparam StringType the container to store strings (e.g., `std::string`). + Note this container is used for keys/names in objects, see @ref object_t. + + #### Default type + + With the default values for @a StringType (`std::string`), the default + value for @a string_t is: + + @code {.cpp} + std::string + @endcode + + #### Encoding + + Strings are stored in UTF-8 encoding. Therefore, functions like + `std::string::size()` or `std::string::length()` return the number of + bytes in the string rather than the number of characters or glyphs. + + #### String comparison + + [RFC 7159](http://rfc7159.net/rfc7159) states: + > Software implementations are typically required to test names of object + > members for equality. Implementations that transform the textual + > representation into sequences of Unicode code units and then perform the + > comparison numerically, code unit by code unit, are interoperable in the + > sense that implementations will agree in all cases on equality or + > inequality of two strings. For example, implementations that compare + > strings with escaped characters unconverted may incorrectly find that + > `"a\\b"` and `"a\u005Cb"` are not equal. + + This implementation is interoperable as it does compare strings code unit + by code unit. + + #### Storage + + String values are stored as pointers in a @ref basic_json type. That is, + for any access to string values, a pointer of type `string_t*` must be + dereferenced. + + @since version 1.0.0 + */ + using string_t = StringType; + + /*! + @brief a type for a boolean + + [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a + type which differentiates the two literals `true` and `false`. + + To store objects in C++, a type is defined by the template parameter @a + BooleanType which chooses the type to use. + + #### Default type + + With the default values for @a BooleanType (`bool`), the default value for + @a boolean_t is: + + @code {.cpp} + bool + @endcode + + #### Storage + + Boolean values are stored directly inside a @ref basic_json type. + + @since version 1.0.0 + */ + using boolean_t = BooleanType; + + /*! + @brief a type for a number (integer) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store integer numbers in C++, a type is defined by the template + parameter @a NumberIntegerType which chooses the type to use. + + #### Default type + + With the default values for @a NumberIntegerType (`int64_t`), the default + value for @a number_integer_t is: + + @code {.cpp} + int64_t + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the range and precision of numbers. + + When the default type is used, the maximal integer number that can be + stored is `9223372036854775807` (INT64_MAX) and the minimal integer number + that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers + that are out of range will yield over/underflow when used in a + constructor. During deserialization, too large or small integer numbers + will be automatically be stored as @ref number_unsigned_t or @ref + number_float_t. + + [RFC 7159](http://rfc7159.net/rfc7159) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. + + As this range is a subrange of the exactly supported range [INT64_MIN, + INT64_MAX], this class's integer type is interoperable. + + #### Storage + + Integer number values are stored directly inside a @ref basic_json type. + + @sa @ref number_float_t -- type for number values (floating-point) + + @sa @ref number_unsigned_t -- type for number values (unsigned integer) + + @since version 1.0.0 + */ + using number_integer_t = NumberIntegerType; + + /*! + @brief a type for a number (unsigned) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store unsigned integer numbers in C++, a type is defined by the + template parameter @a NumberUnsignedType which chooses the type to use. + + #### Default type + + With the default values for @a NumberUnsignedType (`uint64_t`), the + default value for @a number_unsigned_t is: + + @code {.cpp} + uint64_t + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the range and precision of numbers. + + When the default type is used, the maximal integer number that can be + stored is `18446744073709551615` (UINT64_MAX) and the minimal integer + number that can be stored is `0`. Integer numbers that are out of range + will yield over/underflow when used in a constructor. During + deserialization, too large or small integer numbers will be automatically + be stored as @ref number_integer_t or @ref number_float_t. + + [RFC 7159](http://rfc7159.net/rfc7159) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. + + As this range is a subrange (when considered in conjunction with the + number_integer_t type) of the exactly supported range [0, UINT64_MAX], + this class's integer type is interoperable. + + #### Storage + + Integer number values are stored directly inside a @ref basic_json type. + + @sa @ref number_float_t -- type for number values (floating-point) + @sa @ref number_integer_t -- type for number values (integer) + + @since version 2.0.0 + */ + using number_unsigned_t = NumberUnsignedType; + + /*! + @brief a type for a number (floating-point) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store floating-point numbers in C++, a type is defined by the template + parameter @a NumberFloatType which chooses the type to use. + + #### Default type + + With the default values for @a NumberFloatType (`double`), the default + value for @a number_float_t is: + + @code {.cpp} + double + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in floating-point literals will be ignored. Internally, + the value will be stored as decimal number. For instance, the C++ + floating-point literal `01.2` will be serialized to `1.2`. During + deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) states: + > This specification allows implementations to set limits on the range and + > precision of numbers accepted. Since software that implements IEEE + > 754-2008 binary64 (double precision) numbers is generally available and + > widely used, good interoperability can be achieved by implementations + > that expect no more precision or range than these provide, in the sense + > that implementations will approximate JSON numbers within the expected + > precision. + + This implementation does exactly follow this approach, as it uses double + precision floating-point numbers. Note values smaller than + `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` + will be stored as NaN internally and be serialized to `null`. + + #### Storage + + Floating-point number values are stored directly inside a @ref basic_json + type. + + @sa @ref number_integer_t -- type for number values (integer) + + @sa @ref number_unsigned_t -- type for number values (unsigned integer) + + @since version 1.0.0 + */ + using number_float_t = NumberFloatType; + + /*! + @brief a type for a packed binary type + + This type is a type designed to carry binary data that appears in various + serialized formats, such as CBOR's Major Type 2, MessagePack's bin, and + BSON's generic binary subtype. This type is NOT a part of standard JSON and + exists solely for compatibility with these binary types. As such, it is + simply defined as an ordered sequence of zero or more byte values. + + Additionally, as an implementation detail, the subtype of the binary data is + carried around as a `std::uint8_t`, which is compatible with both of the + binary data formats that use binary subtyping, (though the specific + numbering is incompatible with each other, and it is up to the user to + translate between them). + + [CBOR's RFC 7049](https://tools.ietf.org/html/rfc7049) describes this type + as: + > Major type 2: a byte string. The string's length in bytes is represented + > following the rules for positive integers (major type 0). + + [MessagePack's documentation on the bin type + family](https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family) + describes this type as: + > Bin format family stores an byte array in 2, 3, or 5 bytes of extra bytes + > in addition to the size of the byte array. + + [BSON's specifications](http://bsonspec.org/spec.html) describe several + binary types; however, this type is intended to represent the generic binary + type which has the description: + > Generic binary subtype - This is the most commonly used binary subtype and + > should be the 'default' for drivers and tools. + + None of these impose any limitations on the internal representation other + than the basic unit of storage be some type of array whose parts are + decomposable into bytes. + + The default representation of this binary format is a + `std::vector`, which is a very common way to represent a byte + array in modern C++. + + #### Default type + + The default values for @a BinaryType is `std::vector` + + #### Storage + + Binary Arrays are stored as pointers in a @ref basic_json type. That is, + for any access to array values, a pointer of the type `binary_t*` must be + dereferenced. + + #### Notes on subtypes + + - CBOR + - Binary values are represented as byte strings. No subtypes are + supported and will be ignored when CBOR is written. + - MessagePack + - If a subtype is given and the binary array contains exactly 1, 2, 4, 8, + or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8) + is used. For other sizes, the ext family (ext8, ext16, ext32) is used. + The subtype is then added as singed 8-bit integer. + - If no subtype is given, the bin family (bin8, bin16, bin32) is used. + - BSON + - If a subtype is given, it is used and added as unsigned 8-bit integer. + - If no subtype is given, the generic binary subtype 0x00 is used. + + @sa @ref binary -- create a binary array + + @since version 3.8.0 + */ + using binary_t = nlohmann::byte_container_with_subtype; + /// @} + + private: + + /// helper for exception-safe object creation + template + JSON_HEDLEY_RETURNS_NON_NULL + static T* create(Args&& ... args) + { + AllocatorType alloc; + using AllocatorTraits = std::allocator_traits>; + + auto deleter = [&](T * object) + { + AllocatorTraits::deallocate(alloc, object, 1); + }; + std::unique_ptr object(AllocatorTraits::allocate(alloc, 1), deleter); + AllocatorTraits::construct(alloc, object.get(), std::forward(args)...); + JSON_ASSERT(object != nullptr); + return object.release(); + } + + //////////////////////// + // JSON value storage // + //////////////////////// + + /*! + @brief a JSON value + + The actual storage for a JSON value of the @ref basic_json class. This + union combines the different storage types for the JSON value types + defined in @ref value_t. + + JSON type | value_t type | used type + --------- | --------------- | ------------------------ + object | object | pointer to @ref object_t + array | array | pointer to @ref array_t + string | string | pointer to @ref string_t + boolean | boolean | @ref boolean_t + number | number_integer | @ref number_integer_t + number | number_unsigned | @ref number_unsigned_t + number | number_float | @ref number_float_t + binary | binary | pointer to @ref binary_t + null | null | *no value is stored* + + @note Variable-length types (objects, arrays, and strings) are stored as + pointers. The size of the union should not exceed 64 bits if the default + value types are used. + + @since version 1.0.0 + */ + union json_value + { + /// object (stored with pointer to save storage) + object_t* object; + /// array (stored with pointer to save storage) + array_t* array; + /// string (stored with pointer to save storage) + string_t* string; + /// binary (stored with pointer to save storage) + binary_t* binary; + /// boolean + boolean_t boolean; + /// number (integer) + number_integer_t number_integer; + /// number (unsigned integer) + number_unsigned_t number_unsigned; + /// number (floating-point) + number_float_t number_float; + + /// default constructor (for null values) + json_value() = default; + /// constructor for booleans + json_value(boolean_t v) noexcept : boolean(v) {} + /// constructor for numbers (integer) + json_value(number_integer_t v) noexcept : number_integer(v) {} + /// constructor for numbers (unsigned) + json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} + /// constructor for numbers (floating-point) + json_value(number_float_t v) noexcept : number_float(v) {} + /// constructor for empty values of a given type + json_value(value_t t) + { + switch (t) + { + case value_t::object: + { + object = create(); + break; + } + + case value_t::array: + { + array = create(); + break; + } + + case value_t::string: + { + string = create(""); + break; + } + + case value_t::binary: + { + binary = create(); + break; + } + + case value_t::boolean: + { + boolean = boolean_t(false); + break; + } + + case value_t::number_integer: + { + number_integer = number_integer_t(0); + break; + } + + case value_t::number_unsigned: + { + number_unsigned = number_unsigned_t(0); + break; + } + + case value_t::number_float: + { + number_float = number_float_t(0.0); + break; + } + + case value_t::null: + { + object = nullptr; // silence warning, see #821 + break; + } + + default: + { + object = nullptr; // silence warning, see #821 + if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) + { + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.9.1")); // LCOV_EXCL_LINE + } + break; + } + } + } + + /// constructor for strings + json_value(const string_t& value) + { + string = create(value); + } + + /// constructor for rvalue strings + json_value(string_t&& value) + { + string = create(std::move(value)); + } + + /// constructor for objects + json_value(const object_t& value) + { + object = create(value); + } + + /// constructor for rvalue objects + json_value(object_t&& value) + { + object = create(std::move(value)); + } + + /// constructor for arrays + json_value(const array_t& value) + { + array = create(value); + } + + /// constructor for rvalue arrays + json_value(array_t&& value) + { + array = create(std::move(value)); + } + + /// constructor for binary arrays + json_value(const typename binary_t::container_type& value) + { + binary = create(value); + } + + /// constructor for rvalue binary arrays + json_value(typename binary_t::container_type&& value) + { + binary = create(std::move(value)); + } + + /// constructor for binary arrays (internal type) + json_value(const binary_t& value) + { + binary = create(value); + } + + /// constructor for rvalue binary arrays (internal type) + json_value(binary_t&& value) + { + binary = create(std::move(value)); + } + + void destroy(value_t t) noexcept + { + // flatten the current json_value to a heap-allocated stack + std::vector stack; + + // move the top-level items to stack + if (t == value_t::array) + { + stack.reserve(array->size()); + std::move(array->begin(), array->end(), std::back_inserter(stack)); + } + else if (t == value_t::object) + { + stack.reserve(object->size()); + for (auto&& it : *object) + { + stack.push_back(std::move(it.second)); + } + } + + while (!stack.empty()) + { + // move the last item to local variable to be processed + basic_json current_item(std::move(stack.back())); + stack.pop_back(); + + // if current_item is array/object, move + // its children to the stack to be processed later + if (current_item.is_array()) + { + std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(), + std::back_inserter(stack)); + + current_item.m_value.array->clear(); + } + else if (current_item.is_object()) + { + for (auto&& it : *current_item.m_value.object) + { + stack.push_back(std::move(it.second)); + } + + current_item.m_value.object->clear(); + } + + // it's now safe that current_item get destructed + // since it doesn't have any children + } + + switch (t) + { + case value_t::object: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, object); + std::allocator_traits::deallocate(alloc, object, 1); + break; + } + + case value_t::array: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, array); + std::allocator_traits::deallocate(alloc, array, 1); + break; + } + + case value_t::string: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, string); + std::allocator_traits::deallocate(alloc, string, 1); + break; + } + + case value_t::binary: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, binary); + std::allocator_traits::deallocate(alloc, binary, 1); + break; + } + + default: + { + break; + } + } + } + }; + + /*! + @brief checks the class invariants + + This function asserts the class invariants. It needs to be called at the + end of every constructor to make sure that created objects respect the + invariant. Furthermore, it has to be called each time the type of a JSON + value is changed, because the invariant expresses a relationship between + @a m_type and @a m_value. + */ + void assert_invariant() const noexcept + { + JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr); + JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr); + JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr); + JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr); + } + + public: + ////////////////////////// + // JSON parser callback // + ////////////////////////// + + /*! + @brief parser event types + + The parser callback distinguishes the following events: + - `object_start`: the parser read `{` and started to process a JSON object + - `key`: the parser read a key of a value in an object + - `object_end`: the parser read `}` and finished processing a JSON object + - `array_start`: the parser read `[` and started to process a JSON array + - `array_end`: the parser read `]` and finished processing a JSON array + - `value`: the parser finished reading a JSON value + + @image html callback_events.png "Example when certain parse events are triggered" + + @sa @ref parser_callback_t for more information and examples + */ + using parse_event_t = detail::parse_event_t; + + /*! + @brief per-element parser callback type + + With a parser callback function, the result of parsing a JSON text can be + influenced. When passed to @ref parse, it is called on certain events + (passed as @ref parse_event_t via parameter @a event) with a set recursion + depth @a depth and context JSON value @a parsed. The return value of the + callback function is a boolean indicating whether the element that emitted + the callback shall be kept or not. + + We distinguish six scenarios (determined by the event type) in which the + callback function can be called. The following table describes the values + of the parameters @a depth, @a event, and @a parsed. + + parameter @a event | description | parameter @a depth | parameter @a parsed + ------------------ | ----------- | ------------------ | ------------------- + parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded + parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key + parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object + parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded + parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array + parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value + + @image html callback_events.png "Example when certain parse events are triggered" + + Discarding a value (i.e., returning `false`) has different effects + depending on the context in which function was called: + + - Discarded values in structured types are skipped. That is, the parser + will behave as if the discarded value was never read. + - In case a value outside a structured type is skipped, it is replaced + with `null`. This case happens if the top-level element is skipped. + + @param[in] depth the depth of the recursion during parsing + + @param[in] event an event of type parse_event_t indicating the context in + the callback function has been called + + @param[in,out] parsed the current intermediate parse result; note that + writing to this value has no effect for parse_event_t::key events + + @return Whether the JSON value which called the function during parsing + should be kept (`true`) or not (`false`). In the latter case, it is either + skipped completely or replaced by an empty discarded object. + + @sa @ref parse for examples + + @since version 1.0.0 + */ + using parser_callback_t = detail::parser_callback_t; + + ////////////////// + // constructors // + ////////////////// + + /// @name constructors and destructors + /// Constructors of class @ref basic_json, copy/move constructor, copy + /// assignment, static functions creating objects, and the destructor. + /// @{ + + /*! + @brief create an empty value with a given type + + Create an empty JSON value with a given type. The value will be default + initialized with an empty value which depends on the type: + + Value type | initial value + ----------- | ------------- + null | `null` + boolean | `false` + string | `""` + number | `0` + object | `{}` + array | `[]` + binary | empty array + + @param[in] v the type of the value to create + + @complexity Constant. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows the constructor for different @ref + value_t values,basic_json__value_t} + + @sa @ref clear() -- restores the postcondition of this constructor + + @since version 1.0.0 + */ + basic_json(const value_t v) + : m_type(v), m_value(v) + { + assert_invariant(); + } + + /*! + @brief create a null object + + Create a `null` JSON value. It either takes a null pointer as parameter + (explicitly creating `null`) or no parameter (implicitly creating `null`). + The passed null pointer itself is not read -- it is only used to choose + the right constructor. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this constructor never throws + exceptions. + + @liveexample{The following code shows the constructor with and without a + null pointer parameter.,basic_json__nullptr_t} + + @since version 1.0.0 + */ + basic_json(std::nullptr_t = nullptr) noexcept + : basic_json(value_t::null) + { + assert_invariant(); + } + + /*! + @brief create a JSON value + + This is a "catch all" constructor for all compatible JSON types; that is, + types for which a `to_json()` method exists. The constructor forwards the + parameter @a val to that method (to `json_serializer::to_json` method + with `U = uncvref_t`, to be exact). + + Template type @a CompatibleType includes, but is not limited to, the + following types: + - **arrays**: @ref array_t and all kinds of compatible containers such as + `std::vector`, `std::deque`, `std::list`, `std::forward_list`, + `std::array`, `std::valarray`, `std::set`, `std::unordered_set`, + `std::multiset`, and `std::unordered_multiset` with a `value_type` from + which a @ref basic_json value can be constructed. + - **objects**: @ref object_t and all kinds of compatible associative + containers such as `std::map`, `std::unordered_map`, `std::multimap`, + and `std::unordered_multimap` with a `key_type` compatible to + @ref string_t and a `value_type` from which a @ref basic_json value can + be constructed. + - **strings**: @ref string_t, string literals, and all compatible string + containers can be used. + - **numbers**: @ref number_integer_t, @ref number_unsigned_t, + @ref number_float_t, and all convertible number types such as `int`, + `size_t`, `int64_t`, `float` or `double` can be used. + - **boolean**: @ref boolean_t / `bool` can be used. + - **binary**: @ref binary_t / `std::vector` may be used, + unfortunately because string literals cannot be distinguished from binary + character arrays by the C++ type system, all types compatible with `const + char*` will be directed to the string constructor instead. This is both + for backwards compatibility, and due to the fact that a binary type is not + a standard JSON type. + + See the examples below. + + @tparam CompatibleType a type such that: + - @a CompatibleType is not derived from `std::istream`, + - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move + constructors), + - @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments) + - @a CompatibleType is not a @ref basic_json nested type (e.g., + @ref json_pointer, @ref iterator, etc ...) + - @ref @ref json_serializer has a + `to_json(basic_json_t&, CompatibleType&&)` method + + @tparam U = `uncvref_t` + + @param[in] val the value to be forwarded to the respective constructor + + @complexity Usually linear in the size of the passed @a val, also + depending on the implementation of the called `to_json()` + method. + + @exceptionsafety Depends on the called constructor. For types directly + supported by the library (i.e., all types for which no `to_json()` function + was provided), strong guarantee holds: if an exception is thrown, there are + no changes to any JSON value. + + @liveexample{The following code shows the constructor with several + compatible types.,basic_json__CompatibleType} + + @since version 2.1.0 + */ + template < typename CompatibleType, + typename U = detail::uncvref_t, + detail::enable_if_t < + !detail::is_basic_json::value && detail::is_compatible_type::value, int > = 0 > + basic_json(CompatibleType && val) noexcept(noexcept( + JSONSerializer::to_json(std::declval(), + std::forward(val)))) + { + JSONSerializer::to_json(*this, std::forward(val)); + assert_invariant(); + } + + /*! + @brief create a JSON value from an existing one + + This is a constructor for existing @ref basic_json types. + It does not hijack copy/move constructors, since the parameter has different + template arguments than the current ones. + + The constructor tries to convert the internal @ref m_value of the parameter. + + @tparam BasicJsonType a type such that: + - @a BasicJsonType is a @ref basic_json type. + - @a BasicJsonType has different template arguments than @ref basic_json_t. + + @param[in] val the @ref basic_json value to be converted. + + @complexity Usually linear in the size of the passed @a val, also + depending on the implementation of the called `to_json()` + method. + + @exceptionsafety Depends on the called constructor. For types directly + supported by the library (i.e., all types for which no `to_json()` function + was provided), strong guarantee holds: if an exception is thrown, there are + no changes to any JSON value. + + @since version 3.2.0 + */ + template < typename BasicJsonType, + detail::enable_if_t < + detail::is_basic_json::value&& !std::is_same::value, int > = 0 > + basic_json(const BasicJsonType& val) + { + using other_boolean_t = typename BasicJsonType::boolean_t; + using other_number_float_t = typename BasicJsonType::number_float_t; + using other_number_integer_t = typename BasicJsonType::number_integer_t; + using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using other_string_t = typename BasicJsonType::string_t; + using other_object_t = typename BasicJsonType::object_t; + using other_array_t = typename BasicJsonType::array_t; + using other_binary_t = typename BasicJsonType::binary_t; + + switch (val.type()) + { + case value_t::boolean: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_float: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_integer: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_unsigned: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::string: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::object: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::array: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::binary: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::null: + *this = nullptr; + break; + case value_t::discarded: + m_type = value_t::discarded; + break; + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE + } + assert_invariant(); + } + + /*! + @brief create a container (array or object) from an initializer list + + Creates a JSON value of type array or object from the passed initializer + list @a init. In case @a type_deduction is `true` (default), the type of + the JSON value to be created is deducted from the initializer list @a init + according to the following rules: + + 1. If the list is empty, an empty JSON object value `{}` is created. + 2. If the list consists of pairs whose first element is a string, a JSON + object value is created where the first elements of the pairs are + treated as keys and the second elements are as values. + 3. In all other cases, an array is created. + + The rules aim to create the best fit between a C++ initializer list and + JSON values. The rationale is as follows: + + 1. The empty initializer list is written as `{}` which is exactly an empty + JSON object. + 2. C++ has no way of describing mapped types other than to list a list of + pairs. As JSON requires that keys must be of type string, rule 2 is the + weakest constraint one can pose on initializer lists to interpret them + as an object. + 3. In all other cases, the initializer list could not be interpreted as + JSON object type, so interpreting it as JSON array type is safe. + + With the rules described above, the following JSON values cannot be + expressed by an initializer list: + + - the empty array (`[]`): use @ref array(initializer_list_t) + with an empty initializer list in this case + - arrays whose elements satisfy rule 2: use @ref + array(initializer_list_t) with the same initializer list + in this case + + @note When used without parentheses around an empty initializer list, @ref + basic_json() is called instead of this function, yielding the JSON null + value. + + @param[in] init initializer list with JSON values + + @param[in] type_deduction internal parameter; when set to `true`, the type + of the JSON value is deducted from the initializer list @a init; when set + to `false`, the type provided via @a manual_type is forced. This mode is + used by the functions @ref array(initializer_list_t) and + @ref object(initializer_list_t). + + @param[in] manual_type internal parameter; when @a type_deduction is set + to `false`, the created JSON value will use the provided type (only @ref + value_t::array and @ref value_t::object are valid); when @a type_deduction + is set to `true`, this parameter has no effect + + @throw type_error.301 if @a type_deduction is `false`, @a manual_type is + `value_t::object`, but @a init contains an element which is not a pair + whose first element is a string. In this case, the constructor could not + create an object. If @a type_deduction would have be `true`, an array + would have been created. See @ref object(initializer_list_t) + for an example. + + @complexity Linear in the size of the initializer list @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The example below shows how JSON values are created from + initializer lists.,basic_json__list_init_t} + + @sa @ref array(initializer_list_t) -- create a JSON array + value from an initializer list + @sa @ref object(initializer_list_t) -- create a JSON object + value from an initializer list + + @since version 1.0.0 + */ + basic_json(initializer_list_t init, + bool type_deduction = true, + value_t manual_type = value_t::array) + { + // check if each element is an array with two elements whose first + // element is a string + bool is_an_object = std::all_of(init.begin(), init.end(), + [](const detail::json_ref& element_ref) + { + return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string(); + }); + + // adjust type if type deduction is not wanted + if (!type_deduction) + { + // if array is wanted, do not create an object though possible + if (manual_type == value_t::array) + { + is_an_object = false; + } + + // if object is wanted but impossible, throw an exception + if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) + { + JSON_THROW(type_error::create(301, "cannot create object from initializer list")); + } + } + + if (is_an_object) + { + // the initializer list is a list of pairs -> create object + m_type = value_t::object; + m_value = value_t::object; + + std::for_each(init.begin(), init.end(), [this](const detail::json_ref& element_ref) + { + auto element = element_ref.moved_or_copied(); + m_value.object->emplace( + std::move(*((*element.m_value.array)[0].m_value.string)), + std::move((*element.m_value.array)[1])); + }); + } + else + { + // the initializer list describes an array -> create array + m_type = value_t::array; + m_value.array = create(init.begin(), init.end()); + } + + assert_invariant(); + } + + /*! + @brief explicitly create a binary array (without subtype) + + Creates a JSON binary array value from a given binary container. Binary + values are part of various binary formats, such as CBOR, MessagePack, and + BSON. This constructor is used to create a value for serialization to those + formats. + + @note Note, this function exists because of the difficulty in correctly + specifying the correct template overload in the standard value ctor, as both + JSON arrays and JSON binary arrays are backed with some form of a + `std::vector`. Because JSON binary arrays are a non-standard extension it + was decided that it would be best to prevent automatic initialization of a + binary array type, for backwards compatibility and so it does not happen on + accident. + + @param[in] init container containing bytes to use as binary type + + @return JSON binary array value + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @since version 3.8.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = init; + return res; + } + + /*! + @brief explicitly create a binary array (with subtype) + + Creates a JSON binary array value from a given binary container. Binary + values are part of various binary formats, such as CBOR, MessagePack, and + BSON. This constructor is used to create a value for serialization to those + formats. + + @note Note, this function exists because of the difficulty in correctly + specifying the correct template overload in the standard value ctor, as both + JSON arrays and JSON binary arrays are backed with some form of a + `std::vector`. Because JSON binary arrays are a non-standard extension it + was decided that it would be best to prevent automatic initialization of a + binary array type, for backwards compatibility and so it does not happen on + accident. + + @param[in] init container containing bytes to use as binary type + @param[in] subtype subtype to use in MessagePack and BSON + + @return JSON binary array value + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @since version 3.8.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init, std::uint8_t subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(init, subtype); + return res; + } + + /// @copydoc binary(const typename binary_t::container_type&) + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = std::move(init); + return res; + } + + /// @copydoc binary(const typename binary_t::container_type&, std::uint8_t) + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init, std::uint8_t subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(std::move(init), subtype); + return res; + } + + /*! + @brief explicitly create an array from an initializer list + + Creates a JSON array value from a given initializer list. That is, given a + list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the + initializer list is empty, the empty array `[]` is created. + + @note This function is only needed to express two edge cases that cannot + be realized with the initializer list constructor (@ref + basic_json(initializer_list_t, bool, value_t)). These cases + are: + 1. creating an array whose elements are all pairs whose first element is a + string -- in this case, the initializer list constructor would create an + object, taking the first elements as keys + 2. creating an empty array -- passing the empty initializer list to the + initializer list constructor yields an empty object + + @param[in] init initializer list with JSON values to create an array from + (optional) + + @return JSON array value + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows an example for the `array` + function.,array} + + @sa @ref basic_json(initializer_list_t, bool, value_t) -- + create a JSON value from an initializer list + @sa @ref object(initializer_list_t) -- create a JSON object + value from an initializer list + + @since version 1.0.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json array(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::array); + } + + /*! + @brief explicitly create an object from an initializer list + + Creates a JSON object value from a given initializer list. The initializer + lists elements must be pairs, and their first elements must be strings. If + the initializer list is empty, the empty object `{}` is created. + + @note This function is only added for symmetry reasons. In contrast to the + related function @ref array(initializer_list_t), there are + no cases which can only be expressed by this function. That is, any + initializer list @a init can also be passed to the initializer list + constructor @ref basic_json(initializer_list_t, bool, value_t). + + @param[in] init initializer list to create an object from (optional) + + @return JSON object value + + @throw type_error.301 if @a init is not a list of pairs whose first + elements are strings. In this case, no object can be created. When such a + value is passed to @ref basic_json(initializer_list_t, bool, value_t), + an array would have been created from the passed initializer list @a init. + See example below. + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows an example for the `object` + function.,object} + + @sa @ref basic_json(initializer_list_t, bool, value_t) -- + create a JSON value from an initializer list + @sa @ref array(initializer_list_t) -- create a JSON array + value from an initializer list + + @since version 1.0.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json object(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::object); + } + + /*! + @brief construct an array with count copies of given value + + Constructs a JSON array value by creating @a cnt copies of a passed value. + In case @a cnt is `0`, an empty array is created. + + @param[in] cnt the number of JSON copies of @a val to create + @param[in] val the JSON value to copy + + @post `std::distance(begin(),end()) == cnt` holds. + + @complexity Linear in @a cnt. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows examples for the @ref + basic_json(size_type\, const basic_json&) + constructor.,basic_json__size_type_basic_json} + + @since version 1.0.0 + */ + basic_json(size_type cnt, const basic_json& val) + : m_type(value_t::array) + { + m_value.array = create(cnt, val); + assert_invariant(); + } + + /*! + @brief construct a JSON container given an iterator range + + Constructs the JSON value with the contents of the range `[first, last)`. + The semantics depends on the different types a JSON value can have: + - In case of a null type, invalid_iterator.206 is thrown. + - In case of other primitive types (number, boolean, or string), @a first + must be `begin()` and @a last must be `end()`. In this case, the value is + copied. Otherwise, invalid_iterator.204 is thrown. + - In case of structured types (array, object), the constructor behaves as + similar versions for `std::vector` or `std::map`; that is, a JSON array + or object is constructed from the values in the range. + + @tparam InputIT an input iterator type (@ref iterator or @ref + const_iterator) + + @param[in] first begin of the range to copy from (included) + @param[in] last end of the range to copy from (excluded) + + @pre Iterators @a first and @a last must be initialized. **This + precondition is enforced with an assertion (see warning).** If + assertions are switched off, a violation of this precondition yields + undefined behavior. + + @pre Range `[first, last)` is valid. Usually, this precondition cannot be + checked efficiently. Only certain edge cases are detected; see the + description of the exceptions below. A violation of this precondition + yields undefined behavior. + + @warning A precondition is enforced with a runtime assertion that will + result in calling `std::abort` if this precondition is not met. + Assertions can be disabled by defining `NDEBUG` at compile time. + See https://en.cppreference.com/w/cpp/error/assert for more + information. + + @throw invalid_iterator.201 if iterators @a first and @a last are not + compatible (i.e., do not belong to the same JSON value). In this case, + the range `[first, last)` is undefined. + @throw invalid_iterator.204 if iterators @a first and @a last belong to a + primitive type (number, boolean, or string), but @a first does not point + to the first element any more. In this case, the range `[first, last)` is + undefined. See example code below. + @throw invalid_iterator.206 if iterators @a first and @a last belong to a + null value. In this case, the range `[first, last)` is undefined. + + @complexity Linear in distance between @a first and @a last. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The example below shows several ways to create JSON values by + specifying a subrange with iterators.,basic_json__InputIt_InputIt} + + @since version 1.0.0 + */ + template < class InputIT, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type = 0 > + basic_json(InputIT first, InputIT last) + { + JSON_ASSERT(first.m_object != nullptr); + JSON_ASSERT(last.m_object != nullptr); + + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible")); + } + + // copy type from first iterator + m_type = first.m_object->m_type; + + // check if iterator range is complete for primitive values + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + } + break; + } + + default: + break; + } + + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = first.m_object->m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value.number_unsigned = first.m_object->m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value.number_float = first.m_object->m_value.number_float; + break; + } + + case value_t::boolean: + { + m_value.boolean = first.m_object->m_value.boolean; + break; + } + + case value_t::string: + { + m_value = *first.m_object->m_value.string; + break; + } + + case value_t::object: + { + m_value.object = create(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + m_value.array = create(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + case value_t::binary: + { + m_value = *first.m_object->m_value.binary; + break; + } + + default: + JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + + std::string(first.m_object->type_name()))); + } + + assert_invariant(); + } + + + /////////////////////////////////////// + // other constructors and destructor // + /////////////////////////////////////// + + template, + std::is_same>::value, int> = 0 > + basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {} + + /*! + @brief copy constructor + + Creates a copy of a given JSON value. + + @param[in] other the JSON value to copy + + @post `*this == other` + + @complexity Linear in the size of @a other. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is linear. + - As postcondition, it holds: `other == basic_json(other)`. + + @liveexample{The following code shows an example for the copy + constructor.,basic_json__basic_json} + + @since version 1.0.0 + */ + basic_json(const basic_json& other) + : m_type(other.m_type) + { + // check of passed value is valid + other.assert_invariant(); + + switch (m_type) + { + case value_t::object: + { + m_value = *other.m_value.object; + break; + } + + case value_t::array: + { + m_value = *other.m_value.array; + break; + } + + case value_t::string: + { + m_value = *other.m_value.string; + break; + } + + case value_t::boolean: + { + m_value = other.m_value.boolean; + break; + } + + case value_t::number_integer: + { + m_value = other.m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value = other.m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value = other.m_value.number_float; + break; + } + + case value_t::binary: + { + m_value = *other.m_value.binary; + break; + } + + default: + break; + } + + assert_invariant(); + } + + /*! + @brief move constructor + + Move constructor. Constructs a JSON value with the contents of the given + value @a other using move semantics. It "steals" the resources from @a + other and leaves it as JSON null value. + + @param[in,out] other value to move to this object + + @post `*this` has the same value as @a other before the call. + @post @a other is a JSON null value. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this constructor never throws + exceptions. + + @requirement This function helps `basic_json` satisfying the + [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible) + requirements. + + @liveexample{The code below shows the move constructor explicitly called + via std::move.,basic_json__moveconstructor} + + @since version 1.0.0 + */ + basic_json(basic_json&& other) noexcept + : m_type(std::move(other.m_type)), + m_value(std::move(other.m_value)) + { + // check that passed value is valid + other.assert_invariant(); + + // invalidate payload + other.m_type = value_t::null; + other.m_value = {}; + + assert_invariant(); + } + + /*! + @brief copy assignment + + Copy assignment operator. Copies a JSON value via the "copy and swap" + strategy: It is expressed in terms of the copy constructor, destructor, + and the `swap()` member function. + + @param[in] other value to copy from + + @complexity Linear. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is linear. + + @liveexample{The code below shows and example for the copy assignment. It + creates a copy of value `a` which is then swapped with `b`. Finally\, the + copy of `a` (which is the null value after the swap) is + destroyed.,basic_json__copyassignment} + + @since version 1.0.0 + */ + basic_json& operator=(basic_json other) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) + { + // check that passed value is valid + other.assert_invariant(); + + using std::swap; + swap(m_type, other.m_type); + swap(m_value, other.m_value); + + assert_invariant(); + return *this; + } + + /*! + @brief destructor + + Destroys the JSON value and frees all allocated memory. + + @complexity Linear. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is linear. + - All stored elements are destroyed and all memory is freed. + + @since version 1.0.0 + */ + ~basic_json() noexcept + { + assert_invariant(); + m_value.destroy(m_type); + } + + /// @} + + public: + /////////////////////// + // object inspection // + /////////////////////// + + /// @name object inspection + /// Functions to inspect the type of a JSON value. + /// @{ + + /*! + @brief serialization + + Serialization function for JSON values. The function tries to mimic + Python's `json.dumps()` function, and currently supports its @a indent + and @a ensure_ascii parameters. + + @param[in] indent If indent is nonnegative, then array elements and object + members will be pretty-printed with that indent level. An indent level of + `0` will only insert newlines. `-1` (the default) selects the most compact + representation. + @param[in] indent_char The character to use for indentation if @a indent is + greater than `0`. The default is ` ` (space). + @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters + in the output are escaped with `\uXXXX` sequences, and the result consists + of ASCII characters only. + @param[in] error_handler how to react on decoding errors; there are three + possible values: `strict` (throws and exception in case a decoding error + occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD), + and `ignore` (ignore invalid UTF-8 sequences during serialization; all + bytes are copied to the output unchanged). + + @return string containing the serialization of the JSON value + + @throw type_error.316 if a string stored inside the JSON value is not + UTF-8 encoded and @a error_handler is set to strict + + @note Binary values are serialized as object containing two keys: + - "bytes": an array of bytes as integers + - "subtype": the subtype as integer or "null" if the binary has no subtype + + @complexity Linear. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @liveexample{The following example shows the effect of different @a indent\, + @a indent_char\, and @a ensure_ascii parameters to the result of the + serialization.,dump} + + @see https://docs.python.org/2/library/json.html#json.dump + + @since version 1.0.0; indentation character @a indent_char, option + @a ensure_ascii and exceptions added in version 3.0.0; error + handlers added in version 3.4.0; serialization of binary values added + in version 3.8.0. + */ + string_t dump(const int indent = -1, + const char indent_char = ' ', + const bool ensure_ascii = false, + const error_handler_t error_handler = error_handler_t::strict) const + { + string_t result; + serializer s(detail::output_adapter(result), indent_char, error_handler); + + if (indent >= 0) + { + s.dump(*this, true, ensure_ascii, static_cast(indent)); + } + else + { + s.dump(*this, false, ensure_ascii, 0); + } + + return result; + } + + /*! + @brief return the type of the JSON value (explicit) + + Return the type of the JSON value as a value from the @ref value_t + enumeration. + + @return the type of the JSON value + Value type | return value + ------------------------- | ------------------------- + null | value_t::null + boolean | value_t::boolean + string | value_t::string + number (integer) | value_t::number_integer + number (unsigned integer) | value_t::number_unsigned + number (floating-point) | value_t::number_float + object | value_t::object + array | value_t::array + binary | value_t::binary + discarded | value_t::discarded + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `type()` for all JSON + types.,type} + + @sa @ref operator value_t() -- return the type of the JSON value (implicit) + @sa @ref type_name() -- return the type as string + + @since version 1.0.0 + */ + constexpr value_t type() const noexcept + { + return m_type; + } + + /*! + @brief return whether type is primitive + + This function returns true if and only if the JSON type is primitive + (string, number, boolean, or null). + + @return `true` if type is primitive (string, number, boolean, or null), + `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_primitive()` for all JSON + types.,is_primitive} + + @sa @ref is_structured() -- returns whether JSON value is structured + @sa @ref is_null() -- returns whether JSON value is `null` + @sa @ref is_string() -- returns whether JSON value is a string + @sa @ref is_boolean() -- returns whether JSON value is a boolean + @sa @ref is_number() -- returns whether JSON value is a number + @sa @ref is_binary() -- returns whether JSON value is a binary array + + @since version 1.0.0 + */ + constexpr bool is_primitive() const noexcept + { + return is_null() || is_string() || is_boolean() || is_number() || is_binary(); + } + + /*! + @brief return whether type is structured + + This function returns true if and only if the JSON type is structured + (array or object). + + @return `true` if type is structured (array or object), `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_structured()` for all JSON + types.,is_structured} + + @sa @ref is_primitive() -- returns whether value is primitive + @sa @ref is_array() -- returns whether value is an array + @sa @ref is_object() -- returns whether value is an object + + @since version 1.0.0 + */ + constexpr bool is_structured() const noexcept + { + return is_array() || is_object(); + } + + /*! + @brief return whether value is null + + This function returns true if and only if the JSON value is null. + + @return `true` if type is null, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_null()` for all JSON + types.,is_null} + + @since version 1.0.0 + */ + constexpr bool is_null() const noexcept + { + return m_type == value_t::null; + } + + /*! + @brief return whether value is a boolean + + This function returns true if and only if the JSON value is a boolean. + + @return `true` if type is boolean, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_boolean()` for all JSON + types.,is_boolean} + + @since version 1.0.0 + */ + constexpr bool is_boolean() const noexcept + { + return m_type == value_t::boolean; + } + + /*! + @brief return whether value is a number + + This function returns true if and only if the JSON value is a number. This + includes both integer (signed and unsigned) and floating-point values. + + @return `true` if type is number (regardless whether integer, unsigned + integer or floating-type), `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number()` for all JSON + types.,is_number} + + @sa @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 1.0.0 + */ + constexpr bool is_number() const noexcept + { + return is_number_integer() || is_number_float(); + } + + /*! + @brief return whether value is an integer number + + This function returns true if and only if the JSON value is a signed or + unsigned integer number. This excludes floating-point values. + + @return `true` if type is an integer or unsigned integer number, `false` + otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_integer()` for all + JSON types.,is_number_integer} + + @sa @ref is_number() -- check if value is a number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 1.0.0 + */ + constexpr bool is_number_integer() const noexcept + { + return m_type == value_t::number_integer || m_type == value_t::number_unsigned; + } + + /*! + @brief return whether value is an unsigned integer number + + This function returns true if and only if the JSON value is an unsigned + integer number. This excludes floating-point and signed integer values. + + @return `true` if type is an unsigned integer number, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_unsigned()` for all + JSON types.,is_number_unsigned} + + @sa @ref is_number() -- check if value is a number + @sa @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 2.0.0 + */ + constexpr bool is_number_unsigned() const noexcept + { + return m_type == value_t::number_unsigned; + } + + /*! + @brief return whether value is a floating-point number + + This function returns true if and only if the JSON value is a + floating-point number. This excludes signed and unsigned integer values. + + @return `true` if type is a floating-point number, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_float()` for all + JSON types.,is_number_float} + + @sa @ref is_number() -- check if value is number + @sa @ref is_number_integer() -- check if value is an integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + + @since version 1.0.0 + */ + constexpr bool is_number_float() const noexcept + { + return m_type == value_t::number_float; + } + + /*! + @brief return whether value is an object + + This function returns true if and only if the JSON value is an object. + + @return `true` if type is object, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_object()` for all JSON + types.,is_object} + + @since version 1.0.0 + */ + constexpr bool is_object() const noexcept + { + return m_type == value_t::object; + } + + /*! + @brief return whether value is an array + + This function returns true if and only if the JSON value is an array. + + @return `true` if type is array, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_array()` for all JSON + types.,is_array} + + @since version 1.0.0 + */ + constexpr bool is_array() const noexcept + { + return m_type == value_t::array; + } + + /*! + @brief return whether value is a string + + This function returns true if and only if the JSON value is a string. + + @return `true` if type is string, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_string()` for all JSON + types.,is_string} + + @since version 1.0.0 + */ + constexpr bool is_string() const noexcept + { + return m_type == value_t::string; + } + + /*! + @brief return whether value is a binary array + + This function returns true if and only if the JSON value is a binary array. + + @return `true` if type is binary array, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_binary()` for all JSON + types.,is_binary} + + @since version 3.8.0 + */ + constexpr bool is_binary() const noexcept + { + return m_type == value_t::binary; + } + + /*! + @brief return whether value is discarded + + This function returns true if and only if the JSON value was discarded + during parsing with a callback function (see @ref parser_callback_t). + + @note This function will always be `false` for JSON values after parsing. + That is, discarded values can only occur during parsing, but will be + removed when inside a structured value or replaced by null in other cases. + + @return `true` if type is discarded, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_discarded()` for all JSON + types.,is_discarded} + + @since version 1.0.0 + */ + constexpr bool is_discarded() const noexcept + { + return m_type == value_t::discarded; + } + + /*! + @brief return the type of the JSON value (implicit) + + Implicitly return the type of the JSON value as a value from the @ref + value_t enumeration. + + @return the type of the JSON value + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies the @ref value_t operator for + all JSON types.,operator__value_t} + + @sa @ref type() -- return the type of the JSON value (explicit) + @sa @ref type_name() -- return the type as string + + @since version 1.0.0 + */ + constexpr operator value_t() const noexcept + { + return m_type; + } + + /// @} + + private: + ////////////////// + // value access // + ////////////////// + + /// get a boolean (explicit) + boolean_t get_impl(boolean_t* /*unused*/) const + { + if (JSON_HEDLEY_LIKELY(is_boolean())) + { + return m_value.boolean; + } + + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()))); + } + + /// get a pointer to the value (object) + object_t* get_impl_ptr(object_t* /*unused*/) noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (object) + constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (array) + array_t* get_impl_ptr(array_t* /*unused*/) noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (array) + constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (string) + string_t* get_impl_ptr(string_t* /*unused*/) noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (string) + constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (boolean) + boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (boolean) + constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (integer number) + number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (integer number) + constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (unsigned number) + number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (unsigned number) + constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (floating-point number) + number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /// get a pointer to the value (floating-point number) + constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /// get a pointer to the value (binary) + binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept + { + return is_binary() ? m_value.binary : nullptr; + } + + /// get a pointer to the value (binary) + constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept + { + return is_binary() ? m_value.binary : nullptr; + } + + /*! + @brief helper function to implement get_ref() + + This function helps to implement get_ref() without code duplication for + const and non-const overloads + + @tparam ThisType will be deduced as `basic_json` or `const basic_json` + + @throw type_error.303 if ReferenceType does not match underlying value + type of the current JSON + */ + template + static ReferenceType get_ref_impl(ThisType& obj) + { + // delegate the call to get_ptr<>() + auto ptr = obj.template get_ptr::type>(); + + if (JSON_HEDLEY_LIKELY(ptr != nullptr)) + { + return *ptr; + } + + JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()))); + } + + public: + /// @name value access + /// Direct access to the stored value of a JSON value. + /// @{ + + /*! + @brief get special-case overload + + This overloads avoids a lot of template boilerplate, it can be seen as the + identity method + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this + + @complexity Constant. + + @since version 2.1.0 + */ + template::type, basic_json_t>::value, + int> = 0> + basic_json get() const + { + return *this; + } + + /*! + @brief get special-case overload + + This overloads converts the current @ref basic_json in a different + @ref basic_json type + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this, converted into @tparam BasicJsonType + + @complexity Depending on the implementation of the called `from_json()` + method. + + @since version 3.2.0 + */ + template < typename BasicJsonType, detail::enable_if_t < + !std::is_same::value&& + detail::is_basic_json::value, int > = 0 > + BasicJsonType get() const + { + return *this; + } + + /*! + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value + which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + - @ref json_serializer does not have a `from_json()` method of + the form `ValueType from_json(const basic_json&)` + + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get__ValueType_const} + + @since version 2.1.0 + */ + template < typename ValueTypeCV, typename ValueType = detail::uncvref_t, + detail::enable_if_t < + !detail::is_basic_json::value && + detail::has_from_json::value && + !detail::has_non_default_from_json::value, + int > = 0 > + ValueType get() const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), std::declval()))) + { + // we cannot static_assert on ValueTypeCV being non-const, because + // there is support for get(), which is why we + // still need the uncvref + static_assert(!std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + static_assert(std::is_default_constructible::value, + "types must be DefaultConstructible when used with get()"); + + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + } + + /*! + @brief get a value (explicit); special case + + Explicit type conversion between the JSON value and a compatible value + which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + return JSONSerializer::from_json(*this); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json and + - @ref json_serializer has a `from_json()` method of the form + `ValueType from_json(const basic_json&)` + + @note If @ref json_serializer has both overloads of + `from_json()`, this one is chosen. + + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @since version 2.1.0 + */ + template < typename ValueTypeCV, typename ValueType = detail::uncvref_t, + detail::enable_if_t < !std::is_same::value && + detail::has_non_default_from_json::value, + int > = 0 > + ValueType get() const noexcept(noexcept( + JSONSerializer::from_json(std::declval()))) + { + static_assert(!std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + return JSONSerializer::from_json(*this); + } + + /*! + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value. + The value is filled into the input parameter by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + ValueType v; + JSONSerializer::from_json(*this, v); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + + @tparam ValueType the input parameter type. + + @return the input parameter, allowing chaining calls. + + @throw what @ref json_serializer `from_json()` method throws + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get_to} + + @since version 3.3.0 + */ + template < typename ValueType, + detail::enable_if_t < + !detail::is_basic_json::value&& + detail::has_from_json::value, + int > = 0 > + ValueType & get_to(ValueType& v) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; + } + + // specialization to allow to call get_to with a basic_json value + // see https://github.com/nlohmann/json/issues/2175 + template::value, + int> = 0> + ValueType & get_to(ValueType& v) const + { + v = *this; + return v; + } + + template < + typename T, std::size_t N, + typename Array = T (&)[N], + detail::enable_if_t < + detail::has_from_json::value, int > = 0 > + Array get_to(T (&v)[N]) const + noexcept(noexcept(JSONSerializer::from_json( + std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; + } + + + /*! + @brief get a pointer value (implicit) + + Implicit pointer access to the internally stored JSON value. No copies are + made. + + @warning Writing data to the pointee of the result yields an undefined + state. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. Enforced by a static + assertion. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get_ptr} + + @since version 1.0.0 + */ + template::value, int>::type = 0> + auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); + } + + /*! + @brief get a pointer value (implicit) + @copydoc get_ptr() + */ + template < typename PointerType, typename std::enable_if < + std::is_pointer::value&& + std::is_const::type>::value, int >::type = 0 > + constexpr auto get_ptr() const noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() const + return get_impl_ptr(static_cast(nullptr)); + } + + /*! + @brief get a pointer value (explicit) + + Explicit pointer access to the internally stored JSON value. No copies are + made. + + @warning The pointer becomes invalid if the underlying JSON object + changes. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get__PointerType} + + @sa @ref get_ptr() for explicit pointer-member access + + @since version 1.0.0 + */ + template::value, int>::type = 0> + auto get() noexcept -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + /*! + @brief get a pointer value (explicit) + @copydoc get() + */ + template::value, int>::type = 0> + constexpr auto get() const noexcept -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + /*! + @brief get a reference value (implicit) + + Implicit reference access to the internally stored JSON value. No copies + are made. + + @warning Writing data to the referee of the result yields an undefined + state. + + @tparam ReferenceType reference type; must be a reference to @ref array_t, + @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or + @ref number_float_t. Enforced by static assertion. + + @return reference to the internally stored JSON value if the requested + reference type @a ReferenceType fits to the JSON value; throws + type_error.303 otherwise + + @throw type_error.303 in case passed type @a ReferenceType is incompatible + with the stored JSON value; see example below + + @complexity Constant. + + @liveexample{The example shows several calls to `get_ref()`.,get_ref} + + @since version 1.1.0 + */ + template::value, int>::type = 0> + ReferenceType get_ref() + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a reference value (implicit) + @copydoc get_ref() + */ + template < typename ReferenceType, typename std::enable_if < + std::is_reference::value&& + std::is_const::type>::value, int >::type = 0 > + ReferenceType get_ref() const + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a value (implicit) + + Implicit type conversion between the JSON value and a compatible value. + The call is realized by calling @ref get() const. + + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays. The character type of @ref string_t + as well as an initializer list of this type is excluded to avoid + ambiguities as these types implicitly convert to `std::string`. + + @return copy of the JSON value, converted to type @a ValueType + + @throw type_error.302 in case passed type @a ValueType is incompatible + to the JSON value type (e.g., the JSON value is of type boolean, but a + string is requested); see example below + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,operator__ValueType} + + @since version 1.0.0 + */ + template < typename ValueType, typename std::enable_if < + !std::is_pointer::value&& + !std::is_same>::value&& + !std::is_same::value&& + !detail::is_basic_json::value + && !std::is_same>::value +#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914)) + && !std::is_same::value +#endif + && detail::is_detected::value + , int >::type = 0 > + JSON_EXPLICIT operator ValueType() const + { + // delegate the call to get<>() const + return get(); + } + + /*! + @return reference to the binary value + + @throw type_error.302 if the value is not binary + + @sa @ref is_binary() to check if the value is binary + + @since version 3.8.0 + */ + binary_t& get_binary() + { + if (!is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + } + + return *get_ptr(); + } + + /// @copydoc get_binary() + const binary_t& get_binary() const + { + if (!is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + } + + return *get_ptr(); + } + + /// @} + + + //////////////////// + // element access // + //////////////////// + + /// @name element access + /// Access to the JSON value. + /// @{ + + /*! + @brief access specified array element with bounds checking + + Returns a reference to the element at specified location @a idx, with + bounds checking. + + @param[in] idx index of the element to access + + @return reference to the element at index @a idx + + @throw type_error.304 if the JSON value is not an array; in this case, + calling `at` with an index makes no sense. See example below. + @throw out_of_range.401 if the index @a idx is out of range of the array; + that is, `idx >= size()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 1.0.0 + + @liveexample{The example below shows how array elements can be read and + written using `at()`. It also demonstrates the different exceptions that + can be thrown.,at__size_type} + */ + reference at(size_type idx) + { + // at only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + JSON_TRY + { + return m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + } + } + + /*! + @brief access specified array element with bounds checking + + Returns a const reference to the element at specified location @a idx, + with bounds checking. + + @param[in] idx index of the element to access + + @return const reference to the element at index @a idx + + @throw type_error.304 if the JSON value is not an array; in this case, + calling `at` with an index makes no sense. See example below. + @throw out_of_range.401 if the index @a idx is out of range of the array; + that is, `idx >= size()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 1.0.0 + + @liveexample{The example below shows how array elements can be read using + `at()`. It also demonstrates the different exceptions that can be thrown., + at__size_type_const} + */ + const_reference at(size_type idx) const + { + // at only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + JSON_TRY + { + return m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + } + } + + /*! + @brief access specified object element with bounds checking + + Returns a reference to the element at with specified key @a key, with + bounds checking. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw type_error.304 if the JSON value is not an object; in this case, + calling `at` with a key makes no sense. See example below. + @throw out_of_range.403 if the key @a key is is not stored in the object; + that is, `find(key) == end()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Logarithmic in the size of the container. + + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + + @liveexample{The example below shows how object elements can be read and + written using `at()`. It also demonstrates the different exceptions that + can be thrown.,at__object_t_key_type} + */ + reference at(const typename object_t::key_type& key) + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_TRY + { + return m_value.object->at(key); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + } + } + + /*! + @brief access specified object element with bounds checking + + Returns a const reference to the element at with specified key @a key, + with bounds checking. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @throw type_error.304 if the JSON value is not an object; in this case, + calling `at` with a key makes no sense. See example below. + @throw out_of_range.403 if the key @a key is is not stored in the object; + that is, `find(key) == end()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Logarithmic in the size of the container. + + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + + @liveexample{The example below shows how object elements can be read using + `at()`. It also demonstrates the different exceptions that can be thrown., + at__object_t_key_type_const} + */ + const_reference at(const typename object_t::key_type& key) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_TRY + { + return m_value.object->at(key); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + } + } + + /*! + @brief access specified array element + + Returns a reference to the element at specified location @a idx. + + @note If @a idx is beyond the range of the array (i.e., `idx >= size()`), + then the array is silently filled up with `null` values to make `idx` a + valid reference to the last stored element. + + @param[in] idx index of the element to access + + @return reference to the element at index @a idx + + @throw type_error.305 if the JSON value is not an array or null; in that + cases, using the [] operator with an index makes no sense. + + @complexity Constant if @a idx is in the range of the array. Otherwise + linear in `idx - size()`. + + @liveexample{The example below shows how array elements can be read and + written using `[]` operator. Note the addition of `null` + values.,operatorarray__size_type} + + @since version 1.0.0 + */ + reference operator[](size_type idx) + { + // implicitly convert null value to an empty array + if (is_null()) + { + m_type = value_t::array; + m_value.array = create(); + assert_invariant(); + } + + // operator[] only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // fill up array with null values if given idx is outside range + if (idx >= m_value.array->size()) + { + m_value.array->insert(m_value.array->end(), + idx - m_value.array->size() + 1, + basic_json()); + } + + return m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()))); + } + + /*! + @brief access specified array element + + Returns a const reference to the element at specified location @a idx. + + @param[in] idx index of the element to access + + @return const reference to the element at index @a idx + + @throw type_error.305 if the JSON value is not an array; in that case, + using the [] operator with an index makes no sense. + + @complexity Constant. + + @liveexample{The example below shows how array elements can be read using + the `[]` operator.,operatorarray__size_type_const} + + @since version 1.0.0 + */ + const_reference operator[](size_type idx) const + { + // const operator[] only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + return m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()))); + } + + /*! + @brief access specified object element + + Returns a reference to the element at with specified key @a key. + + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + */ + reference operator[](const typename object_t::key_type& key) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + // operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return m_value.object->operator[](key); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + } + + /*! + @brief read-only access specified object element + + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. + + @warning If the element with key @a key does not exist, the behavior is + undefined. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + + @throw type_error.305 if the JSON value is not an object; in that case, + using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + */ + const_reference operator[](const typename object_t::key_type& key) const + { + // const operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + } + + /*! + @brief access specified object element + + Returns a reference to the element at with specified key @a key. + + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.1.0 + */ + template + JSON_HEDLEY_NON_NULL(2) + reference operator[](T* key) + { + // implicitly convert null to object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return m_value.object->operator[](key); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + } + + /*! + @brief read-only access specified object element + + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. + + @warning If the element with key @a key does not exist, the behavior is + undefined. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + + @throw type_error.305 if the JSON value is not an object; in that case, + using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.1.0 + */ + template + JSON_HEDLEY_NON_NULL(2) + const_reference operator[](T* key) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + } + + /*! + @brief access specified object element with default value + + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. + + The function is basically equivalent to executing + @code {.cpp} + try { + return at(key); + } catch(out_of_range) { + return default_value; + } + @endcode + + @note Unlike @ref at(const typename object_t::key_type&), this function + does not throw if the given key @a key was not found. + + @note Unlike @ref operator[](const typename object_t::key_type& key), this + function does not implicitly add an element to the position defined by @a + key. This function is furthermore also applicable to const objects. + + @param[in] key key of the element to access + @param[in] default_value the value to return if @a key is not found + + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. + + @return copy of the element at key @a key or @a default_value if @a key + is not found + + @throw type_error.302 if @a default_value does not match the type of the + value at @a key + @throw type_error.306 if the JSON value is not an object; in that case, + using `value()` with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + + @since version 1.0.0 + */ + // using std::is_convertible in a std::enable_if will fail when using explicit conversions + template < class ValueType, typename std::enable_if < + detail::is_getable::value + && !std::is_same::value, int >::type = 0 > + ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) + { + return it->template get(); + } + + return default_value; + } + + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); + } + + /*! + @brief overload for a default value of type const char* + @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const + */ + string_t value(const typename object_t::key_type& key, const char* default_value) const + { + return value(key, string_t(default_value)); + } + + /*! + @brief access specified object element via JSON Pointer with default value + + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. + + The function is basically equivalent to executing + @code {.cpp} + try { + return at(ptr); + } catch(out_of_range) { + return default_value; + } + @endcode + + @note Unlike @ref at(const json_pointer&), this function does not throw + if the given key @a key was not found. + + @param[in] ptr a JSON pointer to the element to access + @param[in] default_value the value to return if @a ptr found no value + + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. + + @return copy of the element at key @a key or @a default_value if @a key + is not found + + @throw type_error.302 if @a default_value does not match the type of the + value at @a ptr + @throw type_error.306 if the JSON value is not an object; in that case, + using `value()` with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value_ptr} + + @sa @ref operator[](const json_pointer&) for unchecked access by reference + + @since version 2.0.2 + */ + template::value, int>::type = 0> + ValueType value(const json_pointer& ptr, const ValueType& default_value) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if pointer resolves a value, return it or use default value + JSON_TRY + { + return ptr.get_checked(this).template get(); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + return default_value; + } + } + + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); + } + + /*! + @brief overload for a default value of type const char* + @copydoc basic_json::value(const json_pointer&, ValueType) const + */ + JSON_HEDLEY_NON_NULL(3) + string_t value(const json_pointer& ptr, const char* default_value) const + { + return value(ptr, string_t(default_value)); + } + + /*! + @brief access the first element + + Returns a reference to the first element in the container. For a JSON + container `c`, the expression `c.front()` is equivalent to `*c.begin()`. + + @return In case of a structured type (array or object), a reference to the + first element is returned. In case of number, string, boolean, or binary + values, a reference to the value is returned. + + @complexity Constant. + + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. + + @throw invalid_iterator.214 when called on `null` value + + @liveexample{The following code shows an example for `front()`.,front} + + @sa @ref back() -- access the last element + + @since version 1.0.0 + */ + reference front() + { + return *begin(); + } + + /*! + @copydoc basic_json::front() + */ + const_reference front() const + { + return *cbegin(); + } + + /*! + @brief access the last element + + Returns a reference to the last element in the container. For a JSON + container `c`, the expression `c.back()` is equivalent to + @code {.cpp} + auto tmp = c.end(); + --tmp; + return *tmp; + @endcode + + @return In case of a structured type (array or object), a reference to the + last element is returned. In case of number, string, boolean, or binary + values, a reference to the value is returned. + + @complexity Constant. + + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. + + @throw invalid_iterator.214 when called on a `null` value. See example + below. + + @liveexample{The following code shows an example for `back()`.,back} + + @sa @ref front() -- access the first element + + @since version 1.0.0 + */ + reference back() + { + auto tmp = end(); + --tmp; + return *tmp; + } + + /*! + @copydoc basic_json::back() + */ + const_reference back() const + { + auto tmp = cend(); + --tmp; + return *tmp; + } + + /*! + @brief remove element given an iterator + + Removes the element specified by iterator @a pos. The iterator @a pos must + be valid and dereferenceable. Thus the `end()` iterator (which is valid, + but is not dereferenceable) cannot be used as a value for @a pos. + + If called on a primitive type other than `null`, the resulting JSON value + will be `null`. + + @param[in] pos iterator to the element to remove + @return Iterator following the last removed element. If the iterator @a + pos refers to the last element, the `end()` iterator is returned. + + @tparam IteratorType an @ref iterator or @ref const_iterator + + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. + + @throw type_error.307 if called on a `null` value; example: `"cannot use + erase() with null"` + @throw invalid_iterator.202 if called on an iterator which does not belong + to the current JSON value; example: `"iterator does not fit current + value"` + @throw invalid_iterator.205 if called on a primitive type with invalid + iterator (i.e., any iterator which is not `begin()`); example: `"iterator + out of range"` + + @complexity The complexity depends on the type: + - objects: amortized constant + - arrays: linear in distance between @a pos and the end of the container + - strings and binary: linear in the length of the member + - other types: constant + + @liveexample{The example shows the result of `erase()` for different JSON + types.,erase__IteratorType} + + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + @sa @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + template < class IteratorType, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type + = 0 > + IteratorType erase(IteratorType pos) + { + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + case value_t::binary: + { + if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin())) + { + JSON_THROW(invalid_iterator::create(205, "iterator out of range")); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.string); + std::allocator_traits::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; + } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.binary); + std::allocator_traits::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); + break; + } + + default: + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + } + + return result; + } + + /*! + @brief remove elements given an iterator range + + Removes the element specified by the range `[first; last)`. The iterator + @a first does not need to be dereferenceable if `first == last`: erasing + an empty range is a no-op. + + If called on a primitive type other than `null`, the resulting JSON value + will be `null`. + + @param[in] first iterator to the beginning of the range to remove + @param[in] last iterator past the end of the range to remove + @return Iterator following the last removed element. If the iterator @a + second refers to the last element, the `end()` iterator is returned. + + @tparam IteratorType an @ref iterator or @ref const_iterator + + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. + + @throw type_error.307 if called on a `null` value; example: `"cannot use + erase() with null"` + @throw invalid_iterator.203 if called on iterators which does not belong + to the current JSON value; example: `"iterators do not fit current value"` + @throw invalid_iterator.204 if called on a primitive type with invalid + iterators (i.e., if `first != begin()` and `last != end()`); example: + `"iterators out of range"` + + @complexity The complexity depends on the type: + - objects: `log(size()) + std::distance(first, last)` + - arrays: linear in the distance between @a first and @a last, plus linear + in the distance between @a last and end of the container + - strings and binary: linear in the length of the member + - other types: constant + + @liveexample{The example shows the result of `erase()` for different JSON + types.,erase__IteratorType_IteratorType} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + @sa @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + template < class IteratorType, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type + = 0 > + IteratorType erase(IteratorType first, IteratorType last) + { + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) + { + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value")); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + case value_t::binary: + { + if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.string); + std::allocator_traits::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; + } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.binary); + std::allocator_traits::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + default: + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + } + + return result; + } + + /*! + @brief remove element from a JSON object given a key + + Removes elements from a JSON object with the key value @a key. + + @param[in] key value of the elements to remove + + @return Number of elements removed. If @a ObjectType is the default + `std::map` type, the return value will always be `0` (@a key was not + found) or `1` (@a key was found). + + @post References and iterators to the erased elements are invalidated. + Other references and iterators are not affected. + + @throw type_error.307 when called on a type other than JSON object; + example: `"cannot use erase() with null"` + + @complexity `log(size()) + count(key)` + + @liveexample{The example shows the effect of `erase()`.,erase__key_type} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + size_type erase(const typename object_t::key_type& key) + { + // this erase only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return m_value.object->erase(key); + } + + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + } + + /*! + @brief remove element from a JSON array given an index + + Removes element from a JSON array at the index @a idx. + + @param[in] idx index of the element to remove + + @throw type_error.307 when called on a type other than JSON object; + example: `"cannot use erase() with null"` + @throw out_of_range.401 when `idx >= size()`; example: `"array index 17 + is out of range"` + + @complexity Linear in distance between @a idx and the end of the container. + + @liveexample{The example shows the effect of `erase()`.,erase__size_type} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + + @since version 1.0.0 + */ + void erase(const size_type idx) + { + // this erase only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + if (JSON_HEDLEY_UNLIKELY(idx >= size())) + { + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + } + + m_value.array->erase(m_value.array->begin() + static_cast(idx)); + } + else + { + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + } + } + + /// @} + + + //////////// + // lookup // + //////////// + + /// @name lookup + /// @{ + + /*! + @brief find an element in a JSON object + + Finds an element in a JSON object with key equivalent to @a key. If the + element is not found or the JSON value is not an object, end() is + returned. + + @note This method always returns @ref end() when executed on a JSON type + that is not an object. + + @param[in] key key value of the element to search for. + + @return Iterator to an element with key equivalent to @a key. If no such + element is found or the JSON value is not an object, past-the-end (see + @ref end()) iterator is returned. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The example shows how `find()` is used.,find__key_type} + + @sa @ref contains(KeyT&&) const -- checks whether a key exists + + @since version 1.0.0 + */ + template + iterator find(KeyT&& key) + { + auto result = end(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + /*! + @brief find an element in a JSON object + @copydoc find(KeyT&&) + */ + template + const_iterator find(KeyT&& key) const + { + auto result = cend(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + /*! + @brief returns the number of occurrences of a key in a JSON object + + Returns the number of elements with key @a key. If ObjectType is the + default `std::map` type, the return value will always be `0` (@a key was + not found) or `1` (@a key was found). + + @note This method always returns `0` when executed on a JSON type that is + not an object. + + @param[in] key key value of the element to count + + @return Number of elements with key @a key. If the JSON value is not an + object, the return value will be `0`. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The example shows how `count()` is used.,count} + + @since version 1.0.0 + */ + template + size_type count(KeyT&& key) const + { + // return 0 for all nonobject types + return is_object() ? m_value.object->count(std::forward(key)) : 0; + } + + /*! + @brief check the existence of an element in a JSON object + + Check whether an element exists in a JSON object with key equivalent to + @a key. If the element is not found or the JSON value is not an object, + false is returned. + + @note This method always returns false when executed on a JSON type + that is not an object. + + @param[in] key key value to check its existence. + + @return true if an element with specified @a key exists. If no such + element with such key is found or the JSON value is not an object, + false is returned. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The following code shows an example for `contains()`.,contains} + + @sa @ref find(KeyT&&) -- returns an iterator to an object element + @sa @ref contains(const json_pointer&) const -- checks the existence for a JSON pointer + + @since version 3.6.0 + */ + template < typename KeyT, typename std::enable_if < + !std::is_same::type, json_pointer>::value, int >::type = 0 > + bool contains(KeyT && key) const + { + return is_object() && m_value.object->find(std::forward(key)) != m_value.object->end(); + } + + /*! + @brief check the existence of an element in a JSON object given a JSON pointer + + Check whether the given JSON pointer @a ptr can be resolved in the current + JSON value. + + @note This method can be executed on any JSON value type. + + @param[in] ptr JSON pointer to check its existence. + + @return true if the JSON pointer can be resolved to a stored value, false + otherwise. + + @post If `j.contains(ptr)` returns true, it is safe to call `j[ptr]`. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The following code shows an example for `contains()`.,contains_json_pointer} + + @sa @ref contains(KeyT &&) const -- checks the existence of a key + + @since version 3.7.0 + */ + bool contains(const json_pointer& ptr) const + { + return ptr.contains(this); + } + + /// @} + + + /////////////// + // iterators // + /////////////// + + /// @name iterators + /// @{ + + /*! + @brief returns an iterator to the first element + + Returns an iterator to the first element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return iterator to the first element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + + @liveexample{The following code shows an example for `begin()`.,begin} + + @sa @ref cbegin() -- returns a const iterator to the beginning + @sa @ref end() -- returns an iterator to the end + @sa @ref cend() -- returns a const iterator to the end + + @since version 1.0.0 + */ + iterator begin() noexcept + { + iterator result(this); + result.set_begin(); + return result; + } + + /*! + @copydoc basic_json::cbegin() + */ + const_iterator begin() const noexcept + { + return cbegin(); + } + + /*! + @brief returns a const iterator to the first element + + Returns a const iterator to the first element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return const iterator to the first element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).begin()`. + + @liveexample{The following code shows an example for `cbegin()`.,cbegin} + + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref end() -- returns an iterator to the end + @sa @ref cend() -- returns a const iterator to the end + + @since version 1.0.0 + */ + const_iterator cbegin() const noexcept + { + const_iterator result(this); + result.set_begin(); + return result; + } + + /*! + @brief returns an iterator to one past the last element + + Returns an iterator to one past the last element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return iterator one past the last element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + + @liveexample{The following code shows an example for `end()`.,end} + + @sa @ref cend() -- returns a const iterator to the end + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref cbegin() -- returns a const iterator to the beginning + + @since version 1.0.0 + */ + iterator end() noexcept + { + iterator result(this); + result.set_end(); + return result; + } + + /*! + @copydoc basic_json::cend() + */ + const_iterator end() const noexcept + { + return cend(); + } + + /*! + @brief returns a const iterator to one past the last element + + Returns a const iterator to one past the last element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return const iterator one past the last element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).end()`. + + @liveexample{The following code shows an example for `cend()`.,cend} + + @sa @ref end() -- returns an iterator to the end + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref cbegin() -- returns a const iterator to the beginning + + @since version 1.0.0 + */ + const_iterator cend() const noexcept + { + const_iterator result(this); + result.set_end(); + return result; + } + + /*! + @brief returns an iterator to the reverse-beginning + + Returns an iterator to the reverse-beginning; that is, the last element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `reverse_iterator(end())`. + + @liveexample{The following code shows an example for `rbegin()`.,rbegin} + + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref crend() -- returns a const reverse iterator to the end + + @since version 1.0.0 + */ + reverse_iterator rbegin() noexcept + { + return reverse_iterator(end()); + } + + /*! + @copydoc basic_json::crbegin() + */ + const_reverse_iterator rbegin() const noexcept + { + return crbegin(); + } + + /*! + @brief returns an iterator to the reverse-end + + Returns an iterator to the reverse-end; that is, one before the first + element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `reverse_iterator(begin())`. + + @liveexample{The following code shows an example for `rend()`.,rend} + + @sa @ref crend() -- returns a const reverse iterator to the end + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + + @since version 1.0.0 + */ + reverse_iterator rend() noexcept + { + return reverse_iterator(begin()); + } + + /*! + @copydoc basic_json::crend() + */ + const_reverse_iterator rend() const noexcept + { + return crend(); + } + + /*! + @brief returns a const reverse iterator to the last element + + Returns a const iterator to the reverse-beginning; that is, the last + element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).rbegin()`. + + @liveexample{The following code shows an example for `crbegin()`.,crbegin} + + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref crend() -- returns a const reverse iterator to the end + + @since version 1.0.0 + */ + const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator(cend()); + } + + /*! + @brief returns a const reverse iterator to one before the first + + Returns a const reverse iterator to the reverse-end; that is, one before + the first element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).rend()`. + + @liveexample{The following code shows an example for `crend()`.,crend} + + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + + @since version 1.0.0 + */ + const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator(cbegin()); + } + + public: + /*! + @brief wrapper to access iterator member functions in range-based for + + This function allows to access @ref iterator::key() and @ref + iterator::value() during range-based for loops. In these loops, a + reference to the JSON values is returned, so there is no access to the + underlying iterator. + + For loop without iterator_wrapper: + + @code{cpp} + for (auto it = j_object.begin(); it != j_object.end(); ++it) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + Range-based for loop without iterator proxy: + + @code{cpp} + for (auto it : j_object) + { + // "it" is of type json::reference and has no key() member + std::cout << "value: " << it << '\n'; + } + @endcode + + Range-based for loop with iterator proxy: + + @code{cpp} + for (auto it : json::iterator_wrapper(j_object)) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + @note When iterating over an array, `key()` will return the index of the + element as string (see example). + + @param[in] ref reference to a JSON value + @return iteration proxy object wrapping @a ref with an interface to use in + range-based for loops + + @liveexample{The following code shows how the wrapper is used,iterator_wrapper} + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @note The name of this function is not yet final and may change in the + future. + + @deprecated This stream operator is deprecated and will be removed in + future 4.0.0 of the library. Please use @ref items() instead; + that is, replace `json::iterator_wrapper(j)` with `j.items()`. + */ + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) + static iteration_proxy iterator_wrapper(reference ref) noexcept + { + return ref.items(); + } + + /*! + @copydoc iterator_wrapper(reference) + */ + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) + static iteration_proxy iterator_wrapper(const_reference ref) noexcept + { + return ref.items(); + } + + /*! + @brief helper to access iterator member functions in range-based for + + This function allows to access @ref iterator::key() and @ref + iterator::value() during range-based for loops. In these loops, a + reference to the JSON values is returned, so there is no access to the + underlying iterator. + + For loop without `items()` function: + + @code{cpp} + for (auto it = j_object.begin(); it != j_object.end(); ++it) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + Range-based for loop without `items()` function: + + @code{cpp} + for (auto it : j_object) + { + // "it" is of type json::reference and has no key() member + std::cout << "value: " << it << '\n'; + } + @endcode + + Range-based for loop with `items()` function: + + @code{cpp} + for (auto& el : j_object.items()) + { + std::cout << "key: " << el.key() << ", value:" << el.value() << '\n'; + } + @endcode + + The `items()` function also allows to use + [structured bindings](https://en.cppreference.com/w/cpp/language/structured_binding) + (C++17): + + @code{cpp} + for (auto& [key, val] : j_object.items()) + { + std::cout << "key: " << key << ", value:" << val << '\n'; + } + @endcode + + @note When iterating over an array, `key()` will return the index of the + element as string (see example). For primitive types (e.g., numbers), + `key()` returns an empty string. + + @warning Using `items()` on temporary objects is dangerous. Make sure the + object's lifetime exeeds the iteration. See + for more + information. + + @return iteration proxy object wrapping @a ref with an interface to use in + range-based for loops + + @liveexample{The following code shows how the function is used.,items} + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 3.1.0, structured bindings support since 3.5.0. + */ + iteration_proxy items() noexcept + { + return iteration_proxy(*this); + } + + /*! + @copydoc items() + */ + iteration_proxy items() const noexcept + { + return iteration_proxy(*this); + } + + /// @} + + + ////////////// + // capacity // + ////////////// + + /// @name capacity + /// @{ + + /*! + @brief checks whether the container is empty. + + Checks if a JSON value has no elements (i.e. whether its @ref size is `0`). + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `true` + boolean | `false` + string | `false` + number | `false` + binary | `false` + object | result of function `object_t::empty()` + array | result of function `array_t::empty()` + + @liveexample{The following code uses `empty()` to check if a JSON + object contains any elements.,empty} + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `empty()` functions have constant + complexity. + + @iterators No changes. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @note This function does not return whether a string stored as JSON value + is empty - it returns whether the JSON container itself is empty which is + false in the case of a string. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of `begin() == end()`. + + @sa @ref size() -- returns the number of elements + + @since version 1.0.0 + */ + bool empty() const noexcept + { + switch (m_type) + { + case value_t::null: + { + // null values are empty + return true; + } + + case value_t::array: + { + // delegate call to array_t::empty() + return m_value.array->empty(); + } + + case value_t::object: + { + // delegate call to object_t::empty() + return m_value.object->empty(); + } + + default: + { + // all other types are nonempty + return false; + } + } + } + + /*! + @brief returns the number of elements + + Returns the number of elements in a JSON value. + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `0` + boolean | `1` + string | `1` + number | `1` + binary | `1` + object | result of function object_t::size() + array | result of function array_t::size() + + @liveexample{The following code calls `size()` on the different value + types.,size} + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their size() functions have constant + complexity. + + @iterators No changes. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @note This function does not return the length of a string stored as JSON + value - it returns the number of elements in the JSON value which is 1 in + the case of a string. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of `std::distance(begin(), end())`. + + @sa @ref empty() -- checks whether the container is empty + @sa @ref max_size() -- returns the maximal number of elements + + @since version 1.0.0 + */ + size_type size() const noexcept + { + switch (m_type) + { + case value_t::null: + { + // null values are empty + return 0; + } + + case value_t::array: + { + // delegate call to array_t::size() + return m_value.array->size(); + } + + case value_t::object: + { + // delegate call to object_t::size() + return m_value.object->size(); + } + + default: + { + // all other types have size 1 + return 1; + } + } + } + + /*! + @brief returns the maximum possible number of elements + + Returns the maximum number of elements a JSON value is able to hold due to + system or library implementation limitations, i.e. `std::distance(begin(), + end())` for the JSON value. + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `0` (same as `size()`) + boolean | `1` (same as `size()`) + string | `1` (same as `size()`) + number | `1` (same as `size()`) + binary | `1` (same as `size()`) + object | result of function `object_t::max_size()` + array | result of function `array_t::max_size()` + + @liveexample{The following code calls `max_size()` on the different value + types. Note the output is implementation specific.,max_size} + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `max_size()` functions have constant + complexity. + + @iterators No changes. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of returning `b.size()` where `b` is the largest + possible JSON value. + + @sa @ref size() -- returns the number of elements + + @since version 1.0.0 + */ + size_type max_size() const noexcept + { + switch (m_type) + { + case value_t::array: + { + // delegate call to array_t::max_size() + return m_value.array->max_size(); + } + + case value_t::object: + { + // delegate call to object_t::max_size() + return m_value.object->max_size(); + } + + default: + { + // all other types have max_size() == size() + return size(); + } + } + } + + /// @} + + + /////////////// + // modifiers // + /////////////// + + /// @name modifiers + /// @{ + + /*! + @brief clears the contents + + Clears the content of a JSON value and resets it to the default value as + if @ref basic_json(value_t) would have been called with the current value + type from @ref type(): + + Value type | initial value + ----------- | ------------- + null | `null` + boolean | `false` + string | `""` + number | `0` + binary | An empty byte vector + object | `{}` + array | `[]` + + @post Has the same effect as calling + @code {.cpp} + *this = basic_json(type()); + @endcode + + @liveexample{The example below shows the effect of `clear()` to different + JSON types.,clear} + + @complexity Linear in the size of the JSON value. + + @iterators All iterators, pointers and references related to this container + are invalidated. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @sa @ref basic_json(value_t) -- constructor that creates an object with the + same value than calling `clear()` + + @since version 1.0.0 + */ + void clear() noexcept + { + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = 0; + break; + } + + case value_t::number_unsigned: + { + m_value.number_unsigned = 0; + break; + } + + case value_t::number_float: + { + m_value.number_float = 0.0; + break; + } + + case value_t::boolean: + { + m_value.boolean = false; + break; + } + + case value_t::string: + { + m_value.string->clear(); + break; + } + + case value_t::binary: + { + m_value.binary->clear(); + break; + } + + case value_t::array: + { + m_value.array->clear(); + break; + } + + case value_t::object: + { + m_value.object->clear(); + break; + } + + default: + break; + } + } + + /*! + @brief add an object to an array + + Appends the given element @a val to the end of the JSON value. If the + function is called on a JSON null value, an empty array is created before + appending @a val. + + @param[in] val the value to add to the JSON array + + @throw type_error.308 when called on a type other than JSON array or + null; example: `"cannot use push_back() with number"` + + @complexity Amortized constant. + + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON array. Note how the `null` value was silently + converted to a JSON array.,push_back} + + @since version 1.0.0 + */ + void push_back(basic_json&& val) + { + // push_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (move semantics) + m_value.array->push_back(std::move(val)); + // if val is moved from, basic_json move constructor marks it null so we do not call the destructor + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(basic_json&& val) + { + push_back(std::move(val)); + return *this; + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + void push_back(const basic_json& val) + { + // push_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array + m_value.array->push_back(val); + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(const basic_json& val) + { + push_back(val); + return *this; + } + + /*! + @brief add an object to an object + + Inserts the given element @a val to the JSON object. If the function is + called on a JSON null value, an empty object is created before inserting + @a val. + + @param[in] val the value to add to the JSON object + + @throw type_error.308 when called on a type other than JSON object or + null; example: `"cannot use push_back() with number"` + + @complexity Logarithmic in the size of the container, O(log(`size()`)). + + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON object. Note how the `null` value was silently + converted to a JSON object.,push_back__object_t__value} + + @since version 1.0.0 + */ + void push_back(const typename object_t::value_type& val) + { + // push_back only works for null objects or objects + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to array + m_value.object->insert(val); + } + + /*! + @brief add an object to an object + @copydoc push_back(const typename object_t::value_type&) + */ + reference operator+=(const typename object_t::value_type& val) + { + push_back(val); + return *this; + } + + /*! + @brief add an object to an object + + This function allows to use `push_back` with an initializer list. In case + + 1. the current value is an object, + 2. the initializer list @a init contains only two elements, and + 3. the first element of @a init is a string, + + @a init is converted into an object element and added using + @ref push_back(const typename object_t::value_type&). Otherwise, @a init + is converted to a JSON value and added using @ref push_back(basic_json&&). + + @param[in] init an initializer list + + @complexity Linear in the size of the initializer list @a init. + + @note This function is required to resolve an ambiguous overload error, + because pairs like `{"key", "value"}` can be both interpreted as + `object_t::value_type` or `std::initializer_list`, see + https://github.com/nlohmann/json/issues/235 for more information. + + @liveexample{The example shows how initializer lists are treated as + objects when possible.,push_back__initializer_list} + */ + void push_back(initializer_list_t init) + { + if (is_object() && init.size() == 2 && (*init.begin())->is_string()) + { + basic_json&& key = init.begin()->moved_or_copied(); + push_back(typename object_t::value_type( + std::move(key.get_ref()), (init.begin() + 1)->moved_or_copied())); + } + else + { + push_back(basic_json(init)); + } + } + + /*! + @brief add an object to an object + @copydoc push_back(initializer_list_t) + */ + reference operator+=(initializer_list_t init) + { + push_back(init); + return *this; + } + + /*! + @brief add an object to an array + + Creates a JSON value from the passed parameters @a args to the end of the + JSON value. If the function is called on a JSON null value, an empty array + is created before appending the value created from @a args. + + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object + + @return reference to the inserted element + + @throw type_error.311 when called on a type other than JSON array or + null; example: `"cannot use emplace_back() with number"` + + @complexity Amortized constant. + + @liveexample{The example shows how `push_back()` can be used to add + elements to a JSON array. Note how the `null` value was silently converted + to a JSON array.,emplace_back} + + @since version 2.0.8, returns reference since 3.7.0 + */ + template + reference emplace_back(Args&& ... args) + { + // emplace_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()))); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (perfect forwarding) +#ifdef JSON_HAS_CPP_17 + return m_value.array->emplace_back(std::forward(args)...); +#else + m_value.array->emplace_back(std::forward(args)...); + return m_value.array->back(); +#endif + } + + /*! + @brief add an object to an object if key does not exist + + Inserts a new element into a JSON object constructed in-place with the + given @a args if there is no element with the key in the container. If the + function is called on a JSON null value, an empty object is created before + appending the value created from @a args. + + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object + + @return a pair consisting of an iterator to the inserted element, or the + already-existing element if no insertion happened, and a bool + denoting whether the insertion took place. + + @throw type_error.311 when called on a type other than JSON object or + null; example: `"cannot use emplace() with number"` + + @complexity Logarithmic in the size of the container, O(log(`size()`)). + + @liveexample{The example shows how `emplace()` can be used to add elements + to a JSON object. Note how the `null` value was silently converted to a + JSON object. Further note how no value is added if there was already one + value stored with the same key.,emplace} + + @since version 2.0.8 + */ + template + std::pair emplace(Args&& ... args) + { + // emplace only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) + { + JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()))); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to array (perfect forwarding) + auto res = m_value.object->emplace(std::forward(args)...); + // create result iterator and set iterator to the result of emplace + auto it = begin(); + it.m_it.object_iterator = res.first; + + // return pair of iterator and boolean + return {it, res.second}; + } + + /// Helper for insertion of an iterator + /// @note: This uses std::distance to support GCC 4.8, + /// see https://github.com/nlohmann/json/pull/1257 + template + iterator insert_iterator(const_iterator pos, Args&& ... args) + { + iterator result(this); + JSON_ASSERT(m_value.array != nullptr); + + auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator); + m_value.array->insert(pos.m_it.array_iterator, std::forward(args)...); + result.m_it.array_iterator = m_value.array->begin() + insert_pos; + + // This could have been written as: + // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); + // but the return value of insert is missing in GCC 4.8, so it is written this way instead. + + return result; + } + + /*! + @brief inserts element + + Inserts element @a val before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] val element to insert + @return iterator pointing to the inserted @a val. + + @throw type_error.309 if called on JSON values other than arrays; + example: `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + + @complexity Constant plus linear in the distance between @a pos and end of + the container. + + @liveexample{The example shows how `insert()` is used.,insert} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const basic_json& val) + { + // insert only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + // insert to array and return iterator + return insert_iterator(pos, val); + } + + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + } + + /*! + @brief inserts element + @copydoc insert(const_iterator, const basic_json&) + */ + iterator insert(const_iterator pos, basic_json&& val) + { + return insert(pos, val); + } + + /*! + @brief inserts elements + + Inserts @a cnt copies of @a val before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] cnt number of copies of @a val to insert + @param[in] val element to insert + @return iterator pointing to the first element inserted, or @a pos if + `cnt==0` + + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + + @complexity Linear in @a cnt plus linear in the distance between @a pos + and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__count} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, size_type cnt, const basic_json& val) + { + // insert only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + // insert to array and return iterator + return insert_iterator(pos, cnt, val); + } + + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + } + + /*! + @brief inserts elements + + Inserts elements from range `[first, last)` before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` + @throw invalid_iterator.211 if @a first or @a last are iterators into + container for which insert is called; example: `"passed iterators may not + belong to container"` + + @return iterator pointing to the first element inserted, or @a pos if + `first==last` + + @complexity Linear in `std::distance(first, last)` plus linear in the + distance between @a pos and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__range} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const_iterator first, const_iterator last) + { + // insert only works for arrays + if (JSON_HEDLEY_UNLIKELY(!is_array())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + } + + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); } - private: - /* - @brief write a number to output input - - @param[in] n number of type @a T - @tparam T the type of the number - - @note This function needs to respect the system's endianess, because - bytes in CBOR and MessagePack are stored in network order (big - endian) and therefore need reordering on little endian systems. - */ - template - void write_number(T n) + if (JSON_HEDLEY_UNLIKELY(first.m_object == this)) { - // step 1: write number to array of length T - std::array vec; - std::memcpy(vec.data(), &n, sizeof(T)); - - // step 2: write array to output (with possible reordering) - for (size_t i = 0; i < sizeof(T); ++i) - { - // reverse byte order prior to conversion if necessary - if (is_little_endian) - { - oa->write_character(vec[sizeof(T) - i - 1]); - } - else - { - oa->write_character(vec[i]); - } - } + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container")); } - private: - /// whether we can assume little endianess - const bool is_little_endian = true; - - /// the output - output_adapter_t oa = nullptr; - }; + // insert to array and return iterator + return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); + } - public: /*! - @brief create a CBOR serialization of a given JSON value - - Serializes a given JSON value @a j to a byte vector using the CBOR (Concise - Binary Object Representation) serialization format. CBOR is a binary - serialization format which aims to be more compact than JSON itself, yet - more efficient to parse. - - The library uses the following mapping from JSON values types to - CBOR types according to the CBOR specification (RFC 7049): - - JSON value type | value/range | CBOR type | first byte - --------------- | ------------------------------------------ | ---------------------------------- | --------------- - null | `null` | Null | 0xf6 - boolean | `true` | True | 0xf5 - boolean | `false` | False | 0xf4 - number_integer | -9223372036854775808..-2147483649 | Negative integer (8 bytes follow) | 0x3b - number_integer | -2147483648..-32769 | Negative integer (4 bytes follow) | 0x3a - number_integer | -32768..-129 | Negative integer (2 bytes follow) | 0x39 - number_integer | -128..-25 | Negative integer (1 byte follow) | 0x38 - number_integer | -24..-1 | Negative integer | 0x20..0x37 - number_integer | 0..23 | Integer | 0x00..0x17 - number_integer | 24..255 | Unsigned integer (1 byte follow) | 0x18 - number_integer | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 - number_integer | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1a - number_integer | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1b - number_unsigned | 0..23 | Integer | 0x00..0x17 - number_unsigned | 24..255 | Unsigned integer (1 byte follow) | 0x18 - number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 - number_unsigned | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1a - number_unsigned | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1b - number_float | *any value* | Double-Precision Float | 0xfb - string | *length*: 0..23 | UTF-8 string | 0x60..0x77 - string | *length*: 23..255 | UTF-8 string (1 byte follow) | 0x78 - string | *length*: 256..65535 | UTF-8 string (2 bytes follow) | 0x79 - string | *length*: 65536..4294967295 | UTF-8 string (4 bytes follow) | 0x7a - string | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow) | 0x7b - array | *size*: 0..23 | array | 0x80..0x97 - array | *size*: 23..255 | array (1 byte follow) | 0x98 - array | *size*: 256..65535 | array (2 bytes follow) | 0x99 - array | *size*: 65536..4294967295 | array (4 bytes follow) | 0x9a - array | *size*: 4294967296..18446744073709551615 | array (8 bytes follow) | 0x9b - object | *size*: 0..23 | map | 0xa0..0xb7 - object | *size*: 23..255 | map (1 byte follow) | 0xb8 - object | *size*: 256..65535 | map (2 bytes follow) | 0xb9 - object | *size*: 65536..4294967295 | map (4 bytes follow) | 0xba - object | *size*: 4294967296..18446744073709551615 | map (8 bytes follow) | 0xbb + @brief inserts elements - @note The mapping is **complete** in the sense that any JSON value type - can be converted to a CBOR value. + Inserts elements from initializer list @a ilist before iterator @a pos. - @note The following CBOR types are not used in the conversion: - - byte strings (0x40..0x5f) - - UTF-8 strings terminated by "break" (0x7f) - - arrays terminated by "break" (0x9f) - - maps terminated by "break" (0xbf) - - date/time (0xc0..0xc1) - - bignum (0xc2..0xc3) - - decimal fraction (0xc4) - - bigfloat (0xc5) - - tagged items (0xc6..0xd4, 0xd8..0xdb) - - expected conversions (0xd5..0xd7) - - simple values (0xe0..0xf3, 0xf8) - - undefined (0xf7) - - half and single-precision floats (0xf9-0xfa) - - break (0xff) + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] ilist initializer list to insert the values from - @param[in] j JSON value to serialize - @return MessagePack serialization as byte vector + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` - @complexity Linear in the size of the JSON value @a j. + @return iterator pointing to the first element inserted, or @a pos if + `ilist` is empty - @liveexample{The example shows the serialization of a JSON value to a byte - vector in CBOR format.,to_cbor} + @complexity Linear in `ilist.size()` plus linear in the distance between + @a pos and end of the container. - @sa http://cbor.io - @sa @ref from_cbor(const std::vector&, const size_t) for the - analogous deserialization - @sa @ref to_msgpack(const basic_json& for the related MessagePack format + @liveexample{The example shows how `insert()` is used.,insert__ilist} - @since version 2.0.9 + @since version 1.0.0 */ - static std::vector to_cbor(const basic_json& j) + iterator insert(const_iterator pos, initializer_list_t ilist) { - std::vector result; - binary_writer bw(output_adapter::create(result)); - bw.write_cbor(j); - return result; + // insert only works for arrays + if (JSON_HEDLEY_UNLIKELY(!is_array())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + } + + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + // insert to array and return iterator + return insert_iterator(pos, ilist.begin(), ilist.end()); } /*! - @brief create a MessagePack serialization of a given JSON value + @brief inserts elements - Serializes a given JSON value @a j to a byte vector using the MessagePack - serialization format. MessagePack is a binary serialization format which - aims to be more compact than JSON itself, yet more efficient to parse. + Inserts elements from range `[first, last)`. - The library uses the following mapping from JSON values types to - MessagePack types according to the MessagePack specification: + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert - JSON value type | value/range | MessagePack type | first byte - --------------- | --------------------------------- | ---------------- | ---------- - null | `null` | nil | 0xc0 - boolean | `true` | true | 0xc3 - boolean | `false` | false | 0xc2 - number_integer | -9223372036854775808..-2147483649 | int64 | 0xd3 - number_integer | -2147483648..-32769 | int32 | 0xd2 - number_integer | -32768..-129 | int16 | 0xd1 - number_integer | -128..-33 | int8 | 0xd0 - number_integer | -32..-1 | negative fixint | 0xe0..0xff - number_integer | 0..127 | positive fixint | 0x00..0x7f - number_integer | 128..255 | uint 8 | 0xcc - number_integer | 256..65535 | uint 16 | 0xcd - number_integer | 65536..4294967295 | uint 32 | 0xce - number_integer | 4294967296..18446744073709551615 | uint 64 | 0xcf - number_unsigned | 0..127 | positive fixint | 0x00..0x7f - number_unsigned | 128..255 | uint 8 | 0xcc - number_unsigned | 256..65535 | uint 16 | 0xcd - number_unsigned | 65536..4294967295 | uint 32 | 0xce - number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xcf - number_float | *any value* | float 64 | 0xcb - string | *length*: 0..31 | fixstr | 0xa0..0xbf - string | *length*: 32..255 | str 8 | 0xd9 - string | *length*: 256..65535 | str 16 | 0xda - string | *length*: 65536..4294967295 | str 32 | 0xdb - array | *size*: 0..15 | fixarray | 0x90..0x9f - array | *size*: 16..65535 | array 16 | 0xdc - array | *size*: 65536..4294967295 | array 32 | 0xdd - object | *size*: 0..15 | fix map | 0x80..0x8f - object | *size*: 16..65535 | map 16 | 0xde - object | *size*: 65536..4294967295 | map 32 | 0xdf + @throw type_error.309 if called on JSON values other than objects; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if iterator @a first or @a last does does not + point to an object; example: `"iterators first and last must point to + objects"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` - @note The mapping is **complete** in the sense that any JSON value type - can be converted to a MessagePack value. + @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number + of elements to insert. - @note The following values can **not** be converted to a MessagePack value: - - strings with more than 4294967295 bytes - - arrays with more than 4294967295 elements - - objects with more than 4294967295 elements + @liveexample{The example shows how `insert()` is used.,insert__range_object} - @note The following MessagePack types are not used in the conversion: - - bin 8 - bin 32 (0xc4..0xc6) - - ext 8 - ext 32 (0xc7..0xc9) - - float 32 (0xca) - - fixext 1 - fixext 16 (0xd4..0xd8) + @since version 3.0.0 + */ + void insert(const_iterator first, const_iterator last) + { + // insert only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + } - @note Any MessagePack output created @ref to_msgpack can be successfully - parsed by @ref from_msgpack. + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + } - @param[in] j JSON value to serialize - @return MessagePack serialization as byte vector + // passed iterators must belong to objects + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) + { + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + } - @complexity Linear in the size of the JSON value @a j. + m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); + } - @liveexample{The example shows the serialization of a JSON value to a byte - vector in MessagePack format.,to_msgpack} + /*! + @brief updates a JSON object from another object, overwriting existing keys - @sa http://msgpack.org - @sa @ref from_msgpack(const std::vector&, const size_t) for the - analogous deserialization - @sa @ref to_cbor(const basic_json& for the related CBOR format + Inserts all values from JSON object @a j and overwrites existing keys. - @since version 2.0.9 + @param[in] j JSON object to read values from + + @throw type_error.312 if called on JSON values other than objects; example: + `"cannot use update() with string"` + + @complexity O(N*log(size() + N)), where N is the number of elements to + insert. + + @liveexample{The example shows how `update()` is used.,update} + + @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update + + @since version 3.0.0 */ - static std::vector to_msgpack(const basic_json& j) + void update(const_reference j) { - std::vector result; - binary_writer bw(output_adapter::create(result)); - bw.write_msgpack(j); - return result; + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); + } + if (JSON_HEDLEY_UNLIKELY(!j.is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name()))); + } + + for (auto it = j.cbegin(); it != j.cend(); ++it) + { + m_value.object->operator[](it.key()) = it.value(); + } } /*! - @brief create a JSON value from a byte vector in CBOR format - - Deserializes a given byte vector @a v to a JSON value using the CBOR - (Concise Binary Object Representation) serialization format. + @brief updates a JSON object from another object, overwriting existing keys - The library maps CBOR types to JSON value types as follows: + Inserts all values from from range `[first, last)` and overwrites existing + keys. - CBOR type | JSON value type | first byte - ---------------------- | --------------- | ---------- - Integer | number_unsigned | 0x00..0x17 - Unsigned integer | number_unsigned | 0x18 - Unsigned integer | number_unsigned | 0x19 - Unsigned integer | number_unsigned | 0x1a - Unsigned integer | number_unsigned | 0x1b - Negative integer | number_integer | 0x20..0x37 - Negative integer | number_integer | 0x38 - Negative integer | number_integer | 0x39 - Negative integer | number_integer | 0x3a - Negative integer | number_integer | 0x3b - Negative integer | number_integer | 0x40..0x57 - UTF-8 string | string | 0x60..0x77 - UTF-8 string | string | 0x78 - UTF-8 string | string | 0x79 - UTF-8 string | string | 0x7a - UTF-8 string | string | 0x7b - UTF-8 string | string | 0x7f - array | array | 0x80..0x97 - array | array | 0x98 - array | array | 0x99 - array | array | 0x9a - array | array | 0x9b - array | array | 0x9f - map | object | 0xa0..0xb7 - map | object | 0xb8 - map | object | 0xb9 - map | object | 0xba - map | object | 0xbb - map | object | 0xbf - False | `false` | 0xf4 - True | `true` | 0xf5 - Nill | `null` | 0xf6 - Half-Precision Float | number_float | 0xf9 - Single-Precision Float | number_float | 0xfa - Double-Precision Float | number_float | 0xfb + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert - @warning The mapping is **incomplete** in the sense that not all CBOR - types can be converted to a JSON value. The following CBOR types - are not supported and will yield parse errors (parse_error.112): - - byte strings (0x40..0x5f) - - date/time (0xc0..0xc1) - - bignum (0xc2..0xc3) - - decimal fraction (0xc4) - - bigfloat (0xc5) - - tagged items (0xc6..0xd4, 0xd8..0xdb) - - expected conversions (0xd5..0xd7) - - simple values (0xe0..0xf3, 0xf8) - - undefined (0xf7) + @throw type_error.312 if called on JSON values other than objects; example: + `"cannot use update() with string"` + @throw invalid_iterator.202 if iterator @a first or @a last does does not + point to an object; example: `"iterators first and last must point to + objects"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` - @warning CBOR allows map keys of any type, whereas JSON only allows - strings as keys in object values. Therefore, CBOR maps with keys - other than UTF-8 strings are rejected (parse_error.113). + @complexity O(N*log(size() + N)), where N is the number of elements to + insert. - @note Any CBOR output created @ref to_cbor can be successfully parsed by - @ref from_cbor. + @liveexample{The example shows how `update()` is used__range.,update} - @param[in] v a byte vector in CBOR format - @param[in] start_index the index to start reading from @a v (0 by default) - @return deserialized JSON value + @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update - @throw parse_error.110 if the given vector ends prematurely - @throw parse_error.112 if unsupported features from CBOR were - used in the given vector @a v or if the input is not valid CBOR - @throw parse_error.113 if a string was expected as map key, but not found + @since version 3.0.0 + */ + void update(const_iterator first, const_iterator last) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } - @complexity Linear in the size of the byte vector @a v. + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); + } - @liveexample{The example shows the deserialization of a byte vector in CBOR - format to a JSON value.,from_cbor} + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + } - @sa http://cbor.io - @sa @ref to_cbor(const basic_json&) for the analogous serialization - @sa @ref from_msgpack(const std::vector&, const size_t) for the - related MessagePack format + // passed iterators must belong to objects + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object() + || !last.m_object->is_object())) + { + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + } - @since version 2.0.9, parameter @a start_index since 2.1.1 - */ - static basic_json from_cbor(const std::vector& v, - const size_t start_index = 0) - { - binary_reader br(input_adapter::create(v.begin() + static_cast(start_index), v.end())); - return br.parse_cbor(); + for (auto it = first; it != last; ++it) + { + m_value.object->operator[](it.key()) = it.value(); + } } - /*! - @brief create a JSON value from a byte vector in MessagePack format + @brief exchanges the values - Deserializes a given byte vector @a v to a JSON value using the MessagePack - serialization format. + Exchanges the contents of the JSON value with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. - The library maps MessagePack types to JSON value types as follows: + @param[in,out] other JSON value to exchange the contents with - MessagePack type | JSON value type | first byte - ---------------- | --------------- | ---------- - positive fixint | number_unsigned | 0x00..0x7f - fixmap | object | 0x80..0x8f - fixarray | array | 0x90..0x9f - fixstr | string | 0xa0..0xbf - nil | `null` | 0xc0 - false | `false` | 0xc2 - true | `true` | 0xc3 - float 32 | number_float | 0xca - float 64 | number_float | 0xcb - uint 8 | number_unsigned | 0xcc - uint 16 | number_unsigned | 0xcd - uint 32 | number_unsigned | 0xce - uint 64 | number_unsigned | 0xcf - int 8 | number_integer | 0xd0 - int 16 | number_integer | 0xd1 - int 32 | number_integer | 0xd2 - int 64 | number_integer | 0xd3 - str 8 | string | 0xd9 - str 16 | string | 0xda - str 32 | string | 0xdb - array 16 | array | 0xdc - array 32 | array | 0xdd - map 16 | object | 0xde - map 32 | object | 0xdf - negative fixint | number_integer | 0xe0-0xff - - @warning The mapping is **incomplete** in the sense that not all - MessagePack types can be converted to a JSON value. The following - MessagePack types are not supported and will yield parse errors: - - bin 8 - bin 32 (0xc4..0xc6) - - ext 8 - ext 32 (0xc7..0xc9) - - fixext 1 - fixext 16 (0xd4..0xd8) + @complexity Constant. - @note Any MessagePack output created @ref to_msgpack can be successfully - parsed by @ref from_msgpack. + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} + + @since version 1.0.0 + */ + void swap(reference other) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) + { + std::swap(m_type, other.m_type); + std::swap(m_value, other.m_value); + assert_invariant(); + } - @param[in] v a byte vector in MessagePack format - @param[in] start_index the index to start reading from @a v (0 by default) - @return deserialized JSON value + /*! + @brief exchanges the values - @throw parse_error.110 if the given vector ends prematurely - @throw parse_error.112 if unsupported features from MessagePack were - used in the given vector @a v or if the input is not valid MessagePack - @throw parse_error.113 if a string was expected as map key, but not found + Exchanges the contents of the JSON value from @a left with those of @a right. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. implemented as a friend function callable via ADL. - @complexity Linear in the size of the byte vector @a v. + @param[in,out] left JSON value to exchange the contents with + @param[in,out] right JSON value to exchange the contents with - @liveexample{The example shows the deserialization of a byte vector in - MessagePack format to a JSON value.,from_msgpack} + @complexity Constant. - @sa http://msgpack.org - @sa @ref to_msgpack(const basic_json&) for the analogous serialization - @sa @ref from_cbor(const std::vector&, const size_t) for the - related CBOR format + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} - @since version 2.0.9, parameter @a start_index since 2.1.1 + @since version 1.0.0 */ - static basic_json from_msgpack(const std::vector& v, - const size_t start_index = 0) + friend void swap(reference left, reference right) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) { - binary_reader br(input_adapter::create(v.begin() + static_cast(start_index), v.end())); - return br.parse_msgpack(); + left.swap(right); } - /// @} + /*! + @brief exchanges the values - ////////////////////// - // lexer and parser // - ////////////////////// + Exchanges the contents of a JSON array with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. - private: - /*! - @brief lexical analysis - - This class organizes the lexical analysis during JSON deserialization. - */ - class lexer - { - public: - /// token types for the parser - enum class token_type - { - uninitialized, ///< indicating the scanner is uninitialized - literal_true, ///< the `true` literal - literal_false, ///< the `false` literal - literal_null, ///< the `null` literal - value_string, ///< a string -- use get_string() for actual value - value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value - value_integer, ///< a signed integer -- use get_number_integer() for actual value - value_float, ///< an floating point number -- use get_number_float() for actual value - begin_array, ///< the character for array begin `[` - begin_object, ///< the character for object begin `{` - end_array, ///< the character for array end `]` - end_object, ///< the character for object end `}` - name_separator, ///< the name separator `:` - value_separator, ///< the value separator `,` - parse_error, ///< indicating a parse error - end_of_input ///< indicating the end of the input buffer - }; + @param[in,out] other array to exchange the contents with - /// return name of values of type token_type (only used for errors) - static const char* token_type_name(const token_type t) noexcept - { - switch (t) - { - case token_type::uninitialized: - return ""; - case token_type::literal_true: - return "true literal"; - case token_type::literal_false: - return "false literal"; - case token_type::literal_null: - return "null literal"; - case token_type::value_string: - return "string literal"; - case lexer::token_type::value_unsigned: - case lexer::token_type::value_integer: - case lexer::token_type::value_float: - return "number literal"; - case token_type::begin_array: - return "'['"; - case token_type::begin_object: - return "'{'"; - case token_type::end_array: - return "']'"; - case token_type::end_object: - return "'}'"; - case token_type::name_separator: - return "':'"; - case token_type::value_separator: - return "','"; - case token_type::parse_error: - return ""; - case token_type::end_of_input: - return "end of input"; - default: - { - // catch non-enum values - return "unknown token"; // LCOV_EXCL_LINE - } - } - } + @throw type_error.310 when JSON value is not an array; example: `"cannot + use swap() with string"` - explicit lexer(input_adapter_t adapter) - : ia(adapter), decimal_point_char(get_decimal_point()) - {} + @complexity Constant. - private: - ///////////////////// - // locales - ///////////////////// + @liveexample{The example below shows how arrays can be swapped with + `swap()`.,swap__array_t} - /// return the locale-dependent decimal point - static char get_decimal_point() noexcept + @since version 1.0.0 + */ + void swap(array_t& other) + { + // swap only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) { - const auto loc = localeconv(); - assert(loc != nullptr); - return (loc->decimal_point == nullptr) ? '.' : loc->decimal_point[0]; + std::swap(*(m_value.array), other); } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } - ///////////////////// - // scan functions - ///////////////////// - - /*! - @brief get codepoint from 4 hex characters following `\u` + /*! + @brief exchanges the values - @return codepoint or -1 in case of an error (e.g. EOF or non-hex - character) - */ - int get_codepoint() - { - // this function only makes sense after reading `\u` - assert(current == 'u'); - int codepoint = 0; + Exchanges the contents of a JSON object with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. - // byte 1: \uXxxx - switch (get()) - { - case '0': - break; - case '1': - codepoint += 0x1000; - break; - case '2': - codepoint += 0x2000; - break; - case '3': - codepoint += 0x3000; - break; - case '4': - codepoint += 0x4000; - break; - case '5': - codepoint += 0x5000; - break; - case '6': - codepoint += 0x6000; - break; - case '7': - codepoint += 0x7000; - break; - case '8': - codepoint += 0x8000; - break; - case '9': - codepoint += 0x9000; - break; - case 'A': - case 'a': - codepoint += 0xa000; - break; - case 'B': - case 'b': - codepoint += 0xb000; - break; - case 'C': - case 'c': - codepoint += 0xc000; - break; - case 'D': - case 'd': - codepoint += 0xd000; - break; - case 'E': - case 'e': - codepoint += 0xe000; - break; - case 'F': - case 'f': - codepoint += 0xf000; - break; - default: - return -1; - } + @param[in,out] other object to exchange the contents with - // byte 2: \uxXxx - switch (get()) - { - case '0': - break; - case '1': - codepoint += 0x0100; - break; - case '2': - codepoint += 0x0200; - break; - case '3': - codepoint += 0x0300; - break; - case '4': - codepoint += 0x0400; - break; - case '5': - codepoint += 0x0500; - break; - case '6': - codepoint += 0x0600; - break; - case '7': - codepoint += 0x0700; - break; - case '8': - codepoint += 0x0800; - break; - case '9': - codepoint += 0x0900; - break; - case 'A': - case 'a': - codepoint += 0x0a00; - break; - case 'B': - case 'b': - codepoint += 0x0b00; - break; - case 'C': - case 'c': - codepoint += 0x0c00; - break; - case 'D': - case 'd': - codepoint += 0x0d00; - break; - case 'E': - case 'e': - codepoint += 0x0e00; - break; - case 'F': - case 'f': - codepoint += 0x0f00; - break; - default: - return -1; - } + @throw type_error.310 when JSON value is not an object; example: + `"cannot use swap() with string"` - // byte 3: \uxxXx - switch (get()) - { - case '0': - break; - case '1': - codepoint += 0x0010; - break; - case '2': - codepoint += 0x0020; - break; - case '3': - codepoint += 0x0030; - break; - case '4': - codepoint += 0x0040; - break; - case '5': - codepoint += 0x0050; - break; - case '6': - codepoint += 0x0060; - break; - case '7': - codepoint += 0x0070; - break; - case '8': - codepoint += 0x0080; - break; - case '9': - codepoint += 0x0090; - break; - case 'A': - case 'a': - codepoint += 0x00a0; - break; - case 'B': - case 'b': - codepoint += 0x00b0; - break; - case 'C': - case 'c': - codepoint += 0x00c0; - break; - case 'D': - case 'd': - codepoint += 0x00d0; - break; - case 'E': - case 'e': - codepoint += 0x00e0; - break; - case 'F': - case 'f': - codepoint += 0x00f0; - break; - default: - return -1; - } + @complexity Constant. - // byte 4: \uxxxX - switch (get()) - { - case '0': - break; - case '1': - codepoint += 0x0001; - break; - case '2': - codepoint += 0x0002; - break; - case '3': - codepoint += 0x0003; - break; - case '4': - codepoint += 0x0004; - break; - case '5': - codepoint += 0x0005; - break; - case '6': - codepoint += 0x0006; - break; - case '7': - codepoint += 0x0007; - break; - case '8': - codepoint += 0x0008; - break; - case '9': - codepoint += 0x0009; - break; - case 'A': - case 'a': - codepoint += 0x000a; - break; - case 'B': - case 'b': - codepoint += 0x000b; - break; - case 'C': - case 'c': - codepoint += 0x000c; - break; - case 'D': - case 'd': - codepoint += 0x000d; - break; - case 'E': - case 'e': - codepoint += 0x000e; - break; - case 'F': - case 'f': - codepoint += 0x000f; - break; - default: - return -1; - } + @liveexample{The example below shows how objects can be swapped with + `swap()`.,swap__object_t} - return codepoint; + @since version 1.0.0 + */ + void swap(object_t& other) + { + // swap only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + std::swap(*(m_value.object), other); } - - /*! - @brief create diagnostic representation of a codepoint - @return string "U+XXXX" for codepoint XXXX - */ - static std::string codepoint_to_string(int codepoint) + else { - std::stringstream ss; - ss << "U+" << std::setw(4) << std::uppercase << std::setfill('0') << std::hex << codepoint; - return ss.str(); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); } + } - /*! - @brief scan a string literal + /*! + @brief exchanges the values - This function scans a string according to Sect. 7 of RFC 7159. While - scanning, bytes are escaped and copied into buffer yytext. Then the - function returns successfully, yytext is null-terminated and yylen - contains the number of bytes in the string. + Exchanges the contents of a JSON string with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. - @return token_type::value_string if string could be successfully - scanned, token_type::parse_error otherwise + @param[in,out] other string to exchange the contents with - @note In case of errors, variable error_message contains a textual - description. - */ - token_type scan_string() - { - // reset yytext (ignore opening quote) - reset(); + @throw type_error.310 when JSON value is not a string; example: `"cannot + use swap() with boolean"` - // we entered the function by reading an open quote - assert(current == '\"'); + @complexity Constant. - while (true) - { - // get next character - get(); + @liveexample{The example below shows how strings can be swapped with + `swap()`.,swap__string_t} - switch (current) - { - // end of file while parsing string - case std::char_traits::eof(): - { - error_message = "invalid string: missing closing quote"; - return token_type::parse_error; - } + @since version 1.0.0 + */ + void swap(string_t& other) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_string())) + { + std::swap(*(m_value.string), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } - // closing quote - case '\"': - { - // terminate yytext - add('\0'); - --yylen; - return token_type::value_string; - } + /*! + @brief exchanges the values - // escapes - case '\\': - { - switch (get()) - { - // quotation mark - case '\"': - add('\"'); - break; - // reverse solidus - case '\\': - add('\\'); - break; - // solidus - case '/': - add('/'); - break; - // backspace - case 'b': - add('\b'); - break; - // form feed - case 'f': - add('\f'); - break; - // line feed - case 'n': - add('\n'); - break; - // carriage return - case 'r': - add('\r'); - break; - // tab - case 't': - add('\t'); - break; + Exchanges the contents of a JSON string with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. - // unicode escapes - case 'u': - { - int codepoint; - int codepoint1 = get_codepoint(); + @param[in,out] other binary to exchange the contents with - if (JSON_UNLIKELY(codepoint1 == -1)) - { - error_message = "invalid string: '\\u' must be followed by 4 hex digits"; - return token_type::parse_error; - } + @throw type_error.310 when JSON value is not a string; example: `"cannot + use swap() with boolean"` - // check if code point is a high surrogate - if (0xD800 <= codepoint1 and codepoint1 <= 0xDBFF) - { - // expect next \uxxxx entry - if (JSON_LIKELY(get() == '\\' and get() == 'u')) - { - const int codepoint2 = get_codepoint(); - - if (JSON_UNLIKELY(codepoint2 == -1)) - { - error_message = "invalid string: '\\u' must be followed by 4 hex digits"; - return token_type::parse_error; - } - - // check if codepoint2 is a low surrogate - if (JSON_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF)) - { - codepoint = - // high surrogate occupies the most significant 22 bits - (codepoint1 << 10) - // low surrogate occupies the least significant 15 bits - + codepoint2 - // there is still the 0xD800, 0xDC00 and 0x10000 noise - // in the result so we have to subtract with: - // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 - - 0x35FDC00; - } - else - { - error_message = "invalid string: surrogate " + codepoint_to_string(codepoint1) + " must be followed by U+DC00..U+DFFF instead of " + codepoint_to_string(codepoint2); - return token_type::parse_error; - } - } - else - { - error_message = "invalid string: surrogate " + codepoint_to_string(codepoint1) + " must be followed by U+DC00..U+DFFF"; - return token_type::parse_error; - } - } - else - { - if (JSON_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF)) - { - error_message = "invalid string: surrogate " + codepoint_to_string(codepoint1) + " must follow U+D800..U+DBFF"; - return token_type::parse_error; - } + @complexity Constant. - // only work with first code point - codepoint = codepoint1; - } + @liveexample{The example below shows how strings can be swapped with + `swap()`.,swap__binary_t} - // result of the above calculation yields a proper codepoint - assert(0x00 <= codepoint and codepoint <= 0x10FFFF); + @since version 3.8.0 + */ + void swap(binary_t& other) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } - // translate code point to bytes - if (codepoint < 0x80) - { - // 1-byte characters: 0xxxxxxx (ASCII) - add(codepoint); - } - else if (codepoint <= 0x7ff) - { - // 2-byte characters: 110xxxxx 10xxxxxx - add(0xC0 | (codepoint >> 6)); - add(0x80 | (codepoint & 0x3F)); - } - else if (codepoint <= 0xffff) - { - // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx - add(0xE0 | (codepoint >> 12)); - add(0x80 | ((codepoint >> 6) & 0x3F)); - add(0x80 | (codepoint & 0x3F)); - } - else - { - // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - add(0xF0 | (codepoint >> 18)); - add(0x80 | ((codepoint >> 12) & 0x3F)); - add(0x80 | ((codepoint >> 6) & 0x3F)); - add(0x80 | (codepoint & 0x3F)); - } + /// @copydoc swap(binary_t) + void swap(typename binary_t::container_type& other) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + + /// @} + + public: + ////////////////////////////////////////// + // lexicographical comparison operators // + ////////////////////////////////////////// - break; - } + /// @name lexicographical comparison operators + /// @{ - // other characters after escape - default: - error_message = "invalid string: forbidden character after backslash"; - return token_type::parse_error; - } + /*! + @brief comparison: equal - break; - } + Compares two JSON values for equality according to the following rules: + - Two JSON values are equal if (1) they are from the same type and (2) + their stored values are the same according to their respective + `operator==`. + - Integer and floating-point numbers are automatically converted before + comparison. Note that two NaN values are always treated as unequal. + - Two JSON null values are equal. - // invalid control characters - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x08: - case 0x09: - case 0x0a: - case 0x0b: - case 0x0c: - case 0x0d: - case 0x0e: - case 0x0f: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: - { - error_message = "invalid string: control character " + codepoint_to_string(current) + " must be escaped"; - return token_type::parse_error; - } + @note Floating-point inside JSON values numbers are compared with + `json::number_float_t::operator==` which is `double::operator==` by + default. To compare floating-point while respecting an epsilon, an alternative + [comparison function](https://github.com/mariokonrad/marnav/blob/master/include/marnav/math/floatingpoint.hpp#L34-#L39) + could be used, for instance + @code {.cpp} + template::value, T>::type> + inline bool is_same(T a, T b, T epsilon = std::numeric_limits::epsilon()) noexcept + { + return std::abs(a - b) <= epsilon; + } + @endcode + Or you can self-defined operator equal function like this: + @code {.cpp} + bool my_equal(const_reference lhs, const_reference rhs) { + const auto lhs_type lhs.type(); + const auto rhs_type rhs.type(); + if (lhs_type == rhs_type) { + switch(lhs_type) + // self_defined case + case value_t::number_float: + return std::abs(lhs - rhs) <= std::numeric_limits::epsilon(); + // other cases remain the same with the original + ... + } + ... + } + @endcode - // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace)) - case 0x20: - case 0x21: - case 0x23: - case 0x24: - case 0x25: - case 0x26: - case 0x27: - case 0x28: - case 0x29: - case 0x2a: - case 0x2b: - case 0x2c: - case 0x2d: - case 0x2e: - case 0x2f: - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: - case 0x38: - case 0x39: - case 0x3a: - case 0x3b: - case 0x3c: - case 0x3d: - case 0x3e: - case 0x3f: - case 0x40: - case 0x41: - case 0x42: - case 0x43: - case 0x44: - case 0x45: - case 0x46: - case 0x47: - case 0x48: - case 0x49: - case 0x4a: - case 0x4b: - case 0x4c: - case 0x4d: - case 0x4e: - case 0x4f: - case 0x50: - case 0x51: - case 0x52: - case 0x53: - case 0x54: - case 0x55: - case 0x56: - case 0x57: - case 0x58: - case 0x59: - case 0x5a: - case 0x5b: - case 0x5d: - case 0x5e: - case 0x5f: - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6a: - case 0x6b: - case 0x6c: - case 0x6d: - case 0x6e: - case 0x6f: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: - case 0x78: - case 0x79: - case 0x7a: - case 0x7b: - case 0x7c: - case 0x7d: - case 0x7e: - case 0x7f: - { - add(current); - break; - } + @note NaN values never compare equal to themselves or to other NaN values. - // U+0080..U+07FF: bytes C2..DF 80..BF - case 0xc2: - case 0xc3: - case 0xc4: - case 0xc5: - case 0xc6: - case 0xc7: - case 0xc8: - case 0xc9: - case 0xca: - case 0xcb: - case 0xcc: - case 0xcd: - case 0xce: - case 0xcf: - case 0xd0: - case 0xd1: - case 0xd2: - case 0xd3: - case 0xd4: - case 0xd5: - case 0xd6: - case 0xd7: - case 0xd8: - case 0xd9: - case 0xda: - case 0xdb: - case 0xdc: - case 0xdd: - case 0xde: - case 0xdf: - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - continue; - } + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether the values @a lhs and @a rhs are equal - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; - } + @exceptionsafety No-throw guarantee: this function never throws exceptions. - // U+0800..U+0FFF: bytes E0 A0..BF 80..BF - case 0xe0: - { - add(current); - get(); - if (JSON_LIKELY(0xa0 <= current and current <= 0xbf)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - continue; - } - } + @complexity Linear. - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; - } + @liveexample{The example demonstrates comparing several JSON + types.,operator__equal} - // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF - // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF - case 0xe1: - case 0xe2: - case 0xe3: - case 0xe4: - case 0xe5: - case 0xe6: - case 0xe7: - case 0xe8: - case 0xe9: - case 0xea: - case 0xeb: - case 0xec: - case 0xee: - case 0xef: - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - continue; - } - } + @since version 1.0.0 + */ + friend bool operator==(const_reference lhs, const_reference rhs) noexcept + { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; - } + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + return *lhs.m_value.array == *rhs.m_value.array; - // U+D000..U+D7FF: bytes ED 80..9F 80..BF - case 0xed: - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0x9f)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - continue; - } - } + case value_t::object: + return *lhs.m_value.object == *rhs.m_value.object; - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; - } + case value_t::null: + return true; - // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF - case 0xf0: - { - add(current); - get(); - if (JSON_LIKELY(0x90 <= current and current <= 0xbf)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - continue; - } - } - } + case value_t::string: + return *lhs.m_value.string == *rhs.m_value.string; - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; - } + case value_t::boolean: + return lhs.m_value.boolean == rhs.m_value.boolean; - // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF - case 0xf1: - case 0xf2: - case 0xf3: - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - continue; - } - } - } + case value_t::number_integer: + return lhs.m_value.number_integer == rhs.m_value.number_integer; - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; - } + case value_t::number_unsigned: + return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; - // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF - case 0xf4: - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0x8f)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - continue; - } - } - } + case value_t::number_float: + return lhs.m_value.number_float == rhs.m_value.number_float; - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; - } + case value_t::binary: + return *lhs.m_value.binary == *rhs.m_value.binary; - // remaining bytes (80..C1 and F5..FF) are ill-formed - default: - { - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; - } - } + default: + return false; } } - - static void strtof(float& f, const char* str, char** endptr) noexcept + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) { - f = std::strtof(str, endptr); + return static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float; } - - static void strtof(double& f, const char* str, char** endptr) noexcept + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) { - f = std::strtod(str, endptr); + return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); } - - static void strtof(long double& f, const char* str, char** endptr) noexcept + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) { - f = std::strtold(str, endptr); + return lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned); } - /*! - @brief scan a number literal + return false; + } - This function scans a string according to Sect. 6 of RFC 7159. + /*! + @brief comparison: equal + @copydoc operator==(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept + { + return lhs == basic_json(rhs); + } - The function is realized with a deterministic finite state machine - derived from the grammar described in RFC 7159. Starting in state - "init", the input is read and used to determined the next state. Only - state "done" accepts the number. State "error" is a trap state to model - errors. In the table below, "anything" means any character but the ones - listed before. + /*! + @brief comparison: equal + @copydoc operator==(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) == rhs; + } - state | 0 | 1-9 | e E | + | - | . | anything - ---------|----------|----------|----------|---------|---------|----------|----------- - init | zero | any1 | [error] | [error] | minus | [error] | [error] - minus | zero | any1 | [error] | [error] | [error] | [error] | [error] - zero | done | done | exponent | done | done | decimal1 | done - any1 | any1 | any1 | exponent | done | done | decimal1 | done - decimal1 | decimal2 | [error] | [error] | [error] | [error] | [error] | [error] - decimal2 | decimal2 | decimal2 | exponent | done | done | done | done - exponent | any2 | any2 | [error] | sign | sign | [error] | [error] - sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] - any2 | any2 | any2 | done | done | done | done | done + /*! + @brief comparison: not equal - The state machine is realized with one label per state (prefixed with - "scan_number_") and `goto` statements between them. The state machine - contains cycles, but any cycle can be left when EOF is read. Therefore, - the function is guaranteed to terminate. + Compares two JSON values for inequality by calculating `not (lhs == rhs)`. - During scanning, the read bytes are stored in yytext. This string is - then converted to a signed integer, an unsigned integer, or a - floating-point number. + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether the values @a lhs and @a rhs are not equal - @return token_type::value_unsigned, token_type::value_integer, or - token_type::value_float if number could be successfully scanned, - token_type::parse_error otherwise + @complexity Linear. - @note The scanner is independent of the current locale. Internally, the - locale's decimal point is used instead of `.` to work with the - locale-dependent converters. - */ - token_type scan_number() - { - // reset yytext to store the number's bytes - reset(); + @exceptionsafety No-throw guarantee: this function never throws exceptions. - // the type of the parsed number; initially set to unsigned; will be - // changed if minus sign, decimal point or exponent is read - token_type number_type = token_type::value_unsigned; + @liveexample{The example demonstrates comparing several JSON + types.,operator__notequal} - // state (init): we just found out we need to scan a number - switch (current) - { - case '-': - { - add(current); - goto scan_number_minus; - } + @since version 1.0.0 + */ + friend bool operator!=(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs == rhs); + } - case '0': - { - add(current); - goto scan_number_zero; - } + /*! + @brief comparison: not equal + @copydoc operator!=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept + { + return lhs != basic_json(rhs); + } - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_any1; - } + /*! + @brief comparison: not equal + @copydoc operator!=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) != rhs; + } - default: - { - // all other characters are rejected outside scan_number() - assert(false); // LCOV_EXCL_LINE - } - } + /*! + @brief comparison: less than -scan_number_minus: - // state: we just parsed a leading minus sign - number_type = token_type::value_integer; - switch (get()) - { - case '0': - { - add(current); - goto scan_number_zero; - } + Compares whether one JSON value @a lhs is less than another JSON value @a + rhs according to the following rules: + - If @a lhs and @a rhs have the same type, the values are compared using + the default `<` operator. + - Integer and floating-point numbers are automatically converted before + comparison + - In case @a lhs and @a rhs have different types, the values are ignored + and the order of the types is considered, see + @ref operator<(const value_t, const value_t). - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_any1; - } + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is less than @a rhs - default: - { - error_message = "invalid number; expected digit after '-'"; - return token_type::parse_error; - } - } + @complexity Linear. -scan_number_zero: - // state: we just parse a zero (maybe with a leading minus sign) - switch (get()) + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__less} + + @since version 1.0.0 + */ + friend bool operator<(const_reference lhs, const_reference rhs) noexcept + { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) { - case '.': - { - add(decimal_point_char); - goto scan_number_decimal1; - } + case value_t::array: + // note parentheses are necessary, see + // https://github.com/nlohmann/json/issues/1530 + return (*lhs.m_value.array) < (*rhs.m_value.array); - case 'e': - case 'E': - { - add(current); - goto scan_number_exponent; - } + case value_t::object: + return (*lhs.m_value.object) < (*rhs.m_value.object); - default: - { - goto scan_number_done; - } - } + case value_t::null: + return false; -scan_number_any1: - // state: we just parsed a number 0-9 (maybe with a leading minus sign) - switch (get()) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_any1; - } + case value_t::string: + return (*lhs.m_value.string) < (*rhs.m_value.string); - case '.': - { - add(decimal_point_char); - goto scan_number_decimal1; - } + case value_t::boolean: + return (lhs.m_value.boolean) < (rhs.m_value.boolean); - case 'e': - case 'E': - { - add(current); - goto scan_number_exponent; - } + case value_t::number_integer: + return (lhs.m_value.number_integer) < (rhs.m_value.number_integer); - default: - { - goto scan_number_done; - } - } + case value_t::number_unsigned: + return (lhs.m_value.number_unsigned) < (rhs.m_value.number_unsigned); -scan_number_decimal1: - // state: we just parsed a decimal point - number_type = token_type::value_float; - switch (get()) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_decimal2; - } + case value_t::number_float: + return (lhs.m_value.number_float) < (rhs.m_value.number_float); + + case value_t::binary: + return (*lhs.m_value.binary) < (*rhs.m_value.binary); default: - { - error_message = "invalid number; expected digit after '.'"; - return token_type::parse_error; - } + return false; } + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; + } -scan_number_decimal2: - // we just parsed at least one number after a decimal point - switch (get()) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_decimal2; - } + // We only reach this line if we cannot compare values. In that case, + // we compare types. Note we have to call the operator explicitly, + // because MSVC has problems otherwise. + return operator<(lhs_type, rhs_type); + } - case 'e': - case 'E': - { - add(current); - goto scan_number_exponent; - } + /*! + @brief comparison: less than + @copydoc operator<(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept + { + return lhs < basic_json(rhs); + } - default: - { - goto scan_number_done; - } - } + /*! + @brief comparison: less than + @copydoc operator<(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) < rhs; + } -scan_number_exponent: - // we just parsed an exponent - number_type = token_type::value_float; - switch (get()) - { - case '+': - case '-': - { - add(current); - goto scan_number_sign; - } + /*! + @brief comparison: less than or equal - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_any2; - } + Compares whether one JSON value @a lhs is less than or equal to another + JSON value by calculating `not (rhs < lhs)`. - default: - { - error_message = "invalid number; expected '+', '-', or digit after exponent"; - return token_type::parse_error; - } - } + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is less than or equal to @a rhs -scan_number_sign: - // we just parsed an exponent sign - switch (get()) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_any2; - } + @complexity Linear. - default: - { - error_message = "invalid number; expected digit after exponent sign"; - return token_type::parse_error; - } - } + @exceptionsafety No-throw guarantee: this function never throws exceptions. -scan_number_any2: - // we just parsed a number after the exponent or exponent sign - switch (get()) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_any2; - } + @liveexample{The example demonstrates comparing several JSON + types.,operator__greater} - default: - { - goto scan_number_done; - } - } + @since version 1.0.0 + */ + friend bool operator<=(const_reference lhs, const_reference rhs) noexcept + { + return !(rhs < lhs); + } -scan_number_done: - // unget the character after the number (we only read it to know - // that we are done scanning a number) - --chars_read; - next_unget = true; + /*! + @brief comparison: less than or equal + @copydoc operator<=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept + { + return lhs <= basic_json(rhs); + } - // terminate token - add('\0'); - --yylen; + /*! + @brief comparison: less than or equal + @copydoc operator<=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) <= rhs; + } - // try to parse integers first and fall back to floats - if (number_type == token_type::value_unsigned) - { - char* endptr = nullptr; - errno = 0; - const auto x = std::strtoull(yytext.data(), &endptr, 10); + /*! + @brief comparison: greater than - // we checked the number format before - assert(endptr == yytext.data() + yylen); + Compares whether one JSON value @a lhs is greater than another + JSON value by calculating `not (lhs <= rhs)`. - if (errno == 0) - { - value_unsigned = static_cast(x); - if (value_unsigned == x) - { - return token_type::value_unsigned; - } - } - } - else if (number_type == token_type::value_integer) - { - char* endptr = nullptr; - errno = 0; - const auto x = std::strtoll(yytext.data(), &endptr, 10); + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is greater than to @a rhs - // we checked the number format before - assert(endptr == yytext.data() + yylen); + @complexity Linear. - if (errno == 0) - { - value_integer = static_cast(x); - if (value_integer == x) - { - return token_type::value_integer; - } - } - } + @exceptionsafety No-throw guarantee: this function never throws exceptions. - // this code is reached if we parse a floating-point number or if - // an integer conversion above failed - strtof(value_float, yytext.data(), nullptr); - return token_type::value_float; - } + @liveexample{The example demonstrates comparing several JSON + types.,operator__lessequal} - token_type scan_true() - { - assert(current == 't'); - if (JSON_LIKELY((get() == 'r' and get() == 'u' and get() == 'e'))) - { - return token_type::literal_true; - } + @since version 1.0.0 + */ + friend bool operator>(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs <= rhs); + } - error_message = "invalid literal; expected 'true'"; - return token_type::parse_error; - } + /*! + @brief comparison: greater than + @copydoc operator>(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept + { + return lhs > basic_json(rhs); + } - token_type scan_false() - { - assert(current == 'f'); - if (JSON_LIKELY((get() == 'a' and get() == 'l' and get() == 's' and get() == 'e'))) - { - return token_type::literal_false; - } + /*! + @brief comparison: greater than + @copydoc operator>(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) > rhs; + } - error_message = "invalid literal; expected 'false'"; - return token_type::parse_error; - } + /*! + @brief comparison: greater than or equal - token_type scan_null() - { - assert(current == 'n'); - if (JSON_LIKELY((get() == 'u' and get() == 'l' and get() == 'l'))) - { - return token_type::literal_null; - } + Compares whether one JSON value @a lhs is greater than or equal to another + JSON value by calculating `not (lhs < rhs)`. - error_message = "invalid literal; expected 'null'"; - return token_type::parse_error; - } + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is greater than or equal to @a rhs - ///////////////////// - // input management - ///////////////////// + @complexity Linear. - /// reset yytext - void reset() noexcept - { - yylen = 0; - start_pos = chars_read - 1; - } + @exceptionsafety No-throw guarantee: this function never throws exceptions. - /// get a character from the input - int get() - { - ++chars_read; - return next_unget - ? (next_unget = false, current) - : (current = ia->get_character()); - } + @liveexample{The example demonstrates comparing several JSON + types.,operator__greaterequal} - /// add a character to yytext - void add(int c) - { - // resize yytext if necessary; this condition is deemed unlikely, - // because we start with a 1024-byte buffer - if (JSON_UNLIKELY((yylen + 1 > yytext.capacity()))) - { - yytext.resize(2 * yytext.capacity(), '\0'); - } - yytext[yylen++] = static_cast(c); - } + @since version 1.0.0 + */ + friend bool operator>=(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs < rhs); + } + + /*! + @brief comparison: greater than or equal + @copydoc operator>=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept + { + return lhs >= basic_json(rhs); + } - public: - ///////////////////// - // value getters - ///////////////////// + /*! + @brief comparison: greater than or equal + @copydoc operator>=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) >= rhs; + } - /// return integer value - constexpr number_integer_t get_number_integer() const noexcept - { - return value_integer; - } + /// @} - /// return unsigned integer value - constexpr number_unsigned_t get_number_unsigned() const noexcept - { - return value_unsigned; - } + /////////////////// + // serialization // + /////////////////// - /// return floating-point value - constexpr number_float_t get_number_float() const noexcept - { - return value_float; - } + /// @name serialization + /// @{ - /// return string value - const std::string get_string() - { - // yytext cannot be returned as char*, because it may contain a - // null byte (parsed as "\u0000") - return std::string(yytext.data(), yylen); - } + /*! + @brief serialize to stream - ///////////////////// - // diagnostics - ///////////////////// + Serialize the given JSON value @a j to the output stream @a o. The JSON + value will be serialized using the @ref dump member function. - /// return position of last read token - constexpr size_t get_position() const noexcept - { - return chars_read; - } + - The indentation of the output can be controlled with the member variable + `width` of the output stream @a o. For instance, using the manipulator + `std::setw(4)` on @a o sets the indentation level to `4` and the + serialization result is the same as calling `dump(4)`. - /// return the last read token (for errors only) - std::string get_token_string() const - { - // get the raw byte sequence of the last token - std::string s = ia->read(start_pos, chars_read - start_pos); + - The indentation character can be controlled with the member variable + `fill` of the output stream @a o. For instance, the manipulator + `std::setfill('\\t')` sets indentation to use a tab character rather than + the default space character. - // escape control characters - std::string result; - for (auto c : s) - { - if (c == '\0' or c == std::char_traits::eof()) - { - // ignore EOF - continue; - } - else if ('\x00' <= c and c <= '\x1f') - { - // escape control characters - result += "<" + codepoint_to_string(c) + ">"; - } - else - { - // add character as is - result.append(1, c); - } - } + @param[in,out] o stream to serialize to + @param[in] j JSON value to serialize - return result; - } + @return the stream @a o - /// return syntax error message - const std::string& get_error_message() const noexcept - { - return error_message; - } + @throw type_error.316 if a string stored inside the JSON value is not + UTF-8 encoded - ///////////////////// - // actual scanner - ///////////////////// + @complexity Linear. - token_type scan() - { - // read next character and ignore whitespace - do - { - get(); - } - while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); - - switch (current) - { - // structural characters - case '[': - return token_type::begin_array; - case ']': - return token_type::end_array; - case '{': - return token_type::begin_object; - case '}': - return token_type::end_object; - case ':': - return token_type::name_separator; - case ',': - return token_type::value_separator; - - // literals - case 't': - return scan_true(); - case 'f': - return scan_false(); - case 'n': - return scan_null(); - - // string - case '\"': - return scan_string(); - - // number - case '-': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - return scan_number(); - - // end of input (the null byte is needed when parsing from - // string literals) - case '\0': - case std::char_traits::eof(): - return token_type::end_of_input; - - // error - default: - error_message = "invalid literal"; - return token_type::parse_error; - } - } + @liveexample{The example below shows the serialization with different + parameters to `width` to adjust the indentation level.,operator_serialize} - private: - /// input adapter - input_adapter_t ia = nullptr; + @since version 1.0.0; indentation character added in version 3.0.0 + */ + friend std::ostream& operator<<(std::ostream& o, const basic_json& j) + { + // read width member and use it as indentation parameter if nonzero + const bool pretty_print = o.width() > 0; + const auto indentation = pretty_print ? o.width() : 0; - /// the current character - int current = std::char_traits::eof(); + // reset width to 0 for subsequent calls to this stream + o.width(0); - /// whether get() should return the last character again - bool next_unget = false; + // do the actual serialization + serializer s(detail::output_adapter(o), o.fill()); + s.dump(j, pretty_print, false, static_cast(indentation)); + return o; + } - /// the number of characters read - size_t chars_read = 0; - /// the start position of the current token - size_t start_pos = 0; + /*! + @brief serialize to stream + @deprecated This stream operator is deprecated and will be removed in + future 4.0.0 of the library. Please use + @ref operator<<(std::ostream&, const basic_json&) + instead; that is, replace calls like `j >> o;` with `o << j;`. + @since version 1.0.0; deprecated since version 3.0.0 + */ + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&)) + friend std::ostream& operator>>(const basic_json& j, std::ostream& o) + { + return o << j; + } - /// buffer for variable-length tokens (numbers, strings) - std::vector yytext = std::vector(1024, '\0'); - /// current index in yytext - size_t yylen = 0; + /// @} - /// a description of occurred lexer errors - std::string error_message = ""; - // number values - number_integer_t value_integer = 0; - number_unsigned_t value_unsigned = 0; - number_float_t value_float = 0; + ///////////////////// + // deserialization // + ///////////////////// - /// the decimal point - const char decimal_point_char = '.'; - }; + /// @name deserialization + /// @{ /*! - @brief syntax analysis - - This class implements a recursive decent parser. - */ - class parser - { - public: - /// a parser reading from an input adapter - explicit parser(input_adapter_t adapter, - const parser_callback_t cb = nullptr) - : callback(cb), m_lexer(adapter) - {} + @brief deserialize from a compatible input - /*! - @brief public parser interface + @tparam InputType A compatible input, for instance + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. - @param[in] strict whether to expect the last token to be EOF - @return parsed JSON value - - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - */ - basic_json parse(const bool strict = true) - { - // read first token - get_token(); + @param[in] i input to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) - basic_json result = parse_internal(true); - result.assert_invariant(); + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. - if (strict) - { - get_token(); - expect(lexer::token_type::end_of_input); - } + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails - // return parser result and replace it with null in case the - // top-level value was discarded by the callback function - return result.is_discarded() ? basic_json() : std::move(result); - } + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb or reading from the input @a i has a super-linear complexity. - /*! - @brief public accept interface + @note A UTF-8 byte order mark is silently ignored. - @param[in] strict whether to expect the last token to be EOF - @return whether the input is a proper JSON text - */ - bool accept(const bool strict = true) - { - // read first token - get_token(); + @liveexample{The example below demonstrates the `parse()` function reading + from an array.,parse__array__parser_callback_t} - if (not accept_internal()) - { - return false; - } + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__string__parser_callback_t} - if (strict and get_token() != lexer::token_type::end_of_input) - { - return false; - } + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__istream__parser_callback_t} - return true; - } + @liveexample{The example below demonstrates the `parse()` function reading + from a contiguous container.,parse__contiguouscontainer__parser_callback_t} - private: - /*! - @brief the actual parser - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - */ - basic_json parse_internal(bool keep) - { - auto result = basic_json(value_t::discarded); + @since version 2.0.3 (contiguous containers); version 3.9.0 allowed to + ignore comments. + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(InputType&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(detail::input_adapter(std::forward(i)), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } - switch (last_token) - { - case lexer::token_type::begin_object: - { - if (keep and (not callback - or ((keep = callback(depth++, parse_event_t::object_start, result)) != 0))) - { - // explicitly set result to object to cope with {} - result.m_type = value_t::object; - result.m_value = value_t::object; - } + /*! + @brief deserialize from a pair of character iterators - // read next token - get_token(); + The value_type of the iterator must be a integral type with size of 1, 2 or + 4 bytes, which will be interpreted respectively as UTF-8, UTF-16 and UTF-32. - // closing } -> we are done - if (last_token == lexer::token_type::end_object) - { - if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) - { - result = basic_json(value_t::discarded); - } - return result; - } + @param[in] first iterator to start of character range + @param[in] last iterator to end of character range + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) - // parse values - while (true) - { - // store key - expect(lexer::token_type::value_string); - const auto key = m_lexer.get_string(); + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. - bool keep_tag = false; - if (keep) - { - if (callback) - { - basic_json k(key); - keep_tag = callback(depth, parse_event_t::key, k); - } - else - { - keep_tag = true; - } - } + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(IteratorType first, + IteratorType last, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } - // parse separator (:) - get_token(); - expect(lexer::token_type::name_separator); + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) + static basic_json parse(detail::span_input_adapter&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } - // parse and add value - get_token(); - auto value = parse_internal(keep); - if (keep and keep_tag and not value.is_discarded()) - { - result[key] = std::move(value); - } + /*! + @brief check if the input is valid JSON - // comma -> next value - get_token(); - if (last_token == lexer::token_type::value_separator) - { - get_token(); - continue; - } + Unlike the @ref parse(InputType&&, const parser_callback_t,const bool) + function, this function neither throws an exception in case of invalid JSON + input (i.e., a parse error) nor creates diagnostic information. - // closing } - expect(lexer::token_type::end_object); - break; - } + @tparam InputType A compatible input, for instance + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. - if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) - { - result = basic_json(value_t::discarded); - } + @param[in] i input to read from + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) - return result; - } + @return Whether the input read from @a i is valid JSON. - case lexer::token_type::begin_array: - { - if (keep and (not callback - or ((keep = callback(depth++, parse_event_t::array_start, result)) != 0))) - { - // explicitly set result to object to cope with [] - result.m_type = value_t::array; - result.m_value = value_t::array; - } + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. - // read next token - get_token(); + @note A UTF-8 byte order mark is silently ignored. - // closing ] -> we are done - if (last_token == lexer::token_type::end_array) - { - if (callback and not callback(--depth, parse_event_t::array_end, result)) - { - result = basic_json(value_t::discarded); - } - return result; - } + @liveexample{The example below demonstrates the `accept()` function reading + from a string.,accept__string} + */ + template + static bool accept(InputType&& i, + const bool ignore_comments = false) + { + return parser(detail::input_adapter(std::forward(i)), nullptr, false, ignore_comments).accept(true); + } - // parse values - while (true) - { - // parse value - auto value = parse_internal(keep); - if (keep and not value.is_discarded()) - { - result.push_back(std::move(value)); - } + template + static bool accept(IteratorType first, IteratorType last, + const bool ignore_comments = false) + { + return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true); + } - // comma -> next value - get_token(); - if (last_token == lexer::token_type::value_separator) - { - get_token(); - continue; - } + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) + static bool accept(detail::span_input_adapter&& i, + const bool ignore_comments = false) + { + return parser(i.get(), nullptr, false, ignore_comments).accept(true); + } - // closing ] - expect(lexer::token_type::end_array); - break; - } + /*! + @brief generate SAX events - if (keep and callback and not callback(--depth, parse_event_t::array_end, result)) - { - result = basic_json(value_t::discarded); - } + The SAX event lister must follow the interface of @ref json_sax. - return result; - } + This function reads from a compatible input. Examples are: + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. - case lexer::token_type::literal_null: - { - result.m_type = value_t::null; - break; - } + @param[in] i input to read from + @param[in,out] sax SAX event listener + @param[in] format the format to parse (JSON, CBOR, MessagePack, or UBJSON) + @param[in] strict whether the input has to be consumed completely + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default); only applies to the JSON file format. - case lexer::token_type::value_string: - { - result = basic_json(m_lexer.get_string()); - break; - } + @return return value of the last processed SAX event - case lexer::token_type::literal_true: - { - result.m_type = value_t::boolean; - result.m_value = true; - break; - } + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails - case lexer::token_type::literal_false: - { - result.m_type = value_t::boolean; - result.m_value = false; - break; - } + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the SAX consumer @a sax has + a super-linear complexity. - case lexer::token_type::value_unsigned: - { - result.m_type = value_t::number_unsigned; - result.m_value = m_lexer.get_number_unsigned(); - break; - } + @note A UTF-8 byte order mark is silently ignored. - case lexer::token_type::value_integer: - { - result.m_type = value_t::number_integer; - result.m_value = m_lexer.get_number_integer(); - break; - } + @liveexample{The example below demonstrates the `sax_parse()` function + reading from string and processing the events with a user-defined SAX + event consumer.,sax_parse} - case lexer::token_type::value_float: - { - result.m_type = value_t::number_float; - result.m_value = m_lexer.get_number_float(); + @since version 3.2.0 + */ + template + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(InputType&& i, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = detail::input_adapter(std::forward(i)); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } - // throw in case of infinity or NAN - if (JSON_UNLIKELY(not std::isfinite(result.m_value.number_float))) - { - JSON_THROW(out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'")); - } + template + JSON_HEDLEY_NON_NULL(3) + static bool sax_parse(IteratorType first, IteratorType last, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = detail::input_adapter(std::move(first), std::move(last)); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } - break; - } + template + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...)) + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = i.get(); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } - default: - { - // the last token was unexpected - unexpect(last_token); - } - } + /*! + @brief deserialize from stream + @deprecated This stream operator is deprecated and will be removed in + version 4.0.0 of the library. Please use + @ref operator>>(std::istream&, basic_json&) + instead; that is, replace calls like `j << i;` with `i >> j;`. + @since version 1.0.0; deprecated since version 3.0.0 + */ + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&)) + friend std::istream& operator<<(basic_json& j, std::istream& i) + { + return operator>>(i, j); + } - if (keep and callback and not callback(depth, parse_event_t::value, result)) - { - result = basic_json(value_t::discarded); - } - return result; - } + /*! + @brief deserialize from stream - /*! - @brief the acutal acceptor + Deserializes an input stream to a JSON value. - @invariant 1. The last token is not yet processed. Therefore, the - caller of this function must make sure a token has - been read. - 2. When this function returns, the last token is processed. - That is, the last read character was already considered. + @param[in,out] i input stream to read a serialized JSON value from + @param[in,out] j JSON value to write the deserialized input to - This invariant makes sure that no token needs to be "unput". - */ - bool accept_internal() - { - switch (last_token) - { - case lexer::token_type::begin_object: - { - // read next token - get_token(); + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails - // closing } -> we are done - if (last_token == lexer::token_type::end_object) - { - return true; - } + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. - // parse values - while (true) - { - // parse key - if (last_token != lexer::token_type::value_string) - { - return false; - } + @note A UTF-8 byte order mark is silently ignored. - // parse separator (:) - get_token(); - if (last_token != lexer::token_type::name_separator) - { - return false; - } + @liveexample{The example below shows how a JSON value is constructed by + reading a serialization from a stream.,operator_deserialize} - // parse value - get_token(); - if (not accept_internal()) - { - return false; - } + @sa parse(std::istream&, const parser_callback_t) for a variant with a + parser callback function to filter values while parsing - // comma -> next value - get_token(); - if (last_token == lexer::token_type::value_separator) - { - get_token(); - continue; - } + @since version 1.0.0 + */ + friend std::istream& operator>>(std::istream& i, basic_json& j) + { + parser(detail::input_adapter(i)).parse(false, j); + return i; + } - // closing } - if (last_token != lexer::token_type::end_object) - { - return false; - } + /// @} - return true; - } - } + /////////////////////////// + // convenience functions // + /////////////////////////// - case lexer::token_type::begin_array: - { - // read next token - get_token(); + /*! + @brief return the type as string - // closing ] -> we are done - if (last_token == lexer::token_type::end_array) - { - return true; - } + Returns the type name as string to be used in error messages - usually to + indicate that a function was called on a wrong JSON type. - // parse values - while (true) - { - // parse value - if (not accept_internal()) - { - return false; - } + @return a string representation of a the @a m_type member: + Value type | return value + ----------- | ------------- + null | `"null"` + boolean | `"boolean"` + string | `"string"` + number | `"number"` (for all number types) + object | `"object"` + array | `"array"` + binary | `"binary"` + discarded | `"discarded"` - // comma -> next value - get_token(); - if (last_token == lexer::token_type::value_separator) - { - get_token(); - continue; - } + @exceptionsafety No-throw guarantee: this function never throws exceptions. - // closing ] - if (last_token != lexer::token_type::end_array) - { - return false; - } + @complexity Constant. - return true; - } - } + @liveexample{The following code exemplifies `type_name()` for all JSON + types.,type_name} - case lexer::token_type::literal_false: - case lexer::token_type::literal_null: - case lexer::token_type::literal_true: - case lexer::token_type::value_float: - case lexer::token_type::value_integer: - case lexer::token_type::value_string: - case lexer::token_type::value_unsigned: - { - return true; - } + @sa @ref type() -- return the type of the JSON value + @sa @ref operator value_t() -- return the type of the JSON value (implicit) + @since version 1.0.0, public since 2.1.0, `const char*` and `noexcept` + since 3.0.0 + */ + JSON_HEDLEY_RETURNS_NON_NULL + const char* type_name() const noexcept + { + { + switch (m_type) + { + case value_t::null: + return "null"; + case value_t::object: + return "object"; + case value_t::array: + return "array"; + case value_t::string: + return "string"; + case value_t::boolean: + return "boolean"; + case value_t::binary: + return "binary"; + case value_t::discarded: + return "discarded"; default: - { - // the last token was unexpected - return false; - } + return "number"; } } + } - /// get next token from lexer - typename lexer::token_type get_token() - { - last_token = m_lexer.scan(); - return last_token; - } - /*! - @throw parse_error.101 if expected token did not occur - */ - void expect(typename lexer::token_type t) const - { - if (JSON_UNLIKELY(t != last_token)) - { - std::string error_msg = "syntax error - "; - if (last_token == lexer::token_type::parse_error) - { - error_msg += m_lexer.get_error_message() + "; last read: '" + m_lexer.get_token_string() + "'"; - } - else - { - error_msg += "unexpected " + std::string(lexer::token_type_name(last_token)); - } + private: + ////////////////////// + // member variables // + ////////////////////// - error_msg += "; expected " + std::string(lexer::token_type_name(t)); - JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg)); - } - } + /// the type of the current element + value_t m_type = value_t::null; - /*! - @throw parse_error.101 if unexpected token occurred - */ - void unexpect(typename lexer::token_type t) const - { - if (JSON_UNLIKELY(t == last_token)) - { - std::string error_msg = "syntax error - "; - if (last_token == lexer::token_type::parse_error) - { - error_msg += m_lexer.get_error_message() + "; last read '" + m_lexer.get_token_string() + "'"; - } - else - { - error_msg += "unexpected " + std::string(lexer::token_type_name(last_token)); - } + /// the value of the current element + json_value m_value = {}; - JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg)); - } - } + ////////////////////////////////////////// + // binary serialization/deserialization // + ////////////////////////////////////////// - private: - /// current level of recursion - int depth = 0; - /// callback function - const parser_callback_t callback = nullptr; - /// the type of the last read token - typename lexer::token_type last_token = lexer::token_type::uninitialized; - /// the lexer - lexer m_lexer; - }; + /// @name binary serialization/deserialization support + /// @{ public: /*! - @brief JSON Pointer + @brief create a CBOR serialization of a given JSON value - A JSON pointer defines a string syntax for identifying a specific value - within a JSON document. It can be used with functions `at` and - `operator[]`. Furthermore, JSON pointers are the base for JSON patches. + Serializes a given JSON value @a j to a byte vector using the CBOR (Concise + Binary Object Representation) serialization format. CBOR is a binary + serialization format which aims to be more compact than JSON itself, yet + more efficient to parse. - @sa [RFC 6901](https://tools.ietf.org/html/rfc6901) + The library uses the following mapping from JSON values types to + CBOR types according to the CBOR specification (RFC 7049): - @since version 2.0.0 - */ - class json_pointer - { - /// allow basic_json to access private members - friend class basic_json; + JSON value type | value/range | CBOR type | first byte + --------------- | ------------------------------------------ | ---------------------------------- | --------------- + null | `null` | Null | 0xF6 + boolean | `true` | True | 0xF5 + boolean | `false` | False | 0xF4 + number_integer | -9223372036854775808..-2147483649 | Negative integer (8 bytes follow) | 0x3B + number_integer | -2147483648..-32769 | Negative integer (4 bytes follow) | 0x3A + number_integer | -32768..-129 | Negative integer (2 bytes follow) | 0x39 + number_integer | -128..-25 | Negative integer (1 byte follow) | 0x38 + number_integer | -24..-1 | Negative integer | 0x20..0x37 + number_integer | 0..23 | Integer | 0x00..0x17 + number_integer | 24..255 | Unsigned integer (1 byte follow) | 0x18 + number_integer | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 + number_integer | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A + number_integer | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B + number_unsigned | 0..23 | Integer | 0x00..0x17 + number_unsigned | 24..255 | Unsigned integer (1 byte follow) | 0x18 + number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 + number_unsigned | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A + number_unsigned | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B + number_float | *any value representable by a float* | Single-Precision Float | 0xFA + number_float | *any value NOT representable by a float* | Double-Precision Float | 0xFB + string | *length*: 0..23 | UTF-8 string | 0x60..0x77 + string | *length*: 23..255 | UTF-8 string (1 byte follow) | 0x78 + string | *length*: 256..65535 | UTF-8 string (2 bytes follow) | 0x79 + string | *length*: 65536..4294967295 | UTF-8 string (4 bytes follow) | 0x7A + string | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow) | 0x7B + array | *size*: 0..23 | array | 0x80..0x97 + array | *size*: 23..255 | array (1 byte follow) | 0x98 + array | *size*: 256..65535 | array (2 bytes follow) | 0x99 + array | *size*: 65536..4294967295 | array (4 bytes follow) | 0x9A + array | *size*: 4294967296..18446744073709551615 | array (8 bytes follow) | 0x9B + object | *size*: 0..23 | map | 0xA0..0xB7 + object | *size*: 23..255 | map (1 byte follow) | 0xB8 + object | *size*: 256..65535 | map (2 bytes follow) | 0xB9 + object | *size*: 65536..4294967295 | map (4 bytes follow) | 0xBA + object | *size*: 4294967296..18446744073709551615 | map (8 bytes follow) | 0xBB + binary | *size*: 0..23 | byte string | 0x40..0x57 + binary | *size*: 23..255 | byte string (1 byte follow) | 0x58 + binary | *size*: 256..65535 | byte string (2 bytes follow) | 0x59 + binary | *size*: 65536..4294967295 | byte string (4 bytes follow) | 0x5A + binary | *size*: 4294967296..18446744073709551615 | byte string (8 bytes follow) | 0x5B + + @note The mapping is **complete** in the sense that any JSON value type + can be converted to a CBOR value. + + @note If NaN or Infinity are stored inside a JSON number, they are + serialized properly. This behavior differs from the @ref dump() + function which serializes NaN or Infinity to `null`. - public: - /*! - @brief create JSON pointer + @note The following CBOR types are not used in the conversion: + - UTF-8 strings terminated by "break" (0x7F) + - arrays terminated by "break" (0x9F) + - maps terminated by "break" (0xBF) + - byte strings terminated by "break" (0x5F) + - date/time (0xC0..0xC1) + - bignum (0xC2..0xC3) + - decimal fraction (0xC4) + - bigfloat (0xC5) + - expected conversions (0xD5..0xD7) + - simple values (0xE0..0xF3, 0xF8) + - undefined (0xF7) + - half-precision floats (0xF9) + - break (0xFF) - Create a JSON pointer according to the syntax described in - [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). + @param[in] j JSON value to serialize + @return CBOR serialization as byte vector - @param[in] s string representing the JSON pointer; if omitted, the - empty string is assumed which references the whole JSON - value + @complexity Linear in the size of the JSON value @a j. - @throw parse_error.107 if the given JSON pointer @a s is nonempty and - does not begin with a slash (`/`); see example below + @liveexample{The example shows the serialization of a JSON value to a byte + vector in CBOR format.,to_cbor} - @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s - is not followed by `0` (representing `~`) or `1` (representing `/`); - see example below + @sa http://cbor.io + @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the + analogous deserialization + @sa @ref to_msgpack(const basic_json&) for the related MessagePack format + @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the + related UBJSON format - @liveexample{The example shows the construction several valid JSON - pointers as well as the exceptional behavior.,json_pointer} + @since version 2.0.9; compact representation of floating-point numbers + since version 3.8.0 + */ + static std::vector to_cbor(const basic_json& j) + { + std::vector result; + to_cbor(j, result); + return result; + } - @since version 2.0.0 - */ - explicit json_pointer(const std::string& s = "") - : reference_tokens(split(s)) - {} + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } - /*! - @brief return a string representation of the JSON pointer + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } - @invariant For each JSON pointer `ptr`, it holds: - @code {.cpp} - ptr == json_pointer(ptr.to_string()); - @endcode + /*! + @brief create a MessagePack serialization of a given JSON value - @return a string representation of the JSON pointer + Serializes a given JSON value @a j to a byte vector using the MessagePack + serialization format. MessagePack is a binary serialization format which + aims to be more compact than JSON itself, yet more efficient to parse. - @liveexample{The example shows the result of `to_string`., - json_pointer__to_string} + The library uses the following mapping from JSON values types to + MessagePack types according to the MessagePack specification: - @since version 2.0.0 - */ - std::string to_string() const noexcept - { - return std::accumulate(reference_tokens.begin(), - reference_tokens.end(), std::string{}, - [](const std::string & a, const std::string & b) - { - return a + "/" + escape(b); - }); - } + JSON value type | value/range | MessagePack type | first byte + --------------- | --------------------------------- | ---------------- | ---------- + null | `null` | nil | 0xC0 + boolean | `true` | true | 0xC3 + boolean | `false` | false | 0xC2 + number_integer | -9223372036854775808..-2147483649 | int64 | 0xD3 + number_integer | -2147483648..-32769 | int32 | 0xD2 + number_integer | -32768..-129 | int16 | 0xD1 + number_integer | -128..-33 | int8 | 0xD0 + number_integer | -32..-1 | negative fixint | 0xE0..0xFF + number_integer | 0..127 | positive fixint | 0x00..0x7F + number_integer | 128..255 | uint 8 | 0xCC + number_integer | 256..65535 | uint 16 | 0xCD + number_integer | 65536..4294967295 | uint 32 | 0xCE + number_integer | 4294967296..18446744073709551615 | uint 64 | 0xCF + number_unsigned | 0..127 | positive fixint | 0x00..0x7F + number_unsigned | 128..255 | uint 8 | 0xCC + number_unsigned | 256..65535 | uint 16 | 0xCD + number_unsigned | 65536..4294967295 | uint 32 | 0xCE + number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xCF + number_float | *any value representable by a float* | float 32 | 0xCA + number_float | *any value NOT representable by a float* | float 64 | 0xCB + string | *length*: 0..31 | fixstr | 0xA0..0xBF + string | *length*: 32..255 | str 8 | 0xD9 + string | *length*: 256..65535 | str 16 | 0xDA + string | *length*: 65536..4294967295 | str 32 | 0xDB + array | *size*: 0..15 | fixarray | 0x90..0x9F + array | *size*: 16..65535 | array 16 | 0xDC + array | *size*: 65536..4294967295 | array 32 | 0xDD + object | *size*: 0..15 | fix map | 0x80..0x8F + object | *size*: 16..65535 | map 16 | 0xDE + object | *size*: 65536..4294967295 | map 32 | 0xDF + binary | *size*: 0..255 | bin 8 | 0xC4 + binary | *size*: 256..65535 | bin 16 | 0xC5 + binary | *size*: 65536..4294967295 | bin 32 | 0xC6 - /// @copydoc to_string() - operator std::string() const - { - return to_string(); - } + @note The mapping is **complete** in the sense that any JSON value type + can be converted to a MessagePack value. - private: - /*! - @brief remove and return last reference pointer - @throw out_of_range.405 if JSON pointer has no parent - */ - std::string pop_back() - { - if (is_root()) - { - JSON_THROW(out_of_range::create(405, "JSON pointer has no parent")); - } + @note The following values can **not** be converted to a MessagePack value: + - strings with more than 4294967295 bytes + - byte strings with more than 4294967295 bytes + - arrays with more than 4294967295 elements + - objects with more than 4294967295 elements - auto last = reference_tokens.back(); - reference_tokens.pop_back(); - return last; - } + @note Any MessagePack output created @ref to_msgpack can be successfully + parsed by @ref from_msgpack. - /// return whether pointer points to the root document - bool is_root() const - { - return reference_tokens.empty(); - } + @note If NaN or Infinity are stored inside a JSON number, they are + serialized properly. This behavior differs from the @ref dump() + function which serializes NaN or Infinity to `null`. - json_pointer top() const - { - if (is_root()) - { - JSON_THROW(out_of_range::create(405, "JSON pointer has no parent")); - } + @param[in] j JSON value to serialize + @return MessagePack serialization as byte vector - json_pointer result = *this; - result.reference_tokens = {reference_tokens[0]}; - return result; - } + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in MessagePack format.,to_msgpack} + + @sa http://msgpack.org + @sa @ref from_msgpack for the analogous deserialization + @sa @ref to_cbor(const basic_json& for the related CBOR format + @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the + related UBJSON format + + @since version 2.0.9 + */ + static std::vector to_msgpack(const basic_json& j) + { + std::vector result; + to_msgpack(j, result); + return result; + } - /*! - @brief create and return a reference to the pointed to value + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } - @complexity Linear in the number of reference tokens. + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } - @throw parse_error.109 if array index is not a number - @throw type_error.313 if value cannot be unflattened - */ - reference get_and_create(reference j) const - { - pointer result = &j; + /*! + @brief create a UBJSON serialization of a given JSON value - // in case no reference tokens exist, return a reference to the - // JSON value j which will be overwritten by a primitive value - for (const auto& reference_token : reference_tokens) - { - switch (result->m_type) - { - case value_t::null: - { - if (reference_token == "0") - { - // start a new array if reference token is 0 - result = &result->operator[](0); - } - else - { - // start a new object otherwise - result = &result->operator[](reference_token); - } - break; - } + Serializes a given JSON value @a j to a byte vector using the UBJSON + (Universal Binary JSON) serialization format. UBJSON aims to be more compact + than JSON itself, yet more efficient to parse. - case value_t::object: - { - // create an entry in the object - result = &result->operator[](reference_token); - break; - } + The library uses the following mapping from JSON values types to + UBJSON types according to the UBJSON specification: + + JSON value type | value/range | UBJSON type | marker + --------------- | --------------------------------- | ----------- | ------ + null | `null` | null | `Z` + boolean | `true` | true | `T` + boolean | `false` | false | `F` + number_integer | -9223372036854775808..-2147483649 | int64 | `L` + number_integer | -2147483648..-32769 | int32 | `l` + number_integer | -32768..-129 | int16 | `I` + number_integer | -128..127 | int8 | `i` + number_integer | 128..255 | uint8 | `U` + number_integer | 256..32767 | int16 | `I` + number_integer | 32768..2147483647 | int32 | `l` + number_integer | 2147483648..9223372036854775807 | int64 | `L` + number_unsigned | 0..127 | int8 | `i` + number_unsigned | 128..255 | uint8 | `U` + number_unsigned | 256..32767 | int16 | `I` + number_unsigned | 32768..2147483647 | int32 | `l` + number_unsigned | 2147483648..9223372036854775807 | int64 | `L` + number_unsigned | 2147483649..18446744073709551615 | high-precision | `H` + number_float | *any value* | float64 | `D` + string | *with shortest length indicator* | string | `S` + array | *see notes on optimized format* | array | `[` + object | *see notes on optimized format* | map | `{` - case value_t::array: - { - // create an entry in the array - JSON_TRY - { - result = &result->operator[](static_cast(std::stoi(reference_token))); - } - JSON_CATCH (std::invalid_argument&) - { - JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } - break; - } + @note The mapping is **complete** in the sense that any JSON value type + can be converted to a UBJSON value. + + @note The following values can **not** be converted to a UBJSON value: + - strings with more than 9223372036854775807 bytes (theoretical) + + @note The following markers are not used in the conversion: + - `Z`: no-op values are not created. + - `C`: single-byte strings are serialized with `S` markers. + + @note Any UBJSON output created @ref to_ubjson can be successfully parsed + by @ref from_ubjson. + + @note If NaN or Infinity are stored inside a JSON number, they are + serialized properly. This behavior differs from the @ref dump() + function which serializes NaN or Infinity to `null`. + + @note The optimized formats for containers are supported: Parameter + @a use_size adds size information to the beginning of a container and + removes the closing marker. Parameter @a use_type further checks + whether all elements of a container have the same type and adds the + type marker to the beginning of the container. The @a use_type + parameter must only be used together with @a use_size = true. Note + that @a use_size = true alone may result in larger representations - + the benefit of this parameter is that the receiving side is + immediately informed on the number of elements of the container. + + @note If the JSON data contains the binary type, the value stored is a list + of integers, as suggested by the UBJSON documentation. In particular, + this means that serialization and the deserialization of a JSON + containing binary values into UBJSON and back will result in a + different JSON object. - /* - The following code is only reached if there exists a - reference token _and_ the current value is primitive. In - this case, we have an error situation, because primitive - values may only occur as single value; that is, with an - empty list of reference tokens. - */ - default: - { - JSON_THROW(type_error::create(313, "invalid value to unflatten")); - } - } - } + @param[in] j JSON value to serialize + @param[in] use_size whether to add size annotations to container types + @param[in] use_type whether to add type annotations to container types + (must be combined with @a use_size = true) + @return UBJSON serialization as byte vector - return *result; - } + @complexity Linear in the size of the JSON value @a j. - /*! - @brief return a reference to the pointed to value + @liveexample{The example shows the serialization of a JSON value to a byte + vector in UBJSON format.,to_ubjson} - @note This version does not throw if a value is not present, but tries - to create nested values instead. For instance, calling this function - with pointer `"/this/that"` on a null value is equivalent to calling - `operator[]("this").operator[]("that")` on that value, effectively - changing the null value to an object. + @sa http://ubjson.org + @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the + analogous deserialization + @sa @ref to_cbor(const basic_json& for the related CBOR format + @sa @ref to_msgpack(const basic_json&) for the related MessagePack format - @param[in] ptr a JSON value + @since version 3.1.0 + */ + static std::vector to_ubjson(const basic_json& j, + const bool use_size = false, + const bool use_type = false) + { + std::vector result; + to_ubjson(j, result, use_size, use_type); + return result; + } - @return reference to the JSON value pointed to by the JSON pointer + static void to_ubjson(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } - @complexity Linear in the length of the JSON pointer. + static void to_ubjson(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - reference get_unchecked(pointer ptr) const - { - for (const auto& reference_token : reference_tokens) - { - // convert null values to arrays or objects before continuing - if (ptr->m_type == value_t::null) - { - // check if reference token is a number - const bool nums = std::all_of(reference_token.begin(), - reference_token.end(), - [](const char x) - { - return (x >= '0' and x <= '9'); - }); - // change value to array for numbers or "-" or to object - // otherwise - if (nums or reference_token == "-") - { - *ptr = value_t::array; - } - else - { - *ptr = value_t::object; - } - } + /*! + @brief Serializes the given JSON object `j` to BSON and returns a vector + containing the corresponding BSON-representation. + + BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are + stored as a single entity (a so-called document). + + The library uses the following mapping from JSON values types to BSON types: + + JSON value type | value/range | BSON type | marker + --------------- | --------------------------------- | ----------- | ------ + null | `null` | null | 0x0A + boolean | `true`, `false` | boolean | 0x08 + number_integer | -9223372036854775808..-2147483649 | int64 | 0x12 + number_integer | -2147483648..2147483647 | int32 | 0x10 + number_integer | 2147483648..9223372036854775807 | int64 | 0x12 + number_unsigned | 0..2147483647 | int32 | 0x10 + number_unsigned | 2147483648..9223372036854775807 | int64 | 0x12 + number_unsigned | 9223372036854775808..18446744073709551615| -- | -- + number_float | *any value* | double | 0x01 + string | *any value* | string | 0x02 + array | *any value* | document | 0x04 + object | *any value* | document | 0x03 + binary | *any value* | binary | 0x05 + + @warning The mapping is **incomplete**, since only JSON-objects (and things + contained therein) can be serialized to BSON. + Also, integers larger than 9223372036854775807 cannot be serialized to BSON, + and the keys may not contain U+0000, since they are serialized a + zero-terminated c-strings. + + @throw out_of_range.407 if `j.is_number_unsigned() && j.get() > 9223372036854775807` + @throw out_of_range.409 if a key in `j` contains a NULL (U+0000) + @throw type_error.317 if `!j.is_object()` + + @pre The input `j` is required to be an object: `j.is_object() == true`. + + @note Any BSON output created via @ref to_bson can be successfully parsed + by @ref from_bson. - switch (ptr->m_type) - { - case value_t::object: - { - // use unchecked object access - ptr = &ptr->operator[](reference_token); - break; - } + @param[in] j JSON value to serialize + @return BSON serialization as byte vector - case value_t::array: - { - // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and reference_token[0] == '0') - { - JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'")); - } + @complexity Linear in the size of the JSON value @a j. - if (reference_token == "-") - { - // explicitly treat "-" as index beyond the end - ptr = &ptr->operator[](ptr->m_value.array->size()); - } - else - { - // convert array index to number; unchecked access - JSON_TRY - { - ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); - } - JSON_CATCH (std::invalid_argument&) - { - JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } - } - break; - } + @liveexample{The example shows the serialization of a JSON value to a byte + vector in BSON format.,to_bson} - default: - { - JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); - } - } - } + @sa http://bsonspec.org/spec.html + @sa @ref from_bson(detail::input_adapter&&, const bool strict) for the + analogous deserialization + @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the + related UBJSON format + @sa @ref to_cbor(const basic_json&) for the related CBOR format + @sa @ref to_msgpack(const basic_json&) for the related MessagePack format + */ + static std::vector to_bson(const basic_json& j) + { + std::vector result; + to_bson(j, result); + return result; + } - return *ptr; - } + /*! + @brief Serializes the given JSON object `j` to BSON and forwards the + corresponding BSON-representation to the given output_adapter `o`. + @param j The JSON object to convert to BSON. + @param o The output adapter that receives the binary BSON representation. + @pre The input `j` shall be an object: `j.is_object() == true` + @sa @ref to_bson(const basic_json&) + */ + static void to_bson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_bson(j); + } - /*! - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - reference get_checked(pointer ptr) const - { - for (const auto& reference_token : reference_tokens) - { - switch (ptr->m_type) - { - case value_t::object: - { - // note: at performs range check - ptr = &ptr->at(reference_token); - break; - } + /*! + @copydoc to_bson(const basic_json&, detail::output_adapter) + */ + static void to_bson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_bson(j); + } - case value_t::array: - { - if (reference_token == "-") - { - // "-" always fails the range check - JSON_THROW(out_of_range::create(402, "array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range")); - } - // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and reference_token[0] == '0') - { - JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'")); - } + /*! + @brief create a JSON value from an input in CBOR format - // note: at performs range check - JSON_TRY - { - ptr = &ptr->at(static_cast(std::stoi(reference_token))); - } - JSON_CATCH (std::invalid_argument&) - { - JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } - break; - } + Deserializes a given input @a i to a JSON value using the CBOR (Concise + Binary Object Representation) serialization format. - default: - { - JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); - } - } - } + The library maps CBOR types to JSON value types as follows: - return *ptr; - } + CBOR type | JSON value type | first byte + ---------------------- | --------------- | ---------- + Integer | number_unsigned | 0x00..0x17 + Unsigned integer | number_unsigned | 0x18 + Unsigned integer | number_unsigned | 0x19 + Unsigned integer | number_unsigned | 0x1A + Unsigned integer | number_unsigned | 0x1B + Negative integer | number_integer | 0x20..0x37 + Negative integer | number_integer | 0x38 + Negative integer | number_integer | 0x39 + Negative integer | number_integer | 0x3A + Negative integer | number_integer | 0x3B + Byte string | binary | 0x40..0x57 + Byte string | binary | 0x58 + Byte string | binary | 0x59 + Byte string | binary | 0x5A + Byte string | binary | 0x5B + UTF-8 string | string | 0x60..0x77 + UTF-8 string | string | 0x78 + UTF-8 string | string | 0x79 + UTF-8 string | string | 0x7A + UTF-8 string | string | 0x7B + UTF-8 string | string | 0x7F + array | array | 0x80..0x97 + array | array | 0x98 + array | array | 0x99 + array | array | 0x9A + array | array | 0x9B + array | array | 0x9F + map | object | 0xA0..0xB7 + map | object | 0xB8 + map | object | 0xB9 + map | object | 0xBA + map | object | 0xBB + map | object | 0xBF + False | `false` | 0xF4 + True | `true` | 0xF5 + Null | `null` | 0xF6 + Half-Precision Float | number_float | 0xF9 + Single-Precision Float | number_float | 0xFA + Double-Precision Float | number_float | 0xFB - /*! - @brief return a const reference to the pointed to value + @warning The mapping is **incomplete** in the sense that not all CBOR + types can be converted to a JSON value. The following CBOR types + are not supported and will yield parse errors (parse_error.112): + - date/time (0xC0..0xC1) + - bignum (0xC2..0xC3) + - decimal fraction (0xC4) + - bigfloat (0xC5) + - expected conversions (0xD5..0xD7) + - simple values (0xE0..0xF3, 0xF8) + - undefined (0xF7) - @param[in] ptr a JSON value + @warning CBOR allows map keys of any type, whereas JSON only allows + strings as keys in object values. Therefore, CBOR maps with keys + other than UTF-8 strings are rejected (parse_error.113). - @return const reference to the JSON value pointed to by the JSON - pointer + @note Any CBOR output created @ref to_cbor can be successfully parsed by + @ref from_cbor. - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - const_reference get_unchecked(const_pointer ptr) const - { - for (const auto& reference_token : reference_tokens) - { - switch (ptr->m_type) - { - case value_t::object: - { - // use unchecked object access - ptr = &ptr->operator[](reference_token); - break; - } + @param[in] i an input in CBOR format convertible to an input adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + @param[in] tag_handler how to treat CBOR tags (optional, error by default) - case value_t::array: - { - if (reference_token == "-") - { - // "-" cannot be used for const access - JSON_THROW(out_of_range::create(402, "array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range")); - } + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. - // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and reference_token[0] == '0') - { - JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'")); - } + @throw parse_error.110 if the given input ends prematurely or the end of + file was not reached when @a strict was set to true + @throw parse_error.112 if unsupported features from CBOR were + used in the given input @a v or if the input is not valid CBOR + @throw parse_error.113 if a string was expected as map key, but not found - // use unchecked array access - JSON_TRY - { - ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); - } - JSON_CATCH (std::invalid_argument&) - { - JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } - break; - } + @complexity Linear in the size of the input @a i. - default: - { - JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); - } - } - } + @liveexample{The example shows the deserialization of a byte vector in CBOR + format to a JSON value.,from_cbor} - return *ptr; - } + @sa http://cbor.io + @sa @ref to_cbor(const basic_json&) for the analogous serialization + @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for the + related MessagePack format + @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the + related UBJSON format - /*! - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - const_reference get_checked(const_pointer ptr) const - { - for (const auto& reference_token : reference_tokens) - { - switch (ptr->m_type) - { - case value_t::object: - { - // note: at performs range check - ptr = &ptr->at(reference_token); - break; - } + @since version 2.0.9; parameter @a start_index since 2.1.1; changed to + consume input adapters, removed start_index parameter, and added + @a strict parameter since 3.0.0; added @a allow_exceptions parameter + since 3.2.0; added @a tag_handler parameter since 3.9.0. + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } - case value_t::array: - { - if (reference_token == "-") - { - // "-" always fails the range check - JSON_THROW(out_of_range::create(402, "array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range")); - } + /*! + @copydoc from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } - // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and reference_token[0] == '0') - { - JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'")); - } + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler); + } - // note: at performs range check - JSON_TRY - { - ptr = &ptr->at(static_cast(std::stoi(reference_token))); - } - JSON_CATCH (std::invalid_argument&) - { - JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } - break; - } - default: - { - JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); - } - } - } + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } - return *ptr; - } + /*! + @brief create a JSON value from an input in MessagePack format - /*! - @brief split the string input to reference tokens + Deserializes a given input @a i to a JSON value using the MessagePack + serialization format. - @note This function is only called by the json_pointer constructor. - All exceptions below are documented there. + The library maps MessagePack types to JSON value types as follows: - @throw parse_error.107 if the pointer is not empty or begins with '/' - @throw parse_error.108 if character '~' is not followed by '0' or '1' - */ - static std::vector split(const std::string& reference_string) - { - std::vector result; + MessagePack type | JSON value type | first byte + ---------------- | --------------- | ---------- + positive fixint | number_unsigned | 0x00..0x7F + fixmap | object | 0x80..0x8F + fixarray | array | 0x90..0x9F + fixstr | string | 0xA0..0xBF + nil | `null` | 0xC0 + false | `false` | 0xC2 + true | `true` | 0xC3 + float 32 | number_float | 0xCA + float 64 | number_float | 0xCB + uint 8 | number_unsigned | 0xCC + uint 16 | number_unsigned | 0xCD + uint 32 | number_unsigned | 0xCE + uint 64 | number_unsigned | 0xCF + int 8 | number_integer | 0xD0 + int 16 | number_integer | 0xD1 + int 32 | number_integer | 0xD2 + int 64 | number_integer | 0xD3 + str 8 | string | 0xD9 + str 16 | string | 0xDA + str 32 | string | 0xDB + array 16 | array | 0xDC + array 32 | array | 0xDD + map 16 | object | 0xDE + map 32 | object | 0xDF + bin 8 | binary | 0xC4 + bin 16 | binary | 0xC5 + bin 32 | binary | 0xC6 + ext 8 | binary | 0xC7 + ext 16 | binary | 0xC8 + ext 32 | binary | 0xC9 + fixext 1 | binary | 0xD4 + fixext 2 | binary | 0xD5 + fixext 4 | binary | 0xD6 + fixext 8 | binary | 0xD7 + fixext 16 | binary | 0xD8 + negative fixint | number_integer | 0xE0-0xFF - // special case: empty reference string -> no reference tokens - if (reference_string.empty()) - { - return result; - } + @note Any MessagePack output created @ref to_msgpack can be successfully + parsed by @ref from_msgpack. - // check if nonempty reference string begins with slash - if (reference_string[0] != '/') - { - JSON_THROW(parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'")); - } + @param[in] i an input in MessagePack format convertible to an input + adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) - // extract the reference tokens: - // - slash: position of the last read slash (or end of string) - // - start: position after the previous slash - for ( - // search for the first slash after the first character - size_t slash = reference_string.find_first_of('/', 1), - // set the beginning of the first reference token - start = 1; - // we can stop if start == string::npos+1 = 0 - start != 0; - // set the beginning of the next reference token - // (will eventually be 0 if slash == std::string::npos) - start = slash + 1, - // find next slash - slash = reference_string.find_first_of('/', start)) - { - // use the text between the beginning of the reference token - // (start) and the last slash (slash). - auto reference_token = reference_string.substr(start, slash - start); + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. - // check reference tokens are properly escaped - for (size_t pos = reference_token.find_first_of('~'); - pos != std::string::npos; - pos = reference_token.find_first_of('~', pos + 1)) - { - assert(reference_token[pos] == '~'); + @throw parse_error.110 if the given input ends prematurely or the end of + file was not reached when @a strict was set to true + @throw parse_error.112 if unsupported features from MessagePack were + used in the given input @a i or if the input is not valid MessagePack + @throw parse_error.113 if a string was expected as map key, but not found - // ~ must be followed by 0 or 1 - if (pos == reference_token.size() - 1 or - (reference_token[pos + 1] != '0' and - reference_token[pos + 1] != '1')) - { - JSON_THROW(parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'")); - } - } + @complexity Linear in the size of the input @a i. - // finally, store the reference token - unescape(reference_token); - result.push_back(reference_token); - } + @liveexample{The example shows the deserialization of a byte vector in + MessagePack format to a JSON value.,from_msgpack} - return result; - } + @sa http://msgpack.org + @sa @ref to_msgpack(const basic_json&) for the analogous serialization + @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the + related CBOR format + @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for + the related UBJSON format + @sa @ref from_bson(detail::input_adapter&&, const bool, const bool) for + the related BSON format + + @since version 2.0.9; parameter @a start_index since 2.1.1; changed to + consume input adapters, removed start_index parameter, and added + @a strict parameter since 3.0.0; added @a allow_exceptions parameter + since 3.2.0 + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } - /*! - @brief replace all occurrences of a substring by another string + /*! + @copydoc from_msgpack(detail::input_adapter&&, const bool, const bool) + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } - @param[in,out] s the string to manipulate; changed so that all - occurrences of @a f are replaced with @a t - @param[in] f the substring to replace with @a t - @param[in] t the string to replace @a f - @pre The search string @a f must not be empty. **This precondition is - enforced with an assertion.** + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_msgpack(ptr, ptr + len, strict, allow_exceptions); + } - @since version 2.0.0 - */ - static void replace_substring(std::string& s, - const std::string& f, - const std::string& t) - { - assert(not f.empty()); + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } - for ( - size_t pos = s.find(f); // find first occurrence of f - pos != std::string::npos; // make sure f was found - s.replace(pos, f.size(), t), // replace with t - pos = s.find(f, pos + t.size()) // find next occurrence of f - ); - } - /// escape tilde and slash - static std::string escape(std::string s) - { - // escape "~"" to "~0" and "/" to "~1" - replace_substring(s, "~", "~0"); - replace_substring(s, "/", "~1"); - return s; - } + /*! + @brief create a JSON value from an input in UBJSON format + + Deserializes a given input @a i to a JSON value using the UBJSON (Universal + Binary JSON) serialization format. + + The library maps UBJSON types to JSON value types as follows: + + UBJSON type | JSON value type | marker + ----------- | --------------------------------------- | ------ + no-op | *no value, next value is read* | `N` + null | `null` | `Z` + false | `false` | `F` + true | `true` | `T` + float32 | number_float | `d` + float64 | number_float | `D` + uint8 | number_unsigned | `U` + int8 | number_integer | `i` + int16 | number_integer | `I` + int32 | number_integer | `l` + int64 | number_integer | `L` + high-precision number | number_integer, number_unsigned, or number_float - depends on number string | 'H' + string | string | `S` + char | string | `C` + array | array (optimized values are supported) | `[` + object | object (optimized values are supported) | `{` + + @note The mapping is **complete** in the sense that any UBJSON value can + be converted to a JSON value. + + @param[in] i an input in UBJSON format convertible to an input adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.110 if the given input ends prematurely or the end of + file was not reached when @a strict was set to true + @throw parse_error.112 if a parse error occurs + @throw parse_error.113 if a string could not be parsed successfully + + @complexity Linear in the size of the input @a i. - /// unescape tilde and slash - static void unescape(std::string& s) - { - // first transform any occurrence of the sequence '~1' to '/' - replace_substring(s, "~1", "/"); - // then transform any occurrence of the sequence '~0' to '~' - replace_substring(s, "~0", "~"); - } + @liveexample{The example shows the deserialization of a byte vector in + UBJSON format to a JSON value.,from_ubjson} - /*! - @param[in] reference_string the reference string to the current value - @param[in] value the value to consider - @param[in,out] result the result object to insert values to + @sa http://ubjson.org + @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the + analogous serialization + @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the + related CBOR format + @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for + the related MessagePack format + @sa @ref from_bson(detail::input_adapter&&, const bool, const bool) for + the related BSON format - @note Empty objects or arrays are flattened to `null`. - */ - static void flatten(const std::string& reference_string, - const basic_json& value, - basic_json& result) - { - switch (value.m_type) - { - case value_t::array: - { - if (value.m_value.array->empty()) - { - // flatten empty array as null - result[reference_string] = nullptr; - } - else - { - // iterate array and use index as reference string - for (size_t i = 0; i < value.m_value.array->size(); ++i) - { - flatten(reference_string + "/" + std::to_string(i), - value.m_value.array->operator[](i), result); - } - } - break; - } + @since version 3.1.0; added @a allow_exceptions parameter since 3.2.0 + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_ubjson(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } - case value_t::object: - { - if (value.m_value.object->empty()) - { - // flatten empty object as null - result[reference_string] = nullptr; - } - else - { - // iterate object and use keys as reference string - for (const auto& element : *value.m_value.object) - { - flatten(reference_string + "/" + escape(element.first), - element.second, result); - } - } - break; - } + /*! + @copydoc from_ubjson(detail::input_adapter&&, const bool, const bool) + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_ubjson(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } - default: - { - // add primitive value with its reference string - result[reference_string] = value; - break; - } - } - } + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_ubjson(ptr, ptr + len, strict, allow_exceptions); + } - /*! - @param[in] value flattened JSON + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } - @return unflattened JSON - @throw parse_error.109 if array index is not a number - @throw type_error.314 if value is not an object - @throw type_error.315 if object values are not primitive - @throw type_error.313 if value cannot be unflattened - */ - static basic_json unflatten(const basic_json& value) - { - if (not value.is_object()) - { - JSON_THROW(type_error::create(314, "only objects can be unflattened")); - } + /*! + @brief Create a JSON value from an input in BSON format - basic_json result; + Deserializes a given input @a i to a JSON value using the BSON (Binary JSON) + serialization format. - // iterate the JSON object values - for (const auto& element : *value.m_value.object) - { - if (not element.second.is_primitive()) - { - JSON_THROW(type_error::create(315, "values in object must be primitive")); - } + The library maps BSON record types to JSON value types as follows: + + BSON type | BSON marker byte | JSON value type + --------------- | ---------------- | --------------------------- + double | 0x01 | number_float + string | 0x02 | string + document | 0x03 | object + array | 0x04 | array + binary | 0x05 | still unsupported + undefined | 0x06 | still unsupported + ObjectId | 0x07 | still unsupported + boolean | 0x08 | boolean + UTC Date-Time | 0x09 | still unsupported + null | 0x0A | null + Regular Expr. | 0x0B | still unsupported + DB Pointer | 0x0C | still unsupported + JavaScript Code | 0x0D | still unsupported + Symbol | 0x0E | still unsupported + JavaScript Code | 0x0F | still unsupported + int32 | 0x10 | number_integer + Timestamp | 0x11 | still unsupported + 128-bit decimal float | 0x13 | still unsupported + Max Key | 0x7F | still unsupported + Min Key | 0xFF | still unsupported + + @warning The mapping is **incomplete**. The unsupported mappings + are indicated in the table above. + + @param[in] i an input in BSON format convertible to an input adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.114 if an unsupported BSON record type is encountered + + @complexity Linear in the size of the input @a i. - // assign value to reference pointed to by JSON pointer; Note - // that if the JSON pointer is "" (i.e., points to the whole - // value), function get_and_create returns a reference to - // result itself. An assignment will then create a primitive - // value. - json_pointer(element.first).get_and_create(result) = element.second; - } + @liveexample{The example shows the deserialization of a byte vector in + BSON format to a JSON value.,from_bson} - return result; - } + @sa http://bsonspec.org/spec.html + @sa @ref to_bson(const basic_json&) for the analogous serialization + @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the + related CBOR format + @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for + the related MessagePack format + @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the + related UBJSON format + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } - friend bool operator==(json_pointer const& lhs, - json_pointer const& rhs) noexcept - { - return lhs.reference_tokens == rhs.reference_tokens; - } + /*! + @copydoc from_bson(detail::input_adapter&&, const bool, const bool) + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } - friend bool operator!=(json_pointer const& lhs, - json_pointer const& rhs) noexcept - { - return !(lhs == rhs); - } + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_bson(ptr, ptr + len, strict, allow_exceptions); + } - /// the reference tokens - std::vector reference_tokens {}; - }; + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + /// @} ////////////////////////// // JSON Pointer support // @@ -13730,7 +24430,7 @@ class basic_json Uses a JSON pointer to retrieve a reference to the respective JSON value. No bound checking is performed. The function does not change the JSON - value; no `null` values are created. In particular, the the special value + value; no `null` values are created. In particular, the special value `-` yields an exception. @param[in] ptr JSON pointer to the desired element @@ -13776,6 +24476,9 @@ class basic_json pointer @a ptr. As `at` provides checked access (and no elements are implicitly inserted), the index '-' is always invalid. See example below. + @throw out_of_range.403 if the JSON pointer describes a key of an object + which cannot be found. See example below. + @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. See example below. @@ -13816,6 +24519,9 @@ class basic_json pointer @a ptr. As `at` provides checked access (and no elements are implicitly inserted), the index '-' is always invalid. See example below. + @throw out_of_range.403 if the JSON pointer describes a key of an object + which cannot be found. See example below. + @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. See example below. @@ -13883,7 +24589,7 @@ class basic_json @complexity Linear in the size the JSON value. @throw type_error.314 if value is not an object - @throw type_error.315 if object values are not primitve + @throw type_error.315 if object values are not primitive @liveexample{The following code shows how a flattened JSON object is unflattened into the original nested JSON object.,unflatten} @@ -13995,63 +24701,59 @@ class basic_json const auto operation_add = [&result](json_pointer & ptr, basic_json val) { // adding to the root of the target document means replacing it - if (ptr.is_root()) + if (ptr.empty()) { result = val; + return; } - else + + // make sure the top element of the pointer exists + json_pointer top_pointer = ptr.top(); + if (top_pointer != ptr) + { + result.at(top_pointer); + } + + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.back(); + ptr.pop_back(); + basic_json& parent = result[ptr]; + + switch (parent.m_type) { - // make sure the top element of the pointer exists - json_pointer top_pointer = ptr.top(); - if (top_pointer != ptr) + case value_t::null: + case value_t::object: { - result.at(top_pointer); + // use operator[] to add value + parent[last_path] = val; + break; } - // get reference to parent of JSON pointer ptr - const auto last_path = ptr.pop_back(); - basic_json& parent = result[ptr]; - - switch (parent.m_type) + case value_t::array: { - case value_t::null: - case value_t::object: + if (last_path == "-") { - // use operator[] to add value - parent[last_path] = val; - break; + // special case: append to back + parent.push_back(val); } - - case value_t::array: + else { - if (last_path == "-") - { - // special case: append to back - parent.push_back(val); - } - else + const auto idx = json_pointer::array_index(last_path); + if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) { - const auto idx = std::stoi(last_path); - if (static_cast(idx) > parent.size()) - { - // avoid undefined behavior - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); - } - else - { - // default case: insert add offset - parent.insert(parent.begin() + static_cast(idx), val); - } + // avoid undefined behavior + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); } - break; - } - default: - { - // if there exists a parent it cannot be primitive - assert(false); // LCOV_EXCL_LINE + // default case: insert add offset + parent.insert(parent.begin() + static_cast(idx), val); } + break; } + + // if there exists a parent it cannot be primitive + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } }; @@ -14059,7 +24761,8 @@ class basic_json const auto operation_remove = [&result](json_pointer & ptr) { // get reference to parent of JSON pointer ptr - const auto last_path = ptr.pop_back(); + const auto last_path = ptr.back(); + ptr.pop_back(); basic_json& parent = result.at(ptr); // remove child @@ -14067,7 +24770,7 @@ class basic_json { // perform range check auto it = parent.find(last_path); - if (it != parent.end()) + if (JSON_HEDLEY_LIKELY(it != parent.end())) { parent.erase(it); } @@ -14079,12 +24782,12 @@ class basic_json else if (parent.is_array()) { // note erase performs range check - parent.erase(static_cast(std::stoi(last_path))); + parent.erase(json_pointer::array_index(last_path)); } }; // type check: top level value must be an array - if (not json_patch.is_array()) + if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) { JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); } @@ -14095,7 +24798,7 @@ class basic_json // wrapper to get a value for an operation const auto get_value = [&val](const std::string & op, const std::string & member, - bool string_type) -> basic_json& + bool string_type) -> basic_json & { // find value auto it = val.m_value.object->find(member); @@ -14104,13 +24807,13 @@ class basic_json const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; // check if desired value is present - if (it == val.m_value.object->end()) + if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end())) { JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'")); } // check if result is of type string - if (string_type and not it->second.is_string()) + if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) { JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'")); } @@ -14120,14 +24823,14 @@ class basic_json }; // type check: every element of the array must be an object - if (not val.is_object()) + if (JSON_HEDLEY_UNLIKELY(!val.is_object())) { JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); } // collect mandatory members - const std::string op = get_value("op", "op", true); - const std::string path = get_value(op, "path", true); + const auto op = get_value("op", "op", true).template get(); + const auto path = get_value(op, "path", true).template get(); json_pointer ptr(path); switch (get_op(op)) @@ -14153,7 +24856,7 @@ class basic_json case patch_operations::move: { - const std::string from_path = get_value("move", "from", true); + const auto from_path = get_value("move", "from", true).template get(); json_pointer from_ptr(from_path); // the "from" location must exist - use at() @@ -14170,11 +24873,16 @@ class basic_json case patch_operations::copy: { - const std::string from_path = get_value("copy", "from", true);; + const auto from_path = get_value("copy", "from", true).template get(); const json_pointer from_ptr(from_path); // the "from" location must exist - use at() - result[ptr] = result.at(from_ptr); + basic_json v = result.at(from_ptr); + + // The copy is functionally identical to an "add" + // operation at the target location using the value + // specified in the "from" member. + operation_add(ptr, v); break; } @@ -14187,13 +24895,13 @@ class basic_json // the "path" location must exist - use at() success = (result.at(ptr) == get_value("test", "value", false)); } - JSON_CATCH (out_of_range&) + JSON_INTERNAL_CATCH (out_of_range&) { // ignore out of range errors: success remains false } // throw an exception if test fails - if (not success) + if (JSON_HEDLEY_UNLIKELY(!success)) { JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump())); } @@ -14201,7 +24909,7 @@ class basic_json break; } - case patch_operations::invalid: + default: { // op must be "add", "remove", "replace", "move", "copy", or // "test" @@ -14240,13 +24948,14 @@ class basic_json diff for two JSON values.,diff} @sa @ref patch -- apply a JSON patch + @sa @ref merge_patch -- apply a JSON Merge Patch @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) @since version 2.0.0 */ - static basic_json diff(const basic_json& source, - const basic_json& target, + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json diff(const basic_json& source, const basic_json& target, const std::string& path = "") { // the patch @@ -14263,138 +24972,208 @@ class basic_json // different types: replace value result.push_back( { - {"op", "replace"}, - {"path", path}, - {"value", target} + {"op", "replace"}, {"path", path}, {"value", target} }); + return result; } - else + + switch (source.type()) { - switch (source.type()) + case value_t::array: { - case value_t::array: + // first pass: traverse common elements + std::size_t i = 0; + while (i < source.size() && i < target.size()) { - // first pass: traverse common elements - size_t i = 0; - while (i < source.size() and i < target.size()) + // recursive call to compare array values at index i + auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + ++i; + } + + // i now reached the end of at least one array + // in a second pass, traverse the remaining elements + + // remove my remaining elements + const auto end_index = static_cast(result.size()); + while (i < source.size()) + { + // add operations in reverse order to avoid invalid + // indices + result.insert(result.begin() + end_index, object( { - // recursive call to compare array values at index i - auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); - result.insert(result.end(), temp_diff.begin(), temp_diff.end()); - ++i; - } + {"op", "remove"}, + {"path", path + "/" + std::to_string(i)} + })); + ++i; + } + + // add other remaining elements + while (i < target.size()) + { + result.push_back( + { + {"op", "add"}, + {"path", path + "/-"}, + {"value", target[i]} + }); + ++i; + } + + break; + } - // i now reached the end of at least one array - // in a second pass, traverse the remaining elements + case value_t::object: + { + // first pass: traverse this object's elements + for (auto it = source.cbegin(); it != source.cend(); ++it) + { + // escape the key name to be used in a JSON patch + const auto key = json_pointer::escape(it.key()); - // remove my remaining elements - const auto end_index = static_cast(result.size()); - while (i < source.size()) + if (target.find(it.key()) != target.end()) + { + // recursive call to compare object values at key it + auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + } + else { - // add operations in reverse order to avoid invalid - // indices - result.insert(result.begin() + end_index, object( + // found a key that is not in o -> remove it + result.push_back(object( { - {"op", "remove"}, - {"path", path + "/" + std::to_string(i)} + {"op", "remove"}, {"path", path + "/" + key} })); - ++i; } + } - // add other remaining elements - while (i < target.size()) + // second pass: traverse other object's elements + for (auto it = target.cbegin(); it != target.cend(); ++it) + { + if (source.find(it.key()) == source.end()) { + // found a key that is not in this -> add it + const auto key = json_pointer::escape(it.key()); result.push_back( { - {"op", "add"}, - {"path", path + "/" + std::to_string(i)}, - {"value", target[i]} + {"op", "add"}, {"path", path + "/" + key}, + {"value", it.value()} }); - ++i; } - - break; } - case value_t::object: + break; + } + + default: + { + // both primitive type: replace value + result.push_back( { - // first pass: traverse this object's elements - for (auto it = source.begin(); it != source.end(); ++it) - { - // escape the key name to be used in a JSON patch - const auto key = json_pointer::escape(it.key()); + {"op", "replace"}, {"path", path}, {"value", target} + }); + break; + } + } - if (target.find(it.key()) != target.end()) - { - // recursive call to compare object values at key it - auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key); - result.insert(result.end(), temp_diff.begin(), temp_diff.end()); - } - else - { - // found a key that is not in o -> remove it - result.push_back(object( - { - {"op", "remove"}, - {"path", path + "/" + key} - })); - } - } + return result; + } - // second pass: traverse other object's elements - for (auto it = target.begin(); it != target.end(); ++it) - { - if (source.find(it.key()) == source.end()) - { - // found a key that is not in this -> add it - const auto key = json_pointer::escape(it.key()); - result.push_back( - { - {"op", "add"}, - {"path", path + "/" + key}, - {"value", it.value()} - }); - } - } + /// @} - break; - } + //////////////////////////////// + // JSON Merge Patch functions // + //////////////////////////////// - default: + /// @name JSON Merge Patch functions + /// @{ + + /*! + @brief applies a JSON Merge Patch + + The merge patch format is primarily intended for use with the HTTP PATCH + method as a means of describing a set of modifications to a target + resource's content. This function applies a merge patch to the current + JSON value. + + The function implements the following algorithm from Section 2 of + [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396): + + ``` + define MergePatch(Target, Patch): + if Patch is an Object: + if Target is not an Object: + Target = {} // Ignore the contents and set it to an empty Object + for each Name/Value pair in Patch: + if Value is null: + if Name exists in Target: + remove the Name/Value pair from Target + else: + Target[Name] = MergePatch(Target[Name], Value) + return Target + else: + return Patch + ``` + + Thereby, `Target` is the current object; that is, the patch is applied to + the current value. + + @param[in] apply_patch the patch to apply + + @complexity Linear in the lengths of @a patch. + + @liveexample{The following code shows how a JSON Merge Patch is applied to + a JSON document.,merge_patch} + + @sa @ref patch -- apply a JSON patch + @sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396) + + @since version 3.0.0 + */ + void merge_patch(const basic_json& apply_patch) + { + if (apply_patch.is_object()) + { + if (!is_object()) + { + *this = object(); + } + for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it) + { + if (it.value().is_null()) { - // both primitive type: replace value - result.push_back( - { - {"op", "replace"}, - {"path", path}, - {"value", target} - }); - break; + erase(it.key()); + } + else + { + operator[](it.key()).merge_patch(it.value()); } } } - - return result; + else + { + *this = apply_patch; + } } /// @} }; -///////////// -// presets // -///////////// - /*! -@brief default JSON class +@brief user-defined to_string function for JSON values -This type is the default specialization of the @ref basic_json class which -uses the standard template types. +This function implements a user-defined to_string for JSON objects. -@since version 1.0.0 +@param[in] j a JSON object +@return a std::string object */ -using json = basic_json<>; -} // namespace nlohmann +NLOHMANN_BASIC_JSON_TPL_DECLARATION +std::string to_string(const NLOHMANN_BASIC_JSON_TPL& j) +{ + return j.dump(); +} +} // namespace nlohmann /////////////////////// // nonmember support // @@ -14403,20 +25182,6 @@ using json = basic_json<>; // specialization of std::swap, and std::hash namespace std { -/*! -@brief exchanges the values of two JSON objects - -@since version 1.0.0 -*/ -template<> -inline void swap(nlohmann::json& j1, - nlohmann::json& j2) noexcept( - is_nothrow_move_constructible::value and - is_nothrow_move_assignable::value - ) -{ - j1.swap(j2); -} /// hash value for JSON objects template<> @@ -14429,14 +25194,14 @@ struct hash */ std::size_t operator()(const nlohmann::json& j) const { - // a naive hashing via the string representation - const auto& h = hash(); - return h(j.dump()); + return nlohmann::detail::hash(j); } }; /// specialization for std::less -template <> +/// @note: do not remove the space after '<', +/// see https://github.com/nlohmann/json/pull/679 +template<> struct less<::nlohmann::detail::value_t> { /*! @@ -14450,6 +25215,25 @@ struct less<::nlohmann::detail::value_t> } }; +// C++20 prohibit function specialization in the std namespace. +#ifndef JSON_HAS_CPP_20 + +/*! +@brief exchanges the values of two JSON objects + +@since version 1.0.0 +*/ +template<> +inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( + is_nothrow_move_constructible::value&& + is_nothrow_move_assignable::value + ) +{ + j1.swap(j2); +} + +#endif + } // namespace std /*! @@ -14465,6 +25249,7 @@ if no parse error occurred. @since version 1.0.0 */ +JSON_HEDLEY_NON_NULL(1) inline nlohmann::json operator "" _json(const char* s, std::size_t n) { return nlohmann::json::parse(s, s + n); @@ -14483,11 +25268,15 @@ object if no parse error occurred. @since version 2.0.0 */ +JSON_HEDLEY_NON_NULL(1) inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) { return nlohmann::json::json_pointer(std::string(s, n)); } +// #include + + // restore GCC/clang diagnostic settings #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) #pragma GCC diagnostic pop @@ -14497,11 +25286,162 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std #endif // clean up +#undef JSON_ASSERT +#undef JSON_INTERNAL_CATCH #undef JSON_CATCH #undef JSON_THROW #undef JSON_TRY -#undef JSON_LIKELY -#undef JSON_UNLIKELY -#undef JSON_DEPRECATED - -#endif +#undef JSON_HAS_CPP_14 +#undef JSON_HAS_CPP_17 +#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION +#undef NLOHMANN_BASIC_JSON_TPL +#undef JSON_EXPLICIT + +// #include +#undef JSON_HEDLEY_ALWAYS_INLINE +#undef JSON_HEDLEY_ARM_VERSION +#undef JSON_HEDLEY_ARM_VERSION_CHECK +#undef JSON_HEDLEY_ARRAY_PARAM +#undef JSON_HEDLEY_ASSUME +#undef JSON_HEDLEY_BEGIN_C_DECLS +#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#undef JSON_HEDLEY_CLANG_HAS_FEATURE +#undef JSON_HEDLEY_CLANG_HAS_WARNING +#undef JSON_HEDLEY_COMPCERT_VERSION +#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#undef JSON_HEDLEY_CONCAT +#undef JSON_HEDLEY_CONCAT3 +#undef JSON_HEDLEY_CONCAT3_EX +#undef JSON_HEDLEY_CONCAT_EX +#undef JSON_HEDLEY_CONST +#undef JSON_HEDLEY_CONSTEXPR +#undef JSON_HEDLEY_CONST_CAST +#undef JSON_HEDLEY_CPP_CAST +#undef JSON_HEDLEY_CRAY_VERSION +#undef JSON_HEDLEY_CRAY_VERSION_CHECK +#undef JSON_HEDLEY_C_DECL +#undef JSON_HEDLEY_DEPRECATED +#undef JSON_HEDLEY_DEPRECATED_FOR +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#undef JSON_HEDLEY_DIAGNOSTIC_POP +#undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#undef JSON_HEDLEY_DMC_VERSION +#undef JSON_HEDLEY_DMC_VERSION_CHECK +#undef JSON_HEDLEY_EMPTY_BASES +#undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#undef JSON_HEDLEY_END_C_DECLS +#undef JSON_HEDLEY_FLAGS +#undef JSON_HEDLEY_FLAGS_CAST +#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_BUILTIN +#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_EXTENSION +#undef JSON_HEDLEY_GCC_HAS_FEATURE +#undef JSON_HEDLEY_GCC_HAS_WARNING +#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#undef JSON_HEDLEY_GCC_VERSION +#undef JSON_HEDLEY_GCC_VERSION_CHECK +#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#undef JSON_HEDLEY_GNUC_HAS_FEATURE +#undef JSON_HEDLEY_GNUC_HAS_WARNING +#undef JSON_HEDLEY_GNUC_VERSION +#undef JSON_HEDLEY_GNUC_VERSION_CHECK +#undef JSON_HEDLEY_HAS_ATTRIBUTE +#undef JSON_HEDLEY_HAS_BUILTIN +#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_HAS_EXTENSION +#undef JSON_HEDLEY_HAS_FEATURE +#undef JSON_HEDLEY_HAS_WARNING +#undef JSON_HEDLEY_IAR_VERSION +#undef JSON_HEDLEY_IAR_VERSION_CHECK +#undef JSON_HEDLEY_IBM_VERSION +#undef JSON_HEDLEY_IBM_VERSION_CHECK +#undef JSON_HEDLEY_IMPORT +#undef JSON_HEDLEY_INLINE +#undef JSON_HEDLEY_INTEL_VERSION +#undef JSON_HEDLEY_INTEL_VERSION_CHECK +#undef JSON_HEDLEY_IS_CONSTANT +#undef JSON_HEDLEY_IS_CONSTEXPR_ +#undef JSON_HEDLEY_LIKELY +#undef JSON_HEDLEY_MALLOC +#undef JSON_HEDLEY_MESSAGE +#undef JSON_HEDLEY_MSVC_VERSION +#undef JSON_HEDLEY_MSVC_VERSION_CHECK +#undef JSON_HEDLEY_NEVER_INLINE +#undef JSON_HEDLEY_NON_NULL +#undef JSON_HEDLEY_NO_ESCAPE +#undef JSON_HEDLEY_NO_RETURN +#undef JSON_HEDLEY_NO_THROW +#undef JSON_HEDLEY_NULL +#undef JSON_HEDLEY_PELLES_VERSION +#undef JSON_HEDLEY_PELLES_VERSION_CHECK +#undef JSON_HEDLEY_PGI_VERSION +#undef JSON_HEDLEY_PGI_VERSION_CHECK +#undef JSON_HEDLEY_PREDICT +#undef JSON_HEDLEY_PRINTF_FORMAT +#undef JSON_HEDLEY_PRIVATE +#undef JSON_HEDLEY_PUBLIC +#undef JSON_HEDLEY_PURE +#undef JSON_HEDLEY_REINTERPRET_CAST +#undef JSON_HEDLEY_REQUIRE +#undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#undef JSON_HEDLEY_REQUIRE_MSG +#undef JSON_HEDLEY_RESTRICT +#undef JSON_HEDLEY_RETURNS_NON_NULL +#undef JSON_HEDLEY_SENTINEL +#undef JSON_HEDLEY_STATIC_ASSERT +#undef JSON_HEDLEY_STATIC_CAST +#undef JSON_HEDLEY_STRINGIFY +#undef JSON_HEDLEY_STRINGIFY_EX +#undef JSON_HEDLEY_SUNPRO_VERSION +#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#undef JSON_HEDLEY_TINYC_VERSION +#undef JSON_HEDLEY_TINYC_VERSION_CHECK +#undef JSON_HEDLEY_TI_ARMCL_VERSION +#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL2000_VERSION +#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL430_VERSION +#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL6X_VERSION +#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL7X_VERSION +#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#undef JSON_HEDLEY_TI_CLPRU_VERSION +#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#undef JSON_HEDLEY_TI_VERSION +#undef JSON_HEDLEY_TI_VERSION_CHECK +#undef JSON_HEDLEY_UNAVAILABLE +#undef JSON_HEDLEY_UNLIKELY +#undef JSON_HEDLEY_UNPREDICTABLE +#undef JSON_HEDLEY_UNREACHABLE +#undef JSON_HEDLEY_UNREACHABLE_RETURN +#undef JSON_HEDLEY_VERSION +#undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#undef JSON_HEDLEY_VERSION_DECODE_MINOR +#undef JSON_HEDLEY_VERSION_DECODE_REVISION +#undef JSON_HEDLEY_VERSION_ENCODE +#undef JSON_HEDLEY_WARNING +#undef JSON_HEDLEY_WARN_UNUSED_RESULT +#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#undef JSON_HEDLEY_FALL_THROUGH + + + +#endif // INCLUDE_NLOHMANN_JSON_HPP_ \ No newline at end of file diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 942d5d3026..f05ab8df15 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -179,11 +179,12 @@ if (BUILD_CPP_IF) else() set (LIB_DEEPMD_OP_DEVICE "deepmd_op") endif() - if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 4.9) + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 4.8) set (LIB_DEEPMD_NATIVE "deepmd_native_md") - set (LIB_DEEPMD_IPI "deepmd_ipi") + set (LIB_DEEPMD_IPI "deepmd_ipi") + set (LIB_DEEPMD_GROMACS "deepmd_gromacs") else () - message (STATUS "Your gcc/g++ version is ${CMAKE_CXX_COMPILER_VERSION}, so native MD and ipi are disabled. To enable them, use gcc/g++ >= 4.9.") + message (STATUS "Your gcc/g++ version is ${CMAKE_CXX_COMPILER_VERSION}, so native MD, ipi and gromacs plugin are disabled. To enable them, use gcc/g++ >= 4.8.") endif () endif (BUILD_CPP_IF) @@ -196,9 +197,10 @@ endif (BUILD_PY_IF) if (BUILD_CPP_IF) add_subdirectory (api_cc/) add_subdirectory (lmp/) - if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.9) + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.8) # add_subdirectory (md/) add_subdirectory (ipi/) + add_subdirectory (gmx/) endif () endif (BUILD_CPP_IF) diff --git a/source/gmx/CMakeLists.txt b/source/gmx/CMakeLists.txt new file mode 100644 index 0000000000..c0683b01ba --- /dev/null +++ b/source/gmx/CMakeLists.txt @@ -0,0 +1,48 @@ +message(STATUS "Build GROMACS plugin") + +file(GLOB PATCH_VERSIONS patches/*) +FOREACH(PATCH_VERSION ${PATCH_VERSIONS}) + configure_file( + "${PATCH_VERSION}/CMakeLists.txt.patch.in" + "${PATCH_VERSION}/CMakeLists.txt.patch" + @ONLY + ) +ENDFOREACH(PATCH_VERSION) + +set(libgmxname ${LIB_DEEPMD_GROMACS}) +file(GLOB LIB_SRC src/*.cpp) +file(GLOB INC_SRC include/*.h) + +add_library(${libgmxname} SHARED ${LIB_SRC}) +target_link_libraries(${libgmxname} ${LIB_DEEPMD_CC} ${LIB_DEEPMD} ${TensorFlow_LIBRARY} ${TensorFlowFramework_LIBRARY}) +target_include_directories(${libgmxname} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../api_cc/include) +target_include_directories(${libgmxname} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_include_directories(${libgmxname} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/) + +set_target_properties( + ${libgmxname} + PROPERTIES + INSTALL_RPATH "$ORIGIN;${TensorFlow_LIBRARY_PATH}" +) + +install ( + FILES dp_gmx_patch + DESTINATION bin + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE +) + +install ( + DIRECTORY patches/ + DESTINATION share/deepmd_gromacs_patches + PATTERN */CMakeLists.txt.patch.in EXCLUDE +) + +install ( + FILES ${INC_SRC} + DESTINATION include/deepmd +) + +install( + TARGETS ${libgmxname} + DESTINATION lib/ +) \ No newline at end of file diff --git a/source/gmx/dp_gmx_patch b/source/gmx/dp_gmx_patch new file mode 100644 index 0000000000..c9259b7ec7 --- /dev/null +++ b/source/gmx/dp_gmx_patch @@ -0,0 +1,133 @@ +#!/bin/bash +DEEPMD_PATCH_SCRIPT_DIR=$(dirname $(readlink -f "$0")) +DEEPMD_PATCH_ROOT=${DEEPMD_PATCH_SCRIPT_DIR}/../share/deepmd_gromacs_patches +VERSION="NULL" +PATCH_FILES=("CMakeLists.txt" "src/gromacs/mdlib/forcerec.cpp" "src/gromacs/mdlib/sim_util.cpp" "src/gromacs/mdlib/forcerec.h") + +MANUAL="\ +usage: this is a deepmd-kit patch program for gromacs + +arguments: + -h print this help and exit + -d gromacs root + -p patch mode + -r revert mode + -v gromacs version (only support 2020.2 now) +" + +check () { + echo "- Checking GROMACS root: $1" + for ((i=0;i<${#PATCH_FILES[*]};i++)) + do + file=${PATCH_FILES[$i]} + if [ ! -e $1/${file} ]; then + echo "- $1/${file} not exist" + exit 1 + fi + done + echo "- Done" +} + +check_unpatched () { + for ((i=0;i<${#PATCH_FILES[*]};i++)) + do + file=${PATCH_FILES[$i]} + if [ -e ${GMX_ROOT}/${file}.predp ]; then + echo "- ERROR: This is a patched gromacs code, please revert first" + exit 1 + fi + done +} + +check_patched () { + for ((i=0;i<${#INSTALL_FILES[*]};i++)) + do + file=${INSTALL_FILES[$i]} + if [ ! -e ${GMX_ROOT}/${file} ]; then + echo "- WARNING: ${GMX_ROOT}/${file} not found" + fi + done + + for ((i=0;i<${#PATCH_FILES[*]};i++)) + do + file=${PATCH_FILES[$i]} + if [ ! -e ${GMX_ROOT}/${file}.predp ]; then + echo "- ERROR: backup file ${GMX_ROOT}/${file}.predp is lost" + exit 1 + fi + done +} + +dp_gmx_patch () { + echo "- Staring DeepMD patch program to GROMACS ${VERSION}" + echo "- Mode: patch" + if [ ! -d $1 ]; then + echo "- ERROR: invalid gromacs root: $1" + exit 1 + else + check_unpatched $1 + check $1 + for ((i=0;i<${#PATCH_FILES[*]};i++)) + do + file=${PATCH_FILES[$i]} + patch -b -u ${GMX_ROOT}/${file} --suffix=.predp < ${DEEPMD_PATCH_ROOT}/${file}.patch > /dev/null + echo "- Installing ${GMX_ROOT}/${file}" + echo "- Backing up ${GMX_ROOT}/${file}.predp" + done + + for ((i=0;i<${#INSTALL_FILES[*]};i++)) + do + file=${INSTALL_FILES[$i]} + cp ${DEEPMD_PATCH_ROOT}/${file} ${GMX_ROOT}/${file} + echo "- Installing ${GMX_ROOT}/${file}" + done + echo "- Finished" + fi +} + +dp_gmx_revert () { + echo "- Staring DeepMD patch program to GROMACS ${VERSION}" + echo "- Mode: revert" + check_patched $1 + if [ ! -d $1 ]; then + echo "- ERROR: invalid gromacs root: $1" + exit 1 + else + for ((i=0;i<${#INSTALL_FILES[*]};i++)) + do + file=${INSTALL_FILES[$i]} + rm ${GMX_ROOT}/${file} + echo "- Removing ${GMX_ROOT}/${file}" + done + + for ((i=0;i<${#PATCH_FILES[*]};i++)) + do + file=${PATCH_FILES[$i]} + patch ${GMX_ROOT}/${file} -R -u < ${DEEPMD_PATCH_ROOT}/${file}.patch > /dev/null + rm ${GMX_ROOT}/${file}.predp + echo "- Restoring from ${GMX_ROOT}/${file}.predp" + done + echo "- Finished" + fi +} + +check_version () { + case $1 in + "NULL") echo "- ERROR: Please specify a version from -v option" && exit 1;; + "2020.2") echo "- GROMACS version: 2020.2" ;; + *) echo "- ERROR: Invalid version" && exit 1 ;; + esac +} + + +while getopts "hprd:v:" opt +do + case ${opt} in + h) echo "${MANUAL}" && exit 0 ;; + d) GMX_ROOT=${OPTARG};; + v) VERSION=${OPTARG} && DEEPMD_PATCH_ROOT=${DEEPMD_PATCH_ROOT}/${VERSION} ;; + p) check_version ${VERSION} && dp_gmx_patch ${GMX_ROOT} ;; + r) check_version ${VERSION} && dp_gmx_revert ${GMX_ROOT} ;; + *) echo "- ERROR: Invaild option ${opt}" && exit 1 ;; + esac +done diff --git a/source/gmx/include/gmx_plugin.h b/source/gmx/include/gmx_plugin.h new file mode 100644 index 0000000000..78786fc37f --- /dev/null +++ b/source/gmx/include/gmx_plugin.h @@ -0,0 +1,28 @@ +#ifndef _GMX_PLUGIN_H_ +#define _GMX_PLUGIN_H_ +#include "DeepPot.h" + +namespace deepmd +{ + +class DeepmdPlugin +{ + public: + DeepmdPlugin(); + DeepmdPlugin(char*); + ~DeepmdPlugin(); + void init_from_json(char*); + deepmd::DeepPot* nnp; + std::vector dtype; + std::vector dindex; + bool pbc; + float lmd; + int natom; +}; + +} + +const float c_dp2gmx = 0.1; +const float e_dp2gmx = 96.48533132; +const float f_dp2gmx = 964.8533132; +#endif \ No newline at end of file diff --git a/source/gmx/patches/2020.2/CMakeLists.txt.patch.in b/source/gmx/patches/2020.2/CMakeLists.txt.patch.in new file mode 100644 index 0000000000..fa027f156e --- /dev/null +++ b/source/gmx/patches/2020.2/CMakeLists.txt.patch.in @@ -0,0 +1,29 @@ +--- gromacs-2020.2/CMakeLists.txt 2020-04-30 16:33:43.000000000 +0000 ++++ gromacs-2020.2-deepmd/CMakeLists.txt 2021-09-20 08:07:34.000000000 +0000 +@@ -134,6 +134,26 @@ + # (i.e., something that is exposed in installed headers). + set(GMX_PUBLIC_LIBRARIES "") + ++# DeepMD ++message(STATUS "Compling with DeepMD...") ++add_definitions(-w) # close warning ++# define deepmd and tensorflow root ++if (NOT DEFINED GMX_DEEPMD_ROOT) ++ set (GMX_DEEPMD_ROOT @CMAKE_INSTALL_PREFIX@) ++endif() ++if (NOT DEFINED GMX_TENSORFLOW_ROOT) ++ set (GMX_TENSORFLOW_ROOT @TENSORFLOW_ROOT@) ++endif() ++include_directories(${GMX_DEEPMD_ROOT}/include) ++include_directories(${GMX_TENSORFLOW_ROOT}/include) ++link_directories(${GMX_DEEPMD_ROOT}/lib) ++link_directories(${GMX_TENSORFLOW_ROOT}/lib) ++# define high precision, only support high precision now ++add_definitions("-D HIGH_PREC") ++# add link libraries ++list (APPEND GMX_PUBLIC_LIBRARIES deepmd_gromacs) ++# DeepMD ++ + ######################################################################## + # Check and warn if cache generated on a different host is being reused + ######################################################################## diff --git a/source/gmx/patches/2020.2/src/gromacs/mdlib/forcerec.cpp.patch b/source/gmx/patches/2020.2/src/gromacs/mdlib/forcerec.cpp.patch new file mode 100644 index 0000000000..cee4604797 --- /dev/null +++ b/source/gmx/patches/2020.2/src/gromacs/mdlib/forcerec.cpp.patch @@ -0,0 +1,33 @@ +--- /public/wangyingze/soft/gromacs-2020.2/src/gromacs/mdlib/forcerec.cpp 2020-04-30 16:33:43.000000000 +0000 ++++ /public/wangyingze/soft/gromacs-2020.2-deepmd/src/gromacs/mdlib/forcerec.cpp 2021-09-19 07:45:19.000000000 +0000 +@@ -98,6 +98,11 @@ + #include "gromacs/utility/smalloc.h" + #include "gromacs/utility/strconvert.h" + ++// Deepmd ++// #include "deepmd/gmx_plugin.h" ++deepmd::DeepmdPlugin* deepmdPlugin; ++bool useDeepmd; ++ + /*! \brief environment variable to enable GPU P2P communication */ + static const bool c_enableGpuPmePpComms = + (getenv("GMX_GPU_PME_PP_COMMS") != nullptr) && GMX_THREAD_MPI && (GMX_GPU == GMX_GPU_CUDA); +@@ -1491,6 +1496,18 @@ + { + fr->pmePpCommGpu = std::make_unique(cr->mpi_comm_mysim, cr->dd->pme_nodeid); + } ++ // Deepmd ++ deepmdPlugin = new deepmd::DeepmdPlugin; ++ char* json_file = getenv("GMX_DEEPMD_INPUT_JSON"); ++ if (json_file == NULL) ++ { ++ useDeepmd = false; ++ } ++ else ++ { ++ deepmdPlugin->init_from_json(json_file); ++ useDeepmd = true; ++ } + } + + t_forcerec::t_forcerec() = default; diff --git a/source/gmx/patches/2020.2/src/gromacs/mdlib/forcerec.h.patch b/source/gmx/patches/2020.2/src/gromacs/mdlib/forcerec.h.patch new file mode 100644 index 0000000000..03661975af --- /dev/null +++ b/source/gmx/patches/2020.2/src/gromacs/mdlib/forcerec.h.patch @@ -0,0 +1,13 @@ +--- /public/wangyingze/soft/gromacs-2020.2/src/gromacs/mdlib/forcerec.h 2020-04-30 16:33:43.000000000 +0000 ++++ /public/wangyingze/soft/gromacs-2020.2-deepmd/src/gromacs/mdlib/forcerec.h 2021-09-19 07:32:41.000000000 +0000 +@@ -44,6 +44,10 @@ + #include "gromacs/timing/wallcycle.h" + #include "gromacs/utility/arrayref.h" + ++#include "deepmd/gmx_plugin.h" ++extern deepmd::DeepmdPlugin* deepmdPlugin; ++extern bool useDeepmd; ++ + struct gmx_device_info_t; + struct gmx_hw_info_t; + struct t_commrec; diff --git a/source/gmx/patches/2020.2/src/gromacs/mdlib/sim_util.cpp.patch b/source/gmx/patches/2020.2/src/gromacs/mdlib/sim_util.cpp.patch new file mode 100644 index 0000000000..bfbb82b1fb --- /dev/null +++ b/source/gmx/patches/2020.2/src/gromacs/mdlib/sim_util.cpp.patch @@ -0,0 +1,103 @@ +--- /public/wangyingze/soft/gromacs-2020.2/src/gromacs/mdlib/sim_util.cpp 2020-04-30 16:33:43.000000000 +0000 ++++ /public/wangyingze/soft/gromacs-2020.2-deepmd/src/gromacs/mdlib/sim_util.cpp 2021-09-19 07:47:12.000000000 +0000 +@@ -34,6 +34,8 @@ + * To help us fund GROMACS development, we humbly ask that you cite + * the research papers on the package. Check out http://www.gromacs.org. + */ ++ ++#include + #include "gmxpre.h" + + #include "config.h" +@@ -114,6 +116,8 @@ + #include "gromacs/utility/strconvert.h" + #include "gromacs/utility/sysinfo.h" + ++#include "deepmd/gmx_plugin.h" ++ + using gmx::AtomLocality; + using gmx::DomainLifetimeWorkload; + using gmx::ForceOutputs; +@@ -1838,6 +1842,64 @@ + simulationWork.useGpuPmePpCommunication, false, wcycle); + } + ++ /* DeepMD */ ++ double dener; ++ std::vector dforce; ++ if (useDeepmd) ++ { ++ if (DIM != 3) ++ { ++ gmx_fatal(FARGS, "DeepMD does not support DIM < 3."); ++ } ++ else ++ { ++ std::vector dcoord; ++ std::vector dvirial; ++ dcoord.resize(deepmdPlugin->natom * DIM); ++ dforce.resize(deepmdPlugin->natom * DIM); ++ dvirial.resize(9); ++ /* init coord */ ++ for (int i = 0; i < deepmdPlugin->natom; i++) ++ { ++ for (int j = 0; j < DIM; j++) ++ { ++ dcoord[i * DIM + j] = as_rvec_array(x.unpaddedArrayRef().data())[deepmdPlugin->dindex[i]][j] / c_dp2gmx; ++ // std::cout << dcoord[i * DIM + j] << " "; ++ } ++ // std::cout << std::endl; ++ } ++ /* init coord */ ++ ++ /* init box */ ++ std::vector dbox; ++ if (deepmdPlugin->pbc) ++ { ++ dbox.resize(9); ++ for (int i = 0; i < DIM; i++) ++ { ++ for (int j = 0; j < DIM; j++) ++ { ++ dbox[i * DIM + j] = box[i][j] / c_dp2gmx; ++ } ++ } ++ } ++ else ++ { ++ dbox = {}; ++ } ++ /* init box */ ++ deepmdPlugin->nnp->compute(dener, dforce, dvirial, dcoord, deepmdPlugin->dtype, dbox); ++ ++ for (int i = 0; i < deepmdPlugin->natom; i++) ++ { ++ for (int j = 0; j < DIM; j++) ++ { ++ as_rvec_array(forceOut.forceWithShiftForces().force().data())[deepmdPlugin->dindex[i]][j] += dforce[i * DIM + j] * f_dp2gmx * deepmdPlugin->lmd; ++ } ++ } ++ } ++ } ++ + if (stepWork.computeForces) + { + post_process_forces(cr, step, nrnb, wcycle, top, box, as_rvec_array(x.unpaddedArrayRef().data()), +@@ -1848,13 +1910,16 @@ + { + /* Sum the potential energy terms from group contributions */ + sum_epot(&(enerd->grpp), enerd->term); ++ if (useDeepmd) ++ { ++ enerd->term[F_EPOT] += dener * e_dp2gmx * deepmdPlugin->lmd; ++ } + + if (!EI_TPI(inputrec->eI)) + { + checkPotentialEnergyValidity(step, *enerd, *inputrec); + } + } +- + /* In case we don't have constraints and are using GPUs, the next balancing + * region starts here. + * Some "special" work at the end of do_force_cuts?, such as vsite spread, diff --git a/source/gmx/src/gmx_plugin.cpp b/source/gmx/src/gmx_plugin.cpp new file mode 100644 index 0000000000..db68d6e55a --- /dev/null +++ b/source/gmx/src/gmx_plugin.cpp @@ -0,0 +1,128 @@ +#include "gmx_plugin.h" +#include "json.hpp" +#include +#include + +using namespace deepmd; + +DeepmdPlugin::DeepmdPlugin () +{ + nnp = new deepmd::DeepPot; +} + +DeepmdPlugin::DeepmdPlugin (char* json_file) +{ + nnp = new deepmd::DeepPot; + DeepmdPlugin::init_from_json(json_file); +} + +DeepmdPlugin::~DeepmdPlugin() +{ + delete nnp; +} + +void DeepmdPlugin::init_from_json(char* json_file) +{ + std::ifstream fp (json_file); + if (fp.is_open()) + { + std::cout << "Init deepmd plugin from: " << json_file << std::endl; + nlohmann::json jdata; + fp >> jdata; + std::string graph_file = jdata["graph_file"]; + std::string type_file = jdata["type_file"]; + std::string index_file = jdata["index_file"]; + + /* lambda */ + if (jdata.contains("lambda")) + { + DeepmdPlugin::lmd = jdata["lambda"]; + } + else + { + DeepmdPlugin::lmd = 1.0; + } + std::cout << "Setting lambda: " << DeepmdPlugin::lmd << std::endl; + /* lambda */ + + /* pbc */ + if (jdata.contains("pbc")) + { + DeepmdPlugin::pbc = jdata["pbc"]; + } + else + { + DeepmdPlugin::pbc = true; + } + std::cout << "Setting pbc: " << DeepmdPlugin::pbc << std::endl; + /* pbc */ + + std::string line; + std::istringstream iss; + int val; + + /* read type file */ + std::ifstream ft(type_file); + if (ft.is_open()) + { + getline(ft, line); + iss.clear(); + iss.str(line); + while (iss >> val) + { + DeepmdPlugin::dtype.push_back(val); + } + DeepmdPlugin::natom = DeepmdPlugin::dtype.size(); + std::cout << "Number of atoms: " << DeepmdPlugin::natom << std::endl; + } + else + { + std::cerr << "Not found type file: " << type_file << std::endl; + exit(1); + } + /* read type file */ + + /* read index file */ + std::ifstream fi(index_file); + if (fi.is_open()) + { + getline(fi, line); + iss.clear(); + iss.str(line); + while (iss >> val) + { + DeepmdPlugin::dindex.push_back(val); + } + if (DeepmdPlugin::dindex.size() != DeepmdPlugin::natom) + { + std::cerr << "Number of atoms in index file (" << DeepmdPlugin::dindex.size() << ") does not match type file (" << DeepmdPlugin::natom << ")!" << std::endl; + exit(1); + } + } + else + { + std::cerr << "Not found index file: " << index_file << std::endl; + exit(1); + } + /* read index file */ + + /* init model */ + std::cout << "Begin Init Model: " << graph_file << std::endl; + DeepmdPlugin::nnp->init(graph_file); + std::cout << "Successfully load model!" << std::endl; + std::string summary; + DeepmdPlugin::nnp->print_summary(summary); + std::cout << "Summary: " << std::endl << summary << std::endl; + std::string map; + DeepmdPlugin::nnp->get_type_map(map); + std::cout << "Atom map: " << map << std::endl; + /* init model */ + + std::cout << "Successfully init plugin!" << std::endl; + } + else + { + std::cerr << "Invaild json file: " << json_file << std::endl; + exit(1); + } +} \ No newline at end of file diff --git a/source/ipi/driver.cc b/source/ipi/driver.cc index 2732b9b990..61ed851317 100644 --- a/source/ipi/driver.cc +++ b/source/ipi/driver.cc @@ -1,3 +1,5 @@ +#include +#include #include #include #include "sockets.h" From 53f1567360fd67d7ffbb301e45b102deb30686a2 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Tue, 21 Sep 2021 19:59:40 -0400 Subject: [PATCH 076/161] add support for hdf5 (#1163) * make a draft * add support for hdf5 * fix error with old python * fix rglobal * fix tests_path * fix tests * Update test_deepmd_data.py * use `visit` instead of `visititems` * cache file keys to prevent performance issues * improve performance --- deepmd/common.py | 8 +- deepmd/entrypoints/train.py | 6 +- deepmd/utils/data.py | 57 +++--- deepmd/utils/path.py | 338 +++++++++++++++++++++++++++++++ requirements.txt | 2 + source/tests/test.hdf5 | Bin 0 -> 6056 bytes source/tests/test_deepmd_data.py | 17 ++ 7 files changed, 396 insertions(+), 32 deletions(-) create mode 100644 deepmd/utils/path.py create mode 100644 source/tests/test.hdf5 diff --git a/deepmd/common.py b/deepmd/common.py index 03d7d8caf3..9968cff39c 100644 --- a/deepmd/common.py +++ b/deepmd/common.py @@ -23,6 +23,7 @@ from deepmd.env import GLOBAL_TF_FLOAT_PRECISION, GLOBAL_NP_FLOAT_PRECISION from deepmd.utils.sess import run_sess from deepmd.utils.errors import GraphWithoutTensorError +from deepmd.utils.path import DPPath if TYPE_CHECKING: _DICT_VAL = TypeVar("_DICT_VAL") @@ -429,9 +430,10 @@ def expand_sys_str(root_dir: Union[str, Path]) -> List[str]: List[str] list of string pointing to system directories """ - matches = [str(d) for d in Path(root_dir).rglob("*") if (d / "type.raw").is_file()] - if (Path(root_dir) / "type.raw").is_file(): - matches += [root_dir] + root_dir = DPPath(root_dir) + matches = [str(d) for d in root_dir.rglob("*") if (d / "type.raw").is_file()] + if (root_dir / "type.raw").is_file(): + matches.append(str(root_dir)) return matches diff --git a/deepmd/entrypoints/train.py b/deepmd/entrypoints/train.py index 817d603f3c..98090c18af 100755 --- a/deepmd/entrypoints/train.py +++ b/deepmd/entrypoints/train.py @@ -20,6 +20,7 @@ from deepmd.utils.data_system import DeepmdDataSystem from deepmd.utils.sess import run_sess from deepmd.utils.neighbor_stat import NeighborStat +from deepmd.utils.path import DPPath __all__ = ["train"] @@ -181,11 +182,12 @@ def get_data(jdata: Dict[str, Any], rcut, type_map, modifier): raise IOError(msg, help_msg) # rougly check all items in systems are valid for ii in systems: - if (not os.path.isdir(ii)): + ii = DPPath(ii) + if (not ii.is_dir()): msg = f'dir {ii} is not a valid dir' log.fatal(msg) raise IOError(msg, help_msg) - if (not os.path.isfile(os.path.join(ii, 'type.raw'))): + if (not (ii / 'type.raw').is_file()): msg = f'dir {ii} is not a valid data system dir' log.fatal(msg) raise IOError(msg, help_msg) diff --git a/deepmd/utils/data.py b/deepmd/utils/data.py index 34ef29400e..ecf4aaeaba 100644 --- a/deepmd/utils/data.py +++ b/deepmd/utils/data.py @@ -10,6 +10,7 @@ from deepmd.env import GLOBAL_NP_FLOAT_PRECISION from deepmd.env import GLOBAL_ENER_FLOAT_PRECISION from deepmd.utils import random as dp_random +from deepmd.utils.path import DPPath log = logging.getLogger(__name__) @@ -44,17 +45,18 @@ def __init__ (self, """ Constructor """ - self.dirs = glob.glob (os.path.join(sys_path, set_prefix + ".*")) + root = DPPath(sys_path) + self.dirs = root.glob(set_prefix + ".*") self.dirs.sort() # load atom type - self.atom_type = self._load_type(sys_path) + self.atom_type = self._load_type(root) self.natoms = len(self.atom_type) # load atom type map - self.type_map = self._load_type_map(sys_path) + self.type_map = self._load_type_map(root) if self.type_map is not None: assert(len(self.type_map) >= max(self.atom_type)+1) # check pbc - self.pbc = self._check_pbc(sys_path) + self.pbc = self._check_pbc(root) # enforce type_map if necessary if type_map is not None and self.type_map is not None: atom_type_ = [type_map.index(self.type_map[ii]) for ii in self.atom_type] @@ -167,9 +169,9 @@ def check_batch_size (self, batch_size) : """ for ii in self.train_dirs : if self.data_dict['coord']['high_prec'] : - tmpe = np.load(os.path.join(ii, "coord.npy")).astype(GLOBAL_ENER_FLOAT_PRECISION) + tmpe = (ii / "coord.npy").load_numpy().astype(GLOBAL_ENER_FLOAT_PRECISION) else: - tmpe = np.load(os.path.join(ii, "coord.npy")).astype(GLOBAL_NP_FLOAT_PRECISION) + tmpe = (ii / "coord.npy").load_numpy().astype(GLOBAL_NP_FLOAT_PRECISION) if tmpe.ndim == 1: tmpe = tmpe.reshape([1,-1]) if tmpe.shape[0] < batch_size : @@ -181,9 +183,9 @@ def check_test_size (self, test_size) : Check if the system can get a test dataset with `test_size` frames. """ if self.data_dict['coord']['high_prec'] : - tmpe = np.load(os.path.join(self.test_dir, "coord.npy")).astype(GLOBAL_ENER_FLOAT_PRECISION) + tmpe = (self.test_dir / "coord.npy").load_numpy().astype(GLOBAL_ENER_FLOAT_PRECISION) else: - tmpe = np.load(os.path.join(self.test_dir, "coord.npy")).astype(GLOBAL_NP_FLOAT_PRECISION) + tmpe = (self.test_dir / "coord.npy").load_numpy().astype(GLOBAL_NP_FLOAT_PRECISION) if tmpe.ndim == 1: tmpe = tmpe.reshape([1,-1]) if tmpe.shape[0] < test_size : @@ -377,7 +379,7 @@ def _get_subdata(self, data, idx = None) : return new_data def _load_batch_set (self, - set_name) : + set_name: DPPath) : self.batch_set = self._load_set(set_name) self.batch_set, _ = self._shuffle_data(self.batch_set) self.reset_get_batch() @@ -386,7 +388,7 @@ def reset_get_batch(self): self.iterator = 0 def _load_test_set (self, - set_name, + set_name: DPPath, shuffle_test) : self.test_set = self._load_set(set_name) if shuffle_test : @@ -409,13 +411,15 @@ def _shuffle_data (self, ret[kk] = data[kk] return ret, idx - def _load_set(self, set_name) : + def _load_set(self, set_name: DPPath) : # get nframes - path = os.path.join(set_name, "coord.npy") + if not isinstance(set_name, DPPath): + set_name = DPPath(set_name) + path = set_name / "coord.npy" if self.data_dict['coord']['high_prec'] : - coord = np.load(path).astype(GLOBAL_ENER_FLOAT_PRECISION) + coord = path.load_numpy().astype(GLOBAL_ENER_FLOAT_PRECISION) else: - coord = np.load(path).astype(GLOBAL_NP_FLOAT_PRECISION) + coord = path.load_numpy().astype(GLOBAL_NP_FLOAT_PRECISION) if coord.ndim == 1: coord = coord.reshape([1,-1]) nframes = coord.shape[0] @@ -459,12 +463,12 @@ def _load_data(self, set_name, key, nframes, ndof_, atomic = False, must = True, ndof = ndof_ * natoms else: ndof = ndof_ - path = os.path.join(set_name, key+".npy") - if os.path.isfile (path) : + path = set_name / (key+".npy") + if path.is_file() : if high_prec : - data = np.load(path).astype(GLOBAL_ENER_FLOAT_PRECISION) + data = path.load_numpy().astype(GLOBAL_ENER_FLOAT_PRECISION) else: - data = np.load(path).astype(GLOBAL_NP_FLOAT_PRECISION) + data = path.load_numpy().astype(GLOBAL_NP_FLOAT_PRECISION) try: # YWolfeee: deal with data shape error if atomic : data = data.reshape([nframes, natoms, -1]) @@ -491,8 +495,8 @@ def _load_data(self, set_name, key, nframes, ndof_, atomic = False, must = True, return np.float32(0.0), data - def _load_type (self, sys_path) : - atom_type = np.loadtxt (os.path.join(sys_path, "type.raw"), dtype=np.int32, ndmin=1) + def _load_type (self, sys_path: DPPath) : + atom_type = (sys_path / "type.raw").load_txt(dtype=np.int32, ndmin=1) return atom_type def _make_idx_map(self, atom_type): @@ -501,17 +505,16 @@ def _make_idx_map(self, atom_type): idx_map = np.lexsort ((idx, atom_type)) return idx_map - def _load_type_map(self, sys_path) : - fname = os.path.join(sys_path, 'type_map.raw') - if os.path.isfile(fname) : - with open(os.path.join(sys_path, 'type_map.raw')) as fp: - return fp.read().split() + def _load_type_map(self, sys_path: DPPath) : + fname = sys_path / 'type_map.raw' + if fname.is_file() : + return fname.load_txt(dtype=str).tolist() else : return None - def _check_pbc(self, sys_path): + def _check_pbc(self, sys_path: DPPath): pbc = True - if os.path.isfile(os.path.join(sys_path, 'nopbc')) : + if (sys_path / 'nopbc').is_file() : pbc = False return pbc diff --git a/deepmd/utils/path.py b/deepmd/utils/path.py new file mode 100644 index 0000000000..b39fba7e3c --- /dev/null +++ b/deepmd/utils/path.py @@ -0,0 +1,338 @@ +import os +from abc import ABC, abstractmethod +from typing import List +from pathlib import Path +from functools import lru_cache + +import numpy as np +import h5py +from wcmatch.glob import globfilter + +class DPPath(ABC): + """The path class to data system (DeepmdData). + + Parameters + ---------- + path : str + path + """ + def __new__(cls, path: str): + if cls is DPPath: + if os.path.isdir(path): + return super().__new__(DPOSPath) + elif os.path.isfile(path.split("#")[0]): + # assume h5 if it is not dir + # TODO: check if it is a real h5? or just check suffix? + return super().__new__(DPH5Path) + raise FileNotFoundError("%s not found" % path) + return super().__new__(cls) + + @abstractmethod + def load_numpy(self) -> np.ndarray: + """Load NumPy array. + + Returns + ------- + np.ndarray + loaded NumPy array + """ + + @abstractmethod + def load_txt(self, **kwargs) -> np.ndarray: + """Load NumPy array from text. + + Returns + ------- + np.ndarray + loaded NumPy array + """ + + @abstractmethod + def glob(self, pattern: str) -> List["DPPath"]: + """Search path using the glob pattern. + + Parameters + ---------- + pattern : str + glob pattern + + Returns + ------- + List[DPPath] + list of paths + """ + + @abstractmethod + def rglob(self, pattern: str) -> List["DPPath"]: + """This is like calling :metd:`DPPath.glob()` with `**/` added in front + of the given relative pattern. + + Parameters + ---------- + pattern : str + glob pattern + + Returns + ------- + List[DPPath] + list of paths + """ + + @abstractmethod + def is_file(self) -> bool: + """Check if self is file.""" + + @abstractmethod + def is_dir(self) -> bool: + """Check if self is directory.""" + + @abstractmethod + def __truediv__(self, key: str) -> "DPPath": + """Used for / operator.""" + + @abstractmethod + def __lt__(self, other: "DPPath") -> bool: + """whether this DPPath is less than other for sorting""" + + @abstractmethod + def __str__(self) -> str: + """Represent string""" + + def __repr__(self) -> str: + return "%s (%s)" % (type(self), str(self)) + + def __eq__(self, other) -> bool: + return str(self) == str(other) + + def __hash__(self): + return hash(str(self)) + + +class DPOSPath(DPPath): + """The OS path class to data system (DeepmdData) for real directories. + + Parameters + ---------- + path : str + path + """ + def __init__(self, path: str) -> None: + super().__init__() + if isinstance(path, Path): + self.path = path + else: + self.path = Path(path) + + def load_numpy(self) -> np.ndarray: + """Load NumPy array. + + Returns + ------- + np.ndarray + loaded NumPy array + """ + return np.load(str(self.path)) + + def load_txt(self, **kwargs) -> np.ndarray: + """Load NumPy array from text. + + Returns + ------- + np.ndarray + loaded NumPy array + """ + return np.loadtxt(str(self.path), **kwargs) + + def glob(self, pattern: str) -> List["DPPath"]: + """Search path using the glob pattern. + + Parameters + ---------- + pattern : str + glob pattern + + Returns + ------- + List[DPPath] + list of paths + """ + # currently DPOSPath will only derivative DPOSPath + # TODO: discuss if we want to mix DPOSPath and DPH5Path? + return list([type(self)(p) for p in self.path.glob(pattern)]) + + def rglob(self, pattern: str) -> List["DPPath"]: + """This is like calling :metd:`DPPath.glob()` with `**/` added in front + of the given relative pattern. + + Parameters + ---------- + pattern : str + glob pattern + + Returns + ------- + List[DPPath] + list of paths + """ + return list([type(self)(p) for p in self.path.rglob(pattern)]) + + def is_file(self) -> bool: + """Check if self is file.""" + return self.path.is_file() + + def is_dir(self) -> bool: + """Check if self is directory.""" + return self.path.is_dir() + + def __truediv__(self, key: str) -> "DPPath": + """Used for / operator.""" + return type(self)(self.path / key) + + def __lt__(self, other: "DPOSPath") -> bool: + """whether this DPPath is less than other for sorting""" + return self.path < other.path + + def __str__(self) -> str: + """Represent string""" + return str(self.path) + + +class DPH5Path(DPPath): + """The path class to data system (DeepmdData) for HDF5 files. + + Notes + ----- + OS - HDF5 relationship: + directory - Group + file - Dataset + + Parameters + ---------- + path : str + path + """ + def __init__(self, path: str) -> None: + super().__init__() + # we use "#" to split path + # so we do not support file names containing #... + s = path.split("#") + self.root_path = s[0] + self.root = self._load_h5py(s[0]) + # h5 path: default is the root path + self.name = s[1] if len(s) > 1 else "/" + + @classmethod + @lru_cache(None) + def _load_h5py(cls, path: str) -> h5py.File: + """Load hdf5 file. + + Parameters + ---------- + path : str + path to hdf5 file + """ + # this method has cache to avoid duplicated + # loading from different DPH5Path + # However the file will be never closed? + return h5py.File(path, 'r') + + def load_numpy(self) -> np.ndarray: + """Load NumPy array. + + Returns + ------- + np.ndarray + loaded NumPy array + """ + return self.root[self.name][:] + + def load_txt(self, dtype: np.dtype = None, **kwargs) -> np.ndarray: + """Load NumPy array from text. + + Returns + ------- + np.ndarray + loaded NumPy array + """ + arr = self.load_numpy() + if dtype: + arr = arr.astype(dtype) + return arr + + def glob(self, pattern: str) -> List["DPPath"]: + """Search path using the glob pattern. + + Parameters + ---------- + pattern : str + glob pattern + + Returns + ------- + List[DPPath] + list of paths + """ + # got paths starts with current path first, which is faster + subpaths = [ii for ii in self._keys if ii.startswith(self.name)] + return list([type(self)("%s#%s"%(self.root_path, pp)) for pp in globfilter(subpaths, self._connect_path(pattern))]) + + def rglob(self, pattern: str) -> List["DPPath"]: + """This is like calling :metd:`DPPath.glob()` with `**/` added in front + of the given relative pattern. + + Parameters + ---------- + pattern : str + glob pattern + + Returns + ------- + List[DPPath] + list of paths + """ + return self.glob("**" + pattern) + + @property + def _keys(self) -> List[str]: + """Walk all groups and dataset""" + return self._file_keys(self.root) + + @classmethod + @lru_cache(None) + def _file_keys(cls, file: h5py.File) -> List[str]: + """Walk all groups and dataset""" + l = [] + file.visit(lambda x: l.append("/" + x)) + return l + + def is_file(self) -> bool: + """Check if self is file.""" + if self.name not in self._keys: + return False + return isinstance(self.root[self.name], h5py.Dataset) + + def is_dir(self) -> bool: + """Check if self is directory.""" + if self.name not in self._keys: + return False + return isinstance(self.root[self.name], h5py.Group) + + def __truediv__(self, key: str) -> "DPPath": + """Used for / operator.""" + return type(self)("%s#%s" % (self.root_path, self._connect_path(key))) + + def _connect_path(self, path: str) -> str: + """Connect self with path""" + if self.name.endswith("/"): + return "%s%s" % (self.name, path) + return "%s/%s" % (self.name, path) + + def __lt__(self, other: "DPH5Path") -> bool: + """whether this DPPath is less than other for sorting""" + if self.root_path == other.root_path: + return self.name < other.name + return self.root_path < other.root_path + + def __str__(self) -> str: + """returns path of self""" + return "%s#%s" % (self.root_path, self.name) diff --git a/requirements.txt b/requirements.txt index 50b597f2fe..f3ead805b8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,5 @@ pyyaml dargs >= 0.2.6 python-hostlist >= 1.21 typing_extensions; python_version < "3.7" +h5py +wcmatch diff --git a/source/tests/test.hdf5 b/source/tests/test.hdf5 new file mode 100644 index 0000000000000000000000000000000000000000..2849b1b896af9d3a4a0284b10ae325c01add067d GIT binary patch literal 6056 zcmeHLeN5D49DjcIVh4l@gpe7YMbp5sa||b&<@0<36(^RNlWK8Za)vK>bfO$JioiAy zb#tr6v>{?7>ksAD7+^2R700zUBp1j^)V!?rgY%>Ym$v-{<@C zJkRg@+2?ug_kC(sW-X77oE=G&W-<{2nW=Wg;}&nt37SRd!oQJsBrdDDY}RQVi6}op z61hEw&p$ryxo5Mpi5w~B7m^Pn7K7Fk^9Xxb{yz{{na!S2GR@=ZTDbIao^h3wloi|4 zoVLeeD#v9Rs5m!w%N7L}7q%mNV@L`r0u7Rw1PaVATWW_W;)6H+hpTk*^^$=gZw zQT1t*HN~#e>C}NHK964FVWdHg6OUMKR)RCjb8LiE2-l>_b!(o^l8F8`Zq?JY*HM#F zy8QTP;dTuJLd^QmuPyeq%NRQDzv8_cuLPb#>N$VnWP@T0&ZhsJ(@ zqSnARJazNQ%Z6Pr?yiTY=my*T{0aQ<#VO3%^*TPw_fTuqTJ&AI!cxzl#O#N>u*Owj zMxGS~y>r-AeF~};wK3^eE9{;qmU8V3yB<}`V!wTizLr|W@>0KNM~=-wQ{g#wV_pl+ zjXBuyzDE$V_5_-Jd*J(OABOMWh>o`gY2Q2Vvkf)vcyYf6r%oqhY~VfYPKd>i=PQvu z|53L1;4PM#ILw-=-o=u(y|}Psn0-BWJH0%VjKS7uc6{C2C>_egjNwj}KRp^fi{+iVqhBjXqL<;{bv+;o_h=N^Vf#^LAd zZq}Xj8*TBe!Ut)sC|vvkt}gqIZFSY6&D)5`EA7}meGkjYaiKTS%SOj~P|~yl@s~!} z*-RaCNA%OJoo4EK=~d+Q46@}T_3Y@O44l2_LC1kn8o%a2oNQ^pnQvAx+Es`9hSHIH z+6K4%7#g>Hh!l4nBD-=~Qj(V*7^#Fir;%MSb>rcM5m>l-FZJg@SPLT%Mj(tp7=e34 z;I4IdoVI|dx9)Md@bG^b0)chP0yP6kD`RheFGujaMAad5z5FGu*ka6c<+~-NnMs0) zB)qBiL)9@yc^#9?BQ+6N#}xYc|87OAe+{B&C+67DEAn!fxWolPkZ?1%SH}kl;AXK8 z>t=&JqpyHlMi d`Qh_VD}i|Y`MD$bAaD`-#PR9i_)IN+{0=mjRr&w` literal 0 HcmV?d00001 diff --git a/source/tests/test_deepmd_data.py b/source/tests/test_deepmd_data.py index 8532d8d9a4..b1256b67f5 100644 --- a/source/tests/test_deepmd_data.py +++ b/source/tests/test_deepmd_data.py @@ -4,6 +4,7 @@ from deepmd.utils.data import DeepmdData from deepmd.env import GLOBAL_NP_FLOAT_PRECISION +from common import tests_path if GLOBAL_NP_FLOAT_PRECISION == np.float32 : places = 6 @@ -257,3 +258,19 @@ def test_get_nbatch(self): def _comp_np_mat2(self, first, second) : np.testing.assert_almost_equal(first, second, places) + + +class TestH5Data (unittest.TestCase) : + def setUp (self) : + self.data_name = str(tests_path / 'test.hdf5') + + def test_init (self) : + dd = DeepmdData(self.data_name) + self.assertEqual(dd.idx_map[0], 0) + self.assertEqual(dd.type_map, ['X']) + self.assertEqual(dd.test_dir, self.data_name + '#/set.000') + self.assertEqual(dd.train_dirs, [self.data_name + '#/set.000']) + + def test_get_batch(self) : + dd = DeepmdData(self.data_name) + data = dd.get_batch(5) From ba087c40f1b16cf71bd6930e1100c3a0a21f200f Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Thu, 23 Sep 2021 20:28:34 -0400 Subject: [PATCH 077/161] automatic batch size for `dp test` (#1165) * automatic batch size for `dp test` Resolves #1149. We start nbatch * natoms from 1024 (or we can set a different number), and iteratively multiply it by 2 until catching the OOM error. A small issue is that it's a bit slow to catch the TF OOM error. It's a problem of TF and I don't know how to resolve it. Luckily we only need to catch once. * replace `execuate` with `execute` * add unittest; bugfix --- deepmd/entrypoints/test.py | 11 +- deepmd/infer/deep_pot.py | 2 +- deepmd/utils/batch_size.py | 129 +++++++++++++++++++++ deepmd/utils/errors.py | 3 + deepmd/utils/sess.py | 3 +- doc/troubleshooting/model-compatability.md | 4 +- source/tests/test_auto_batch_size.py | 42 +++++++ 7 files changed, 188 insertions(+), 6 deletions(-) create mode 100644 deepmd/utils/batch_size.py create mode 100644 source/tests/test_auto_batch_size.py diff --git a/deepmd/entrypoints/test.py b/deepmd/entrypoints/test.py index 65887d8f91..1a5b18969c 100644 --- a/deepmd/entrypoints/test.py +++ b/deepmd/entrypoints/test.py @@ -9,10 +9,11 @@ from deepmd.utils import random as dp_random from deepmd.utils.data import DeepmdData from deepmd.utils.weight_avg import weighted_average +from deepmd.utils.batch_size import AutoBatchSize if TYPE_CHECKING: from deepmd.infer import DeepDipole, DeepPolar, DeepPot, DeepWFC - from deepmd.infer.deep_eval import DeepTensor + from deepmd.infer.deep_tensor import DeepTensor __all__ = ["test"] @@ -69,6 +70,7 @@ def test( # init model dp = DeepPotential(model) + auto_batch_size = AutoBatchSize() for cc, system in enumerate(all_sys): log.info("# ---------------output of dp test--------------- ") @@ -82,6 +84,7 @@ def test( err = test_ener( dp, data, + auto_batch_size, system, numb_test, detail_file, @@ -159,6 +162,7 @@ def save_txt_file( def test_ener( dp: "DeepPot", data: DeepmdData, + auto_batch_size: AutoBatchSize, system: str, numb_test: int, detail_file: Optional[str], @@ -226,7 +230,10 @@ def test_ener( else: aparam = None - ret = dp.eval( + ret = auto_batch_size.execute_all( + dp.eval, + numb_test, + natoms, coord, box, atype, diff --git a/deepmd/infer/deep_pot.py b/deepmd/infer/deep_pot.py index 63625905e8..b8d557ae76 100644 --- a/deepmd/infer/deep_pot.py +++ b/deepmd/infer/deep_pot.py @@ -324,7 +324,7 @@ def _eval_inner( feed_dict_test[self.t_fparam] = np.reshape(fparam, [-1]) if self.has_aparam: feed_dict_test[self.t_aparam] = np.reshape(aparam, [-1]) - v_out = self.sess.run (t_out, feed_dict = feed_dict_test) + v_out = run_sess(self.sess, t_out, feed_dict = feed_dict_test) energy = v_out[0] force = v_out[1] virial = v_out[2] diff --git a/deepmd/utils/batch_size.py b/deepmd/utils/batch_size.py new file mode 100644 index 0000000000..981572ecd8 --- /dev/null +++ b/deepmd/utils/batch_size.py @@ -0,0 +1,129 @@ +import logging +from typing import Callable, Tuple + +import numpy as np + +from deepmd.utils.errors import OutOfMemoryError + +class AutoBatchSize: + """This class allows DeePMD-kit to automatically decide the maximum + batch size that will not cause an OOM error. + + Notes + ----- + We assume all OOM error will raise :metd:`OutOfMemoryError`. + + Parameters + ---------- + initial_batch_size : int, default: 1024 + initial batch size (number of total atoms) + factor : float, default: 2. + increased factor + + Attributes + ---------- + current_batch_size : int + current batch size (number of total atoms) + maximum_working_batch_size : int + maximum working batch size + minimal_not_working_batch_size : int + minimal not working batch size + """ + def __init__(self, initial_batch_size: int = 1024, factor: float = 2.) -> None: + # See also PyTorchLightning/pytorch-lightning#1638 + # TODO: discuss a proper initial batch size + self.current_batch_size = initial_batch_size + self.maximum_working_batch_size = 0 + self.minimal_not_working_batch_size = 2**31 + self.factor = factor + + def execute(self, callable: Callable, start_index: int, natoms: int) -> Tuple[int, tuple]: + """Excuate a method with given batch size. + + Parameters + ---------- + callable : Callable + The method should accept the batch size and start_index as parameters, + and returns executed batch size and data. + start_index : int + start index + natoms : int + natoms + + Returns + ------- + int + executed batch size * number of atoms + tuple + result from callable, None if failing to execute + + Raises + ------ + OutOfMemoryError + OOM when batch size is 1 + """ + try: + n_batch, result = callable(max(self.current_batch_size // natoms, 1), start_index) + except OutOfMemoryError as e: + # TODO: it's very slow to catch OOM error; I don't know what TF is doing here + # but luckily we only need to catch once + self.minimal_not_working_batch_size = min(self.minimal_not_working_batch_size, self.current_batch_size) + if self.maximum_working_batch_size >= self.minimal_not_working_batch_size: + self.maximum_working_batch_size = int(self.minimal_not_working_batch_size / self.factor) + if self.minimal_not_working_batch_size <= natoms: + raise OutOfMemoryError("The callable still throws an out-of-memory (OOM) error even when batch size is 1!") from e + # adjust the next batch size + self._adjust_batch_size(1./self.factor) + return 0, None + else: + n_tot = n_batch * natoms + self.maximum_working_batch_size = max(self.maximum_working_batch_size, n_tot) + # adjust the next batch size + if n_tot >= self.current_batch_size and self.current_batch_size * self.factor < self.minimal_not_working_batch_size: + self._adjust_batch_size(self.factor) + return n_batch, result + + def _adjust_batch_size(self, factor: float): + old_batch_size = self.current_batch_size + self.current_batch_size = int(self.current_batch_size * factor) + logging.info("Adjust batch size from %d to %d" % (old_batch_size, self.current_batch_size)) + + def execute_all(self, callable: Callable, total_size: int, natoms: int, *args, **kwargs) -> Tuple[np.ndarray]: + """Excuate a method with all given data. + + Parameters + ---------- + callable : Callable + The method should accept *args and **kwargs as input and return the similiar array. + total_size : int + Total size + natoms : int + The number of atoms + **kwargs + If 2D np.ndarray, assume the first axis is batch; otherwise do nothing. + """ + def execute_with_batch_size(batch_size: int, start_index: int) -> Tuple[int, Tuple[np.ndarray]]: + end_index = start_index + batch_size + end_index = min(end_index, total_size) + return (end_index - start_index), callable( + *[(vv[start_index:end_index] if isinstance(vv, np.ndarray) and vv.ndim > 1 else vv) for vv in args], + **{kk: (vv[start_index:end_index] if isinstance(vv, np.ndarray) and vv.ndim > 1 else vv) for kk, vv in kwargs.items()}, + ) + + index = 0 + results = [] + while index < total_size: + n_batch, result = self.execute(execute_with_batch_size, index, natoms) + if not isinstance(result, tuple): + result = (result,) + index += n_batch + if n_batch: + for rr in result: + rr.reshape((n_batch, -1)) + results.append(result) + + r = tuple([np.concatenate(r, axis=0) for r in zip(*results)]) + if len(r) == 1: + # avoid returning tuple if callable doesn't return tuple + r = r[0] + return r diff --git a/deepmd/utils/errors.py b/deepmd/utils/errors.py index 4a6617c055..c4579c43a1 100644 --- a/deepmd/utils/errors.py +++ b/deepmd/utils/errors.py @@ -3,3 +3,6 @@ class GraphTooLargeError(Exception): class GraphWithoutTensorError(Exception): pass + +class OutOfMemoryError(Exception): + """This error is caused by out-of-memory (OOM).""" \ No newline at end of file diff --git a/deepmd/utils/sess.py b/deepmd/utils/sess.py index 21f1581d35..07723c13c4 100644 --- a/deepmd/utils/sess.py +++ b/deepmd/utils/sess.py @@ -1,6 +1,7 @@ import os from deepmd.env import tf +from deepmd.utils.errors import OutOfMemoryError def run_sess(sess: tf.Session, *args, **kwargs): @@ -35,4 +36,4 @@ def run_sess(sess: tf.Session, *args, **kwargs): "variable (current value: %s).\n" % ( os.getenv("CUDA_VISIBLE_DEVICES", None), )) - raise RuntimeError(MESSAGE) from e + raise OutOfMemoryError(MESSAGE) from e diff --git a/doc/troubleshooting/model-compatability.md b/doc/troubleshooting/model-compatability.md index fcc73f6cb3..bc1b464047 100644 --- a/doc/troubleshooting/model-compatability.md +++ b/doc/troubleshooting/model-compatability.md @@ -4,7 +4,7 @@ When the version of DeePMD-kit used to training model is different from the that DeePMD-kit guarantees that the codes with the same major and minor revisions are compatible. That is to say v0.12.5 is compatible to v0.12.0, but is not compatible to v0.11.0 nor v1.0.0. -One can execuate `dp convert-from` to convert an old model to a new one. +One can execute `dp convert-from` to convert an old model to a new one. | Model version | v0.12 | v1.0 | v1.1 | v1.2 | v1.3 | v2.0 | |:-:|:-----------:|:----------:|:----------:|:----------:|:----------:|:----------:| @@ -12,5 +12,5 @@ One can execuate `dp convert-from` to convert an old model to a new one. **Legend**: - 😄: The model is compatible with the DeePMD-kit package. -- 😊: The model is incompatible with the DeePMD-kit package, but one can execuate `dp convert-from` to convert an old model to v2.0. +- 😊: The model is incompatible with the DeePMD-kit package, but one can execute `dp convert-from` to convert an old model to v2.0. - 😢: The model is incompatible with the DeePMD-kit package, and there is no way to convert models. diff --git a/source/tests/test_auto_batch_size.py b/source/tests/test_auto_batch_size.py new file mode 100644 index 0000000000..f8aa0b8e60 --- /dev/null +++ b/source/tests/test_auto_batch_size.py @@ -0,0 +1,42 @@ +import unittest + +import numpy as np + +from deepmd.utils.batch_size import AutoBatchSize +from deepmd.utils.errors import OutOfMemoryError + +class TestAutoBatchSize(unittest.TestCase): + def oom(self, batch_size, start_index): + if batch_size >= 512: + raise OutOfMemoryError + return batch_size, np.zeros((batch_size, 2)) + + def test_execute_oom(self): + # initial batch size 256 = 128 * 2 + auto_batch_size = AutoBatchSize(256, 2.) + # no error - 128 + nb, result = auto_batch_size.execute(self.oom, 1, 2) + self.assertEqual(nb, 128) + self.assertEqual(result.shape, (128, 2)) + # no error - 256 + nb, result = auto_batch_size.execute(self.oom, 1, 2) + self.assertEqual(nb, 256) + self.assertEqual(result.shape, (256, 2)) + # error - 512 return 0, None + nb, result = auto_batch_size.execute(self.oom, 1, 2) + self.assertEqual(nb, 0) + self.assertIsNone(result) + # 256 again + nb, result = auto_batch_size.execute(self.oom, 1, 2) + self.assertEqual(nb, 256) + self.assertEqual(result.shape, (256, 2)) + # 256 again + nb, result = auto_batch_size.execute(self.oom, 1, 2) + self.assertEqual(nb, 256) + self.assertEqual(result.shape, (256, 2)) + + def test_execute_all(self): + dd1 = np.zeros((10000, 2, 1)) + auto_batch_size = AutoBatchSize(256, 2.) + dd2 = auto_batch_size.execute_all(np.array, 10000, 2, dd1) + np.testing.assert_equal(dd1, dd2) From a2708ea495f1a1de8c1e0d15a5620b851db54f9b Mon Sep 17 00:00:00 2001 From: Shaochen Shi Date: Fri, 24 Sep 2021 14:42:09 +0800 Subject: [PATCH 078/161] Fix freezing error on checkpoint from parallel training. (#1166) --- deepmd/entrypoints/freeze.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/deepmd/entrypoints/freeze.py b/deepmd/entrypoints/freeze.py index 511f58598d..afbb7659d4 100755 --- a/deepmd/entrypoints/freeze.py +++ b/deepmd/entrypoints/freeze.py @@ -159,6 +159,11 @@ def freeze( clear_devices = True # We import the meta graph and retrieve a Saver + try: + # In case paralle training + import horovod.tensorflow as _ + except ImportError: + pass saver = tf.train.import_meta_graph( f"{input_checkpoint}.meta", clear_devices=clear_devices ) From 5ba70dbbfddd0c53dabb3acac362614356bf6fce Mon Sep 17 00:00:00 2001 From: Shaochen Shi Date: Fri, 24 Sep 2021 14:43:33 +0800 Subject: [PATCH 079/161] Allow to scale LR in different ways. (#1167) --- deepmd/train/trainer.py | 13 ++++++++++++- deepmd/utils/argcheck.py | 4 +++- doc/train/parallel-training.md | 14 ++++++++++++-- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/deepmd/train/trainer.py b/deepmd/train/trainer.py index 7556cc1ebb..a8d85d86e6 100644 --- a/deepmd/train/trainer.py +++ b/deepmd/train/trainer.py @@ -157,6 +157,13 @@ def _init_param(self, jdata): # learning rate lr_param = j_must_have(jdata, 'learning_rate') + scale_by_worker = lr_param.get('scale_by_worker', 'linear') + if scale_by_worker == 'linear': + self.scale_lr_coef = float(self.run_opt.world_size) + elif scale_by_worker == 'sqrt': + self.scale_lr_coef = np.sqrt(self.run_opt.world_size).real + else: + self.scale_lr_coef = 1. lr_type = lr_param.get('type', 'exp') if lr_type == 'exp': self.lr = LearningRateExp(lr_param['start_lr'], @@ -330,7 +337,11 @@ def _build_network(self, data): def _build_training(self): trainable_variables = tf.trainable_variables() if self.run_opt.is_distrib: - optimizer = tf.train.AdamOptimizer(learning_rate = self.learning_rate*self.run_opt.world_size) + if self.scale_lr_coef > 1.: + log.info('Scale learning rate by coef: %f', self.scale_lr_coef) + optimizer = tf.train.AdamOptimizer(self.learning_rate*self.scale_lr_coef) + else: + optimizer = tf.train.AdamOptimizer(self.learning_rate) optimizer = self.run_opt._HVD.DistributedOptimizer(optimizer) else: optimizer = tf.train.AdamOptimizer(learning_rate = self.learning_rate) diff --git a/deepmd/utils/argcheck.py b/deepmd/utils/argcheck.py index 8e55187574..0ea84d8e73 100644 --- a/deepmd/utils/argcheck.py +++ b/deepmd/utils/argcheck.py @@ -452,8 +452,10 @@ def learning_rate_variant_type_args(): def learning_rate_args(): + doc_scale_by_worker = 'When parallel training or batch size scaled, how to alter learning rate. Valid values are `linear`(default), `sqrt` or `none`.' doc_lr = "The definitio of learning rate" - return Argument("learning_rate", dict, [], + return Argument("learning_rate", dict, + [Argument("scale_by_worker", str, optional=True, default='linear', doc=doc_scale_by_worker)], [learning_rate_variant_type_args()], doc = doc_lr) diff --git a/doc/train/parallel-training.md b/doc/train/parallel-training.md index bd1bdb889a..b252446971 100644 --- a/doc/train/parallel-training.md +++ b/doc/train/parallel-training.md @@ -3,9 +3,19 @@ Currently, parallel training is enabled in a sychoronized way with help of [Horovod](https://github.com/horovod/horovod). Depend on the number of training processes (according to MPI context) and number of GPU cards avaliable, DeePMD-kit will decide whether to launch the training in parallel (distributed) mode or in serial mode. Therefore, no additional options is specified in your JSON/YAML input file. -Horovod works in the data-parallel mode, resulting in a larger global batch size. For example, the real batch size is 8 when `batch_size` is set to 2 in the input file and you launch 4 workers. Thus, `learning_rate` is automatically scaled by the number of workers for better convergence. The number of decay steps required to achieve same accuracy will also reduce based on the number of cards (e.g., 1/4 of steps in the above case), but needs to be scaled manually in the input file. +## Tuning learning rate -Technical details of such heuristic rule are discussed at [Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour](https://arxiv.org/abs/1706.02677). +Horovod works in the data-parallel mode, resulting in a larger global batch size. For example, the real batch size is 8 when `batch_size` is set to 2 in the input file and you launch 4 workers. Thus, `learning_rate` is automatically scaled by the number of workers for better convergence. Technical details of such heuristic rule are discussed at [Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour](https://arxiv.org/abs/1706.02677). + +The number of decay steps required to achieve same accuracy can decrease by the number of cards (e.g., 1/2 of steps in the above case), but needs to be scaled manually in the input file. + +In some cases, it won't work well when scale learning rate by worker count in a `linear` way. Then you can try `sqrt` or `none` by setting argument `scale_by_worker` like below. +```json + "learning_rate" :{ + "scale_by_worker": "none", + "type": "exp" + } +``` ## Scaling test From 9eb78bf312436892a84e9027e6f63733f133e6bb Mon Sep 17 00:00:00 2001 From: Yingze Wang Date: Sat, 25 Sep 2021 11:37:28 +0800 Subject: [PATCH 080/161] Update DP+GROMACS in README.md (#1169) * Add entry of install gromacs in README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3c1a42e6f3..177cb5071f 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ A full [document](doc/train/train-input-auto.rst) on options in the training inp - [Install from source code](doc/install/install-from-source.md) - [Install LAMMPS](doc/install/install-lammps.md) - [Install i-PI](doc/install/install-ipi.md) + - [Install GROMACS](doc/install/install-gromacs.md) - [Building conda packages](doc/install/build-conda.md) - [Data](doc/data/index.md) - [Data conversion](doc/data/data-conv.md) From d8acbb8b5647d1d7316f182f0f588f4eb5038f28 Mon Sep 17 00:00:00 2001 From: Yingze Wang Date: Sun, 26 Sep 2021 08:03:53 +0800 Subject: [PATCH 081/161] Bug fix of memory overflow when calculating model deviation (#1154) --- deepmd/infer/model_devi.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/deepmd/infer/model_devi.py b/deepmd/infer/model_devi.py index 0ac7c025ff..080fd59fcb 100644 --- a/deepmd/infer/model_devi.py +++ b/deepmd/infer/model_devi.py @@ -195,12 +195,13 @@ def make_model_devi( nframes_tot = 0 devis = [] for data in data_sets: - coord = data["coord"] - box = data["box"] - atype = data["type"][0] - devi = calc_model_devi(coord, box, atype, dp_models, nopbc=nopbc) - nframes_tot += coord.shape[0] - devis.append(devi) + coords = data["coord"] + boxs = data["box"] + atypes = data["type"] + for coord, box, atype in zip(coords, boxs, atypes): + devi = calc_model_devi(np.array([coord]), np.array([box]), atype, dp_models, nopbc=nopbc) + nframes_tot += 1 + devis.append(devi) devis = np.vstack(devis) devis[:, 0] = np.arange(nframes_tot) * frequency write_model_devi_out(devis, output) From 9d9cbf4068b1d4b260faba5a0f7576964a0dbf98 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sun, 26 Sep 2021 09:04:42 -0400 Subject: [PATCH 082/161] [ImgBot] Optimize images (#1172) /examples/water/gmx/rdf.png -- 167.16kb -> 141.24kb (15.5%) Signed-off-by: ImgBotApp Co-authored-by: ImgBotApp --- examples/water/gmx/rdf.png | Bin 171170 -> 144634 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/examples/water/gmx/rdf.png b/examples/water/gmx/rdf.png index ea87475b7b304b780ea7733eb4f45023c28d3322..abe8f1fdb939369ed45b1206f6c6e0e50ec9412c 100644 GIT binary patch literal 144634 zcmce;cRbd6A3v_s)RKrqNk&FSHW?|A5u)r;Sy{=*$SSD_DHK;(rIM_WElLzuMk10G z$q3ome4ihx(>eEje;>a;evhyFai4RH>$={b_jtWt&+&TuX{af#TfJp91qH=ArGxUC z6clT+DJYgVt@;Z;k>))ji+?RMmpdd!L6I7?X6EE_e81iJpynY8it`*46c>CbD5mhE z3*8hH=L9GyzMY_;kPN4wpg$Xtdsqs;SaC{4QJ!L+_+QNPxDfn=^6Wu92MP*4R^tCl zTpr3g;pG%c@^VL9KJ_)58`7Fj&(AKG_xX6^g2?UD0jb4w#=@`9B<|kAb2~iT?0y*g zoy`Yc{PmX;wL$SlH+r`N#(o>~c8G2$_V(WH?U>m_mF+bWU0(EbSNeswU9;^kH;spX ziIC_T%@3Ha2vA;$0sQ`=UCRCF_cz3Ulq`{B`11#}YnGY*`Mt)j4WUc^_`%-Py&5b2 z_@1KVum5Ln@Pe`DLxRqm>st+S9ees3(x2z&YbWa3eXETR4W)fJ^`^L(Ny_D2eSQ6_ zSFhf@dDGfDKQlSPz`#&WtO^B%EuXjG!hgI_m6w-)Jo?P4o=^QyWt23JOjC37u?bE- zzRK6H6(&mGzZb+;beH?|51XO0U&*(oXUAKQa-=);>(d;X`;=i7xQCsQk&*Sv9?uz< ztTWBZp`7>eHw128_YaiXuf!?@+B-Y%XVNp&#K(sCE?Jinxy@9V9&y1w(R=3lov>) zT%0wJ53>d;7@we-j44kwep4J>_v9##>|ww1@^a-{+R?xkxyrQ+9G1< zyG9K|8l@a)&eLi7_KHfjw#V^p7{9GGg`1n(Q0hSb$aNOQ{^q=T`4E0eviso6m4WL< zhTbqUip%Tk>yLC+usdg-Y0j;AbjTpjssE0Mm7$>_mY9{RyCyE^;;gZ;ae__?pG3av zNT-l_Wz*A>ucE!?BrM)P;6D1~;Of_bjBjkE7oUIo@8`ckNzNas+!G%ke?=Gr#WeTS z#QCqeLdAS$l+tVRYgoIsKOL{&%tvZg1={l1Q)}5M$=D;DGQ}x{bKKjCK7E?1lARBg zs#c7xPBhF(llR-$PBwjeo8kR@LQ26#=A&5S3pmk-6cnzQEO~t7$^NVBhw`Y1VO$Vf z{_}vG;4tyVVp~>5NL&#v6XB#`V6d>T;G+H5(7?{dw%IPTy1Kfqu5Mp+FuTg@*RQvD zy4bBDHx+moZ1lS6Dyg=2?}h9%_sL;?qk_-@e%E;IvS4=pW4WPPL-!B(7nfvM)mN66 zcZh9Ycy`-mzxVn^va@Hfoi3C?#yvZGo=SFRyU{$hzJITIc{q#Fe1mzkU$3;Z^w-`z$DSR@^SvpB z6YEwHpZ9|C;_vMc98>Px>LF%IrX5O|`O#>bBf6W7Gu@_9)UIM@wEL;z2GP^+_BM5N zSI4?a=IbsuJzn5$VQkFm+4Fv+tLiJ2>yJLt#6{_&3ECe{3rlRe>f__9KG&bss-~h6 zGT=2i{BbSd%efwZ~im8EHTaLlY%gb(L$T2@nK&w%cdt;`}6{ZZ+w{$~?3+LzD)NV0* zjNZ&1ZuOcMc~BS@7B=yi*o^k+mbH&rW~QQ~-SjgoAHFxXu$aV}^?%CXQBQZo)FBKd z8|K(QiMYjUkm=A>xrUyvTx9p+0CRTy8lZfp9BFiP6esT*>nen|V2e{HG&(yg?zX2P zQka^VZE=a>;^Jx-zI6HWW$M)6Q=3BLbyM$W=Uk*RbRF*Sn|Q39q;G0)J2cb;+dH(O zp~2L|Wb^y^rW}X%xys7QD@}=sF{<|V!#YMD56Kl_MxK-HCH{t0Y`nbvZ>XIOj zcH#z|AN`H>FGl)4rp8XZz!Yz`OFbaP`Q`if?|prSDaWN(u3TwlWwmnU)!cKvPTy+F zDk^vd1OysiD66SOh_ifeN;Ot|71aOk-u~pFt;FW6D80$LZonu(o40K*4^Kiw6gT2h zz&2_9i8oo;I+(1{k&(vSa|zMWt?II~9o^lWlo`UdEry*eVl{E_D@*A_)42x^f`_Z17 z^!|uDcl2`|Erzrl>Js&0t54fYwmp0H%=pc9i9LH5=RPwn9R2dOf5ukHNVVbfcOv&V zn>7{>E`+$L-jtG(HFUd5$Jm;in@g?+FbKWQ_VuM@;DBrPMg}^QAwg^AQuAQjf6MP3p-k@NF$V4(GMDmEf zm6eq>@sVBSc6Yt|*aw~VJN=DWj4fWZj}NySj}NqBsG9LdTHQwuC07(qb}|mJ(Ck#J zO&J;++qBx+#>V0NIL*TBZTMB%5u=Q)=~=OE3mczq>Z!|~AyUgt9VMIH2A=OsE_Cb( zRvjAp@}{Ju;*N;W)G2>6jYkLsvU3w(5NWo_xO2`ON%+u`@9KcTp+*UlSLo~SA85*9 zt(lCA_9Tn+1&5UQ)B6XF4GmSN^nA)lU^y9PeJET&&b<$3acz!XW+svQazul!U(d|Q z*mV7g#y#&jgIt`^%ij2;RLX_n-r)J2(>k_Fq??$I%yn3y_#FaC~tViz(BnFYAp??{m&J%0RnBfIjdv9E8# z)DIo1$Q;IOwXgIEpm zD~XNpG1a)Ft2Nv1O9c1G$TxN)Bcq|zmX?+&kxAFF_t==02meyAPhacx{q3cdRK+XT zZxKFN_y~!#{f_2CWsljh`jzXM4k{^~R9_g+UgqDYcI`sVi_qZv_Vx9xJae_aF{&tZ zCT`#qm>AUEKd{D5@5G4{o_s$JQIU&FO8jV4kk3)PHE6401I@*A3%a|z+plXF?QhgE zO!t^}dNn zf_Aj5SLNK|*yw(5vdx=Am6VmAAhn6B4}N~Rv`vfETaZK|-QqsFaeeBuX8{2Lp|wr9 z=Tw?9k?>rVRl413E(s!&zqqjEO1?+gt5^FCJ3BkgCN?A|C+EoYbxjWx($^>IrN17W zdv%qHp4n|~X4261_Vec+QD!|TapRq(L?v_f+_~p&<9v(@>lhQx z`gn?0;PW!dt}Xh&p|d(2RG@v5gEiB(wF_EGZOw~ixp z^z^9ZLJhwN($UcYeypLQGS6F>Dq4fz^|gQ$X9$Tj+2S_fSF=2|s3u5fpyz#}gTrnK})$@D}(Wu$UV-wHH&`>BXrVUSD)kTU#45pVP8SOX{#f{?i~P2|-l+L9%nYGnE{EX{K+tN!a`RNAN9L z$O4V2##gh6+^1J~#*io>GAwEo+&{-_#rXuW`Q1PP@OA!F#dc1I(eA@T>@%Y^>J7K0 zJ!f;wA?LVXKw~>8#w2s%V z!$rvzW z%~w`V0%i_fBsPf5RJ%WaF?0XNRMvTVs}*vjKt{1^{4YyN*yEIkMqG|?A3bs;lw~$r z#)DhtTZp<$S>`S-uCn*=@w40SE>wXroByr?k6#i*#hjI0fOCvf)J>FZ_9Q`5pQZhMeH%A! zL|8~SuPUw@ahdMVssR@DN;TUMYG-FhZBztAV*jMCJ|#Na9;NY>(bi0xyAzL5u1d#| zQAqmp@E^}Veb9@UnR)2&+uOWCgJw33nH4X%f~Ce!Bg{>b2J@T;ly9Hb*WWU|II!&B z^|L$%CK;Km7__Z@zo^Y86Gvi6X9r(VwHxDs)gzBboSK;!x@Jghu{)wRcWT}~VIvCC z;!8(Ytlhj_=37Tuh{RFl9eYA{s*67G0`4nTynOX4qf2^3WaPSt9p>idLl?~}Bg(oW zf|zBGHwmz@`FJd*^`_qmv&c859Hl}@N^?9;%!v~*)pDemnC-L9vjm`RH(I-Xy>`c` z*3p=9gyE7df&)Ofh`9Epy`2a~v|+pS1SKW)3%nd1pJilZoIQx3BE030S_$gyjnM%i zAzI1mvpMI-o=n^T+}Proj=YV+%Rcgcc=+Z$d-k+FUcHICV}6MMQFca`O^$Rmh%Fq- zl6AjhYrraU_vpU0v(w{Zl02~REcXjfeU`DivuPvdAk0}SE zv(8ms`F2-Tbe81@W%pr#VQkxB#pc{|Z^>k3@#3PQQ}c7P5}O7Zv(%dkfn>1ThG!8B zcE}XJe%(pF#UrtaUoTDG&=66K^ro#lZ4G&qZ6~=541q-{Eil(-WU>{N?)&!b3)~`7 zToc@T3A`M>1Z4k?WqibBaU8ny;D|;i>X!DfWq!agfasj(QV(!*Y9$+@1kFROIy5o# z1yM=j7V3$~>1mxr-NS-H`0-Vi*Z@W`wcxEowl_^ z*DA4H7UH@?txiJh^t%^0Z$PzzU1zZvbiDe$Zm7nBthW%)a^aNkZ!nJZ4ScOv;O=_B z8>dEXc$nj5y^&YaXQD6)ILm?akGkQD4sz&g{ z3kncNrJShj58V;I$uu`NJ&>1^lVp%}2GA0@ZQ%KM)Pv9d`~^3BEbNDY%WE8wS-c4UxAq}+b=f#eX&pEr;8AV90j zd}Efa&XXf$gL5GCP};iY>WNs^9#m18?n|#)%hV1|DTWdE14^A%R*dt zx%4|e?2wfs)z%)Fjq}?~A6k7{m|(pgr~^-iKL02t&8d~3odmv!_4T!&AZ%vary1eG4ZIOXu$4F+J9+gL6{G-ML-{d1K9Dl6&$-*cpmAYaTi=+sZhejoi>ax)Bl7m`TPvW^uF6Oib@eK%Xux&7ZL*&Ec=P3J=*j`? zdfq=og7E}dVYicM)24IyFaTqI{b!{}WO#X1l`YQI%a<>4ymzTBOs?b406{?!5=^y7 zJYDeq#p6;4{MvN1TYvu@&CguAw_5HOW&Z!`pT+2AgKReb>OS7=tQvpuXHB8u_-hk# z(y2vCKGal0$r7%)^ygH^bpHYae>404`zH%ej)3xu-CO*g;@a|`N9^bKoOHhmn#CW) z=>B@k|Cet-y9XO&$$S^mK-SNJE&QuR7deL7djo#E%yf`>$&w}9fBrh1X2S+(8o!r~ z_}zs`l-mDz#)U_U=g~>_tzS97y-4LPzKbp25Y2wjYWp-+%-i+W@t*VmCh(_R#DYKn zBw1=X%PR6ynZ<7x|Jib6nQ6>NLP`4l#YtX8-kUo1=Sww0sY5TG>izQrV>mImlDa>2 zoxO(@G*(hR=)t z>(7K@KUkIwOdM2GGgG68Im^z@PT4q&0$_M5hW}raWu}m%?SDy-5Ys3rXp=`r9fv9t zt4|L*{>OU)2Xf6xiV6vZNG!P^cHaEoD`|#Mae>rd{;xf@z~l*}N0Rauib9Q~f3Ge!1;q)oJwpGUnf-{^<<|dg z@U$M()YL`JcUTnyPVA#_GIRaU2O-@Lidn1r095A_Ch!btsw6=WJw!dUVm*^M${2vV zog5sU@$3&{8IR+0xW<)`m&2_Eszs&FAu-HKN$fcWAczb)>_eaOYsNO~nbudDDJE8eU z4Ah9A4&|C!MSdY)EJw<5=u+_YxqSH;GH$USjTZ9qdX@YB=4mw);@jqjMn+b$jFzv4WSn)f2Dgwo4s}l8jrdW$UKUIb@QB( zst@6F45ynQ>*Wm;g!t$qC9^vCiWBU4lvV=@0&t3#$*8tUN=j0R?GXPf&;8@wmNSQz z+0RVa;V8Cof?EcG*LB4D1c4FzTMLaM3iY0zSVF9K`0AfPi3okP=F3d4$#@IC)6pe} z6nZ8mYjg9ROuAqsP-s~5tXt3wfn3*=0E`$D`$Fp%AjQisv)VKHs9}P(N$0u@ zouZ-&LS+>tZnuM#^;P#hV7?!ye?au0lG((gjhaXA(FA_dTc0xi<3|)=!vk;08nX9pK6%CdHM3>a7S5H zlynGlFJMZr$^P@_bF;FXQMR+Mv$3{*9l$U$HD)!`h9YtTz^6EfvFwzsZQmkL-F<+X(nYL2FjylTe~T=+E$nXSfldxF_wjzVf_{a zyk4d?NdqhVhKLi7khqCGu<%vFAV<2-UmsHr5Om~<-PyC^PJQ9!y1}&vt*x!WRG4lN zwRq14MA7>Z2t+$x^S~n)6uE+W3`!biKtog13>P0h{QB)fLi+mJ)4~MADbGe0D0CUR ze=%$Z5EsC*)Dg@zU|CK%Q%knpe$Q>i0dre*vKJ`5)73lLbU`Nr1ozf=aB-%i#Pb$Z z{%`SeaiG_??kQH6j@L;!4mD#XWCNGM&zPcX}2G``ub+G z10VzeK-@rv0zvQ{zVD-1Pxd7aQ_V zf~q@Rmq>b-bikhuabW^$jS8EGGCNT>bw}eF0D0_gS{>~tN8-WMU4j%LXi|#kR{cOJ z1ROF+PJ@GuP2QaZuvVSY_Ce)x#($;+*C|O+tt404u=1e6koY><7t(@koLY!kyNzEn zNuL)M5jk4;8B*9*NyiOw8(^PE->MR^rEGNp7-1JTH-wBeBKk>ACD%5!my?l{(~r9y z!2{sD_%btwgoX+On%+xu?0GM-T)X&QVB$T449jy6ckC(<&~d_eLo;k4%l+uro1eaG zTR>=`*%O57c|$`@Zr!YkcKi{@pTTF4v#ne5b0(UV8axYUpyK&5XE}Ut%CYvUiIi|K zgX;sp+xCoxLc)jJ{6@#c-KR!-rbJvsx(_BA?jSVu#55!cT25*dJc zH|Z22@;!T~`@><0n(;pdTKQ$K7S2s2Xv`GO&#Dh2#Z7{8P|fAOwok(h$8Ok;-A=;{ zP66s2ZEbD!DMqbOl4Im9B_<{oXPv0Ne_$bM@B3HO-d!J~YH4Yys#=bHTGHc#({GqA#9-V6D)zSPJ7M6 z!{aM|17ReQFG66uJoV_%U1b>?h_rQzV5}NC?bi;$LFC)Cs!PE6yWu1m+vcmVhRh{F zkSVn52i`yQ_q-?$;>5^n_LhwvQOqm|>z@N5@{S+(%I%(*ZPsB<5YgvYwh)rH%dR4a zYiV$Ba-ME4_9Frh;9Rnw4>Dk{^M*JDq!(7NQ+#}U`-ZTB9sV#-)FhIjvzZW&0bY>I zuYWJ?aIC`c7mxskG8L=^CX-p>l(Auu-4a^JWNvBcV>3P2g~cIAGULeCW%&x+1_*;k zx#Cd7NvsR4iYW(y$H&x?=WILc4RO@6E-}Y#{LBv%Rwpx$Ulriqdo`z@?blgO=|xlR zH7QH7TvxNeb5NWR_7?1<4y9bb{tVVSYM2JJYTW>u)H9C{BTAm<;BHX?SWOVL+chAsPa!-2OFQ|E8Usf;q zOKymBp6b@Dx`&00we!zqUF)aTP?6{slVa@Gp5BkD8J&j>1!arWU(P5R=BzMb#a*PmH8Xm%QOK6vl|ylFM& zJdQLE%AS6g?v02DWAeAaRpisccc9~}zFM^)b25dNos)Rk9jOPca1pXpp#O+(sONb69SVP(P`gdI(bM~Rx<1V#nrGsTAv%8!asKY`lI~K>Z`6_C{oy?)2 z9jIk64pUXt0M!Ia8jcO2H@QmZ6T(ITxKNjnU%mQ*x-Ba;mHMg*)b35Rv?fhYI3XQ) zFJI$h%*)H0e#~W=>1wOL(a7rhhCS+0c}K&RnI6wQN;xsUEo6}(IKkvCcp%zc0ckK^CH(ZI8?Q+N>rAfW97nQz7QuW=gF9W*jVE_MdbdfjH9qv zQR3r1Ha2!m8GJ^8yUzn|zQDYD@SEJ;V0-QQbX&bl(v_c@~U~7lR1h`*RerSnw zUM?)-IRi%S{k|c@#lXBcu&JBw7#4aq`>wydiBRq3QCVD$-Nnnhbm>wDZVpZ@-M@jS zhHn(#>^=@ony)qc;ywtfEbbxK0{}@ORAjvCfeA<0FdLjS5Fv2TUMBb$(fEi^Ne zG&MD4ru&5M^Q}KK^RkSsu}|ZUM9oPxMJl>6@3KLLk~5<%mi+eo&u1B zi!AuBByBesrjW;0d#s?^X5I8eLyA+#`FqoqJU6O%W(J5 znk&OwN}P-t_)np2P#@>6MGS%u@<&fi7es-uVtF`>@3nICaI!v4OS60zBZq1N?hkw+ z8}3@qZF;~Ga~xo>>Do%u;f~lKnW^zsFNc2he?)xmh zi<2rD$tNh7=G6abxa^u(72(*&2@(0iZaFkH8ZVbJt1HU2+C$TyQ z?kzI~GAWp7_XmSznW4XbiYAmR@oyT1lPRvN+x(Pho;&G~hFLw;KR()i9btv_@$muS z|EZy&=PDxv(_H(n%7F^8Lfb#>BTzF6Fo-=zlk|@YhCqb}{9QvyS!R)l{f}y5e!y$q z>Rk*lkExnn2&#YHW4Xf7h4rdiL%9y$FE+4!j#p*?I8$vFcu`T&KiKvP+tBsfzvf3^ z?(+6SV&l?E+dFd%#w0_QCg+IWn#pxiSl*oQrhL($egPRPJOmj81_55Zv|=#zpy@&K zPr8~Z?Y^lMtPoorvB&ug3OZO*phSFvAOU%`&4y;<#>!~fLg?6w^#9w&MmNOctLwK4 zP90M@VJ^z_JIAa_wt4SmA5c}x{Mz=psO2>)QL0-Kov$MnDcQ}}W7b~Fk&>2Q>4Lof zi+YOLV9m7MuZS&A!c*L}H`1IkN$R>nwlcG5tU=2aiN(MyfRCVc0HXH%JNuPRt1ap=Z5~PdbAH{#y-y^k$z6fhW|1t@BWg84X-!(Q0KNB9(T{(JHBUH z?N>VVo|4&Y{lbU6*t^V>P>wE6{t>vB)b!09-{>tNkTi1BpvuQPy!hD*3mbNkY6H)c z-xkNXmJn4TPmLp{Z)@VNWjJXk^yVbpUv z7lo7DB$QT}%_K?$KUw!ynq7oa$V$L~FE;#U zP&FaUiU=f~GBp)JL&Jf&!W#?v@$jx)8nR1-&NUuT`Y=@E)f;B>-e)Wrk$*rHzCia&OczeOA9h`Mje>=edt) zo|Epo>8Zi-qmJvp3<(JuJp$v1c(zU1_o`2iuHiGxeiyH`)6UwIaELG+VG8vVcmWWw z#!Pep2-E7_G5#Jr@pe+sFqzt~{~ZV>eKTLKk? ztUn5zY)7>ihIAM$2NH9K2;%~Nx|Y~+$VjD~e@RVl^Z6;|Lc#Lq1;U!?t}AF3EbkO2 zR{iFVSz!*yAFDGcYT7E}E-u@=FrMvIUOUEYc>D@?*)hyX^4|4g&hD&M)0`YgV-DZD zFo}Q6;^q`AVo?*~8k=m=;>%MsrL2_N`P0p2qHIjx70CsY-#VNZytw$vD}=zduqk#& z!2l-U3@c8h`l3Oy^zNvM(vJgO$DOA36KCBlp8WH&jb%U;epc-w(!z zfYlsee7Nn_`mK^uuET8eOBptXmTAJwfbxLuWE zRToIQ~VXA@wu6uDSx16Z*9=jx%@MD(OLEies5vAF}WZrGX`DT2JyKup#yD-|# zTxjxm`BE-C@4|%(e{B}#wLn*$fmnnu-cb%tWa*fTssBQeD$UC=g;3cpDnc@ z^3nzr7t&)GHsv^cx+D5Z^5#aw}2-rzC(Lx?*`GD!7*&=}1mH*GeJ_Xi+UR1jMX5pLs5VZOs0R zenR#G#~kCR;~%^XYWn)Ugh-2@GmL0b>PIa{+J&Qa481Q3u{Q@6bR|4-fy#shOmmF7$RgX9&NVnp)B5vFx+=C=_PkOS()AGqtKTN;F70J2*eZk=TaQ zTHGA@-kcWx*0YICarXP}yJd?J@CK$uKn3Do>D0D-;28xm>XoMU{UdlXnDD?%=`mSa zfJy))Q@iSWNf5@N()e96G6fi9dnV9RU%B8a@@9S!kqKxA(lW9S?X69XXRtJ*U6_)+AV**9+T^amp`xm)3QD-- z-tD$mSAS_;>>M0G*IJx&Kt2s5(+${AKs7!&$QAI!h>iJ**|yNmUjbXPd(3z7BO^M* zXTvycJ{9oRf6>%-yv>wRYx?ndmtQ&O!UtXe^uD0Sok;jqwGuuM9J<#S^XARrHV@kt zlnaxxjdoQa`%;Cvd}0D_LUh4uUCx>QmXM+^7SCg|Fv|fPLjR|LJ7E?ZQ^G?17J_u zGQ#tNRmP8SH29z`!Q?M-v;`>*pcDACQt~3=I^@Ey=BJai8EI*s*?m!9W{!rMiDpcL z&TGN{Rdr)ke}&gXO1n8hz`{F?V2Q@Ynx9!{9X#l(oggx`aT|I7AW~ouZs*SfuQ`M8 z01gRpZN2dkJ#6qJf;1MiwY7zqhfR@UkcDU0+P!=CWPaS`&7o!bhhcil*>*Q>(IPEJ zf{`Ij=1oc(Vr$;PK}iNheXaHE{S@?*fh*K|bNu=F4Df)0CBWBVW#qn3 z87iimVQ~cRWw2MAnVm(TusNtcn^Il#{ynI6^Uqws*yL;xt!_CIv?W~wgEd#HNJ5I9 zM>m8XJ`37#VG`&b!35I&e7vRi{lm8xmMp!pe(NWg;1Kcsbu4e=L*ZimDsFmZxoL4v zA)({-H8_CA!8SQvpBy>&W2jn@*_5$;clCVi;=a?|mNG*G1w1%g^I!QGJ(@aAET5KijZ=9)jmro#iRVH5V5IX(}1o5YbI*^zAnK zgrp$)f`;jwisD$l;FI9A+_vi*Us)d0Ak*o0H-q?<*4O5Q^-EcaX{}y3)Y~t`s8Yvi zCzMz$RaAU~fZ%JZWoFii4Urx!+%6U9uJO9AE)#Sa_bv2&Xv@jTwckMp9i#^+xo9z2 zBgb@08NWzE{djFYA#+hdPt?}CvX&vZ|FSPDwVuRAw7J{b4s>*w+=CdIted*8sUN0i zy;eIoM{RHp+uyuqV`oR$P=37YSgxa5kTDF_ifA=`6TpCeifZe^02ye|pjv#iQGlq! z#n5wo7t#B=)fpd+z&;jj?tqC~%+tyR3WW|`*Uu(m7o?=5xU5(Pnak7D6JB61H0|L8 zY+9e9m;PwxNvR0um#(gzQIilvoH`>Qlm!u#s=3?+dX zg2e(xR}%%e$RG!gB5<#E*|z|;Xho`|7?q0n@;r8KaWpB?nvGGdm%aVa^a<(cVgQZk z=VoBpF(;bvVy_e9E#X9f`Z)ZD}dGw_c-P5B~G~=g-Yy%s$&zge6(IVg(fP_wtv( zUQZxFKqk5?>s0_bFf6S8+9n>S?@bJJblSQj3-><6oUXRiYTn9zGEAD$eVwFsY@J$G z7}K6p)X~`d7_s`BBiPxRacR&4&u{K6-Y1^6#n1{=bocZ*OOI?lu zdp0{;oOA^a|9vwhWb!vC4uU1fGSrgLsN@RwV$D$AsS)Hhcn)4ZAPH-=5^P^OG8#2T zgg!vgK_RD?BT<3u^?|8n<_oAtoS~A~r_LCz|9Op7qRDU}Pf~I;a zUc{7R?hp$&&SZe+bTUJe$f61JLfONeJ@e;Y4ZT{jVp~#_fx3Mkv)0Zgx2V$m#UixB zCr0(~dNK<5{S%_;i1Imgz@~4|Vg#(?E(WFUl@KR<4DDHWq}(|p8IT(*EBRz(jz2O$ zd3L!SmXy*Xmcm7E5SkFsPX)wc3; zqbUVb%8y&0S%h?G*|FV6Gs19EaaG*V;NT5@Y^{^prU;5_qZ?~$YXPuP5nKylXtx8g z!hW?61=a*E*hqfetc7mDdrCMELhx7i(%ii3IXh(<_#QmFp})gaU^KMVd;6~zFQK0u zH&==hGVvwmCQj$Vrq)P3s$azH2}}N0eBZD<&1Wyo=br2pC`)ZLED*QvWuRHJ@JKgk zpbSZH3c;e$)>sZe+o8{|W{)EYX&g?EuX!jF)w+LxPcl@DhUTFm3y@P5*M6`V^-709 zPImUH5-@1N{B!94O6OJy*$kOoIaGB4I~F}AB#ra@J}Hs zE7`p2W1M*CH$|It4_)O@SuZQyUz0@7`p*uRHA9LPsh>h~CL?Odu{IvuZm^FB zA!dK&58NM8gr?WEH{H3f%M+q<*2cmyqW8KW8yIDuotg-QFQUat%c%oR1;HF}UTl(S z&a#cJ&N>IQGW80g%vetiV3hNTrXhu8rnmWyaWkROE$M@$srKap0>(>9)}I!lI=>7Jn`O zx3qD>lEurD-!ErIC_!c0bua2C%%P;*DT-eMBF-uaQFG9y69>{E$tc$`UXuWkz|Ru* z4A9Y=JO&^|{|#jd|Kzi$-h!}wR8EzmeuYtcIEiRf7y6F;$l?C2zu)HCp5zafunml$ zuJn)QVVz(9D1!F9hJrhd!+Gb;att(o3k4VAo5yCAH#$hPNpGim6PI60MOM{IyY2V0 zQJNC&KWntR6lIa%Rw>UYK>faYg(=~YaopqBt&u#2QibN5n9V1)g<%25=`nvuF_NuU zA-eU;v(a2o6bI5qsM!OZU#LMgzP=-5ex3TzYB5g2zg?aWV-+rV$b`pU_1WKI$s;<^ ztV?X2_NQM4xlV~4tQcJ#yuPNh-2BvHDQ&+`;}NHSP!RvoCr9eCNI-9JGzgYp8}jI- zJ@A%eu+ulip&Ty32XRu6!;|fc3#2(Z6MK*(C?OnkFVDh#qvR^ORInV$Dgd z8CGFuIb`Q&(y6x+Zcyz6Jm>bP^1}JKJ&nrVf*gN~@)TNbQhD)gooQZ^9a85Gl4g@1 zyb2)ksqR`hCVSN&liTfUl7qmzXV2Rs>o*JRWja*qrNJRAT#Fl(5(-d?f^`Vx+P23j zQNR%ZZEVeI9?mZv9id~@FsCKqjt9%&n>UA`#lBr>%9a%i+V@f301neF*Px?O+Go=)dl$*}y=1Xe}+sUm_S~*}LEA;CWvv6t^!-(q} zO(S%&XxXe==*^ofmGPXLh!P#1Z0aqDZ1cdZ4Y$#7yWh8V(N54?Pphr*2#BjF4q9}1 z<|X|kqV0*DCcHnJaNut{4E#v2W;6wO9|+nh$DePUC!=tJ!O9`hb;vEzUed-Dwq)E; z^8pvgBpiv~5%n64ZX7#zb~=kBzy$msdLJ~;gC#r}kmTGo>X#FVan<=d|%R9n8- z;PUvtJBjP=DVaKQRpovD2nTOAa#cNPuD!EfJuDp ziy(3!NXsX&v9S--ao0*xVj`Tv8K|>Sh0o5R)d7f&eyR{s2-=Gkb}^P*62vtq0TU^o zKYs>e0!7Ceb}iK2oapspvmV8PRk7bEnBW}~;QdGR(6>loXP}RCYhb-P-WjjhK6Nj6 zJ<`4J`CCPOi+RMvAL4%82TS6KKV!q%mz%~d0P+btkiK7Q=_v(whJq5TIHg2^-ImC=IhEQoEFb~+5wHuL%RW}TebgXO)+I!^78pAJfPlnfd6_e1Fj7dcu)G|!Luj=?Yx6-9 zc^OXDS9Ntxz|;!-;YF*-u(h5>NjXdvT;}wPG5FbEq^dkS@dO%OxAy6lJIg9>J7$0T z)EE7fUX_3G%wsp^%fbJEh>e>Iw2*pHBoH26T+Rie>h9oyE)ZasZDr#Q-F?*hGMgX| z!B;vuJLA5MC!PXDAd{f<;dY1{0~)?W1_--xLjVepbu2GPreitwrgiD+!@_4k!ULNS zZV;Q};7f=D38;;hOd95Mm=v&j@IjM{qaq?A6oR(RqB~(Gkxpx+Dazazc0psw6xJ|43PyelM?L*JT*w7^olbOnO`BrF=)qG0CCZjJD=yjmZleUvX=z z(Zml}52|SQ!$<>l(hkdu8v)RX{So#+YwNzdg0f0VN3{b; za7x~@vEcW^6FNd|=A*5x&!hx=dVVHCbdsi1Gcw|~qPt@s%wdSr)Y2l+#JGI_ z-B)r!>40_g4$5ppM;o+njnuJEWqNA7cCKpuV{e29;v}v%^6*5%i0Ud0YvgeV`@hfDpH?{e< zQH&yFwNafq-lw}-w(6L$Wgv9)=*hQXLeCd#w}u^@BMPNc`_d8cU=hXi0*kF#w+@-v z5*K=b9e_(YW+O?;RZwUJZP%YuRJwdC`;xF`nO27Cou+krf@S>aTm=7oTMY(l@MA#fF()vRkPfd(1xe)ebuTHrc|a?dlNobl8hH;3NF@vG*rFt zd4F8xJ+I_=TBGxT@O`cF&cnenSL49Qeo1jzd`TM7*ZQZULbV&HKc+m2CN8k&S?kgF zT}JhE>g}$BKPv=6r~k9{>jI^8&VA-k3HPB)>d0U<{+pwe4j!@-Wjvjm7d~L&o|r#- z!UVQfJU0$@RC#1DDKs9%So7_*lTt$1nWL4L3r++tM$OUR8syJL${MADY^C5cu5H@= zoyM`-meM$B&bJA#OgJZp9 z@D)S&Hva4}3$GlJ<_kWpa-;VRWjw>{QDxuvRAlQ<&b~^EjV130DeWKg)K&KSaPaB3 z)b(n(S>v(S)r0j^E$vqx{-)~FBrafph^5Xm7&+Mn)q%@Vr7GxXv zoo-KF!GlHxBYF;~Tlo6VNsXkJsbH(VE#lxMlG|A08m*jb1A66uKCGJen>-AKyH!uY z+23&Z@&khlZ?^x>H{X;b>X3Vh?bC4AlZOi9ZrM|eN?FD=d4d%SgQ573$&Y`)u53^2 zx&%>MkzAvWI;bG@`obN;*ie+kdtSYUOwt23wUm@-UwryIDOO z>`m1pmOsRKowT^RH-1|&e)j2d+<#th09SLweOF*?))*1Lt5`eZFe0P1Zeje}|KrfG zjRmBz)!J8$U9t*l4;e0gO6BK(1lT9iv(tI3BLru-% zm<(h;*h`35PRRStq%&{0l*B($Ft7Pg{~uJZGWUAzkJ>IdK)eEzRK)~mS%mffAIXa!RcFn zOv&CvPM``f^&JBP(KVM7i3`89QGB2yFF2Zk6cbPY8*58}N8TUy?icdMD{c5FGbUWG zSD05#kK`$z-xew6{pY*(t;5|141y-Uupip8uP5v3>Jm*upk0hS#y${bA#MSJnuJtZ z_Qeu+XV{WRWm*~v4>d6dmIRTI_Q%u)cT6U;)ux?o8Fl}W|GZZ3{m4MWbCTuaVWEUr zUJL|8&60pdx1z8kd{xogQMupGfmSL!!H|xshN_CfGW7O(0=QKEhXJc;iuGa1R#O#2;DF57v=y9cqtHPU6wW2DTpO?qT?*t+=6r;ruXO@0uSDX@?1 zy=-J$1*2*}Egu;eaV84~!Q5hL_eW_O-~V&KJHw!CdtT-1O1CP`*%};Wab{C{$op{H z#kbw_ZlN6dgeDbK z4C!hoK&OIgo@CHZ_^@L6a=1#W-@GwL5s%fsopZmVqXXa%lK970uZ&?n+^H5JFgPaZ zjoZFJlO^k=o1yqbg$W}yPT6Wo%4*BNdG)^kp=+xsD+_jf1j~cIEfe@=u~c|1_!%P% z34|OJi(yxF|NRMdt{9!vsxj;|spl`ud!}lgojq-i|bB?Cpu=(9q z%^FUqk}5T?@5-v|?y}t`wZwQre8I0K> zN5btp0JRnJ|;?ZF?Uz9#X9LJ;!r{OP8x>!x`Vb@4e&ok8>Q3{aZQLTr-~etj}3l=6%JRz>Wq1ri)_b5Ed7SZRsgN^!4^? z9!$3dtQJ`JpvEuC$9SUTAMA&$^s!pVCIHf>9mW9;1;kl$!4U;Leq!Zwb~YqB3sIEh zJQks}jdCAuJOf`7h!EotTWA=?N$IOyw<@4EfL$;fn8Lvg1bQ@V)BvuBY;$#N^~8bY z2|6Jmp$}rfC1$&d1Y0}CJ*^4a)?%xq7+PhX`jg$i_AQKq=$-@VsY->>WtNZ$2A@Y2{f=) z;-jG8Iszb~C>nn40JBf)A0o`vAKr+qhfE$+C^;ruz!i%O*aF#@n zvmYPdpsS&(O5{X5kjDl1^t^>|#S7UfFiXs(0L+!GLvk4y!{8tgCPQg{NDIG*4L__{ zfWyc;2%m_FK_pR8S~;+V@qP!r(_x)u;o$y$Z2%g6cvK&BnWF+gB=f1c%o_QNKzkPd z;~55{FgO~>uEqVUF$sJZ94XADXHOrx(^&;bvANvWiXADjSxut%Z8U6?)Gxzb?H>&0ZwF|@2}e{91OQ&35HLS$D{{%(G84(BPGG>1rUW+;iC^foEEypzZV8bD@K*zLhHu}^ml4~A5M2FGFr66cu9V`69rnOApzCzesrF9 z;6`pVq^Q=tU&EItsdVle8$%-9;Oj9+V*%tsGUrh}4Py(G0=(QJ1XJSR!PS8m)Bru9 zBQEd+-a%KRH3lqcJ)p59MMW>6P(b&2UJ#xXNCTY%KwS)d=_&i{&!68GcGi>dWdMBz zj>zW=$%!$Zz^|&Kx!!l97yJ)siCrUTp#JiYss4{rzp^p^FH*ll#Q(F@?}A@BV_Kx= z6QD$iQ%W3p(Png|tU?eFKA;M~iV44@;|5{oZ?O4I5QOL`ZTOp!KQvH1J6)+7Q{li+i@YsF+i4Mpo5~KqM93V zB&Uua*IE5`n*o#M$OL#V3Pma*ElnC5RU2^e43LaeuZ<;u3&N4?*Yn4uYFkWNTc+V` z{#qDiY2GO7tnqDJG2}9=-9DI*0vL&u#ArNAPEa%NO*bJ3bmVaD@cOL(;-c*be^|J4 zhEDxo_6-0k;<>2cDFO@xZAAT5%ZVpXp3GNZ?xWW>2|M-3=@$T15pae4nr+*#nmb$z&|$@tdusqV09^VH(C416RNh~KJ@Dx{s2ocG zN&o@GA&ApKT)Y)@Kji=64=4nd>OTs3BMZTr4*+Yv&E=QcZASw02$o-Gxr(^uw-qiB z!+@s-s18==5ON9%m>!rjFg>8RzFFJ~cIPuCNNHRiHp8U9&Njnog*<+wCsa{8vskH} zZ)dy%ya!~oE?{^I#AsmfF!~s8Dl?Co;~XdWq%-QwYH4J!ogY2Zm^;(7tAH4L8%9LT?5sLpBnmzI*ZME;j zXxc>m-?tq_ZVNzgnm>Q(XvVgD(lE;VVYq}=xo!HtJjrW=q71FlNJ;Q3o?3uSUz=47 zc-b49z>7c|L8>HM`ThIl$tZy3jX53y3pm6EqP*D!kgq*SZF|s9Sg%R1hSjTfD*fG*RTIS z<%sok{yRsUmG!Vq1Y#Dx$dkZA;5zmeoeMSuow(mIs>KsjR2KQ_CGb?vfTSxV+b&!? z=2!C$%3MWH+XDo%R`b!H(Sdok)OY(;&uf@c6FH=aT7nhRoSYE7)liQ9V{eZ$e5F)i z_6I1s@ z`dE617eNidXJ^?v)_ib3=;v=>T;$*tl@a3+7+xzFTVHboV?RmD;u~P1QQ&^mUY%q) zl-_M;{^0U?>3HM+*rEfa7v0hP043K{H{PZDGU7}+%F5>8e^fZ^tQsNSv{Gz+13+}(Hu zu#4Xv>VtNmAP_eD6+$0u7RZ(6H5RT4DDj467v9HWubRQ2JQ5ETf**^DiY^&}S8%Fi z)~)Y%uA_pEYR+%JDqEf_2}M|(!d4fjy6C#ry$!yW-;%lL05tf2pM^qvt=tDXaAkq+ zba!(jWxKapNV(_xbOh9@=)=BUF-%vA4kHcuD{1kXcCmhhyBIqS8shF^(nA+GJS)Yy!hS#(( zfA#@*Lng5m4J=p*-L~Kqva(99RsqrZof1VXXY>rfqyRcG_yqjJHn7$W4aYDH-GDGg z-F-(dMSE2CI3)Y&IXM@AP=h2Q8i?5;!hyj_17;91c7!FpplQL=zgZauLZUYhcL-cl z0M+Y<>NQN@^2Rel-X-Ji+|R3D0R4^O+5lAm#p{z$eFo`zpzHX00FAy=EFK=|`G}&< zkEsQQ0u%-J;g=i!zCK0A1MdJWLvAW2_n-TJ+j8yp2|eDc$u=g>)#A~@3mXzz zMVXP*zxRJ3CP)YYPw9NJ5wF28?FtBN!W7F=QBbGdVtM}-MvDT+P-RUGQ+qT9#&q(e zjkA(1Y&QT)AtVQu!H5jzX%MIkdG1{R5J_;i{P#iOKL?GS_NLIL%SEZXXH8?{t3YIoyM1j^NCvE*0JtKY}W!Tr|l+iR<Iz2n-LBQ8lRcb1W5hnQ%s#X=z55f(DY&G9H4Oi+&LYP zn2tN@xYG67BzuMt<8E-hpw2OG`^jL-Pd^&_GlR@jb{sLmU`vIi9sdbq+m*^m-+8&;EP+MJIfEXC0Z~1?F&DX8IF9`YXS8c1OO4i=;AW#xpXUJ7H z=)@VQ`}gbD-!U^wN=`O!Q;vZ6vbdC#zVze4KXM4QTU`Y*C&hd!5WPsk&;qY zno428rS-V(_0BQsa)WQc=)ANBd>)+V{*m>qD4I`V5&N8^#(%RE{h__-7lHnu?#Cbp z?D{`u-LKz!5VHL59Q@_(-yehw#xtY<+%DiA)<^HFu={O>kOcB)yuZSj;au#t8F9N8 zvT(P03o3sP;s0CiSQhQQF>@+9RZ}hFMk$&p^(3U5?9l@MXm=-hTH33mEpYTzyA-O&Tn3^=>%@9q1|_4v5V7gMP^X^)h* z^Etgj;d{=_j$LO4w=3z#Ae?H^0ze*k3I|H5RKaxs^c_5817!5+;G6)8p8|kFP__q< z>mbI*VHk2I!W2xoI3uf|oDgHi+ANbk!KIJaBzK(fc<4?xT$TSf$Oc5CSGiJW(th^%HoA*?%c;?}*iseS7~uQwP6PxVy0*UonMIG$-6p6G)jeSordUN$wArGruCd<@$;;QEKafW zzf`pI^FX|L>uFuSz)6pz#@+jgQgg{9-SGfJ3of^aLODA>i8DQPQBNC<>W6nlbGr<6 zJK z6wEBB*E*3s0+;#lQVBbhYU5~%{Z8fGkEAU*aVaBWzFaS6l0cs={IK?R6X`%KdZ@1& zk{+Dr@MLoVP4v{97h>$GFLpU!YaNHXZu)9<{94Om!WXwrWh5W()-;n;>=7^x$9(!k{`u1%tHYHM14_)~ z4HJHT-YiSab;B{@oz?wXU5j`9`y7;)Giw3{PDHiqisj}^iJ{afPuRo-0E`{htp5YW6 zz!{-<<1Hpx@KgW*RH3xPS@{Uwfl)wV0}+IjsA#p!EqXYjI6|+;=*)JICXJ21BrpGH z2-Ea^Qr0chzuihTv;ueCNjz3P1Bt-oF*nMLal zii5fSJ32}U zF)pkuImMi29&O~^{9HyQVud3>f^9GWZsG0KlO1*ETp#d{{AL()nq{fOW%F}NUtyyL z?o{F71?_T8Ul$hRIV$SQVr)TNDx(>!m*y7;Q(iE9V@0;}<3F1<`(_|!4Njg3=#h7a zW4_1r)JbS9*goL2|BtC15a7$Z;d{?vSZ$+X^2*60D<+OyH4mz?*Z;HaZ&B_xbT$56 zDM#cuf6!B~nJBr`JP>KC?`PW?2!?`rduJkN!z_G;n(m-$%E1<8WkPDq#qmaVLj_B!8Vi%zEU9XOk4qy=pnbng@s` zv@h;4As7UP(|NlV$8Cn4`n*`m*`;ya{DB*7sPp7(Mg~qZ|FLI;gbULb;g^e4_wxs4 z%xwG*lTG#d{b!qHWA?tZ%F7K~-)+mEQK_(!&wNl1lgxU4W@l|LL0DjC0OwuQ#C%zt zT&hAWazzYz#GgBjC^0QRq~O^ZgEruuw;@IcZacU#ilyvN3NpwaxNk}2e>m2L{ml9l z-GBbLC}=GlPHSbCZTTmGd=TV3$1%bG{3V@qD)X!4sjgwevN=rw0C|K8Am04vXgRuB zMDMc@*l+5W%{9M+>$K|4)Ci810T#CY(C)?t)!T0Vto?qoKZ(6%? z6@{={P%*o+Tkp=s0uIGtB3G=+GelKlJw5iJO)jp&3)-}Sn|WJr(PL}_o0&cjddh`Y zh9`|>4LYd*t3^Lw@`n=e%e6Mjcf=HeIYoM3H;L|8vG2&=C=^(|0n;M|ko16Ng+fe- z-oSGXrVvAd$UJ0c9Ssa(;zdfpZGad(B&5N>MM2yNa`IW@saQK@*;nxdhO z_y!pOV1#qZe z!b)%I)czKIs6g!lKIk=Js0gB>pisNgj~(j{ORzt9_rpY0EAlew*mMs zrzj~k_Tzdx;aaBUtLiYSbb;%pd8hD7W58jG!A?f`zxT1}uk8P{0W! zAm5DA*OdCf_=lqX6Aa40$o;NKm`wfu6cFso;4uZiH6dCYk>V<0T1~kJ?3Rnc(8OGtqq>? zjU2*L%B{AFEH%H`rzX*nPA^R5YNldjiJr3t}A*KPvBLMuFhffx~uEM_gCH9 zcL(0%4jM+?Xvp{G^g2oe6#yvxOb@)&+Mb{&VUz`xd!fvjn6w{kGhY`$FUcS_Ix2UA z8%1A}bT7EK_J@0nz*Povg)z~Bxe7{7VDv;dqJ%=!%>ypt#E=1ABFv64LrxDrdaEH0L$q$!)eid^+S^% zll-s;gG@LpiA>O?529GU560!HH2reAC;prnC70IXQlsFAzj#AE^pDLOf~j@-tk8{2 zz_7^Hp~O|87`Um+Z&(4^_4SYKNs=PyT+-`8OX;ZGCk=IUfEqyOUIe`H`N3QKlenm;PpXWl=&{#;`-hr3k49XRGxd|qhmt~7uBPKv zN`-5Raaeqz7qY+^@NmDH0r|uo1%)3Nu)SnA1pGWL9u8F=`QVMIKYCFJF}<>L0EoS9 z=mN*$I@5>V2(iCds!)}rUve*J^agqs7`C$hvo!?+0|Owlg;xME!8Snm5N0gT6d=KZ zJkw(bJ9{G$;*-@rH$ipKpG}8U@W%|yn-`IOFTxw5g@c7+NxtNI7YX`$7TDL&{-f!) zm@V*6+Gg_+x|yU|q&D6bC35s8@h@dv&o8&TIO{MPC+@G6f)wBO~JL9Yo} z^X+IF1NTVJvyh$yxCO-8-fx`)UyXGU^Ca527nrvSuYQGh%7F+zD$0_ahKA-bHcDCQ z5vD1BLgi3*el9e+G<|`5D3Y#@B}{45Y=9~p3Iv6j+&nChI0K zWQZ6s@ee$knD0j2b+AukZ>AX^zeQS}W*}cdEjHzz+nIlcux#aN(BjE}{<~y_d2=w| z|9GnAI`ioO#5C0gwTnk92YN*e_n=a5;9@{GkF28hlofY$Mx&i@vz~UzXH~n#o?yetbO*^ z>Ggqu+rC!RxouJnvNrNNEss8l^ql*8)ZG|T#a2>KKm+flNSFpYIS<%pwl|o0c1#>?Dh#0u@KpuzLFx+_d@j7l6bTy*LfL%v zIjB<0ejxhb!GrIr7&lFJv?+K64VvJVpsO6-O&>xt@Lb{L@LR$PRT_LWdW?-mQ5J15MTjb(CWxrWt^uU{5gOA0ZbJh_#R z_gwl&Nu$j?I9A^x4dA$H! ztU}5u|5`&R41mzevtRHoBu`2cB+j1E1KIpr_HcfObXk6#vZv>>u!rxZkFF`*U4NCF z1ju>NRJu|oQ~Mo4ZP5N>rl*mZO4ja0nOI`tVID5vH&0%bb~{NVf|+`0+CiqyokFjU*{H@c+w_Vw`RCJ@uC{(5T)=_74!<( zv0^VB-LH?10%m|RcvwfJaHZD?SR4Rk_XTPcL2>rvTImA1u@wQqd=}W|Dke@=OxY1Q z=?TiIY$*sRc?BzE0SKzv;^jq9lVDppqfrjWu_XtxUq?RmiGO4!D{>Dh|nIFkAqi^%)Wv0M`P7 z3{XNBAxjRc1o?FcGqOHDnG2~)AgUUJgfo!0!G8gOl(%iAp8uLy1bbq)HvZR{&Bph4 zo4*^{js8PwxBak}PE^zqQOt=5_T29|k^!Es9Yu8iSiWr!qrz4z1W{YV> zy}sX(x!m83GP`%Y%*W)rVGxozGe>jGBEESBi#Yts-}gHhXOfZ){l&ZI2Gr=z2p+94 z`^ME){GCW>wm7ymyg@z(=VDz)>&L`Sj44=z2`OkZ_{uuD~z7Z@3f99F{w|DAT|C*}a=$`N`f(r9YNHL$FZugq*{)=I6&v z18f#wCDUGsC&SfSZCpySmVGqH|kujogr`nJqOMo8}3_67WDP0 z9U@;MTs=Fpw){4<{4!GbKNkTRu`~lebq|)p{4@!5$C@}+Am8RcR~CU{5WB%_&o9W- zZYFsq}) zY`&p4b0?k1O^(>_n2)nD5DCS_59Zm8N@>JIN9NsJ;n&{Msez$4L)HC(mTqRp@b8y` z80gM2eBv#lJLm6FTu!ul_I?VLPv}jk8+sYk-i^zbgJ~LqsgAZo2mR7+H#XvI4Yjl4GBARX~8e{+Y)1I)7 zd+WD?OYRY_(`r_MDIp!fThX&4<%Pd?PS0bMN6`1{zOs3_5AB2_ZW-i5ap-Dk*nf{U zb|#DNkbj*MDH+N*I-gHmdT5py@t@0}6{@Z%c4MUT!wu26uZ@qCttkk$-q-zC|IW=O zSxgp|5+|5TpQsa}CBq*%GxB=<%@S+nzOw|D*S60jT4;HO^IGFXH??AJ=dE z^KyvUH+uH_^l#$GblL4!d^}b1OPifV-k>=p*AGTP3NrcrpG(bjwtiu^h zFK|+mUP7VOd!ejBmf-f$6|fF^3+oPG_rhmlzy3=zFjW|1^*u(v66kX$g=)?D*Bfss z$X4b$me>hn`;3N1+|GwUtfAJpv|emZIG8e|O~ILNA;0*9-(`V?6J%JL!0QPo{m!MY zfzJ_YAaj-!(4jpUQG+b1-^P+7Jr9tVysWrCgJqF@>_^XdQ*U7(-gRCn>Q)9pMtPqI z=#`an9_;5%&xomp8XFN@$5(bcu8nLI=GT`QW%8G+d4DK^z<;Xos{ZD3(cUIkIZ$T< zF9r)G_=$5PIQh{T=AuY|oymVngNsv;kyE$# zFMHfn#CNAsX=`yzPd=bMQH{y}Yjz4OrDw#IQcB$NHSaE8s*vl&>KIm&2X{yO(k(@t zWet9$?b|apFNduTnh=hahD4TFKNVK?h1JNQ4=#KK7jRn(xmMiPUWr-KayP3@s^n2r zy4*F1Vq`SXZg1P$oaNjDAF1G8RAVi(fyQRUfePm(E0;Y}pyw`PsE&~|U)zA6IYfDs zM@n{)_24LDnWSW&SC9UU_t^UVeA`7~Zg^d`L_z5?Q3(1Pqrn4@lKeeGiyFw%ksU2J zWKIlnxQHZvP2Rh@Z%>z_KT56~uC;xX*Zg{|Uq<+{E=Bj?o*gL5=PCovseFIM`cUGn z!>@zyt(fd-Nl7o9T2DS^$xKPLqJA?;TDG1im}}4A)gl}WU?sjIi(rZaj{}4+*KxSM zkD#`Z!pRZat{gd48pAA?zez){^LNAC?q}%VMj{{opxZtb4y@|=G=f3CMYc+xPGy?v zIt%DldD`NPvFP2I=$7)d47xLQwX>&Yy3Jb;PLf|vi^W-mG!J)9KUGGoGbOcTM~SWt zT|g$o!iKQGO~U?&ZhIlWcXRGxNGvoKn&YaN zM|W|&b#;3ku84lBH*qu_3COe^e<3V3$+3W&;fVeQ7BH!W{pRb*5L=v&Ia&aaPuYPNMuzh`o5>rc+1{nx8!16 zXB9G2f2P|@Bui((iw!B0RvH$rGo|5uIgU!8s5(Q}_H9KU2LcP1YQ-%3t&m(%8*ZRH}e_ZHp1bJFNIYDh(S{w#{ z9k{tsz>3pye!Hh8x{=*ow05zH;`YYt9QvYDkQPX(dg!p`K%jZdW5gi*m;%?hhxDGo zB{lLhyo|0#r7LEpJ~H^p3C$-^CP-@tEon<+x3ArmRorU4s6mSDjY)i4zs>&)wQ@Wr zwq_u^VWed>MrKcSiE*JVI)^x04>Tu%;?hQ3%D?y5AkSgVi9?$BJk!FRrI)j();^<% zgoN*61#8)uiXWtNiKI&@WC~n-QI-3Ioba(9`9^H-v_7{>x6f*%q*-6%L9IFM zEdmLklblY1s=u|pMwm*R1DK}UO0ACXWl-Ga#R86o?Y?p2j~^CC-+ zt%0nbRCA8J8n=r?gk&>XmwC}q7BThed%qvIXmGn!yPiLGCwHq~-`)Ls{edvHasC{e zLlJBXKO?K!rtZn78C$`@Ta0E(0h}g8s1#_oRe!M8lnVR7sH7xiuCmRp1{1PjZ40E! z#`B&t2@m6xZYYfgXyy#hs8Cb;k@K6qWWl;uG^cj-k->$|B8sPXNqGvdmTgXYrk;GN z9Zs>XAS_%e5v9cpTtHbjTXiG|-~g|${F>P_^XlxagJp~hXEZ1W(B4N}Sw+8G3H_NC z8L2%K?&W>NF$q#tEj3d1?YtM`rt4CWc^ufKlY6_n%L%PR!ly?<=y`7EZdp2_=|#WT zzHf=2RsO7E*4!zU>g}*O*hmJwqxr|1LMFn_{8+xdacA!m7^7v6-3eY>*0^gP{w`3y zNOy^5AE)(5b^m7#-U13~v(x-`#(@&bMXH>KYYR7C?^%VRkZ#JSchl7bPPdM-WT(E| ziVM1}PFf)p>>qY&YdAM2^>B&fujyC~Oi&jOFJ5$8LvVyFjy8a4Q&jb_(|Ky*^W0ou zQJ&LKMEAP_26Tp@pv6XFWcEHwf#E$1w}{`4ujBV?8hyNVv}g1oQ9673N-kRaXkY7C zO8-E6*E8YZb4!THJ>N|^20j%ImQKBu(hobdVD#HOg-n~M8n_Qh8Z7B6ZwZ)rCnYkDwSHM>S1dn)xJ-;ifk*YdWjE(b0r z*09xo7f9U|Zl!k2FIecK4Q+^JcgAJ($@>NtL8@iG1?%0X`1^84$SX{{Ladq#A9iWr zUT0wv+1{b_j?X*{3`0+87&G*oBSg6)!UY6CRq7)HP6QMRjxP1DiS+UDFj_X3aj_9& z_V)M0JM=6&sJ_a=O_>xbc*VD2gKg$av?kl6wJIwqi;h@eo`xG;pfkz?wZ&>V{6lrm z=SeDA*Pts`^ixr8%Xh8xqD80_BvM?{7b8>Wu1HXXsuUJJxo{`{DyCn&-!sn8E=7Tt z*5+?>z5@lUX-CepH+eay4AVJTuP7i@1XNm6Ym6)EHqx~$$*}(a_1fdxCa9S~Q|MXH z&;`b~X+wng`v+*|o1IM-Zl>fXHffY5R9& z&HQ|aTN82Kgx6)u{fSf#Tit^rF3VOndpe|lGeL$?xIJ3vF#EA7;(0bjJ_X7W1JpS( zyWlQ5m4`J|CDrUye#Yuu6*!%1y`ytOtlaYnn1Z|OPoD2^K##7$9ihmlL}{&~WT%8G zqdOwanvgcu)wE}QIP+yTU_UP`WQy8RWjm`Jvy1QeYykGjIkFj$s3=`8ZSy`5DaF+a zb%eDQc6FnHwH|jH8R&Tj2+JDihDF>;x7p~Jc2BEIF0zn%Fx{ioOPS=7I6{8=6tU3* zZx5WN967k{6?0z@bOAw5ieJePwM!AmwmW9$0LIv%6c=4!s_7Kz70QV#8+d48yQjlP z8wdj7;E^LHchVQAOV)K)<1fAI6@Osf?x5kGMTS6m6mBIyaGtIMbFl!|Y1Z#fdl7;M zYgru^=0)v_^SfGwe}1Y$Q5{MN`>b;(E!*Tp0!EBf2Btb&?U79n?QwGjR-)~>-b!R} zkE}1OY!5_qnQvrQFlc~D=GQ+DTFTTm={PI0YdEuvP`ORea5`H67Z-W_*W|3XS@?}x+qCB$wVs?>rSX!8h4 zwAnEmhmlET@vQQ3#i*f@LSw@)tAO{Y^LRqvqa&_F++N>EB#!2Wg z)%L-Ek7y)vHo{D8*0b2g!oekY1LQj({hcnF8|zux?@F=+V$-TqkgfLr777|6p=_q1 zMH)s<#CX+6cXl}srQ72=uZTXC=PrWpm+q!DSqiRqY;D{MCS}jZUK(!a5wTD;#9 zNuG2s=Q2j8HU?Q+IGL}Qy4y;?DXL48(R9@WSH&%Por^b_CZksQp~XW?o*WM^diOAB z4>Z$(ibn?Top9$U9FCEq zIz#REYFb4rTmW;4+)`(oakg@0Eio%7;u$K^&gl~3FM&aS>=aVZc<6lw8&;rswxa{^ zPl_CUeYBQjs!J8#5|uV3zoqWlzGAQ6YhFEnuGFGcXl(>t&wPf%l^@RGp3Wp&* zn5(P9va`jojd12j#a;FQha5Wfz^{n6EpGS>({)#k%Y3CQj$x+!m}KYllY35dT^6mG zYT(Zcb?U|3`za$mN%nPx>e8;#4R5&Ij}UNU*V-qzuA2tCTuiri?4r|i4qc$4vl_i? zGjv>8x5{ML4_jZ{3|wSy3b%K)G7Ks-o*M!Y(Rvd+ijTb(M9;Z#5cmfM@&Hx|kQJEn zWuWIWgW4&8mWp^$K>mph6aQ0q{?OvYe*N^xaP{yb%E)_^TT)^++zm`k;d6Z>i}$b_ z;k?{>?Ds+(N>=#{Z0tww z+T#olLB12#73(qvezxku;V-bU3p8x9&*(kET{v03049O$8jACG7i&j>j9Gi|u0Ish zxd1HZZjl-QvirAUK2R5*1%hDi!d(6{uGhv|Xnt648ny>!R6mg*z;|jx4F~cDD_(oJ z`sD~VPT&I-2k7!=WSXak?@Gf1y^|>gGfxPpav|Typ4;!XxVSi2A^=n!0P7;FNj#vB zhljSL=pCho`>a&G-OsQb+-HIh0af?{N{#0+PR|w-7hDiO=ZbQEZ_%g_$pL*fXbxI| zS1GSP(uRDT4FBA%xi7Vf${MFUDr&bT28Dz55B&tHkaSGoSpU*|Se093eRm9xzhOBv zPfDvcArq(7cz$JMII%C;rECV_WhA>KW<(fL(uv{c za_`Z`jr2y1-)BYs4yMo}XG?Ue=*OR(phlJ?%rP*$k8Q zA4PhW(%}*~J)wB8*6%I=k-nEwIt;&ETU#6HmcqavE)V-KxU)(O)p$iQFU2$} z?}x;$YwyP?N;Yix_^|>wm85`t=bz4Oc}<*RHFOZ2^8&hZ7n6~}oO(h_^JWR8C@cZ6Y@pKL)?hZOL_8vmR7H@qmPD%$^zp3P_nGSTpXKz*F|=rRL80b z;Q)Pb9PcW2nOb{jFR*lO6>df6&HP@fYn%r4t26c`>b813J+_ztjouh2o5*3Jad_@! z3)QqmQnX&sw>blR0ClMR`ucjLpD*-xeK>o%u_Msx{xJYDeD;jkmtoaZNmDHc{ydl$ zB8t;~&s>I9koG z3PtfJv!@PYukp6}(`wm0qo8Z3^*VA5bro6-EY*&cAQm*uyDwn3CjN#j{at{njD^{2 ze%A@J>og`JV@q1)fG2{dKT^CATpx$xVt;dZz#8wdUYc3MI`}@7YeTi!U7&p3NhFru zv@Xmqd`#dD2(g)~Dmkb*r#7bC^SkYHAYds_Q~g~m%z3`X-=Crb$iG3+zh?dg7?Y0W zn#M*n{XPtJF^v4G^C-I-#zV?>Ht4XM%!rfmxsQn;ekXNqe<4JqIX7J8osY{T!GWm% z0OerQWkl?(*J42S&%xURCnob28u>h48w0Wbd_CyKPh6(U^)Te(BNDaQ>IJIPZX|Wc zbu?S2+g9)LSSiblrG3cnB|cjQYyfNpQ|TW$;j!^rAVz?|I0C&8aeE0hskJ2@juq1w zDJUYxS`67ez3&&5R;}1?MtElTuOpR?YFfz`u>78W3@uQI_#9$c~uRHr1q5>9OISq=qTMh1OVlzPa@pM19`ThG@r>W0?WR>vk1VpHkf(kHB`5Iyp zipk&q&c|m8hGYTgUdxhI^J6-{uEfe`9+0tIxemU^a%D%#35-ZR_M>66!2?44l92%Q z*Nr1Vx^Kv@7|Htn;5KbbLXtoYd+2a5=p}K}VKV9i!ogv|P_AI1Y+H2xWtwx=Vxkd| z=J&N=4?*cdNv7otFEzZ%%gZaZw6U?-04ip{{0yr1JSac%S%ozoD=ZZ2RtBIoIwBoA zvfpRvVBEe9^fXM)1;xa|%)|=#5vQ>^xYJTQq<{f@+A~}D(MT!!=pC)Od{xuQuw&GIYX{-AUHMpp#3o=%zy}kD%~?zjJ^RH9hjC%0NsgsatDn)*>|pN!JDwbErV~;%@q+h zHfajy>t_u=raqdiMb$HB38M99Q@}RN$0Z?m!{MKn9u7Hy6il@p1&Kp=ttubWjOQ$t zUOa{D(ezOrX*}9Q)VqZCT8BGB zKN(xeplmlbC3u!A)yT&SVq=eWr8SF(CTgS zY!lvO{bTA5!-*oq@*SodZ7kJE%@7PdwR{KL>&w?WLrynXXr!T&hh(!xn-7PX|7Erd zFO&I&=GV#WkoE{4)iv!=0$8a~hri+D zqy)qbP%pis4k_4@na6rNIFI=HPyql~WUxEkCC|=BXH2@+i>E#-B3ABUp@$u>X-JY= zc&CNWPFZM{W{Snw(EtMk4#2_;RCL})YrkfvUYr($4BUH4Q~Y56?}>M)Q}Q#$P6aq_ zyci>3-!y6y+0(1!X--x^m!#RN8?SUg{u7l$P4vpqIF1l&!>mCKDr$9>+p9lAz%KL6RmYj zB@g0*FS6z-ml|2ClQ@=>kyQk?b~Amy3~-{awj=tr->8y7mjqlh1koq%vpv|%Y`>H@ znWj`c(2xL;nYJH7=K$JbQ!dGa%`{$b-bt@1JhwFgf3~fiFZJ-om5^pu(L{dk)z9T> zYwu%}Nnlr+lB}^N+GfQ^_HJDAR!m|$C?H4PT`UdvN^7Je`Le^(q_TTvEsIu(wF-+? z$2`xo1ASYiGVaPe5zXBVI4R29m7-GWLa-qMiX5!qOXt}kVYN@chGSr^Bh>t|Dzm1^ zO@x}8D>?}K4BTA%aD+wzIO=`YP#4o zh4kzh?0TaXEE39q2Le;3`C2EV71!BjoM5m^9d*wR9JO>dnVm49Oqg!ynY6|&u=MT% z&l(`$)oUBI?z^2*kUxY(9BH&-WhyxWKJVIU*jmxJyCid1Qv-}~hmf(ndpKcww<}Bu zb1rb-g|w{%+P$0V>6p9g|4f~fs;!pC%dsS_zq_89%z3`9fBNr$!w>RsGiKPGaeW9T za@v6uQ0tT$HG6*&Y~4cp1)XH)u^&{ox%!p{97?;?r7TDG4EiZP52TjtWc=;k!BHXL z^{1dT9j=-`YC7UT^qjO6RaRhl-~c#~yn#5Wu-=u5n6?6DfbOt9{2IMd)+;PMvQE`b zz1*xUb1@-L%7dEVK=)h)B^wrF`*?UKZy8^oWMc*I(Zx)j`P4I99!!nT>%XzyYyVTC zFEcl8^of)-5n&v}^`%|kY~arKq22Snz>x@Z%o8{hVE2xPp_qJFA)IP`QHR~7z4GIG zvMX1a7$Y&E@oYA$65R5f+g%>x$~CyvT>Ek)3TlX=G!Ncj&Z=q?occ62Leg3&lp($( z<4XSHlI@!83T~Dswr5FWwYK(|i(Clo9S?kjY^Mtq<*Ly!Ib%@42Y!^~G_H8m7Eebl zxKs2-%oVd>)6z6M1NO=7*?>|g)FrqxD0}iUH@YFqM-$y_EFDW2hcMl=d0mSZ0ZWcT zlWH@4&$m*1D**EVQz_~F>TTndyyB`QVbM=b9|cdzBJ83lZtLef8mkgMva(dvbPAh3 z@Oip@a74Qk8u;A(Bmo(?r0OnF&{Pq|PJDy;f|6(4%k>Z~c?G+^nAihc_ZENMUi<8v zgH0@7Pv4GP(pY&Y8$#xlyNOyp0i@X4?Ws7V(qofCm)hzbmOL&)u*+nh6R>Lv_7?gd%H-;dx%D3>N*<${ClDC^#?cbYJaY5?~ zWZul0sO@>9QhHal$f`b60OC96?;%dF05N<49(hBQwv-SCmBIUA@dd}OMF;)hmAYq0QT1jQ=Dp5FfM zL(m<3&x%<12-9mXMI1?IYKXm(8i_x@u)tZOT!>C-a6L_8Bei0B565#awv&hXdc<)h z0*Owi8sg80FHEAm1ew4i1{RF$Xe0?OTJ#Los$8dR58Y}lc zhx2ecjqGk3^W-J3mUXxcYiv2De7T=eF}}gLkeJvqNg! z*hMSe4xp}Fd~Ju_n6y&Y^UKs!)|OMuTeAjECGhX6gxqzW=NL5~VDbV|7!_D= zQcmY0tLci-x3ZhS;y&TvQa`z8tn}?g*FeXkl;iDGa`koKcmc!%)@Por3I8H0N`JLg z!uzOe_nz)NuLbUBddJ(N$z+kus=U3JL_IJ4t*pumLFH$w8d$eyVJnO@Fhu>Z9?WPoyeIs3y&RgFKyiX|W+=P-xh|zR{u`Cj!1ZA|MRNtwfGSVmW#b zf)cF+scTKs_wxE|GVSfQ(w;f?fj!CEll{XBx16*)%lWO*B`<_S`luXzoybGxR(f;8 z%p>QSjvjrKd%oc1MQZBIG`JeT8a&x9Z99Y&9a^ZZ#^#hxWLk|C_mIcJ@^GDW0v%HM z?hL_Ozt|Uo8XwHff~YfP!Ljp|4#Zbn(&%pA({Zg-!Qch@4o-adlBcrp_$@iOJ*=N) zf7W$zCM%h7)|G7)$q=;O)2Y$Y^VOF%0c*PASv48u5ir^%h99ZlxUE+z?5)~k?e{Bk zh5r2Z@9q{;lWx~Re+9~V(4WxJb(>>i*t?G>9VY0`hWZ<*m#clGG{7`_I74|+vd@#r#QPnX}m)|`Yx2k^l_lXoG4 zB}^})7w>(`a66;gHSQMaq1@+06F0Hmlw$wyDPFOwp9Ndo&XTQ_KF~go|7G-HRl#WZ z?mH?!(j!OhInXo$NT3kKABY<($F2-SJzwIxE=zA{ImkCWG6sM!@Ij?j_&&hL^D`R4 zWYpYw7mCxsBxA#7fgkJ9Y@cPf=S#@r!xHnLqmFsd6_;nbbPX;r_L(J&3;iU`*|7O0 z8nMn(LB{PgWVWYMcPGS&VTomfa5&0JGb-c}BH+O4AK*!ku3HekFvO9P8`*L|Sf^a+ zu+1-=2tmCbPK2(+Ifc+@r6)IfL!ug&uIO4ePwc-FfVeAUq^C4J!Q(>Z8Yc-!QjKfa zZ)>h^tC0OirhZ(h4m69iw}`h3%C)->@DB({!%PLPNTQOGTpHassfY=k3_JPuOt#oW zi@iE9$yhc^fe+S}_F&Y#wvqJLmU=^9se~_daY{Qm&I-AAYEm!cOF)Pe508d?=U_*s z_9D38+eZs!a6nCUQfWM`hsJo6oGYbhhaaZ$n)4*B4S@^FX`2AA5TAG4S3$;JbFwg1wda2>lfR zyKS9Ddy9|j^}PXfVi$Y)F&Qw5>_?`ok=QO3cIqBZTuGYSgf2BF`}|E_C>OiZiGIWY zAH>ILN$VQ;&y9Z?G9-W-rz87o5q6rgzjmh?$AdgrMrYHvp&M`a$Z@Ta6&+%AjpA7l=U za0Pw)^6!a)Qt^BbYvh``4&Qy0VJGxu#2BZ3nxk1Rm&Wy1L+th6)E%yMvRloV!5uhp zhMfaPs*mD77|1%{#v9L^V8GBYWgtFuugE!{nh1rKDH2UZhrDS`-7U=zV*SDlK z(FvXz?hk63&mrNwRtlT}T|<=38s}#>`p`*WT>sAK?FjZ&enuoXGX;*=&E1{wtDm2F z40S8L-5%7`eh)j*r}HcO6AlufD9%T;!A&GY$uMLXrTm7t7Ly&da82pL9)n2hZ5PJp z8pDc5f24+FW!BY#%D3I$^6&xIj3_Lrd+)Q)+H0?P*Snl+Dv&$DQr^|8#x@L|-0-j0xGz&6pnSNC zc5`D(kwcX1wd;chga7Q5^pP11I0!qvujb~l72}>qy=%-%1Kd7hAQF8mU^!kd95Gc1E~B!Fyb{_*K681 z@t;ldbap&=pWQ`JnY)#oV%deIBGy%C>Vy2WDLQcKXPTb*#z^K$v&ZRVFlQkqil3M0 zGW*|qtpCWG{+B$0Y=^^~)t-VTLd@CV4VhsHM-s+2=9}L?GbV#s0xt3e{HG;SJH2Z8U{Oy`>g$9<`PoW?WM5EPi2LnVjJ3{l%4nHk$gN`fc0dEI3lHOMC?cZZJwWWW zWov!Z%iu2-^WMKg%kO^!i6q_^1k$N?Uq%qMq0!K|?5sgeMWxd_^Cz}0`mi(JBqky{ zyN3vVHG$i)Kz}hDjmSEoUwqC)1Iw9J({cX835oC(&7#;38)2=74;^Bf#@$shSxTz? z&-_OoujRqwzbu@5#jl@Zp$pOvXEV5?F>+__0Aos}*g0zNqRo|LbW}Y#rj79GDzEdy9hwig`N9e|^FOq0eBjs(gop<1i(X|Rsm}>mycClaVi&)T88IRKMF(W+w3Gyp= z)Ee7;l?)lrJC#4Eb2y4!tMa))B|viM+_w+6n@LF-A)KdKVzi~9P;%(c7d#*!8t?rQ z#is9FjxXO^OCB?nAImBhT^I#Tc(93+#?ATWs6(dNLMH6^P=W#y(utYWEV9q~&%^%K zeaGS9;x(^dp_sh&Y8Gd_>D%*m)k=#NItvF2n}JIV!{oLO*AUhoyyrb#^GU)*oabtu zO?$cFjFUw14ShorJl^=!_du&icuU299?!fON)i^}q^mFIx}hD| zcPNfinZhl-f<$L9Dye=d$pzLdG*B{2@@sZI}gu`|gIO6aCWv`%6X6>6>ku0j| zk5wXmIX;FsFM2tCG&3Z)ul>u%9Ad>cPUDRp9v)@dG^DNSGb)cNv)MWD@9;!^*A@KE zI@1?Zz&c!x{%F`)p?so$5P_uGaWWQ9s!X~mT5oK{CXp%5@^Nu>gyy7ad6}b2&uF|A zBEx?~wL`lnm)vYAbz4F}>8JpE^6xd!uJovq3l&kzS{=zvTj7g4s%X__dOiL1 z;t38H{L||ta=QlMRjrIi%2vM@U=6*QJAm>8yrySQrQhh)vqfp#jQWSe!!_pOe&|bH z7bXhAS6^L!_zEXv2BN+?TuE3HURmNmtDc*R zgcTg1q$4K588ztNz=wK?g0Ji?BD~;%5ggj`0B$nA{%;s4iyVu16O420@=5C=QM9aG zzc-i$t{DTBD*&k?{&xjJ2D1osx;V@JgibL8j^LluEvsSuc?QJ?!>g*-^^~lvpt#{( ztHF>(A0#Z!<2ONNVONsH;***aA;WrKJA^aWwK3%S!u!BCE+XpA`U=LbZcoyJy58xk zsC>2Cs#*^Re3dn!0pq8~yTZiK^lah7@P2lZf)k4QhDIi3UP`MYvq{UvZM+fm{q3)> z_ft_(jf{=AO{)oha%xCNzaYrL>s_nnJL?--@d-$)Zx@``3CwEqJ^sypbTb^OerwGz~HhNDOh0ImbkUYc^rH+(oXDo}Y-Szf!0q@i9 zxa$Gb)}z?iXZrSfx5*1h%Ajq?`W+<*^GW5s?Wb#8Yi3bQM=izw3SHCcI42v~v%H}w z*VNTk!+an`aT0fPFfClkiY{n-{)yLrpN5MR9d0NS#SDsISd`K8S0$jTqu5eXKHIxD zbp2v_a~QJ%xN?v5v_B5P_waikJG5&P*~nhaFQSZ>6KRt?w=fEJ>w#B+DEF0eHZx1~ zwzo$chwGyGU>e|;se+KeIl!62@J1EU*>``xef)3)%x_r~!&zKdIl_VN83E&yUCu&~ zUYFTDuEs@~ddGY_bp;f27`<`$e;@BH@f50jeL$E}KN{4{%ZNg;KV>k#T}FXN+sDGC ztsnYj_rG~d=rgGGIoj$k;a}h~$!;og!0$!j+7&+z&8CTT&_w6|JZG^AfMMjc===XD zqb)QQt++R5iuC*F)%u*BE&ls-_KpiY-LRhjD-9np&Do_`(J zF9k`v_-!6SmBY^5uHyD>=)fdB*ITewFjw3SZ4IDFb(VF%J2a61T*>Eme*trO8}uar ziVlRI1Gr9rqFZ@!gNSIrPg=xxGCmtk<<(9c@AX2OB?;<3_zg zcD5vbiT&H18d<_w^CvdC0?m+28{^6V9jFex$8!u7#{rWGt;(TC0Pg%rKVSoeVs`qrG2j?hXMp7lp<~f=8VyF_HtHcmkAr3;+g>Q_pfzSo z8`@>%JI+CmD(oStx1`Lj^AcnQ0Cxi<5wqn!1h@p4mCCuI`5%XAc(sC_;aBtqvlId= zTG}pQE5#7?P5oU8h z5BQ1=y$tZ;w_!gBO*{*FWdR)&L1IbiG_* z#;un>CwrH9e`x8#LW$&J#TlTEAFD^Akc}AI=Dq|jp`G8z!1qfz5N!8QzwtrD7 zQ`LXbdDMCv;_BG6y88N(j1ao3fJ{*B1K@jstAz zYSm{)s41OXzwcoIwIy$HDj^W#WRtTqFkm zfQf<*4Q|kBMc;Ly47#mBAAmlyjw}Es?a!t;(4VR3+0V-AY6*~VZzgDobpsEq^U4$$ zPDBTdL=^K<>PWZ@Ps27tWoBSv!J{w}l3vBR=Fby3y5on-(2p*ca=2LPz4>%rgl8xbvW#^0jqxoEv&u z_#VA>mjDz~>^hP*9UTwD!&HdQ)(jr1q9V~uEnKXEMi}4{9rT7iUE3BgNzt5J+B`2% zi>0i4b|YOGV{bS(I5;^-;IK>H*>sm4+f-5jh5y%y!Iyzlu9E2hEM%f-;Kh&4$Jzi%ZZ>jNf_v<(an~ zB^XQqg1KVvh5!iWHyNyBa$SmyZw>)c3giJyBk*%6_Z#b@`4LTNjT!Fz+ zMg&9a(|Ipw1Oiz8*JgL2=>_PMM`g@CfGWKC&?Z#ZLDMNaT-@HcQ5}}HMyYAHSjkDl z8wP!%b|?M%$i(jqgb35^HvNc0k>W+!FeJAOFaIou`AB{Q@LI(6gJScRD&jJ|H<14H z0N%iPa>zax$_p}pBn(*A<-C zW~0~;99X4#iY{O=HANkDJgQOeHqd?oPM>gBVa5&-Wzt@^=860vik`l$)!63%r8rM8 z2-V)bCa-!p+anHrB}M|3K|e+SnAR^YakWOUo+_$qp`01|@{TP|x4}-w^;!W8?OI5W z>r(A?1*8sv4G&(-Hk=Fqk^g7}TBZ+*_*jgPgqY&z1Lf{mkhr+btkUOTJLoQz%$=|5o&B= zf(d+rba1I6BD!YO*Mr1Uqo$}ia|=csADvbf6{jM2D_-kuwuu9xyFndt*mq|*9O^A25vFBn(8~I7O12Mkaj<dCo!>#>JhQeNjD|${_DjNN=~T81>B*0-cPM>_sb>|WAq;CnM6Aov-LCGzQnZz zw>&G%i^6_IbYNAO)V(_hEVlnGaVXBMqaQMA;C{+~K49WAOWA*yhd*cHsor515?-C= zZMwaalr#}rQxhh5*i!)754o8~sR7K}X*^2f;hjVPHv z62}f*e{5;>!lRIfNwtSnz^p0V!+1b{Ho|G1k!yLv!thRM@uME#X}ANyKOB zV$bkqx|41x2j17{YOpck;Jm&GHU2;kn>>Hjs4K@h=7dQm=#InugUW@)Ul0g!~-mYJ#Eecs`Y9;!c z%im`sdqLFXEI7jm!JM(iL( zJzbf@uy`3!m&@~rEqkY=R354@Sgw315P?-#{E3E$(&3A`$FALwPNR!@AZb;Hz1c9_ z;yy=%8R+AmJDR@^xVV3BI&`8fmhoi*6L2dHx0wd(08+==C<#$AUt?%U)2{2N+c?SK z*kk)ahB+0-I0RO92DFiL%*%-6u4;jqD0r^^}IP&CE{W_JY2a zkmYu!;gs$5s2Ea7rRi1U1=1a<={Ypbqu=cp=<&j*<|BWw3Od*V!zBDL+x|LkhX0Xa zc3T##za$9Wz=(J#SOR^39lkDOKmDzo9O$Cwi)K4bVtAd~;f7OR{Fzp`DBt)Kj)D&U zPG_fYoec(&_%z5)mfBnBXb-{$mA{p3!-bon2PBnDakmyop|Xhi;X+G8VhzE&J^7)@ zL*Tk?*pDwa21_)O)$!Lf*t*obT57Mn(xGvzwpCujgw9*>v3F2~!PqAHmO(sy;t^`I z(RXoik$lwTOS4x+xJveqctbO7SaeHb{u>e2~oyY$ePFF(R@A^W2z@&+9 zl**TEp{i=3o(H<^KBH9Pa{dAig`6;0JwXp1c%z6}188gKWNl_f9tDD#n75G!-|Lr% zFNvga(IeFb(vPVzbo~`zn!O=7)F)$sO{8peY|Plm$PJ7wus%nirtNVJV8aDO?SH}f zC?0-1;xF@hTnm~ir}JC@w^DKvO4qlGt6B zYsQwxvC3t~SrBOiA7E1n;3lOXOq`jR3s~o*1EJKIyDN3Apjv>)ffk>GAUb=Qoz&HQ2lbAU|e$)N&Io|8ZWQTz_9bck+o7=gFkhA$z#kebFk+*v273-~JEf9q1FE$Moj<3)R3mPzGn>CLixPNF0{Pxmfg|(x~N8r#X z^&p|qu|ZePA$_=@f-%4bsgmf-6tzBhg-x{&BT$+Jh-``Pwi@aiExn+$uS0z0eQ9wmXibCXm-WMV4xbAei}_AnhX8 zF&t<1(~}86!4JB#6V?TR@_=t*bmc-C{qDyD_DX!fl^wCT*IxSVv@Wyh`yBIz9Gjq2EvQ~!Bda~#Wp7Rb_AB5p5+XmQXwRme<48{)Us zoYKaIDcN5nDFUh)^FOAM4&0iUq+?FFEmMQu)e(BD}M zg^8Y99}kkl-?zljliU}l6t_W1%9e{=?aLvp447TY=QK(5o@zhT#jsR4RmNQ5JK7Gh zPg9eQ#d|OQwLd${iKvqYG+&<(1ZI9GXrBl17X9oiOG`^$N9k9=gl2-H`kbEH${kAl z-ZG>5u5 z33Ay-368i|H{y~}QTZ@RWJ|xHgS>*3j%kj1P3m))4)^G)V#6qjC!dpC?w)>PG2WSs+keh4&AobLe&tTg-o3{ z3P?FLb6oXKTi7qzd)|=)kOF5KbRUXRGTWAI7#sPWLsDOew+`&y7Bsb2_e}OM(SmyQ%q1jNyu10@*NKf@L*LWESDeve3%@88-c~T2 z=R;x$5Iu3I4&j~UF+*ZMCu(ALklgj!+uB!ObTjM~31(?!=VS|C7&^lHPus!h$qOYi z4RvX#tt?jjSsk!RZh#lSVzCzteiuS1Yst<^nlEZ+J=0TGSs8+Tm>t+OXh7%=UC&a_ zZXojn?4;A-ZUgoOWr8_vLGwTpF!%tNwzqQ#Z%s6680)IZ@H7FFjWH9Us_DV%*yA%6 z-|r#hSKpysDW_6o2ekNsW8pa4lLzGE(9Jsvh1mhSP)FGush(zkcOl*67;|N%7;9-( zb4DVVzN_Oi%5_R5v~A)O4^4irEiD<8#faZsHkql*8rwY4 zy#128)J)v&UOceGqG|`2`M~UE#?ET62$qZ$$(ah4yd^w8cGX*1E8(R{?8|OWg{$;e z>QRsBiuC;3lser=vi#!+_#dH}QNO{qf#f2y1lQx~N>=3qiU{&7k|2wB_Q2jAWQ=_W z)6X3KqaQTTg_Ph6u=|p`3OQ-NDO)v)rV^64cXxas3CZcJ)*o^Cz4th~iwcK?giBqd z_;9&zjG&2U1LVsf5XZU%8uhKMl*v-8Az|1&cAd2Eqz(a4`4+)nhI zuJDmAQL?d|`IhB*qq6#gzS5YnMoTiCki_S!Q3GM`tGx~=-k!VTrr#&^fgntSfe+DP za6jnS*9u>+e3yw+TV6^|D|UwdBD4JXqP!C~%(oL+Bm}bL;+SI>dn(2~ZpalatOVqB zl342<7m`>k!jRMhn262vGa%oWMC(P*;`fn3s`6wIEi@^S7!t~c$h#)r4<4X zP&k`*{+vB1QeLgh3SN#%{B&luvq#d;Q1&^Am1a>Keav+*2-$l>E0N?@PjtpGeg8>Z zhpyY~p-pq)qTrhZ*`Ak+i>4drCqG8?+$RK$ z-gT~jJTI|0J4}{Zgut3E%fVF;nAbVUEOpYoUd>a#47K{>6OBjlj_{DaG``Z{$xlvA z4*`Snx4cJ+3$csUWPFIeKNJd`LVxdGJWcxQ)_U}YGQW#)zQpZ%xXu-HY&>A{D|;Ta zC8m+o)}^@I!x|uH(9Qg!wV8j93!!q?K~X-ik2!MLI621?QQKyNoDI1ZhVSqqR8pD% zW#(>ivfi({Nxnz*M8qbN0i_PGrwD5NXC@CcB&%wHh=8PI zk^A;$f>vuR+7kjX5ZuYz9rtUz-Wy2pa|`-Ehmt53MWhm(%z~56K4a;HD}zvh_^=mL zI1GG)Gz=IA`RhAT%lyvL(+O&GeHd#-_>kx2LSkzgWtgL) zqoad^Z-2h6si_HWW;uhskN1ri_|w|?sP0hunPHbKCBwmd-6;gMlEbSwX53!SNF$^T z$>y?f;RWTsjj|}T#hh`Gg%&~m+z4RjvajM!1c%5Mg3fBm(mtpBVcGDjy{-PryX1wo zF}#m+c*S#RV2#qUqlh&uqBqY04VO(_WY~hT6!0a)W{pcrnfz;1Rq*)09S2J**!(t> z&w?EWQa*M%@E&3dl=jsGdJI$qmFiMziW&U$0n-pd93U4D))4OHpoO2qF%N(a%yg!X ze^!W>a7sBUPXukieGmJp4x``U2^k20tS^}6Fi~QRdly0MY*aQj7E1jmKa|`soCFky z2bbK++l2Luy!6f5daPcz(^Vw$b zCVsF(3u8Nik;rgkrw^gr)TDgDp6)Q8UJ7A%$YXJ0s=J4a&CLz#*M^w20ria?ALZ%G z?;wCjY{b|U5IRD0YhC=OBYko z)pyv1R6GEICv0fzY0qI}`|m~d`CuTpod{x$_Vf%rDk4bO8FD$QpfCZWD%)~KjZ>IB z@VB>Crb0tQX)XnZhJuw?0&xZrNlEBiIs>_s2mzzl+BXn;bl`4ws_qFef`qJ%Pg_XT zPEw7?f-A4Fm{S-(SO!r-&rtI95v0nz;coYrY-4<=;mJ?dR~`Z+BqR{lkQ#eu(1dG; zc^x-KL;LOPC4Ip)gA#Ci_$v*&8Df0{cLDU9%Kw3{HYewBsYHkXs*K3oN>#dPEj<}3 znGaxDH?*VMM_$DC5Argj1_E((3u&>XG}1+PjR|tf5O9u56(A;NP4SSjIX8nzz$M1T z;6^x6NQdn$h0pkxQLjZ90W?e8-A-FXK>mG1Hkx76a53jn!Dtb>S{<9AMN2r0g1 z4#NlTE}xap-*fOFYQdzI?Fg&r zg1att+IP z{EQua9#(Q`AP}^n2C(Bz*iJ0IhyIrE^Q2-ATF`(>K9F_z)){(1_sD@R1XTbdh$LIW z24Whv(AB`fTT_p+5t%^z7Rh4KVMznRHtyBlioFZd&pvePOtN6T9DyqCne0XPW<-8P zRx@IO5s}lfw+Q!y0wsa%j4cORD8mQ%@B4DEZkRN1WkVZnZYUR}&ylg2n((R<*Mn&nhlw4tG1uNGWoaZ@KDY6K9H z%KpXF+ZkYmWsKLN27Us^5`{AYF9XnNCE-i$6@D_*WRmlf`1@_?C)=O)IGT3 zhh(115G5NV+qoO;XNy7hCs_3v<~xC1F4^%5Q>OTXvh#t2ecY?%6ni&0CrN!xnnO5W z-2XZK+5;b`tLV!YW6$pkbdqmaY0#S?U!4a+zN1}On=vmA5F+@FrhDJmZ0R-Xx;`-pFoq3qZ-XYBjeZ{XNG)-m3-*-kxo_?rTkBF% z*uJ|JDJ=)_xgF3rNJ^~Ot>EwBL<`to`uMny*T3Rn$T8MA#+(G`U&s=&=j*jBYoOab zwXiF9Db{p*L?@OJUeo&*eiF9l*)49aKPxVVC zb;9}j=ZBIdx7Wol{_M-2yGv2uasJFshP}vK?}Nt#N%PwckdS6f)K+^|RJ^3nlQkS1 zKP*L&ex_t{BSLM`c~-`kMAtV7GhE8l2V^a9%>k*#3&JKJ{CktD=~`fue|F;}{GT(| z?}$~r?P*^thR5AQLp@UyP4s|q)ZkkO6y%%|lCbH#y!R4&AgXt*EAGdEO0Oua;)mnM zB1%J<9f1k{q^6ZP{s>}S{x920h1ibLXxt1<$x%WpG2JbSRjlgOm_y+yqQ%kwJ3vkL#Y9qn|C>eq%7%l$ zWD5oalzA!zr-~}h(L9Wbu*Jr$kZuP`Nu68dj)=^AsL)f`~vruzh4XK zgVZM4As(2qxtrJfEEnET(6!)VF|Jlv4pW5tk>0`jJen~19*%Q=cabc@pUJfcg_Vi$^IFs@(A2}qR6syc!HeY~o(@HFTFgWoOJ+Vr3<6wITM zDu3>lt?Yc018>%L;yX*3fWD4zG4+itJz)ZVFXT!Q|IFr< zhOKpwh6szI!~rrAFL-&XGj<&LJ3w)?cc*XNYv{>&LtrL(Ma<^e$kbwD` z_WQ=!du1ik*mDz`5IBxFjo*XQdm!6=wX^f}@N{0E@GBlyvla!kA)A4Y^lRY~s60^l zxb|cp`xn!Td1ehOANDf~|7Ys$MoRQ8LmkhE6PoJ?$619eP+O~h*}xWJYvxF1AhYuA z*-70o2eSO)!6o_|yB}Z=q4yo5nze17?4tC8o z{dsdDTEFm-ZNnpo_Gwt@n-Ri#T&O*Ku&JJRw`Y7sgRw7L`g|8~pE>!vD{Lm{Y*~cs zA@tDACBgGpLOvEe!&7Yd?W@|uhVIvDSbhQXuSaoGYTkj;5Egy`Ao>roz!*(+d#t*M zJpMGo4$LkbriJ4k(FgGHz9)+cs|c!PHB#Ij`n;lmnQ40Z0Zj=0mfOecG*txyPV{1! z>{^Qp-4>X?2uQj(S7nvN^)h>|k{b4DEe4Qop-u_)*=Z?w;saxsH`t+b?ns3{fCm~N zsTaKy^yb89V=5=L@C?#(;kJW%(yP1OS|vI~H#7I^cpCE@@&MxU^OP`_`*}3kl|tW`0Ds zf*)Nx%c{W(@fH}^y?qk~cEcS&ZwZ>W_hj1ZlPUB?Z#p!rgZdXCpdN$t?MRFOYl4p= z*Ox~+|49-yZ^;GTF6c;W3$8RZMQxAZkJbF$595uXfMjZ_}yp|xtk}IyMihS54hlF#zp52i!viSGG>3keoCyc@1Y54{D* z@~B)xRWgO&DNHoXC%PgE+IjHjTSW%ub_JEOjqwP+*8zm=`oo>{^Bairsr86o2}N0Tlmw> z<_m!V2FTLFN+I-;pao4cgCR-+x!T<*$4nrauH5iX)Il3yRhV-NY|GNCx%< zgdhk4JfA6op@-`A!2kd&?5#W{YTn83c&ERu`RZzsm(F7#IPiEUi(}tbT|SmxfTCph z(SXq}u+X<6Q&=?EK+G?(r}Z?hR&j)_LB1lSv~=t$*&%v{WP~7BZC~M~on;;jk+tY( z_e%EA+%h~YXDH=VCu_ya2NmlJV5hLBL8?+%qV>rA(?BAv)OfX{31Zg=RW0;$?UrT( zG$esUet;NSD46a|1!3MBVFIHNZ8_&tq674~KzLhSU41wjAe@jB@6T%2i+~wHh#+O8 z7$#6ghyWe4o{SYiOB*`P*xN5ID(Gc#Ix1snD2$9u5++!uX`4mFo|R?Q$9%K1s7(Iz zHjO^=JG3^q|F22m{g!8Yw;=l_htr3sOb>L^bO&_IKCkktj${x~xu5 z9tuQg1z+ZTvTkNMBY+wX_0kD0l0cli$5TV_!8&T{J+5{{F8n;cl%b&43SV)6m~YO` ziHBNHjquSlL1`a4bO@OA9s-OyVLNRZiOeI(e*nAMVDM0cU)u9d>nT?S1uYouS^)+O zo;cX$vaPCjgm}cBVSVOSBGU^;t|QzBsAs_6M@IIAqYfSCXy)(ySl5x5dF%6+^pYB$mL*dFD? zaqy*2+FJ6}R64LfjvdQ`dUI}U%2jO*xO1c?B-j0>tTwS92N8*t^_w_r+j?wlGgj`MiQxcDX0X}XCbGshea)6X_*TZSoibs%|;SlqJ`b^W^r3vmD zB}26qTX*5c;Etkrjt_}U90rwPAgDL`xsdW}eANGPry4MoLig5fxXS@S14wE?0fEicnF6cci);Q(NM^P0 zc`b&L_}T$s%(P!}xS|;~@wMnvi`V31evW;lIWXRkJ&2;nGlz;p7<)!#B|>qB4?lLS z&skosz}1o_Zv@5Dp)l}N5^&7C!d$zT-NL8)*d3{c>Fc{3s-D^#HZPygeaP&gAl7;bqM}l=T=uX&?0DEf%gUezHmz%FFBNuD|Q^2kKf zAHQV8=I|p&GSl)gS7+aal#17+ZF?_xi!#~`#%Be3eE0-64>afTAtY{$RA6?1)McKZ zv;&<+0RVXJ{G7Vsi`(eK2O06PZfMlhu=7xsoF9@yvqG$u!4 zMqx z84fe=aTW_m?_KweQ6&4qbi5G){Tv0Ws-o_l`v67$htXCIuk10b)^9@MRg%x0xmit``kkKHX#I*CJFA zL(yiE8&w2#IrDYz`WI~J%QH#Vq=~j`p^o%?_&2MHjq?-$N#Q+G)dOje1g-V?pAb!&?U;Z*XacpGHlN(^t2z4~fxgiyr({*l0* zMwex$cI*()owwx-2(1c=wSBtC3@)0)5( z@Pk?(@O+h~_4O|L9?O`IU>(=a5)_l1+`)#HU=yak^x!9RD}4uX=EB?b@_MZZy$_Y0 z<(fsw(Zt3~X&WlVshxABSqRCD4F}LhiQe7aFhCT8S*Nl5UI0NxdnCU&LVGLs44qR@UL?+kB-E#LhFc3%X-#0R%jY`^a`sox!a<)4V?ta z<)YY|w4LoKcCSs>s4K&5K8Ne#A?g^h`ar;du(9%^#E5-#@!^C@P%yVowT@%&BRE*I zQ2FJJ%D(sW1z`J^5JOSE)zHw83Ue0<0!Xp(v}fJHFgwV@*dy}t1u)GhDe1XaCJX#F zB7WrunUiH+u{YbEQwI_+7H#LM(kAz!c z&;`@O1H`Kza)sBK&jvaiyRI1qgn)?}e#p#EpJQczbbcvyX=DN>{Ds@LnC~QBqvZapNzE zHLSZbpi5yhILQWt!!i`W#ia_tf-T_I&jbi?0CRO^Wf=SgDZA{lNXx61j{!IRFME_T zPsMZB-hnX~JiR*@&0sjt^b{E!V%=QL)1W7w>=t3hWnziC(B7pq#(# zyQQc`uDmF5a;#Li|LqP$*F424fLp%gXt_T^f_%k>MNvCP-qjN!2I9209ruRl+YJIU z1=${@#`%)gUNoPf>@cZyixaPJvsTv3`Z8Fz7(M9*MEj(It0iO zwiCS@Iwc13YPDq6JTnYCe1m+mqoIRgvI=PEpSrQ2=kQvnV7@m zDqnS4f?-6$K_}(sNw@3X&=ECwYe6EI60EuPddpE;~Bh`HD`~YRTeQe)G^LLb+OPyiu z^1x+a;&Bx)luud(hKD%>LYmkDHR6Mz#g?P*oRJP$jw?eI1NMIlWmpgR0Dt-de81u{!E~9wv6LG$Hkf!b7X)clw zmb9M;--tc`m3=S$j#FPzoqd~gn>Z_L(G}L8*21b@eDG)MI8#6KU#f|2Ge|F&0v#HL}HH={&XD?QVD>B_fjbimfqa zN6^ z%k6kAkN-+f9v-anq4p3!zB3V#4_SfGQ>Gy9&zAn~EleenL0B)v;@XF26*5MdA)m8x%PwawgS6sK>p7(w`-z zjm|$akW{{*dn7@{{=xW>dC~?#RvEu277`DE4?uW=w6`H5GH43g^awaOX;R$%gUPkB z`%1fT(7YGmR$yH+PdE~IG<}}0=o*mcwyd-@0)gMm<7#}E4L=l76R>4!1=0rPLA`ST zyFwonAtGH&T&GfU5cGA5b`IUCQxGPrvd16~e7DK^wiSsISKnYAo;*UOq%@ByAz!DR5>3w^M?% zhRDaslP4ED!PFxnI@O1P1j{_>D3ml5sVV*1-n z7vHMRtaK|R1>byeoQ{wy#t$8TS;aUL=sX2S-iY?iFE6{6d0ALm@}44LW@bhXJrU9G z#GW}R82>jmn&lw3*YeWVBsvVciZ#0(8qOxKDceTh$2}Z&@us*^*sWxR&q7&C>;_IxFsWF0(j$}83Is`kNRu>e*MwGJ3MU|A5fOa%m%lH zlCHn<91+9>xyw#nJ}1eg+Nmp^@~7!0nxZQA*{`?5H`JZfgS0Fcd!GH?m0;vK?qfl; zDX++@?%cT}TSD~q?ORZixHInLxb1#iZ$rcRS+A>b0*Q$3#+{Tnr#i$EMpvD{e5+OU zIz^Sxny7Me{*l!NPXPz3*YG)g8iQW{nFQFc z2kr8#3m0CjhplgIRV(jG>vdxFhECsb*!zH;NSyCbxHw)sJ>&n?<=m(X&@ z&ywygTqj-LEgUPtYj?t?oB`_EOoSXeU<3knW%buIG&I~d@vrvc+=X8Fl+Wm=L)~TX z8nLjie(QR)vYdQl`vY%K)njw!Q~gP^zxv**P1}ta{b!@0heQo{@xSUv0Ia^)yesyT zp8edme#bsgHc>2at;GJ40tFK39-NdRC7pERn0@oQl2Mq2TvEpzzq`!bKicWLEQkm1 z23Zx?xpUqzl^~fTi74a+?&co)nbjipa^d=+t^3Rcu8xW&RYJM_AJsVI+{86l z7|N4Qo1fu28DSyesB-@0>OY^22rLZzsF6}`TG$&cEiHeKYQ8-hBqlpB9MME^q58sy zUs7+08N=Lz&5jl+C*9y4-ulQ?_xN>>s$x~O z^e27)vs!Vos*%yr?eg=_pFjV5SnpNG7FweRv+}5Gf^vZY8@b2b3^s2|-=UUEI#TI; zCOg;6)uem0!26a^;0?-5{HGu+RrVR224MG<=Auf~FVVvafhs(g_Xn?QPNk~aiwJWK zoAPWQOAuq1AG^w{XGs_GQ9Onzh)lkUslU>yyyqj;jQPmz{|xy?f=kf#uXhs@rmwc% zg@==qlgpzhe9I-E3Cw(kxR_YQZ49!2?>269%(v26N@qiwDDoJb!eHqUc8)Mf&U2=q z1t47OuZ$kMlt5+a-A?^KllT~}tgN(R#)pPZr?x;nqK;y7Umstd`G{n}Wn^4eAhSI)y}WQkOk(Y=>a{k+NXnKr`qT_iSQi-2-Sj8Ee?gK{PZqu;3c^?JIy&slHdMH|O4(#)y&>c~Qf zdxJY}8>YS;-I~dxoHmviG?Xhr^=VOFfkv}Jn1^G`4A25ms zYjPZ?1Cf}7DWRf}9pv9V!O%~?2QI0&pYhC@wJ!AR^MiLr#CWTl<3wXFe(60zfOiKPW#uG9A~ZEc4<~Qg+J5U<#cK|%wzZ%nSD;@c z*LLj6>=gG?iaEPEGtFqN4_QG&kLe>B);b;5r(;`cg{r9}@jxA9Swuu&VpN1~Zf?GO zc~Dzh`^=d$jEszsRD)ocv$M0hnpy=0FBMNtHQtn?sERilmq(dDUpgc|aOMlg1Fg2i zst5cDVx?UYzumI6FXV7NDqwoVsQCYn|D=YXyME;!b%IJm2f4hUs|P*aS&PP;SeG@G zeHXHllv#3(TZzGa|@+%N?_;X62nHe?HOoTvo3A0#kT}ckG|Q&jBZGR-gw-2T1^W9#)I z4bsrsMfBGPLhmK~JKPl`Ho9y~IL!C2JG66g`%%>jH&NK~*u{@r%677j_|5*gQ6J6i zU$QW&AwGC9=TXpKQngO6)&Ef<#?F(DMAFP)rr6d&Op8m414sw5Kl*-ONZp$+8 zU5M{y*aEfut1HX;ljB7UwqduvC;MwOz9wuV^5=Pmf9>Vzr8BUX%e^yjb=3)d9J!?Q zJTm+@``K@ol(=txY5T1q{$BHK*p(Drd|aL|$S@SS{w-tcjZE@~*nZrNS-$#E^&z*o z?>T>skG8L>BIi`(gUWQUXE}XRd=sc_-p)jlE0N|OCo;Uu#WK}g>8dQy8}5U-{31gZ zHx)m3QlRwNg9^6if2r+#uDhQv|8us~H*EwT>Av48PhgItPLn2c;ws}-96oWIlvY(< zt$aN_Mq;OQ%t1*)l8aJ<*~^Of$<4us{t2vxPGX(vT|bSt3`IXVjhj9GX#Aq%uw;F} zW(F6(CZ>DV{mR8zR!X1BF;}V+yBl=5?%&^7R7ZhU`lv`T%eEhVqbL_EMG9MrnND1Y z8io9;+?p`q8MidMx<|89T*9v5OH{oFiTE44ZyezE@0vS`xo&RY9$x-Vw8B+eOGe5M+kT0fhJh(pV+IMAKAYZmhIBsCiPGu+@ubjXXPqDiI631v2)cjOGeXH8Brt^T;cSb1{VLA8Fr zPsOf&8>?KtiuZUV6U`pBiZAKO{y(a&I;!gDX$yh^DoA&Chk(*um%fxV(v5($gmgF3 z9ZE|`ryy|wk(LmY?(Te7e_zFy;~$*EIqqk6XD6O{X12o7(nd#9TNk_`__$8}&(_90 z{Krsdb_IFDuyrTT-^ z%1Nyby2iY9uYS%y@hBUJM*kXuz2{E~!%)tifnAU3pn!a$EMPHp*C^~T`qDh2WEfv39zv`XQfHlD-tNB8e5&+0y}h($p5=Is zG8g@*w+KnsT4B9PT+o#cdvW8G?3huXT8Si{C9$F{-S>^ z%P#AT-uW|gIo05J%5VJK!0CQvADHu5;ww}tQq8K^#U8}hCY3EW=6gr1i_9iq-_XO! zIP9?Xyo23(`GP(?e{k$r&xpV<8uJnN~U=z7Lxl37nMo$Dza(X=&`oPt|p{taW;?ZH5ihUa;Bq1fKO5eVuA$>%l z+}PTasR6^Fp-_=lKmkK*C!8Y?paB$eo;F#%OXuPf4X~V|nwFyD>#0b%H!mqD>A6CD zSB!%DUMAZKX-qeY_rCB`ma|l&W%-@Cf{$nuzcsFG{;MT9Xx;@TOVT`fm5s3^JLbgZ z>9YYv3i~;#GHtJ`(gk)y-|6nOWI$yOh#ae_+c?P=^^HWcN zR~m9<^+v)hO6E+k09UJev)-C_JK9vBX`OkY8AeFO63GODVKxvku zn`-z$Op!S!)cjyfGedz@)xvF(G2N>5T7Ud|2P)+Omh=C(#|c&aK#3r}ed zyKL``Y=W@g4?|SN)0E5`12oNaVVcx9LEPSV#YhoPr7#mU->t1Bs3|^1DEl21fzR+M zA^Ypb@rE^GL`nCVN-Sa>q#-IO0(6uV;vdcf|&J6Q!6QROQqiBD?@!{DVpvd z$;tk)l!337rKFRlvl5t>qI;&KAvH!J3$bhZ^fAGMDKhk`qCQuQ8C+$8kr}*}E7iGA z;0lUT#J9f>N@2#x7%{cvx*udhNLukEr>WIW91-`ga}xsMq7orD+5Y|bCu)jb#W)5S z%3atzNBICs_87}GCBlTyzl>N0nntRCzby&^SF$w3I*g;D_2jn-0a@k;ebAY3w!nq%F}QSUZG}`dh`>XH$vy zYV|Bm%n3>FCiJhWH@;7(tXsS_3jBhQWQf2&yfY=cK-sNUow(*-0}S@DTok4E$&njnQMOTm4saz2H5J+Sm=r-J_X;v`ra@BXbF6ocIjrx#1b z4!1)VoqD#tu-OfkAIi!F%Rj7AiPtij)xBAN=b3U44;{o-U_7M|Ou;m;H1DB`Xd$I% ziv9GwXoEm)lH1dW;x#J%OBL8CN7QI!KXm^_lDUVwLhGFL_v>Z9PWzv{ge`AWHj(4K zW=|UJ)nh--yO0kHqNj*pryH6H^3|784!};!M99Rue*x*rOnmqJYNm%f= z;w@fFd{!)aVcxv9)VhmaX{TNFxkKQrnDTuZMI?HDP*=tVFEtMJ<6~_PXM){u?=0fl`Mmlj0Hw=|IP0}ty3l`* zP@v|*pstCaL*K`y#@2GBd$z}ve`mTA5Y7I#+MPDK+qe)9db&wu!t_Ms;^|tDPH3ID zjMM77bw<(BbqL3rSnH#){BkeTRurN0b=iE~{8hy}Zd6#&UoNCV{TA7mnKdd?^RW!@ zwF|S3WIj@H%@SGU5Z@hpqEC0`ZakVB_1}7TB*FVB|K)>i(G&-{@o)N*8mIF}9r#b5 zQ4haJF)2d&xV~lkF6sj3okY`lvPYf3!Pc@$k!6h4n`?wN#(rb4=l~_}b$$p$2mHOLF5cUzG)fWP82e zLfcH^>Hr`<6gCzuGkf8XSJHpyK)2xhq{RJU;58b%aV#z~}!lOL+h4?b6AF ziX@9)#GtH@ap|-cB%`DJlcESr?R`}&L3~9ML)?>zij7qf{1d24HC>w(?c-2|$pT`P zA#}F6ymtcM`q8{pxWU=J92aaZY9?JeCse>)b5Me)cPZPfXikp`iiiC@+}jk&KsrH? z>kR$$H1JUDEe~_+M9yb*^7oKCu8_oVl*yua|s8mDa zgF+Gf2Ws>em&Mee}F{4R&WJ{3OuLGfF@Xh+hPWwUlX!HTP=B+P%gnU>KT?ND` zDyiOYx1YygkWef?loznc+`Pv`a_I2g$$tw#a~ubn?K0R%gnu$-rGKu>aNd<7%*W)b zqYeE9+V1^TMuerBB?jsGd7I>ThcU~6n6cg}?W(npYH5@1cNS$`m@VqnVJRY18xj19 z0^}4NxwIJQ^oocCJK@m@lh^6)!tPGA>wofse^Oc>a^e(yB|i>SN$9cNO7%zw(UR^G z&>(R;M@ap*5;PMhhivJ(DJQP{8p}P)Rj5zz_?(Df8l?a=B})6wa$S7T11@Sa6aKMU zCWCv6PY<)C|D2iOKYIFsOR(Uf+P%hP*~!*GT`^ErIO3w0uIDhiq6$xRyfAU{GNp)v zPOGsL`%{n;U>ocsA^H!fuhHZ0f7XOji57Mh^6TUsm34mVDlFH@U(=nP3ZwY5YY#>K zMyG84-R`Lz3i#_rtR^=(krW_63M!_Sx52bDhIkxa+3_j;;sE~-5x6)R8k_acQS+5&+LcNuT3fRw%|3NCTR5CHqg3|ct zi}Xvuhs&?rWBphc8Kv8`rg&Gei+fSx#?E-)zfc58#P1X26K1Qy(TF&Cv>stz#ZYkI zhf64DEa!b{nvt&y?E&{}pkK55s6_jlAKjmv`p#cVeFv;$OX}&*LBsGIhF)=05s%j$ zbu7f7(0(e4o_Z#jZ}|DepvmduL`oJFhNdwqMlEcEoB%9NI}Hzxp4X|@MbGsmFZ&0k%YkxIp$i8c0W{Q2L0JVAK%+7Q8<=razc_cLryw8&O!X88XI-jURB>9 zidq%J_(^=|QiDa`?qx=P#vzYKk$chC(mR;6WH8dz4l8;`OXTQ-n&>=4dS6~H z@1=@jGs}8l=NnEAvh?m=ZlxX9*ErHk)b!ShG{Vb0E)%Ysq@w^eh^r%|o-})LHGQGF z=VVyfN|^b%qQ?8Voh5RZj>l`XZ-_D;OZ>Ue0k^TV50#9Uk)WpmN8@`{82NF)as0z> z)J$R&wQ5&CT_9-c=G!eXqKEv_^!p&lve7Z$m_%49N?7Hi8`d%?vB+!x z7!MRsZ2QmA-jRtNC}gGci@EA|Np*h)MIBNYFF<%!CZ((KJ}1Bf)D|K-+!bp^6dof~ z^g71*PT(70d~4YS5UXsIN$_DSS)-*==7i7SPrXs-yIT3yc_WK5EW$34f&4~HMpIqA*U`7jYdXE)e=OL9@t>9eVEL~> z5so!h>oI~!8$ViLY*C_GiU!yqJsfJfF>wHcmZUr1}m= z`}P+7kPe-F0x8GDqa(M0q6OrL_ru_sACBh+>yE75WLjo=(A}n3nn(cFLHp0fm$%=YWuFf z{%~)d5*L~QX%cn{XZ42GB$wl-sDBUnd(A0sb66l(2w@z@z2puprmJW_xcl^LkHT~w z0HM%==Grflptb8gW*pvRze-bu6ZNH-=oE*{ZH(6_jreIXc%RHGr=&u68EHt7d=Fd5 z_Bh6#w4TaLE?a%fs;lH4{d05$0{@DJGdY*_P`JRNEY_zWCE7SQA#MZEuPdofD(Gm)>&JaQB9`L8}jR zOG-29Ywewdk&d5?0`1c0GX{iS$_t-opzo!0kHN5@L*>Xr?T6eO$G-!vf~T`smI z=itLT?kK+O`j*~eOVI0XfDh|*76NO^)60Y#=&6RA0{}yD^ z{eSl4+pDK0Gwh!XS`Wom9n(6;y6LsP!-um%*VTA;~F53hM;H!4*Yk(NNNX@^&sF?04TvI8^c?<&myZQl26D{cS-Z=?uD|=1Q8<-dig+3LuqM=WT zW}o_1T}Komb|)x(z~ zU+Nu|B;&nO?l@6F2F`?+9x^L;DNRgJ@EtS#m$U!GIcx*Bi8?U#%4VosbVjA*CO4I3 z93cKkA1O0;3hV2C7d3=JSV=Qqfx?Hf#ufEGLihweTryx#HzXS+a_F@~7Uog1yEp|0 z#07++Ra4HjhrEAnU;*a87pca=Qv%=QVa3z1JZMnP)ipuXK|I zG54M$h?F>LOBL4JhO#u^{>Z<_p-a_8@BYy&PIv76AeQ>+&vlcbHm56;gOivr4=JGX-df&aR!ITy*;;Z+6(owm)|L7cu-CFR7=c$*F z+zpM=FL*yQ=~W-^#1AJ33h;|WjSbI<~y8~8H@A1P;8X8A%4OOE0b1DtDlI&{rtx|^ZAf7={ z*?T#&JGm76)0Fzx&-jv|kcslW_D%sEaT^3+v5PVos@dCGkGSKy4yeoP@>(=Uo|0mx z)ZFtxl1XgIIQ#sXI&qqWC-{%1wT<3JVp+8eeu5>>bu=lL9yhBBK-3o;I!!*8=$U_g zfV{9bGmGs0j5|tT(3T|s6ePFiMn2x?yV8A57%Cj(>l-x$QjUnCSQfliFzV{reEloB zBekl8%kWQ-+oQ?36{s4!>uWbwv35fq(I;sGrAmTYBRfXZqu62U&Ts_g&zL8{7gr}L zq2A2v1XFlF^x^Gf<$61dZaAZUYD|s?p~!pV*I;@^_p%1#mzQUz0&W_2;P8wshFwaHnu2~6`o1r8yBTxx#{p1#)n3P`%vD=eI9!D|*=%;?cQSU<}z?`Syk0y!%2=>DA1#VaE9V zLsxM)qir4P+F}+>1d%LAsP9u@K}`j_K<;H)*?6L5HZho%yYgS%(7N5q_yA34AVz9Z zl>~(aB}~dM66#B!SeZ#@dW7UlXZR%->qAcjkA2iLIy@co)@Z1AF+bxg4!X*=@KBTp zo>*V>OcrH_=O&NG_Kw-~luy41 z!F0kY7ZPgjpI`TS^yz^5{b;ug1qFOaG(6iW_I~kD)ERB`*kP3KA_i#=(E})c zqP)Y7mo`@Ch@xMj-S=eNKE(n6t9wLqyLHln)E*j&n0w~BmdE-V4R0Eg|Uimz5x=i5m!`h`e6e*X~ z%h&M*vFK2LmeTe;>@8ITJPXyGO}+UxE(Re{z^z7h2anr)~N-g-yFn zVWUsXg-kZtz8ECNUtPF*0-}nSM+=aTi|dWxHO#6!2XEA z4KNy&_z$Q`isDX@j^K(a*`N!|?zJD1NlR2}o|$EeJVprplK}1C7Bq$1{e!x8%_H{Gl?b^mmg3iw zL_wrOmyTW{BUk_Io(sGd5w%NHxcgtTvWWjTu=O$QcoeVPAstr*JuY7_%#q?uxk$yX z@o6T~6FSg^JnGBYq&*Zt_IZq1_+^M`Vx|6`4np{P-NWws&Oo6A| zFA$=c%yy)|%;vYwdtd2~H;&f6^^mY5ubhYYl1ciB$IivgJ|ywVyAs|-Npn!kq?K&G zA+(~HJdxJN0IM~(NSft;vJQ?u={<2mwCl^MpmQHi1d(&yxTFMjb-tDfei^z?@N)lM zF|v2JI-$D3loyU0D5Mfs>8<5RAdpFpg^XPvsHrvjLHW2!7V|1gf>`44K{iNp-UeEf zWHLT4B5VQ4TJ#w=*GASE_oQAVM!PH$V%`D?kmm^lQ`IWU?Z(|E#XgdyJB3eR-{ul3 zym->R#7FznJ_><2TPKVqafLGGi3AErfp@*&QDLziv58*NEHX0~>5i9Ci(t02lwV%W`52 zMX%$1H!d9)(}*??aulH|ws@v$SIbut-A5x(&xy9DmLeLes?>SL4+Uv?zSE(Ht7{`? zU$8gFFyPN!<}B$>@c5wo7ts1N-5UL`-RIF9P`m{rW)p9Ku0lAB;#O!7%U{H&bLR&R zr06StTG$V`1SP6$oyY-J?l2^D0>Xk!S%qZ21k#slVJsE1WFr~bf^6KM;V^IwAZAS0 zS9&kagwXzq^$#Bci&zBdxg9hR8B=s7kLZQAfvk+_=JEH^A8O$zRoSD_-6=8{omBjX zaX4ZZJ`g67+{jko$@|lZgp&;!L{i78^ zf3?CV2lM5ckv}~<95j-xk2|-M15iVicNt8nrM~v{Y*{4x2iX#v>l)PhI7qNa>WEu) zyrRHB_lOV+!xxec!v4Gc)4Z0s4omtnboABSF@=`M)+*(ALO$yOJ;EJ>Ipu z{+flulIf2|9@1BYJN{h3Y6@3V!#?ET#(5|AC3h~gUZS1?aGl31HonfX*|_GZOB9yJ z;+c~zkmd-odHGq#tWkuN9*KHZg_@zV(>wm}H5njQ6V~60tMt-ui|cTx-S?7-KFFNL z0&0l=G{=t475{=q2QIF`SIxd;dXeAVRF=l`h`NJQtoo^3+#G9Jo92PfVtn|C1P0GN zR|#FNoQpXbYc>GL)BMc;8`b`L{rEqM6nMzhYJXMCZwY$|idv895ft02q^KlCovN9s zLc6@jk&+ffgJf8{V+)he{Mlp(0O}3+5ucMMy1n$tE3I+xB%qZXJq(eSj%z$*|m@O za{v=66y! z2qm@WVU$XwNsqCWMF(mDYKBKpG`4k{eKfKBwXMSK*g@0KE_l9VQkg-u%b!>GsY8FN zF8V{dri)YLBxBwWCw12-K2eP4pae#siho(f7BrI`F5mCDuL2LX) zsj+x^wp-$_L?3VA9Tb+i8*~$<4{y%$55>mo%X1gmUc)?Wm*&rEUyM&k#mHjR2N>|HxkY|g0&$m|9NHYXd`xeX66_i6J4}4(MV_b9 z7!2gyh+LpkQFO>3UHFGJL|4Z|d(Vc>JtqDN**D4T zX;f^Rw6(N2wN>B2npn}12~XUO90nnVclAvSNqn*u-$eN*G!2x+be{=}UCOK4!iX~q z8BJu^-MER63Q4f3h2hkYZF~e%?my93?`<-IrBg7VA5Y#!w3V5^DMW{tN6S{JU8O9e z;7#hu&m}Z_@{vDzH|3>!h6Nz#ojD&+T2b0p2xoAeZVY=Toc7l?H3@)IGM;(-wzDL> zV#+>Osk7!kl{0|eqAQ4r7WR=G*NOHg8uS+~FL_t@!iLMBs-XJDGTt3;Sl}k)rK{Lg z`$=OSUQTH0CTTRuKMZldu(sYX`Uq7Z(ZC{)2I~bpTGA^7_8(5^v(ci<;X(jmb^JEU)`}Ry6>rEV(ZTd-;3anER>(nYc6TZ^utPCYAJtLiIBbLK;6E> zBM{kU>h(B2-fzBbV^o*+>s9=0+KY9oR^qQzjy+~imewzo$!g{l>w2dNX65Arhiz+o1rbF0ErJi)Z`ABW%|E#Z( zck{sH8|(OHZ2Z*%;XJ)t7(kDZe1)lHV&y!&Qlb6O(g5%Y=m^>ACGuc=;>Yx`LLXs; zhx-8;E>XiONC7Ef7x!X1?wssk7^gFnQ60 zShMXE4`qFZGbSYmz0ynTs{Pyq2zOQdC5XIAnn_D>Rnwl&W@t`x4Zl^H_&(Z!9=coq z8#uuF*QA;>^t~5JHgn0_B!v=-_G*{B(hK20?LBmtrZ*+rc;e=2NFkbs@lJFQhMs>(6_uxD{H}Z`pR$?DOF}6o~)tnspUiq&-v@JWs&{k=8K0?WFNu4SwSY}iu=PbYQ6!E zvk4It>%tc*hwj*QCx{YlP@W9K`fx_N-WilQ>nwMtB$mgqIF^SG?z;`LWIZF(*9t@0 zI$3KbC~9$j8+!w3?&-2c1?hkau_77l{n~3HHLp`m7;E4VA!(PMV{c_}X)vpm-Nw@L zxYLxl@m%HM^uX%|{m(y&LiYv6M!r~1FN|uav$t#;dU3v9V4sVvv;BU`&3)iUb+m-0 zs(4vweHsxvp=2xB zdsW~0Q=53~Kuk91!~HW*m5`H2zvA!v2J0Avwy2>o+$wYfQps|2z;%wPST1Y&kCh#urd;$^sh#6Lp$#_=!G&F?Cwq5&oooBAP{uJ|jcjbHM z2nG*@J%vfo5!UQuXDi^BQ$6B$2B9cQUNNAO*)|Fto+(~T1_Z)rSLe663=IPPXp7{J zpNyAAoP|_P9WO9Ubu%w;Sn`e(^EFoykVw3xZ|zargH_3wuvlzkzlizG>PV{m=Svqp z%;wMND7p!mGcgyP%%alSEU=?JF2Ce-0_)lGZt z_pINAN{-g3-meq4UBuS@qDM-0)pPpOrFBf~2vFTc7%I3fi=_H=p>&o!#tHi;T;Z2a zYJji-It1%bxX}iP}vbb4_Nz~{?c#*%iCN4 zMV=~_?JlIcHlFeqqg*Bo)TorDD=fDV~N-Xg!k*cw5 z^c$PdF4}6%b}-AWoCK*V>5;sO0L7DY=0tXVv4t-t)8eWA4F7!~PsP47H>&`kA++j5JV- zT$L%k^T45q@amOI`ADiwW9_BE9^1SX80V^#9~la&wKXReYswPkpBX#KXMWuibA zK;_q!Iqevs$#_{5S61&FKLKMWcuf+K8l1SM>(;A8Y3XCmXs!_BdCFl6Qn!^p_#!KtPns&%dHAV*Ym#uwtw)m>*VC43mDkbLqg#rudSOP-{vh`i zC6KL8EfqS*JN|V4;LxAq=OBxaN?Ddyksu3^9L>ZBf05h|xGElYdbYK3r34G$};Vu@C%0xlpgAePa8;GcEYND5g;?P!NwRUpUGyYQ(!ulM~LiqcKQHEg8pLV)L+-+3F>h?@Mm z42cJ~l7J1iMhFUA+u{!te`V@Y&`RJn9CfMFYZtZeDf{t?E4*9}40OFBN!}1NfwXPV zhFv0+SMh%zG)_o*0p{-))YwJ94nQuB&!tO&KHv1~Yj;!r(J=L7Y>8INnGs5x68CpI z3XuL!Wo@VlSXK;>oW$zN996H3Y?u0{0Ciu!Hr3%vLP)=Aa^x*IoI_{XAXd=DS--0S z)nzC=$nHc}x9J+&9~T6$2o0)7^iJIOmqYXinc+V)A=md+4|T_CfXcrWk?UObsI<4d zw!lFNF8UUEX8<*7L#I_+{3|hsbjZU7G52&#!D&wO-Sl4^WeB<97bIo?3AT zZWJT5b6lm|t=aeMf3@J=NqT)7cg7@8oe$d~9Gr^M2mSJ|z0Y0Wy640iHqD}*Zyoj5 zk)62(mfkHr%<2!T#I18?8xp6OU#(i?@8U9a)MgYw`=NcQ#x8uPZ+$k-n75 zy88LNZw$16CbA1Dd3G3m>@eF&sm~kVuYmn-VV1jv@3VULDbnbkdS4{%$OsuH^f5rX zDBd$5C0suBT{UZ#Dzr>n!K`H-)9o zqQAO^&CY_ToKH&&kUo7Le*AD>vjtKA#!lQcB-Sr=iWE>Z*bxPhN?l~;`i%Q-W}jj2 z_AI~Vm=2NSrtPDY7G+GdUEenygWLEU4`#lRq?%qH8U3r&@ z&c$O|GZP|YoP8wNq#%%ZC(BZwR-i06RJVV|FnYa zAFasuAW+^u7_VxHsT9)hd{;gdH=7m-x!&4(|5Fm8wh(~|8+#%L3Xb~SGc`Iu!O4Jv zOH92`97UB)vg0^W#z3*;A?2cujD}bWvUSyU# zrg6kRN4T}fPGA>{W7YFmt&%Ef@l|fd@WWTJpW%$Z5{r5tKVRUe4?hF{HL3E@m0s_G z>N57R$4^X+npM16;LHjg6gT4096y)EZM?&Aa^mcC1@{31=> z)}FB^izxMB#5gKtxtfvqWyErimvG&L=!I4|%%DNroO9%SjLM`*3Uw7|)TmjN$vdP& zURr?5h5^^MTER0@r-XLjPZn;Sun12-in$^CN^s2~y5E`Nf={WJeZjHkq4xYT=Snm703e+9~eqobte&_=#NcCQxZaZ9Pw{mwbee9%0;z`-Jx%O-`suNj^mE zU3!0%v*q)ZahF{2J7kC+(WJ_wIfT#iOb_b-X8p{UAa4Mt@|n-#qpYOzP_mv-I?y$g zeYEY$g0K=|V`U5q77G6_9#rKfHO#`%jWsP5-iud#%6^1gis;oS0WwhCj3*ANm zg>##%M?iz0eTsyR0S9we=d6L&R^ME)KJs958QTf#=l&b(5b@6AZe&{3YN~LO?-#K| z#<}8bptd=8^R^Rx1Q747lE3sOBz=dwLuJ0PH2bpeT9kTMirP%^XkbCcG>L^u)>qnB znB7DU6mN=pi=K1zy$K&Cu0FMqn=k?F_44yBM}~d?o`W3g()zL6EO`f4X-#uf*zhmh zpe^Yizxnq3lxm6FwxE3ndOT>;mw(^n-sx$>`Wz=V&DWhM4Ry7ivAB}WvOIlQvzG^e z#mdNfqQRJUQR;V~%!bhw!C?)wqDGveWrZiCBVQt%=fN01WtcXo$>On8iq*lT z?`?OaB!0y(2AsE%ErsmUS;cyfRA+BcB8f|lU0XvzCl{p-S~#lyY8Pfkgm)DekJAVKxcHZ@N=*FrXuSQd(*Acr7~BrB1<jI`l;a0AXu{S{PEIZ>D+8})fFJM+7-sA+OZw~2C|Ao{*Um8F zY2!Fb=rN#EWGynhr1{9|HF;oH1v<>4P;_CcoMW05yG`)_~6_8=PJq0h$?H7 ztr^Kv9rA4k+>AB2de_dU6N!*~7ms5Fj*MmDG~=Y-BE;~p7{gRJmPwKgMBfI+ZqhKp zBi&)M2cj2LRt}Gi9gUZ2gV$EiGH=dE?eoFoR9yCGu$2SwQq`|nnm1Wz>&4}VM@MI` zTVDU}9`~W>8=DiZmrXrfEQ!Cm0*xy5p&xCnlqDP7OL^iyNeZgO)Y8g4?o8-mJS^!4 z9r*~`mhFs;mWJiZHQQz_j4y=gREdw^B-%G{HA@nnF`cV!wP_T+!RJb+!|=)Z`N01y z;7o*MoRvfe)u@a#`m}a`M|`CK_;==}JL(skS&>U!y7u6y zhtGn|b|ZGT&HTGkSqEPcycU_Vp%qi@eMSj5zR4>hZv*1Skc};nk~bs^v|=kxdS2py z&Bt+#dz5VKegC=v?oK#(8v#RHO6ul#+766EyNp7Y(@`oYDY5?fDd2{D3_P<1>+4(k z@Vh+% z8bnl}8gE={dc$lrs}2dQ3ci?&G~#skIkSyzuQ69V7@oQpgGAYVXXJ+oNE$}tK++KT z0N+UXUU9T?1IQmZMlzTVsrxZaZU32 z*!dLbtsB1Zp1m#t1$huSqO4$FCtOdKPTJhH0Y!iZC`O`>5&CKYMWfo^4#%=ttmelP z0=kSZ5YgeJpkSVboY3<~T4bF!V1JX2V@1;@`_a1CEZ-lo;5(wN4ez0&=YU@QAkiR@ zLwh@%S*$W|gBo5`CCSG})gdl~KhRtgJu=aRT0dL$0S+VRFj%MB;2-n`j>((t2icYU zJ{VZyEpej;W(UL?i+H?oNZW_MK%Bqf#dR*rZ(Z6M6nmzGk+kB})UjJ^|B&z?$&UJi zI-j7SyZAe-IVfVx!2{v*LBB?tiiwE@?K~zTx@^Bs8tn4C-eHM~N&r3n zm*c?8Cm8UC+VWC0uF6c_D34RZ(&vrZyOksxbj)b-KVX-5!&X$V(Q|S|q^MEvKC}VT zRY)EPE-SAO^=*7k2uLJ=^ja%(FFl!yeQEY&4*>iqMdWc`&y?nyuK{EYx*hkLpZi!? zN7Ev4v{D*=!RvUQ)xlDk3!jhKs?WT|@Tdr-5>_=VH_VUgY+F1rK2}x?%C#Y&DSC6J zd*ca=?R;`+C1!Sp<%50}(K{k2aSlOwL#KU6d~G|W4S>$;ChW>PZ?t|kQGDQdxk}xf zS0yeW;+cK*Df^~j)%yd*E`deEOQO3SAXYa&Qajn+NC{X;tT~K4QVa}!yi~iAYMiop zG~Ebh^CW+LBDf@Y_Y$rer_H<~wMWP0(@E%8SGXJo6)P)aub=rI(sTXu*I}pOoiDx0 zXp;@(?#)nLmB#@Sqw}9G*{}Hoz;7N4IXAz*U%U_DJ&{-2V=B48|&-p{+sMVS7|zq8=s0)s8;|zo=_Zn zH}jwc&mP?*j}h5hAydTDr!R5POnXd)y2X#xM0@eWs`KVnXP|gC(i2i2V=?+VR0uu48gkUQU8KuI0`3&#!@6qB(@S%S1}Q<<+RPd|;1+ko zfB*C@pvsqszcq@XOzke%X~T8^@dIc?sTXCrdL(SCy$yh}e(?gw3P(rps>5y!^c*c` z8y!y_Loq`&MeHm8#Ty0Wh3AW|%hlJ zN9V_eQ%3Z7y%+CzlQnWpfBdG#r>RXTL)k<_M0($#0c1EeP>w;rheol-e8?SkNCV0K zU2-wM|B-Jncgh!BNt!pyYT1x=aP{oJjGdOc$qJhTaS$mIO#18C7}qmt-EntOKMv9C z-Gf0P+|0T0apyb%8*A&%Mo;kSU=DK2Q}9j{xM^*4PE~?AGsHbeuK_lyP7&tdcck9W z93N9wPZsI5lHa7|bzg+pky*-n<3=M&ACnNnKH-fa9rY8yE~=l>L!R>=zw+NK6HDk~ z$R_`3(AEy_3K{|tU`W}lRJR@%hllM$Dv79PQ|t`CTx*UAHaB2m!zZclR9`%sowW_< zcgw}k*_cRX4j!hwn9}Q08L;Ui&S2GCXprP*Mv;ltAwO}uJ^+1|op51bxC7{db*~d( zzG5ES&$t3*%%!dQ%xiDZxeuy7U}#vPbAoBLvG|09N4)zg)dmqVZ{?^xy~z#WB=F$5 z?#ituUtM3+y4q-NHMpO~O6Iz`5NMD}4IcKb)bv|S4kV=#3OtRxj;0>Gso$P-;bUs$ z**`3PV5+a@w*UDvtv;#iuui;KbHk1cW;eQf-;=&>HRuF5V=tw?b?f9M@PU|y&te3RsZuSUir-7%tDZ2$N z1wXxQtshw9ev^^TPkY3t-B`r%w4t_3{(y>6 z70;@QDR!H&2ZN)>0q@KE%=$M>y`(E=S53W=29t10T4o_OnSty1Ak7N01Du@5Lh~I} zuMc`>YZ%l)^^h{hgc->x96Fwx66J+#}5ZOm!{2ad+T>UBzHl|-PzcxR~44`(sh`F5mkGAr=aH?3}FFbULqx{?3trjCPW;rw&P7u=8DkVRwH3P zo`v>y^(8QhE@#_QJFVX7(+!E+LUXp)z0q-z5|Lv9V<0}9FAbU&^A3Z4Q-0PY`_#?W zf$r7n!qHE}``6bbiLy0`F-t!$c+oNIicMrsymtt}LtNWG3Tir-BwF&;yE%>eWXk zW6q!BYtEK9?=5jrzpY()dN}I*@Dj+QAOw4>{cON?%}L!pLGndD)0{71 znP%caw?gVm3gUgylZW&D8ZGA^6Hm@aoVOrd!TY?i=ZQ zK70OCo9~@2zmdGB-PPIdK`brC2mK#<=Q}bzuA+KSqgb;-Gh1zzCCz{_f8}L{)3Jq# z@i*hh%?Q>cse-CZr4bVFWlEx=`EPakUdzOQfLK4i*>!A>CnoxpAlcqq_eTrG2VG@% zn|hO8y=$qx7VilG6jac|48}%Av(7J!Jv2z$Id3@c@Z8+rzUD}YsS+9I!;Y!p@J0nu zCz$M-jSRmsJuvsPN{n8imu8Npgl5yUe*myhu%EP5w0X6IM#Tcnf{=(15TjAbM^sHY zeH2M~qn43i8oONo!4WiwmpN#DoOY>|DF}HsNsoox3~O4IB}hl&L02XOB@q^TbrNf% z@8gZ-8lfutOh+Xq6F9H1bRz5chxgUa3(uW4*}XA-J~#_`6vErf`%YA$@N;p1Dj0yu zo;V%!Uk?z*b>R+91>ZdP`T9?>0`$Bu{t z0)F|-&1+-n=n~9B)TKwdcSr0bMnziKTC=;EOCy)GKb%+$Q*3ckgW&zrht~WB+A-?9O}+mK zkHe`0GljVQkv)Sw!#gp>Ir(}*Jb@edpesVfl#)_3QTjcM1A0AaE{cX07A>#yh9Qr+M-E z^bDBhk8vNZzHzZK^QCqlGaZe}jx`dwxh3wDEh+Q?DtofoQ@fx+)D^=Oxa&6Q``rIW z)mukJ*>>&2K6U{XsH6fCBBFF7UD6`bDxE_&jDoa)bV~_HH$$T!9YZ$^-Q6)T-{yVZ z_qW!!7Rx`l?}a$mdF^xWqxL=DxE)U@~BrM2yIL6a)b?2zB%m`eFetJE1$d&fD0RG(e?iLt`C_Kuwx1s;=p-K_8wjqXG<=!?W#HkjdUHxf|dbKy~_b zWo8dT1Tlfte7pExnPuiZH*b-rl7rQ`#c_ObZm)GzB9ru3vq zh0-6+d+YYCIdTsLMwDz2!sYvv)iMrn)319OEUn zu`zr)+3jLHd9(Zy_N%2X^VP#kTxMK=@BnLt@u?|5BNH69X8_2=>$&ZP+e*FfbGh8C z9Na+YodlRKz-~#+PBV{M7|b`}zQ)tEt9)}wuBT+kHf!+xF+~~~KMx2abXIh<5#*`f zqxDj{l341Rp-)?1zg&V*R=Pmi&SXFFe?urSugfX^Uve2RH5$ z-vc9bq=WM@m_NTtd|}9K-jAZql`jl;C6g@bPd6`3bW1fr;L1>w1Dbb+*o+9!kGypy}$n zt7Q)=7gLu5*eI!Dr`}dxX^d_JXiPEfH7B8!G2N!Zu#zL~ekY^{1ll}5eRoQ39`rB9 z|9Y@>LD6o4rDw0-$CfA-HQIUo(rA}-naH#C2wN10zXm&YB+(bwxx4v<(LZ;U%)kG`gBX9>it9W2uF zS4y_t;JPTg^2S8ezF{-c64XK<6G^a2D4s8FtP%Sv@#c<*CPLFe<`9yB7V6P$Xw%m~ z4xOG&A9QaB#Ms9FQ>vrpcxHV$Zi*0~JHuy2lDx(_qa$TbM!mYFc*PK?(du$Up8MnE z$;@BK*AYf*(96=-@@ZqW;T_%$YwYqFi67!oq0A#C;IDz#9YJ23W1;r6ex8z$ zM%s3x{k^4j;$x>?KH93^uM26m)%r?aceoe92n#JM{4y=p=K637q2X8^SWT+!gfTFHEJ+5rqMFt(tW1|pD33gJrN1V! zGd@H4EX7y8<@kK4_oB?_Xz;MXuV7I4jz&F%%hD;W8Hf0HJ3E|BZQ15LPmt4QtmR>5aXdXo$+y8b-= z-lgpk?FNqMPR|6Avr|0ZQ7}sZ&c3!goY)5B9r?6z=rT<*BJ6Y!^cfmrYHwdAs-&I5S>)Op# zFsMU_jbf~#Keu1S)yl@*B&LF=mW!i2DH;M>f{mA2bA0)=Fhx}c;_iWfq$CIbno?Vh zfvnu!(@-rfgrtF_uGufJ)4OG!^*3iZWA~q2;BNV!%=cTZRSsK1F7plk4}<1^c;0&G zN3gZM9rAyrEOmHmmtd!Q+)NQa$>qcaA|W7#>78OSFD;vtS=$Hev|~-$0BStnge=<- zTZ;z5_in;yus#axVW`I_fSH-KP8)y`W!vlENDLj|N#>ctTM99^0V^@V?!@;HsqkS9 zk72NRa)o49gjXoLQJ(v^IBJJp%uNiy6Nj$j+9iUTh_~%orh_TD=l5VtY!aj!z&TJ# zmWG5sh_3kVcG(`PQ#bDbWC_<((f4q`#}>Sx6lxETD0a&TIHG6~>_6yualX8f5hO0W?%+21YGES2Sym9!xnKg==kXz%w5xS`ME+zqd8dq*h8ltmuHQ8e8)!@ovO zzNGw)L6V{7sTPy+1`Mq9O1IS()CqDjR50oYpb^f~suaMYv;`wId^pC>jLO5y=8ETF zc|)0yxviY-XW?Y=3~%V*6)J-Da#P{7lG7GfevW5gKL%!^Ap#R@$?P|jTf)h%|N9P!A&mn~_*ckeJ%4WJY`TU= zvm6S!EqpYS|R(


>gkrU=nzAdZaqWJLL3r^QYtM0q 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

HMQttV9;f4 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 From 4e0002d0fff5bfc413251025ece3357558ec818c Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sun, 26 Sep 2021 09:10:45 -0400 Subject: [PATCH 083/161] generate sphinx doc on the fly for training parameters (#1171) * generate sphinx doc on the fly for training parameters Sometimes we will forget re-generating train-input-auto.rst, so we may want to regenerate it when running sphinx. This will not affect that file on the GitHub. * get string * remove JSON file --- doc/conf.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index dd946fa2e1..3803691044 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -12,7 +12,7 @@ # import os import subprocess -# import sys +import sys import recommonmark from recommonmark.transform import AutoStructify @@ -130,6 +130,10 @@ def generate_doxygen_xml(app): else: subprocess.call("doxygen Doxyfile", shell=True) +def generate_train_input(app): + with open("train-input-auto.rst", 'w') as f: + f.write(subprocess.check_output((sys.executable, "-m", "deepmd", "doc-train-input"), universal_newlines=True)) + def run_apidoc(_): from sphinx.ext.apidoc import main import sys @@ -143,6 +147,7 @@ def setup(app): # Add hook for building doxygen xml when needed app.connect("builder-inited", generate_doxygen_xml) app.connect('builder-inited', run_apidoc) + app.connect('builder-inited', generate_train_input) # -- General configuration --------------------------------------------------- From 5843c02211efec21f17ac20f2effd828e2d09262 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Tue, 28 Sep 2021 07:12:41 -0400 Subject: [PATCH 084/161] move AutoBatchSize from dp test to DeepPot.eval (#1173) * move AutoBatchSize from dp test to dp.eval Bugfix. * Revert "Bug fix of memory overflow when calculating model deviation (#1153)" This reverts commit d8acbb8b5647d1d7316f182f0f588f4eb5038f28. * reshape coords before get shape * use the same AutoBatchSize for all models in model deviation --- deepmd/entrypoints/test.py | 9 +-------- deepmd/infer/deep_eval.py | 34 +++++++++++++++++++++++++++++++--- deepmd/infer/deep_pot.py | 25 +++++++++++++++++++++---- deepmd/infer/model_devi.py | 17 +++++++++-------- deepmd/utils/batch_size.py | 2 +- 5 files changed, 63 insertions(+), 24 deletions(-) diff --git a/deepmd/entrypoints/test.py b/deepmd/entrypoints/test.py index 1a5b18969c..877c6948ae 100644 --- a/deepmd/entrypoints/test.py +++ b/deepmd/entrypoints/test.py @@ -9,7 +9,6 @@ from deepmd.utils import random as dp_random from deepmd.utils.data import DeepmdData from deepmd.utils.weight_avg import weighted_average -from deepmd.utils.batch_size import AutoBatchSize if TYPE_CHECKING: from deepmd.infer import DeepDipole, DeepPolar, DeepPot, DeepWFC @@ -70,7 +69,6 @@ def test( # init model dp = DeepPotential(model) - auto_batch_size = AutoBatchSize() for cc, system in enumerate(all_sys): log.info("# ---------------output of dp test--------------- ") @@ -84,7 +82,6 @@ def test( err = test_ener( dp, data, - auto_batch_size, system, numb_test, detail_file, @@ -162,7 +159,6 @@ def save_txt_file( def test_ener( dp: "DeepPot", data: DeepmdData, - auto_batch_size: AutoBatchSize, system: str, numb_test: int, detail_file: Optional[str], @@ -230,10 +226,7 @@ def test_ener( else: aparam = None - ret = auto_batch_size.execute_all( - dp.eval, - numb_test, - natoms, + ret = dp.eval( coord, box, atype, diff --git a/deepmd/infer/deep_eval.py b/deepmd/infer/deep_eval.py index fb3cc07776..0d7355c89b 100644 --- a/deepmd/infer/deep_eval.py +++ b/deepmd/infer/deep_eval.py @@ -1,17 +1,31 @@ import os -from typing import List, Optional, TYPE_CHECKING +from typing import List, Optional, TYPE_CHECKING, Union import numpy as np from deepmd.common import make_default_mesh from deepmd.env import default_tf_session_config, tf, MODEL_VERSION from deepmd.utils.sess import run_sess +from deepmd.utils.batch_size import AutoBatchSize if TYPE_CHECKING: from pathlib import Path class DeepEval: - """Common methods for DeepPot, DeepWFC, DeepPolar, ...""" + """Common methods for DeepPot, DeepWFC, DeepPolar, ... + + Parameters + ---------- + model_file : Path + The name of the frozen model file. + load_prefix: str + The prefix in the load computational graph + default_tf_graph : bool + If uses the default tf graph, otherwise build a new tf graph for evaluation + auto_batch_size : bool or int or AutomaticBatchSize, default: False + If True, automatic batch size will be used. If int, it will be used + as the initial batch size. + """ _model_type: Optional[str] = None _model_version: Optional[str] = None @@ -21,7 +35,8 @@ def __init__( self, model_file: "Path", load_prefix: str = "load", - default_tf_graph: bool = False + default_tf_graph: bool = False, + auto_batch_size: Union[bool, int, AutoBatchSize] = False, ): self.graph = self._load_graph( model_file, prefix=load_prefix, default_tf_graph=default_tf_graph @@ -34,6 +49,19 @@ def __init__( f"model in graph (version {self.model_version}) is incompatible" f"with the model (version {MODEL_VERSION}) supported by the current code." ) + + # set default to False, as subclasses may not support + if isinstance(auto_batch_size, bool): + if auto_batch_size: + self.auto_batch_size = AutoBatchSize() + else: + self.auto_batch_size = None + elif isinstance(auto_batch_size, int): + self.auto_batch_size = AutoBatchSize(auto_batch_size) + elif isinstance(auto_batch_size, AutoBatchSize): + self.auto_batch_size = auto_batch_size + else: + raise TypeError("auto_batch_size should be bool, int, or AutoBatchSize") @property def model_type(self) -> str: diff --git a/deepmd/infer/deep_pot.py b/deepmd/infer/deep_pot.py index b8d557ae76..f58226c374 100644 --- a/deepmd/infer/deep_pot.py +++ b/deepmd/infer/deep_pot.py @@ -1,5 +1,5 @@ import logging -from typing import TYPE_CHECKING, List, Optional, Tuple +from typing import TYPE_CHECKING, List, Optional, Tuple, Union import numpy as np from deepmd.common import make_default_mesh @@ -7,6 +7,7 @@ from deepmd.infer.data_modifier import DipoleChargeModifier from deepmd.infer.deep_eval import DeepEval from deepmd.utils.sess import run_sess +from deepmd.utils.batch_size import AutoBatchSize if TYPE_CHECKING: from pathlib import Path @@ -25,6 +26,9 @@ class DeepPot(DeepEval): The prefix in the load computational graph default_tf_graph : bool If uses the default tf graph, otherwise build a new tf graph for evaluation + auto_batch_size : bool or int or AutomaticBatchSize, default: True + If True, automatic batch size will be used. If int, it will be used + as the initial batch size. Examples -------- @@ -49,7 +53,8 @@ def __init__( self, model_file: "Path", load_prefix: str = "load", - default_tf_graph: bool = False + default_tf_graph: bool = False, + auto_batch_size: Union[bool, int, AutoBatchSize] = True, ) -> None: # add these tensors on top of what is defined by DeepTensor Class @@ -83,7 +88,8 @@ def __init__( self, model_file, load_prefix=load_prefix, - default_tf_graph=default_tf_graph + default_tf_graph=default_tf_graph, + auto_batch_size=auto_batch_size, ) # load optional tensors @@ -224,12 +230,23 @@ def eval( atom_virial The atomic virial. Only returned when atomic == True """ + # reshape coords before getting shape + natoms = len(atom_types) + coords = np.reshape(np.array(coords), [-1, natoms * 3]) + numb_test = coords.shape[0] if atomic: if self.modifier_type is not None: raise RuntimeError('modifier does not support atomic modification') + if self.auto_batch_size is not None: + return self.auto_batch_size.execute_all(self._eval_inner, numb_test, natoms, + coords, cells, atom_types, fparam = fparam, aparam = aparam, atomic = atomic, efield = efield) return self._eval_inner(coords, cells, atom_types, fparam = fparam, aparam = aparam, atomic = atomic, efield = efield) else : - e, f, v = self._eval_inner(coords, cells, atom_types, fparam = fparam, aparam = aparam, atomic = atomic, efield = efield) + if self.auto_batch_size is not None: + e, f, v = self.auto_batch_size.execute_all(self._eval_inner, numb_test, natoms, + coords, cells, atom_types, fparam = fparam, aparam = aparam, atomic = atomic, efield = efield) + else: + e, f, v = self._eval_inner(coords, cells, atom_types, fparam = fparam, aparam = aparam, atomic = atomic, efield = efield) if self.modifier_type is not None: me, mf, mv = self.dm.eval(coords, cells, atom_types) e += me.reshape(e.shape) diff --git a/deepmd/infer/model_devi.py b/deepmd/infer/model_devi.py index 080fd59fcb..3a9c7f0cc4 100644 --- a/deepmd/infer/model_devi.py +++ b/deepmd/infer/model_devi.py @@ -1,6 +1,7 @@ import numpy as np from .deep_pot import DeepPot from ..utils.data import DeepmdData +from ..utils.batch_size import AutoBatchSize def calc_model_devi_f(fs: np.ndarray): @@ -174,8 +175,9 @@ def make_model_devi( in a trajectory by a MD engine (such as Gromacs / Lammps). This paramter is used to determine the index in the output file. ''' + auto_batch_size = AutoBatchSize() # init models - dp_models = [DeepPot(model) for model in models] + dp_models = [DeepPot(model, auto_batch_size=auto_batch_size) for model in models] # check type maps tmaps = [dp.get_type_map() for dp in dp_models] @@ -195,13 +197,12 @@ def make_model_devi( nframes_tot = 0 devis = [] for data in data_sets: - coords = data["coord"] - boxs = data["box"] - atypes = data["type"] - for coord, box, atype in zip(coords, boxs, atypes): - devi = calc_model_devi(np.array([coord]), np.array([box]), atype, dp_models, nopbc=nopbc) - nframes_tot += 1 - devis.append(devi) + coord = data["coord"] + box = data["box"] + atype = data["type"][0] + devi = calc_model_devi(coord, box, atype, dp_models, nopbc=nopbc) + nframes_tot += coord.shape[0] + devis.append(devi) devis = np.vstack(devis) devis[:, 0] = np.arange(nframes_tot) * frequency write_model_devi_out(devis, output) diff --git a/deepmd/utils/batch_size.py b/deepmd/utils/batch_size.py index 981572ecd8..435a90aa49 100644 --- a/deepmd/utils/batch_size.py +++ b/deepmd/utils/batch_size.py @@ -79,7 +79,7 @@ def execute(self, callable: Callable, start_index: int, natoms: int) -> Tuple[in n_tot = n_batch * natoms self.maximum_working_batch_size = max(self.maximum_working_batch_size, n_tot) # adjust the next batch size - if n_tot >= self.current_batch_size and self.current_batch_size * self.factor < self.minimal_not_working_batch_size: + if n_tot + natoms > self.current_batch_size and self.current_batch_size * self.factor < self.minimal_not_working_batch_size: self._adjust_batch_size(self.factor) return n_batch, result From d52877fc6b16d1ada002fbd9a472bf9e736c2e77 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Thu, 30 Sep 2021 05:15:31 -0400 Subject: [PATCH 085/161] bump default LAMMPS version to `stable_29Sep2021` (#1176) `stable_29Sep2021` will be released today. --- doc/install/install-from-source.md | 2 +- doc/install/install-lammps.md | 18 +++++++++--------- source/install/build_cc.sh | 2 +- source/install/build_lammps.sh | 2 +- source/lmp/CMakeLists.txt | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/doc/install/install-from-source.md b/doc/install/install-from-source.md index 8efa20a374..c9764e548c 100644 --- a/doc/install/install-from-source.md +++ b/doc/install/install-from-source.md @@ -174,7 +174,7 @@ One may add the following arguments to `cmake`: | -DCUDA_TOOLKIT_ROOT_DIR=<value> | Path | Detected automatically | The path to the CUDA toolkit directory. | | -DUSE_ROCM_TOOLKIT=<value> | `TRUE` or `FALSE` | `FALSE` | If `TRUE`, Build GPU support with ROCM toolkit. | | -DROCM_ROOT=<value> | Path | Detected automatically | The path to the ROCM toolkit directory. | -| -DLAMMPS_VERSION_NUMBER=<value> | Number | `20201029` | Only neccessary for LAMMPS built-in mode. The version number of LAMMPS (yyyymmdd). | +| -DLAMMPS_VERSION_NUMBER=<value> | Number | `20210929` | Only neccessary for LAMMPS built-in mode. The version number of LAMMPS (yyyymmdd). | | -DLAMMPS_SOURCE_ROOT=<value> | Path | - | Only neccessary for LAMMPS plugin mode. The path to the LAMMPS source code (later than 8Apr2021). If not assigned, the plugin mode will not be enabled. | If the cmake has executed successfully, then diff --git a/doc/install/install-lammps.md b/doc/install/install-lammps.md index d86a7e909f..d50be897ee 100644 --- a/doc/install/install-lammps.md +++ b/doc/install/install-lammps.md @@ -12,12 +12,12 @@ make lammps DeePMD-kit will generate a module called `USER-DEEPMD` in the `build` directory. If you need low precision version, move `env_low.sh` to `env.sh` in the directory. Now download the LAMMPS code (`29Oct2020` or later), and uncompress it: ```bash cd /some/workspace -wget https://github.com/lammps/lammps/archive/stable_29Oct2020.tar.gz -tar xf stable_29Oct2020.tar.gz +wget https://github.com/lammps/lammps/archive/stable_29Sep2021.tar.gz +tar xf stable_29Sep2021.tar.gz ``` -The source code of LAMMPS is stored in directory `lammps-stable_29Oct2020`. Now go into the LAMMPS code and copy the DeePMD-kit module like this +The source code of LAMMPS is stored in directory `lammps-stable_29Sep2021`. Now go into the LAMMPS code and copy the DeePMD-kit module like this ```bash -cd lammps-stable_29Oct2020/src/ +cd lammps-stable_29Sep2021/src/ cp -r $deepmd_source_dir/source/build/USER-DEEPMD . ``` Now build LAMMPS @@ -43,13 +43,13 @@ Starting from `8Apr2021`, LAMMPS also provides a plugin mode, allowing one build Now download the LAMMPS code (`8Apr2021` or later), and uncompress it: ```bash cd /some/workspace -wget https://github.com/lammps/lammps/archive/patch_30Jul2021.tar.gz -tar xf patch_30Jul2021.tar.gz +wget https://github.com/lammps/lammps/archive/stable_29Sep2021.tar.gz +tar xf stable_29Sep2021.tar.gz ``` -The source code of LAMMPS is stored in directory `lammps-patch_30Jul2021`. Now go into the LAMMPS code and create a directory called `build` +The source code of LAMMPS is stored in directory `lammps-stable_29Sep2021`. Now go into the LAMMPS code and create a directory called `build` ```bash -mkdir -p lammps-patch_30Jul2021/build/ -cd lammps-patch_30Jul2021/build/ +mkdir -p lammps-stable_29Sep2021/build/ +cd lammps-stable_29Sep2021/build/ ``` Now build LAMMPS. Note that `PLUGIN` and `KSPACE` package must be enabled, and `BUILD_SHARED_LIBS` must be set to `yes`. You can install any other package you want. ```bash diff --git a/source/install/build_cc.sh b/source/install/build_cc.sh index 16a269404d..c7e7686812 100755 --- a/source/install/build_cc.sh +++ b/source/install/build_cc.sh @@ -20,7 +20,7 @@ NPROC=$(nproc --all) BUILD_TMP_DIR=${SCRIPT_PATH}/../build mkdir -p ${BUILD_TMP_DIR} cd ${BUILD_TMP_DIR} -cmake -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} -DINSTALL_TENSORFLOW=TRUE ${CUDA_ARGS} -DLAMMPS_VERSION=patch_30Jul2021 -DUSE_TTM=TRUE .. +cmake -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} -DINSTALL_TENSORFLOW=TRUE ${CUDA_ARGS} -DLAMMPS_VERSION=stable_29Sep2021 -DUSE_TTM=TRUE .. make -j${NPROC} make install diff --git a/source/install/build_lammps.sh b/source/install/build_lammps.sh index c11aa2c733..912f577979 100755 --- a/source/install/build_lammps.sh +++ b/source/install/build_lammps.sh @@ -15,7 +15,7 @@ BUILD_TMP_DIR=${SCRIPT_PATH}/../build_lammps mkdir -p ${BUILD_TMP_DIR} cd ${BUILD_TMP_DIR} # download LAMMMPS -LAMMPS_VERSION=patch_30Jul2021 +LAMMPS_VERSION=stable_29Sep2021 if [ ! -d "lammps-${LAMMPS_VERSION}" ] then curl -L -o lammps.tar.gz https://github.com/lammps/lammps/archive/refs/tags/${LAMMPS_VERSION}.tar.gz diff --git a/source/lmp/CMakeLists.txt b/source/lmp/CMakeLists.txt index bb89a6357b..970959c678 100644 --- a/source/lmp/CMakeLists.txt +++ b/source/lmp/CMakeLists.txt @@ -1,7 +1,7 @@ add_subdirectory(plugin) if (NOT DEFINED LAMMPS_VERSION_NUMBER) - # set the default to 29 Oct 2020 - set(LAMMPS_VERSION_NUMBER 20201029) + # set the default to stable_29Sep2021 + set(LAMMPS_VERSION_NUMBER 20210929) endif() message(STATUS "LAMMPS version is ${LAMMPS_VERSION_NUMBER}") From f6de9c384874176b08412ae9d3b95cb5ad542a93 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 4 Oct 2021 02:51:56 -0400 Subject: [PATCH 086/161] doc for LAMMPS plugin: install library into `lib` (#1184) This commit updates the document for LAMMPS plugin mode. The LAMMPS library installation directory is assigned to `lib`, then one could load deepmd-kit library from a path without prefix. (`dopen` loads library from `rpath`) --- doc/install/install-lammps.md | 4 ++-- doc/third-party/lammps-command.md | 2 +- examples/water/lmp/in.plugin.lammps | 4 ++-- source/install/build_lammps.sh | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/install/install-lammps.md b/doc/install/install-lammps.md index d50be897ee..d7c979ef39 100644 --- a/doc/install/install-lammps.md +++ b/doc/install/install-lammps.md @@ -53,7 +53,7 @@ cd lammps-stable_29Sep2021/build/ ``` Now build LAMMPS. Note that `PLUGIN` and `KSPACE` package must be enabled, and `BUILD_SHARED_LIBS` must be set to `yes`. You can install any other package you want. ```bash -cmake -D PKG_PLUGIN=ON -D PKG_KSPACE=ON -D LAMMPS_INSTALL_RPATH=ON -D BUILD_SHARED_LIBS=yes -D CMAKE_INSTALL_PREFIX=${deepmd_root} ../cmake +cmake -D PKG_PLUGIN=ON -D PKG_KSPACE=ON -D LAMMPS_INSTALL_RPATH=ON -D BUILD_SHARED_LIBS=yes -D CMAKE_INSTALL_PREFIX=${deepmd_root} -D CMAKE_INSTALL_LIBDIR=lib -D CMAKE_INSTALL_FULL_LIBDIR=${deepmd_root}/lib ../cmake make -j4 make install ``` @@ -61,4 +61,4 @@ make install If everything works fine, you will end up with an executable `${deepmd_root}/lmp`. ```bash ${deepmd_root}/lmp -h -``` \ No newline at end of file +``` diff --git a/doc/third-party/lammps-command.md b/doc/third-party/lammps-command.md index ff6c490aeb..96c40bbb5d 100644 --- a/doc/third-party/lammps-command.md +++ b/doc/third-party/lammps-command.md @@ -5,7 +5,7 @@ If you are using the plugin mode, enable DeePMD-kit package in LAMMPS with `plugin` command: ``` -plugin load path/to/deepmd/lib/libdeepmd_lmp.so +plugin load libdeepmd_lmp.so ``` The built-in mode doesn't need this step. diff --git a/examples/water/lmp/in.plugin.lammps b/examples/water/lmp/in.plugin.lammps index c27cc54d36..7161735c23 100644 --- a/examples/water/lmp/in.plugin.lammps +++ b/examples/water/lmp/in.plugin.lammps @@ -11,8 +11,8 @@ read_data water.lmp mass 1 16 mass 2 2 -# load the plugin at /lib/libdeepmd_lmp.so -plugin load ../../../dp/lib/libdeepmd_lmp.so +# load the deepmd plugin +plugin load libdeepmd_lmp.so pair_style deepmd frozen_model.pb pair_coeff * * diff --git a/source/install/build_lammps.sh b/source/install/build_lammps.sh index 912f577979..399847ae25 100755 --- a/source/install/build_lammps.sh +++ b/source/install/build_lammps.sh @@ -25,7 +25,7 @@ fi cd ${BUILD_TMP_DIR}/lammps-${LAMMPS_VERSION} mkdir -p ${BUILD_TMP_DIR}/lammps-${LAMMPS_VERSION}/build cd ${BUILD_TMP_DIR}/lammps-${LAMMPS_VERSION}/build -cmake -C ../cmake/presets/all_off.cmake -D PKG_PLUGIN=ON -D PKG_KSPACE=ON -D BUILD_SHARED_LIBS=yes -D LAMMPS_INSTALL_RPATH=ON -D CMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} ../cmake +cmake -C ../cmake/presets/all_off.cmake -D PKG_PLUGIN=ON -D PKG_KSPACE=ON -D BUILD_SHARED_LIBS=yes -D LAMMPS_INSTALL_RPATH=ON -D CMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} -D CMAKE_INSTALL_LIBDIR=lib -D CMAKE_INSTALL_FULL_LIBDIR=${INSTALL_PREFIX}/lib ../cmake make -j${NPROC} make install From 7bfeb6c22ad99e33d77c0d2dd6de2cde1f8b5e32 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Thu, 7 Oct 2021 20:49:35 -0400 Subject: [PATCH 087/161] enable RTD build for pdf and epub (#1191) --- .readthedocs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index a105ddb2b2..90fedfd292 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,3 +1,4 @@ version: 2 conda: - environment: doc/environment.yml \ No newline at end of file + environment: doc/environment.yml +formats: all From 35d333e7a903e566f1f6baeb68a9efab146dafff Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Fri, 8 Oct 2021 22:53:49 -0400 Subject: [PATCH 088/161] add `start_pref_pf` and `limit_pref_pf` to `loss` Argument (#1200) fix #1199 --- deepmd/utils/argcheck.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/deepmd/utils/argcheck.py b/deepmd/utils/argcheck.py index 0ea84d8e73..857dc04e10 100644 --- a/deepmd/utils/argcheck.py +++ b/deepmd/utils/argcheck.py @@ -478,6 +478,8 @@ def loss_ener(): doc_limit_pref_v = limit_pref('virial') doc_start_pref_ae = start_pref('atom_ener') doc_limit_pref_ae = limit_pref('atom_ener') + doc_start_pref_pf = start_pref('atom_pref') + doc_limit_pref_pf = limit_pref('atom_pref') doc_relative_f = 'If provided, relative force error will be used in the loss. The difference of force will be normalized by the magnitude of the force in the label with a shift given by `relative_f`, i.e. DF_i / ( || F || + relative_f ) with DF denoting the difference between prediction and label and || F || denoting the L2 norm of the label.' return [ Argument("start_pref_e", [float,int], optional = True, default = 0.02, doc = doc_start_pref_e), @@ -488,6 +490,8 @@ def loss_ener(): Argument("limit_pref_v", [float,int], optional = True, default = 0.00, doc = doc_limit_pref_v), Argument("start_pref_ae", [float,int], optional = True, default = 0.00, doc = doc_start_pref_ae), Argument("limit_pref_ae", [float,int], optional = True, default = 0.00, doc = doc_limit_pref_ae), + Argument("start_pref_pf", [float,int], optional = True, default = 0.00, doc = doc_start_pref_pf), + Argument("limit_pref_pf", [float,int], optional = True, default = 0.00, doc = doc_limit_pref_pf), Argument("relative_f", [float,None], optional = True, doc = doc_relative_f) ] From d4775075b884e628243e695e141681566e10ffef Mon Sep 17 00:00:00 2001 From: marian-code Date: Thu, 14 Oct 2021 01:57:52 +0200 Subject: [PATCH 089/161] Fix the bug when type_map has only one element (#1196) * Update data.py Numpy has a bit odd behavior I ran into. The method `ndarray.loadtxt()` when loading from array file of length 1 outputs only the array element, not array of length=1. ```python import numpy as np np.__version__ "1.20.3" !cat array.raw A np.loadtxt("array.raw").tolist() "A" ``` * satisfy suggestions --- deepmd/utils/data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deepmd/utils/data.py b/deepmd/utils/data.py index ecf4aaeaba..99337aa41d 100644 --- a/deepmd/utils/data.py +++ b/deepmd/utils/data.py @@ -508,7 +508,7 @@ def _make_idx_map(self, atom_type): def _load_type_map(self, sys_path: DPPath) : fname = sys_path / 'type_map.raw' if fname.is_file() : - return fname.load_txt(dtype=str).tolist() + return fname.load_txt(dtype=str, ndmin=1).tolist() else : return None From 7045ba5584596e00207501aff1b759d925c410bd Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Thu, 14 Oct 2021 19:58:33 -0400 Subject: [PATCH 090/161] rename `descrpt_list` to `list` to keep compatibility (#1214) fix #1211 --- deepmd/descriptor/hybrid.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/deepmd/descriptor/hybrid.py b/deepmd/descriptor/hybrid.py index 66a11be959..9d8967faee 100644 --- a/deepmd/descriptor/hybrid.py +++ b/deepmd/descriptor/hybrid.py @@ -26,15 +26,17 @@ class DescrptHybrid (Descriptor): Parameters ---------- - descrpt_list : list + list : list Build a descriptor from the concatenation of the list of descriptors. """ def __init__ (self, - descrpt_list : list + list : list ) -> None : """ Constructor """ + # warning: list is conflict with built-in list + descrpt_list = list if descrpt_list == [] or descrpt_list is None: raise RuntimeError('cannot build descriptor from an empty list of descriptors.') formatted_descript_list = [] From 4bc3b279a963919f0c75b4c9086e1dba637e22d1 Mon Sep 17 00:00:00 2001 From: Denghui Lu Date: Fri, 15 Oct 2021 08:12:11 +0800 Subject: [PATCH 091/161] fix single precision error (#1212) --- deepmd/descriptor/se_a.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/deepmd/descriptor/se_a.py b/deepmd/descriptor/se_a.py index 8881ac3a14..ce8af71379 100644 --- a/deepmd/descriptor/se_a.py +++ b/deepmd/descriptor/se_a.py @@ -3,7 +3,7 @@ from typing import Tuple, List, Dict, Any from deepmd.env import tf -from deepmd.common import get_activation_func, get_precision, ACTIVATION_FN_DICT, PRECISION_DICT, docstring_parameter, get_np_precision +from deepmd.common import get_activation_func, get_precision, ACTIVATION_FN_DICT, PRECISION_DICT, docstring_parameter from deepmd.utils.argcheck import list_to_doc from deepmd.env import GLOBAL_TF_FLOAT_PRECISION from deepmd.env import GLOBAL_NP_FLOAT_PRECISION @@ -13,7 +13,7 @@ from deepmd.utils.tabulate import DPTabulate from deepmd.utils.type_embed import embed_atom_type from deepmd.utils.sess import run_sess -from deepmd.utils.graph import load_graph_def, get_tensor_by_name_from_graph, get_embedding_net_variables +from deepmd.utils.graph import load_graph_def, get_tensor_by_name_from_graph from .descriptor import Descriptor from .se import DescrptSe @@ -133,7 +133,6 @@ def __init__ (self, self.compress_activation_fn = get_activation_func(activation_function) self.filter_activation_fn = get_activation_func(activation_function) self.filter_precision = get_precision(precision) - self.filter_np_precision = get_np_precision(precision) self.exclude_types = set() for tt in exclude_types: assert(len(tt) == 2) @@ -687,7 +686,7 @@ def _filter_lower( net = 'filter_-1_net_' + str(type_i) else: net = 'filter_' + str(type_input) + '_net_' + str(type_i) - return op_module.tabulate_fusion(self.table.data[net].astype(self.filter_np_precision), info, xyz_scatter, tf.reshape(inputs_i, [natom, shape_i[1]//4, 4]), last_layer_size = outputs_size[-1]) + return op_module.tabulate_fusion(tf.cast(self.table.data[net], self.filter_precision), info, xyz_scatter, tf.reshape(inputs_i, [natom, shape_i[1]//4, 4]), last_layer_size = outputs_size[-1]) else: if (not is_exclude): xyz_scatter = embedding_net( From 1cf3e50b182616a6c5f7bb42bea090c7313682da Mon Sep 17 00:00:00 2001 From: Han Wang <92130845+wanghan-iapcm@users.noreply.github.com> Date: Tue, 19 Oct 2021 21:53:05 +0800 Subject: [PATCH 092/161] Model compression for se-t descriptor (#1225) * add model compression for se-t descriptor * optimize range structure for se-t * add CPU support for se-t type model compression * add gradient define for op TabulateFusion * add ut for st_t model compression descriptor * fix UT error * add rocm support * address comments * Update compress.md * fix typo * bump model version from 1.0 to 1.1 * bump model version from 1.0 to 1.1 * fix bug of model compression * fix single precision error * fix UT error * address comments * upgrade convert support from 2.0 to 2.1 * add 2.0 convert-from support * update doc for model compatibility * Update model-compatability.md * Update model-compatability.md Co-authored-by: denghuilu --- deepmd/descriptor/se_a.py | 11 +- deepmd/descriptor/se_t.py | 93 +++- deepmd/entrypoints/compress.py | 2 - deepmd/entrypoints/convert.py | 8 +- deepmd/entrypoints/main.py | 2 +- deepmd/utils/argcheck.py | 2 - deepmd/utils/convert.py | 75 +++- deepmd/utils/tabulate.py | 276 +++++++----- doc/freeze/compress.md | 6 +- doc/troubleshooting/model-compatability.md | 8 +- source/config/MODEL_VER | 2 +- source/lib/include/tabulate.h | 138 +++++- source/lib/src/cuda/tabulate.cu | 329 +++++++++++++- source/lib/src/rocm/tabulate.hip.cu | 330 +++++++++++++- source/lib/src/tabulate.cc | 252 ++++++++++- source/lib/tests/test_tabulate.cc | 24 +- source/op/_tabulate_grad.py | 20 +- source/op/tabulate_multi_device.cc | 372 ++++++++++++++-- source/tests/infer/deepdipole.pbtxt | 2 +- source/tests/infer/deepdipole_fake.pbtxt | 2 +- source/tests/infer/deepdipole_new.pbtxt | 2 +- source/tests/infer/deeppolar.pbtxt | 2 +- source/tests/infer/deeppolar_new.pbtxt | 2 +- source/tests/infer/deeppot-1.pbtxt | 2 +- source/tests/infer/deeppot-r.pbtxt | 2 +- source/tests/infer/deeppot.pbtxt | 2 +- source/tests/infer/dipolecharge_d.pbtxt | 2 +- source/tests/infer/dipolecharge_e.pbtxt | 4 +- ...sion.py => test_model_compression_se_a.py} | 0 source/tests/test_model_compression_se_t.py | 420 ++++++++++++++++++ 30 files changed, 2143 insertions(+), 249 deletions(-) rename source/tests/{test_model_compression.py => test_model_compression_se_a.py} (100%) create mode 100644 source/tests/test_model_compression_se_t.py diff --git a/deepmd/descriptor/se_a.py b/deepmd/descriptor/se_a.py index ce8af71379..74b12a412a 100644 --- a/deepmd/descriptor/se_a.py +++ b/deepmd/descriptor/se_a.py @@ -323,12 +323,19 @@ def enable_compression(self, suffix : str, optional The suffix of the scope """ + # do some checks before the mocel compression process assert ( not self.filter_resnet_dt ), "Model compression error: descriptor resnet_dt must be false!" + for tt in self.exclude_types: + if (tt[0] not in range(self.ntypes)) or (tt[1] not in range(self.ntypes)): + raise RuntimeError("exclude types" + str(tt) + " must within the number of atomic types " + str(self.ntypes) + "!") + if (self.ntypes * self.ntypes - len(self.exclude_types) == 0): + raise RuntimeError("empty embedding-net are not supported in model compression!") + self.compress = True self.table = DPTabulate( - model_file, self.type_one_side, self.exclude_types, self.compress_activation_fn, suffix=suffix) + self, self.filter_neuron, model_file, self.type_one_side, self.exclude_types, self.compress_activation_fn, suffix=suffix) self.table_config = [table_extrapolate, table_stride_1, table_stride_2, check_frequency] self.lower, self.upper \ = self.table.build(min_nbor_dist, @@ -686,7 +693,7 @@ def _filter_lower( net = 'filter_-1_net_' + str(type_i) else: net = 'filter_' + str(type_input) + '_net_' + str(type_i) - return op_module.tabulate_fusion(tf.cast(self.table.data[net], self.filter_precision), info, xyz_scatter, tf.reshape(inputs_i, [natom, shape_i[1]//4, 4]), last_layer_size = outputs_size[-1]) + return op_module.tabulate_fusion_se_a(tf.cast(self.table.data[net], self.filter_precision), info, xyz_scatter, tf.reshape(inputs_i, [natom, shape_i[1]//4, 4]), last_layer_size = outputs_size[-1]) else: if (not is_exclude): xyz_scatter = embedding_net( diff --git a/deepmd/descriptor/se_t.py b/deepmd/descriptor/se_t.py index f43fb6f40c..d5ffdf3970 100644 --- a/deepmd/descriptor/se_t.py +++ b/deepmd/descriptor/se_t.py @@ -10,6 +10,8 @@ from deepmd.env import default_tf_session_config from deepmd.utils.network import embedding_net, embedding_net_rand_seed_shift from deepmd.utils.sess import run_sess +from deepmd.utils.graph import load_graph_def, get_tensor_by_name_from_graph +from deepmd.utils.tabulate import DPTabulate from .descriptor import Descriptor from .se import DescrptSe @@ -98,6 +100,7 @@ def __init__ (self, self.useBN = False self.dstd = None self.davg = None + self.compress = False self.embedding_net_variables = None self.place_holders = {} @@ -224,6 +227,53 @@ def compute_input_stats (self, self.dstd = np.array(all_dstd) + def enable_compression(self, + min_nbor_dist : float, + model_file : str = 'frozon_model.pb', + table_extrapolate : float = 5, + table_stride_1 : float = 0.01, + table_stride_2 : float = 0.1, + check_frequency : int = -1, + suffix : str = "", + ) -> None: + """ + Reveive the statisitcs (distance, max_nbor_size and env_mat_range) of the training data. + + Parameters + ---------- + min_nbor_dist + The nearest distance between atoms + model_file + The original frozen model, which will be compressed by the program + table_extrapolate + The scale of model extrapolation + table_stride_1 + The uniform stride of the first table + table_stride_2 + The uniform stride of the second table + check_frequency + The overflow check frequency + suffix : str, optional + The suffix of the scope + """ + assert ( + not self.filter_resnet_dt + ), "Model compression error: descriptor resnet_dt must be false!" + self.compress = True + self.table = DPTabulate( + self, self.filter_neuron, model_file, activation_fn = self.filter_activation_fn, suffix=suffix) + self.table_config = [table_extrapolate, table_stride_1 * 10, table_stride_2 * 10, check_frequency] + self.lower, self.upper \ + = self.table.build(min_nbor_dist, + table_extrapolate, + table_stride_1 * 10, + table_stride_2 * 10) + + graph, _ = load_graph_def(model_file) + self.davg = get_tensor_by_name_from_graph(graph, 'descrpt_attr%s/t_avg' % suffix) + self.dstd = get_tensor_by_name_from_graph(graph, 'descrpt_attr%s/t_std' % suffix) + + def build (self, coord_ : tf.Tensor, atype_ : tf.Tensor, @@ -497,25 +547,30 @@ def _filter(self, env_ij = tf.einsum('ijm,ikm->ijk', env_i, env_j) # with (natom x nei_type_i x nei_type_j) ebd_env_ij = tf.reshape(env_ij, [-1, 1]) - # with (natom x nei_type_i x nei_type_j) x out_size - ebd_env_ij = embedding_net(ebd_env_ij, - self.filter_neuron, - self.filter_precision, - activation_fn = activation_fn, - resnet_dt = self.filter_resnet_dt, - name_suffix = f"_{type_i}_{type_j}", - stddev = stddev, - bavg = bavg, - seed = self.seed, - trainable = trainable, - uniform_seed = self.uniform_seed, - initial_variables = self.embedding_net_variables, - ) - if (not self.uniform_seed) and (self.seed is not None): self.seed += self.seed_shift - # with natom x nei_type_i x nei_type_j x out_size - ebd_env_ij = tf.reshape(ebd_env_ij, [-1, nei_type_i, nei_type_j, outputs_size[-1]]) - # with natom x out_size - res_ij = tf.einsum('ijk,ijkm->im', env_ij, ebd_env_ij) + if self.compress: + info = [self.lower, self.upper, self.upper * self.table_config[0], self.table_config[1], self.table_config[2], self.table_config[3]] + net = 'filter_' + str(type_i) + '_net_' + str(type_j) + res_ij = op_module.tabulate_fusion_se_t(tf.cast(self.table.data[net], self.filter_precision), info, ebd_env_ij, env_ij, last_layer_size = outputs_size[-1]) + else: + # with (natom x nei_type_i x nei_type_j) x out_size + ebd_env_ij = embedding_net(ebd_env_ij, + self.filter_neuron, + self.filter_precision, + activation_fn = activation_fn, + resnet_dt = self.filter_resnet_dt, + name_suffix = f"_{type_i}_{type_j}", + stddev = stddev, + bavg = bavg, + seed = self.seed, + trainable = trainable, + uniform_seed = self.uniform_seed, + initial_variables = self.embedding_net_variables, + ) + if (not self.uniform_seed) and (self.seed is not None): self.seed += self.seed_shift + # with natom x nei_type_i x nei_type_j x out_size + ebd_env_ij = tf.reshape(ebd_env_ij, [-1, nei_type_i, nei_type_j, outputs_size[-1]]) + # with natom x out_size + res_ij = tf.einsum('ijk,ijkm->im', env_ij, ebd_env_ij) res_ij = res_ij * (1.0 / float(nei_type_i) / float(nei_type_j)) if result is None: result = res_ij diff --git a/deepmd/entrypoints/compress.py b/deepmd/entrypoints/compress.py index b03e8cf653..8cb8070594 100644 --- a/deepmd/entrypoints/compress.py +++ b/deepmd/entrypoints/compress.py @@ -93,8 +93,6 @@ def compress( name = 'train_attr/min_nbor_dist', dtype = GLOBAL_ENER_FLOAT_PRECISION) jdata["model"]["compress"] = {} - jdata["model"]["compress"]["type"] = 'se_e2_a' - jdata["model"]["compress"]["compress"] = True jdata["model"]["compress"]["model_file"] = input jdata["model"]["compress"]["min_nbor_dist"] = t_min_nbor_dist jdata["model"]["compress"]["table_config"] = [ diff --git a/deepmd/entrypoints/convert.py b/deepmd/entrypoints/convert.py index 25f2271cdb..3f277c5134 100644 --- a/deepmd/entrypoints/convert.py +++ b/deepmd/entrypoints/convert.py @@ -1,4 +1,4 @@ -from deepmd.utils.convert import convert_13_to_20, convert_12_to_20 +from deepmd.utils.convert import convert_20_to_21, convert_13_to_21, convert_12_to_21 def convert( *, @@ -8,8 +8,10 @@ def convert( **kwargs, ): if FROM == '1.2': - convert_12_to_20(input_model, output_model) + convert_12_to_21(input_model, output_model) elif FROM == '1.3': - convert_13_to_20(input_model, output_model) + convert_13_to_21(input_model, output_model) + elif FROM == '2.0': + convert_20_to_21(input_model, output_model) else: raise RuntimeError('unsupported model version ' + FROM) diff --git a/deepmd/entrypoints/main.py b/deepmd/entrypoints/main.py index 721eed357c..4f772526c8 100644 --- a/deepmd/entrypoints/main.py +++ b/deepmd/entrypoints/main.py @@ -386,7 +386,7 @@ def parse_args(args: Optional[List[str]] = None): parser_transform.add_argument( 'FROM', type = str, - choices = ['1.2', '1.3'], + choices = ['1.2', '1.3', '2.0'], help="The original model compatibility", ) parser_transform.add_argument( diff --git a/deepmd/utils/argcheck.py b/deepmd/utils/argcheck.py index 857dc04e10..db31469c29 100644 --- a/deepmd/utils/argcheck.py +++ b/deepmd/utils/argcheck.py @@ -371,13 +371,11 @@ def modifier_variant_type_args(): # --- model compression configurations: --- # def model_compression(): - doc_compress = f"The name of the frozen model file." doc_model_file = f"The input model file, which will be compressed by the DeePMD-kit." doc_table_config = f"The arguments of model compression, including extrapolate(scale of model extrapolation), stride(uniform stride of tabulation's first and second table), and frequency(frequency of tabulation overflow check)." doc_min_nbor_dist = f"The nearest distance between neighbor atoms saved in the frozen model." return [ - Argument("compress", bool, optional = False, doc = doc_compress), Argument("model_file", str, optional = False, doc = doc_model_file), Argument("table_config", list, optional = False, doc = doc_table_config), Argument("min_nbor_dist", float, optional = False, doc = doc_min_nbor_dist), diff --git a/deepmd/utils/convert.py b/deepmd/utils/convert.py index 6ace6a2132..b17178c761 100644 --- a/deepmd/utils/convert.py +++ b/deepmd/utils/convert.py @@ -3,22 +3,32 @@ from google.protobuf import text_format from tensorflow.python.platform import gfile -def convert_13_to_20(input_model: str, output_model: str): +def convert_13_to_21(input_model: str, output_model: str): convert_pb_to_pbtxt(input_model, 'frozen_model.pbtxt') convert_dp13_to_dp20('frozen_model.pbtxt') + convert_dp20_to_dp21('frozen_model.pbtxt') convert_pbtxt_to_pb('frozen_model.pbtxt', output_model) if os.path.isfile('frozen_model.pbtxt'): os.remove('frozen_model.pbtxt') - print("the converted output model (2.0 support) is saved in %s" % output_model) + print("the converted output model (2.1 support) is saved in %s" % output_model) -def convert_12_to_20(input_model: str, output_model: str): +def convert_12_to_21(input_model: str, output_model: str): convert_pb_to_pbtxt(input_model, 'frozen_model.pbtxt') convert_dp12_to_dp13('frozen_model.pbtxt') convert_dp13_to_dp20('frozen_model.pbtxt') + convert_dp20_to_dp21('frozen_model.pbtxt') convert_pbtxt_to_pb('frozen_model.pbtxt', output_model) if os.path.isfile('frozen_model.pbtxt'): os.remove('frozen_model.pbtxt') - print("the converted output model (2.0 support) is saved in %s" % output_model) + print("the converted output model (2.1 support) is saved in %s" % output_model) + +def convert_20_to_21(input_model: str, output_model: str): + convert_pb_to_pbtxt(input_model, 'frozen_model.pbtxt') + convert_dp20_to_dp21('frozen_model.pbtxt') + convert_pbtxt_to_pb('frozen_model.pbtxt', output_model) + if os.path.isfile('frozen_model.pbtxt'): + os.remove('frozen_model.pbtxt') + print("the converted output model (2.1 support) is saved in %s" % output_model) def convert_pb_to_pbtxt(pbfile: str, pbtxtfile: str): with gfile.FastGFile(pbfile, 'rb') as f: @@ -88,3 +98,60 @@ def convert_dp13_to_dp20(fname: str): .replace('DescrptSeR', 'ProdEnvMatR') with open(fname, 'w') as fp: fp.write(file_content) + +def convert_dp20_to_dp21(fname: str): + with open(fname) as fp: + file_content = fp.read() + old_model_version_node = """ +node { + name: "model_attr/model_version" + op: "Const" + attr { + key: "dtype" + value { + type: DT_STRING + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_STRING + tensor_shape { + } + string_val: "1.0" + } + } + } +} +""" + new_model_version_node = """ +node { + name: "model_attr/model_version" + op: "Const" + attr { + key: "dtype" + value { + type: DT_STRING + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_STRING + tensor_shape { + } + string_val: "1.1" + } + } + } +} +""" + file_content = file_content\ + .replace(old_model_version_node, new_model_version_node)\ + .replace('TabulateFusion', 'TabulateFusionSeA')\ + .replace('TabulateFusionGrad', 'TabulateFusionSeAGrad')\ + .replace('TabulateFusionGradGrad', 'TabulateFusionSeAGradGrad') + with open(fname, 'w') as fp: + fp.write(file_content) \ No newline at end of file diff --git a/deepmd/utils/tabulate.py b/deepmd/utils/tabulate.py index fe95173890..f0dc571142 100644 --- a/deepmd/utils/tabulate.py +++ b/deepmd/utils/tabulate.py @@ -2,14 +2,17 @@ import math import logging import numpy as np +import deepmd from typing import Callable from typing import Tuple, List +from scipy.special import comb from deepmd.env import tf from deepmd.env import op_module from deepmd.common import ACTIVATION_FN_DICT from deepmd.utils.sess import run_sess from deepmd.utils.graph import get_tensor_by_name_from_graph, load_graph_def from deepmd.utils.graph import get_embedding_net_nodes_from_graph_def +from deepmd.descriptor import Descriptor from tensorflow.python.platform import gfile from tensorflow.python.framework import tensor_util @@ -25,6 +28,10 @@ class DPTabulate(): Parameters ---------- + descrpt + Descriptor of the original model + neuron + Number of neurons in each hidden layers of the embedding net :math:`\mathcal{N}` model_file The frozen model type_one_side @@ -38,6 +45,8 @@ class DPTabulate(): The suffix of the scope """ def __init__(self, + descrpt : Descriptor, + neuron : List[int], model_file : str, type_one_side : bool = False, exclude_types : List[List[int]] = [], @@ -47,13 +56,12 @@ def __init__(self, """ Constructor """ - + self.descrpt = descrpt + self.neuron = neuron self.model_file = model_file self.type_one_side = type_one_side self.exclude_types = exclude_types self.suffix = suffix - if self.type_one_side and len(self.exclude_types) != 0: - raise RuntimeError('"type_one_side" is not compatible with "exclude_types"') # functype if activation_fn == ACTIVATION_FN_DICT["tanh"]: @@ -72,42 +80,36 @@ def __init__(self, try: self.sel_a = self.graph.get_operation_by_name('ProdEnvMatA').get_attr('sel_a') - self.descrpt = self.graph.get_operation_by_name ('ProdEnvMatA') + self.prod_env_mat_op = self.graph.get_operation_by_name ('ProdEnvMatA') except Exception: self.sel_a = self.graph.get_operation_by_name('DescrptSeA').get_attr('sel_a') - self.descrpt = self.graph.get_operation_by_name ('DescrptSeA') + self.prod_env_mat_op = self.graph.get_operation_by_name ('DescrptSeA') self.davg = get_tensor_by_name_from_graph(self.graph, f'descrpt_attr{self.suffix}/t_avg') self.dstd = get_tensor_by_name_from_graph(self.graph, f'descrpt_attr{self.suffix}/t_std') self.ntypes = get_tensor_by_name_from_graph(self.graph, 'descrpt_attr/ntypes') - self.rcut = self.descrpt.get_attr('rcut_r') - self.rcut_smth = self.descrpt.get_attr('rcut_r_smth') + self.rcut = self.prod_env_mat_op.get_attr('rcut_r') + self.rcut_smth = self.prod_env_mat_op.get_attr('rcut_r_smth') self.embedding_net_nodes = get_embedding_net_nodes_from_graph_def(self.graph_def, suffix=self.suffix) - for tt in self.exclude_types: - if (tt[0] not in range(self.ntypes)) or (tt[1] not in range(self.ntypes)): - raise RuntimeError("exclude types" + str(tt) + " must within the number of atomic types " + str(self.ntypes) + "!") - if (self.ntypes * self.ntypes - len(self.exclude_types) == 0): - raise RuntimeError("empty embedding-net are not supported in model compression!") - - self.layer_size = len(self.embedding_net_nodes) // ((self.ntypes * self.ntypes - len(self.exclude_types)) * 2) - self.table_size = self.ntypes * self.ntypes - if type_one_side : - self.layer_size = len(self.embedding_net_nodes) // (self.ntypes * 2) - self.table_size = self.ntypes - # self.value_type = self.embedding_net_nodes["filter_type_0/matrix_1_0"].dtype #"filter_type_0/matrix_1_0" must exit~ - # get trained variables + # move it to the descriptor class + # for tt in self.exclude_types: + # if (tt[0] not in range(self.ntypes)) or (tt[1] not in range(self.ntypes)): + # raise RuntimeError("exclude types" + str(tt) + " must within the number of atomic types " + str(self.ntypes) + "!") + # if (self.ntypes * self.ntypes - len(self.exclude_types) == 0): + # raise RuntimeError("empty embedding-net are not supported in model compression!") + self.layer_size = self._get_layer_size() + self.table_size = self._get_table_size() + self.bias = self._get_bias() self.matrix = self._get_matrix() - for item in self.matrix["layer_" + str(self.layer_size)]: - if len(item) != 0: - self.data_type = type(item[0][0]) - self.last_layer_size = item.shape[1] - # define tables + self.data_type = self._get_data_type() + self.last_layer_size = self._get_last_layer_size() + self.data = {} @@ -129,6 +131,8 @@ def build(self, The uniform stride of the first table stride1 The uniform stride of the second table + neuron + Number of neurons in each hidden layers of the embedding net :math:`\mathcal{N}` Returns ---------- @@ -139,34 +143,57 @@ def build(self, """ # tabulate range [lower, upper] with stride0 'stride0' lower, upper = self._get_env_mat_range(min_nbor_dist) - xx = np.arange(lower, upper, stride0, dtype = self.data_type) - xx = np.append(xx, np.arange(upper, extrapolate * upper, stride1, dtype = self.data_type)) - xx = np.append(xx, np.array([extrapolate * upper], dtype = self.data_type)) - self.nspline = int((upper - lower) / stride0 + (extrapolate * upper - upper) / stride1) - for ii in range(self.table_size): - if self.type_one_side or (ii // self.ntypes, int(ii % self.ntypes)) not in self.exclude_types: - vv, dd, d2 = self._make_data(xx, ii) - if self.type_one_side: - net = "filter_-1_net_" + str(ii) - else: - net = "filter_" + str(ii // self.ntypes) + "_net_" + str(int(ii % self.ntypes)) - self.data[net] = np.zeros([self.nspline, 6 * self.last_layer_size], dtype = self.data_type) - # for jj in tqdm(range(self.nspline), desc = 'DEEPMD INFO |-> deepmd.utils.tabulate\t\t\t' + net + ', tabulating'): - for jj in range(self.nspline): - for kk in range(self.last_layer_size): - if jj < int((upper - lower) / stride0): - tt = stride0 - else: - tt = stride1 - hh = vv[jj + 1][kk] - vv[jj][kk] - self.data[net][jj][kk * 6 + 0] = vv[jj][kk] - self.data[net][jj][kk * 6 + 1] = dd[jj][kk] - self.data[net][jj][kk * 6 + 2] = 0.5 * d2[jj][kk] - self.data[net][jj][kk * 6 + 3] = (1 / (2 * tt * tt * tt)) * (20 * hh - (8 * dd[jj + 1][kk] + 12 * dd[jj][kk]) * tt - (3 * d2[jj][kk] - d2[jj + 1][kk]) * tt * tt) - self.data[net][jj][kk * 6 + 4] = (1 / (2 * tt * tt * tt * tt)) * (-30 * hh + (14 * dd[jj + 1][kk] + 16 * dd[jj][kk]) * tt + (3 * d2[jj][kk] - 2 * d2[jj + 1][kk]) * tt * tt) - self.data[net][jj][kk * 6 + 5] = (1 / (2 * tt * tt * tt * tt * tt)) * (12 * hh - 6 * (dd[jj + 1][kk] + dd[jj][kk]) * tt + (d2[jj + 1][kk] - d2[jj][kk]) * tt * tt) + + if isinstance(self.descrpt, deepmd.descriptor.DescrptSeA): + xx = np.arange(lower, upper, stride0, dtype = self.data_type) + xx = np.append(xx, np.arange(upper, extrapolate * upper, stride1, dtype = self.data_type)) + xx = np.append(xx, np.array([extrapolate * upper], dtype = self.data_type)) + self.nspline = int((upper - lower) / stride0 + (extrapolate * upper - upper) / stride1) + for ii in range(self.table_size): + if self.type_one_side or (ii // self.ntypes, ii % self.ntypes) not in self.exclude_types: + if self.type_one_side: + net = "filter_-1_net_" + str(ii) + else: + net = "filter_" + str(ii // self.ntypes) + "_net_" + str(ii % self.ntypes) + self._build_lower(net, xx, ii, upper, lower, stride0, stride1, extrapolate) + elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeT): + xx = np.arange(extrapolate * lower, lower, stride1, dtype = self.data_type) + xx = np.append(xx, np.arange(lower, upper, stride0, dtype = self.data_type)) + xx = np.append(xx, np.arange(upper, extrapolate * upper, stride1, dtype = self.data_type)) + xx = np.append(xx, np.array([extrapolate * upper], dtype = self.data_type)) + self.nspline = int((upper - lower) / stride0 + 2 * ((extrapolate * upper - upper) / stride1)) + idx = 0 + for ii in range(self.ntypes): + for jj in range(ii, self.ntypes): + net = "filter_" + str(ii) + "_net_" + str(jj) + self._build_lower(net, xx, idx, upper, lower, stride0, stride1, extrapolate) + idx += 1 return lower, upper + def _build_lower(self, net, xx, idx, upper, lower, stride0, stride1, extrapolate): + vv, dd, d2 = self._make_data(xx, idx) + self.data[net] = np.zeros([self.nspline, 6 * self.last_layer_size], dtype = self.data_type) + # for jj in tqdm(range(self.nspline), desc = 'DEEPMD INFO |-> deepmd.utils.tabulate\t\t\t' + net + ', tabulating'): + for jj in range(self.nspline): + for kk in range(self.last_layer_size): + if isinstance(self.descrpt, deepmd.descriptor.DescrptSeA): + if jj < int((upper - lower) / stride0): + tt = stride0 + else: + tt = stride1 + elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeT): + if jj > int((lower - extrapolate * lower) / stride1) and jj < (int((lower - extrapolate * lower) / stride1) + int((upper - lower) / stride0)): + tt = stride0 + else: + tt = stride1 + hh = vv[jj + 1][kk] - vv[jj][kk] + self.data[net][jj][kk * 6 + 0] = vv[jj][kk] + self.data[net][jj][kk * 6 + 1] = dd[jj][kk] + self.data[net][jj][kk * 6 + 2] = 0.5 * d2[jj][kk] + self.data[net][jj][kk * 6 + 3] = (1 / (2 * tt * tt * tt)) * (20 * hh - (8 * dd[jj + 1][kk] + 12 * dd[jj][kk]) * tt - (3 * d2[jj][kk] - d2[jj + 1][kk]) * tt * tt) + self.data[net][jj][kk * 6 + 4] = (1 / (2 * tt * tt * tt * tt)) * (-30 * hh + (14 * dd[jj + 1][kk] + 16 * dd[jj][kk]) * tt + (3 * d2[jj][kk] - 2 * d2[jj + 1][kk]) * tt * tt) + self.data[net][jj][kk * 6 + 5] = (1 / (2 * tt * tt * tt * tt * tt)) * (12 * hh - 6 * (dd[jj + 1][kk] + dd[jj][kk]) * tt + (d2[jj + 1][kk] - d2[jj][kk]) * tt * tt) + def _load_sub_graph(self): sub_graph_def = tf.GraphDef() with tf.Graph().as_default() as sub_graph: @@ -177,42 +204,46 @@ def _get_bias(self): bias = {} for layer in range(1, self.layer_size + 1): bias["layer_" + str(layer)] = [] - if self.type_one_side: - for ii in range(0, self.ntypes): - node = self.embedding_net_nodes[f"filter_type_all{self.suffix}/bias_{layer}_{ii}"] - tensor_value = np.frombuffer (node.tensor_content, dtype = tf.as_dtype(node.dtype).as_numpy_dtype) - tensor_shape = tf.TensorShape(node.tensor_shape).as_list() - bias["layer_" + str(layer)].append(np.reshape(tensor_value, tensor_shape)) - else: - for ii in range(0, self.ntypes * self.ntypes): - if (ii // self.ntypes, int(ii % self.ntypes)) not in self.exclude_types: - node = self.embedding_net_nodes[f"filter_type_{ii // self.ntypes}{self.suffix}/bias_{layer}_{ii % self.ntypes}"] - tensor_value = np.frombuffer(node.tensor_content, dtype = tf.as_dtype(node.dtype).as_numpy_dtype) - tensor_shape = tf.TensorShape(node.tensor_shape).as_list() - bias["layer_" + str(layer)].append(np.reshape(tensor_value, tensor_shape)) - else: - bias["layer_" + str(layer)].append(np.array([])) + if isinstance(self.descrpt, deepmd.descriptor.DescrptSeA): + if self.type_one_side: + for ii in range(0, self.ntypes): + node = self.embedding_net_nodes[f"filter_type_all{self.suffix}/bias_{layer}_{ii}"] + bias["layer_" + str(layer)].append(tf.make_ndarray(node)) + else: + for ii in range(0, self.ntypes * self.ntypes): + if (ii // self.ntypes, ii % self.ntypes) not in self.exclude_types: + node = self.embedding_net_nodes[f"filter_type_{ii // self.ntypes}{self.suffix}/bias_{layer}_{ii % self.ntypes}"] + bias["layer_" + str(layer)].append(tf.make_ndarray(node)) + else: + bias["layer_" + str(layer)].append(np.array([])) + elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeT): + for ii in range(self.ntypes): + for jj in range(ii, self.ntypes): + node = self.embedding_net_nodes[f"filter_type_all{self.suffix}/bias_{layer}_{ii}_{jj}"] + bias["layer_" + str(layer)].append(tf.make_ndarray(node)) return bias def _get_matrix(self): matrix = {} for layer in range(1, self.layer_size + 1): matrix["layer_" + str(layer)] = [] - if self.type_one_side: - for ii in range(0, self.ntypes): - node = self.embedding_net_nodes[f"filter_type_all{self.suffix}/matrix_{layer}_{ii}"] - tensor_value = np.frombuffer (node.tensor_content, dtype = tf.as_dtype(node.dtype).as_numpy_dtype) - tensor_shape = tf.TensorShape(node.tensor_shape).as_list() - matrix["layer_" + str(layer)].append(np.reshape(tensor_value, tensor_shape)) - else: - for ii in range(0, self.ntypes * self.ntypes): - if (ii // self.ntypes, int(ii % self.ntypes)) not in self.exclude_types: - node = self.embedding_net_nodes[f"filter_type_{ii // self.ntypes}{self.suffix}/matrix_{layer}_{ii % self.ntypes}"] - tensor_value = np.frombuffer(node.tensor_content, dtype = tf.as_dtype(node.dtype).as_numpy_dtype) - tensor_shape = tf.TensorShape(node.tensor_shape).as_list() - matrix["layer_" + str(layer)].append(np.reshape(tensor_value, tensor_shape)) - else: - matrix["layer_" + str(layer)].append(np.array([])) + if isinstance(self.descrpt, deepmd.descriptor.DescrptSeA): + if self.type_one_side: + for ii in range(0, self.ntypes): + node = self.embedding_net_nodes[f"filter_type_all{self.suffix}/matrix_{layer}_{ii}"] + matrix["layer_" + str(layer)].append(tf.make_ndarray(node)) + else: + for ii in range(0, self.ntypes * self.ntypes): + if (ii // self.ntypes, ii % self.ntypes) not in self.exclude_types: + node = self.embedding_net_nodes[f"filter_type_{ii // self.ntypes}{self.suffix}/matrix_{layer}_{ii % self.ntypes}"] + matrix["layer_" + str(layer)].append(tf.make_ndarray(node)) + else: + matrix["layer_" + str(layer)].append(np.array([])) + elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeT): + for ii in range(self.ntypes): + for jj in range(ii, self.ntypes): + node = self.embedding_net_nodes[f"filter_type_all{self.suffix}/matrix_{layer}_{ii}_{jj}"] + matrix["layer_" + str(layer)].append(tf.make_ndarray(node)) return matrix # one-by-one executions @@ -223,13 +254,28 @@ def _make_data(self, xx, idx): for layer in range(self.layer_size): if layer == 0: xbar = tf.matmul( - xx, self.matrix["layer_" + str(layer + 1)][idx]) + self.bias["layer_" + str(layer + 1)][idx] - yy = self._layer_0( - xx, self.matrix["layer_" + str(layer + 1)][idx], self.bias["layer_" + str(layer + 1)][idx]) - dy = op_module.unaggregated_dy_dx_s( - yy, self.matrix["layer_" + str(layer + 1)][idx], xbar, tf.constant(self.functype)) - dy2 = op_module.unaggregated_dy2_dx_s( - yy, dy, self.matrix["layer_" + str(layer + 1)][idx], xbar, tf.constant(self.functype)) + xx, self.matrix["layer_" + str(layer + 1)][idx]) + self.bias["layer_" + str(layer + 1)][idx] + if self.neuron[0] == 1: + yy = self._layer_0( + xx, self.matrix["layer_" + str(layer + 1)][idx], self.bias["layer_" + str(layer + 1)][idx]) + xx + dy = op_module.unaggregated_dy_dx_s( + yy, self.matrix["layer_" + str(layer + 1)][idx], xbar, tf.constant(self.functype)) + tf.ones([1, 1], yy.dtype) + dy2 = op_module.unaggregated_dy2_dx_s( + yy, dy, self.matrix["layer_" + str(layer + 1)][idx], xbar, tf.constant(self.functype)) + elif self.neuron[0] == 2: + tt, yy = self._layer_1( + xx, self.matrix["layer_" + str(layer + 1)][idx], self.bias["layer_" + str(layer + 1)][idx]) + dy = op_module.unaggregated_dy_dx_s( + yy - tt, self.matrix["layer_" + str(layer + 1)][idx], xbar, tf.constant(self.functype)) + tf.ones([1, 2], yy.dtype) + dy2 = op_module.unaggregated_dy2_dx_s( + yy - tt, dy, self.matrix["layer_" + str(layer + 1)][idx], xbar, tf.constant(self.functype)) + else: + yy = self._layer_0( + xx, self.matrix["layer_" + str(layer + 1)][idx], self.bias["layer_" + str(layer + 1)][idx]) + dy = op_module.unaggregated_dy_dx_s( + yy, self.matrix["layer_" + str(layer + 1)][idx], xbar, tf.constant(self.functype)) + dy2 = op_module.unaggregated_dy2_dx_s( + yy, dy, self.matrix["layer_" + str(layer + 1)][idx], xbar, tf.constant(self.functype)) else: ybar = tf.matmul( yy, self.matrix["layer_" + str(layer + 1)][idx]) + self.bias["layer_" + str(layer + 1)][idx] @@ -254,21 +300,19 @@ def _layer_1(self, x, w, b): t = tf.concat([x, x], axis=1) return t, self.activation_fn(tf.matmul(x, w) + b) + t - def _save_data(self): - for ii in range(self.ntypes * self.ntypes): - net = "filter_" + str(ii // self.ntypes) + "_net_" + str(int(ii % self.ntypes)) - np.savetxt('data_' + str(ii), self.data[net]) - + # Change the embedding net range to sw / min_nbor_dist def _get_env_mat_range(self, min_nbor_dist): - lower = 100.0 - upper = -10.0 + lower = +100.0 + upper = -100.0 sw = self._spline5_switch(min_nbor_dist, self.rcut_smth, self.rcut) - for ii in range(self.ntypes): - if lower > -self.davg[ii][0] / self.dstd[ii][0]: - lower = -self.davg[ii][0] / self.dstd[ii][0] - if upper < ((1 / min_nbor_dist) * sw - self.davg[ii][0]) / self.dstd[ii][0]: - upper = ((1 / min_nbor_dist) * sw - self.davg[ii][0]) / self.dstd[ii][0] + if isinstance(self.descrpt, deepmd.descriptor.DescrptSeA): + lower = np.min(-self.davg[:, 0] / self.dstd[:, 0]) + upper = np.max(((1 / min_nbor_dist) * sw - self.davg[:, 0]) / self.dstd[:, 0]) + elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeT): + var = np.square(sw / (min_nbor_dist * self.dstd[:, 1:4])) + lower = np.min(-var) + upper = np.max(var) log.info('training data with lower boundary: ' + str(lower)) log.info('training data with upper boundary: ' + str(upper)) return math.floor(lower), math.ceil(upper) @@ -285,3 +329,35 @@ def _spline5_switch(self, else: vv = 0 return vv + + def _get_layer_size(self): + layer_size = 0 + if isinstance(self.descrpt, deepmd.descriptor.DescrptSeA): + layer_size = len(self.embedding_net_nodes) // ((self.ntypes * self.ntypes - len(self.exclude_types)) * 2) + if self.type_one_side : + layer_size = len(self.embedding_net_nodes) // (self.ntypes * 2) + elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeT): + layer_size = len(self.embedding_net_nodes) // int(comb(self.ntypes + 1, 2) * 2) + return layer_size + + def _get_table_size(self): + table_size = 0 + if isinstance(self.descrpt, deepmd.descriptor.DescrptSeA): + table_size = self.ntypes * self.ntypes + if self.type_one_side : + table_size = self.ntypes + elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeT): + table_size = int(comb(self.ntypes + 1, 2)) + return table_size + + def _get_data_type(self): + for item in self.matrix["layer_" + str(self.layer_size)]: + if len(item) != 0: + return type(item[0][0]) + return None + + def _get_last_layer_size(self): + for item in self.matrix["layer_" + str(self.layer_size)]: + if len(item) != 0: + return item.shape[1] + return 0 \ No newline at end of file diff --git a/doc/freeze/compress.md b/doc/freeze/compress.md index c7abd90737..ed55db0c4b 100644 --- a/doc/freeze/compress.md +++ b/doc/freeze/compress.md @@ -68,7 +68,7 @@ optional arguments: **Parameter explanation** Model compression, which including tabulating the embedding-net. -The table is composed of fifth-order polynomial coefficients and is assembled from two sub-tables. The first sub-table takes the stride(parameter) as it's uniform stride, while the second sub-table takes 10 * stride as it's uniform stride. +The table is composed of fifth-order polynomial coefficients and is assembled from two sub-tables. For model descriptor with `se_e2_a` type, the first sub-table takes the stride(parameter) as it's uniform stride, while the second sub-table takes 10 * stride as it's uniform stride; For model descriptor with `se_e3` type, the first sub-table takes 10 * stride as it's uniform stride, while the second sub-table takes 100 * stride as it's uniform stride. The range of the first table is automatically detected by deepmd-kit, while the second table ranges from the first table's upper boundary(upper) to the extrapolate(parameter) * upper. Finally, we added a check frequency parameter. It indicates how often the program checks for overflow(if the input environment matrix overflow the first or second table range) during the MD inference. @@ -79,3 +79,7 @@ Model compression, with little loss of accuracy, can greatly speed up MD inferen **Acceptable original model version** The model compression interface requires the version of deepmd-kit used in original model generation should be `2.0.0-alpha.0` or above. If one has a frozen 1.2 or 1.3 model, one can upgrade it through the `dp convert-from` interface.(eg: ```dp convert-from 1.2/1.3 -i old_frozen_model.pb -o new_frozen_model.pb```) + +**Acceptable descriptor type** + +Note only descriptors with `se_e2_a` or `se_e3` type are supported by the model compression feature. Hybrid mixed with above descriptors is also supported. diff --git a/doc/troubleshooting/model-compatability.md b/doc/troubleshooting/model-compatability.md index bc1b464047..2b7e46a4b7 100644 --- a/doc/troubleshooting/model-compatability.md +++ b/doc/troubleshooting/model-compatability.md @@ -6,11 +6,11 @@ DeePMD-kit guarantees that the codes with the same major and minor revisions are One can execute `dp convert-from` to convert an old model to a new one. -| Model version | v0.12 | v1.0 | v1.1 | v1.2 | v1.3 | v2.0 | -|:-:|:-----------:|:----------:|:----------:|:----------:|:----------:|:----------:| -| Compatibility | 😢 | 😢 | 😢 | 😊 | 😊 | 😄 | +| Model version | v0.12 | v1.0 | v1.1 | v1.2 | v1.3 | v2.0 | v2.1 | +|:-:|:-----------:|:----------:|:----------:|:----------:|:----------:|:----------:|:----------:| +| Compatibility | 😢 | 😢 | 😢 | 😊 | 😊 | 😄 | 😄 | **Legend**: - 😄: The model is compatible with the DeePMD-kit package. -- 😊: The model is incompatible with the DeePMD-kit package, but one can execute `dp convert-from` to convert an old model to v2.0. +- 😊: The model is incompatible with the DeePMD-kit package, but one can execute `dp convert-from` to convert an old model to v2.1. - 😢: The model is incompatible with the DeePMD-kit package, and there is no way to convert models. diff --git a/source/config/MODEL_VER b/source/config/MODEL_VER index d3827e75a5..9459d4ba2a 100644 --- a/source/config/MODEL_VER +++ b/source/config/MODEL_VER @@ -1 +1 @@ -1.0 +1.1 diff --git a/source/lib/include/tabulate.h b/source/lib/include/tabulate.h index ccc0e6fa65..841848380e 100644 --- a/source/lib/include/tabulate.h +++ b/source/lib/include/tabulate.h @@ -3,7 +3,7 @@ namespace deepmd{ template -void tabulate_fusion_cpu( +void tabulate_fusion_se_a_cpu( FPTYPE * out, const FPTYPE * table, const FPTYPE * table_info, @@ -14,7 +14,7 @@ void tabulate_fusion_cpu( const int last_layer_size); template -void tabulate_fusion_grad_cpu( +void tabulate_fusion_se_a_grad_cpu( FPTYPE * dy_dem_x, FPTYPE * dy_dem, const FPTYPE * table, @@ -27,7 +27,7 @@ void tabulate_fusion_grad_cpu( const int last_layer_size); template -void tabulate_fusion_grad_grad_cpu( +void tabulate_fusion_se_a_grad_grad_cpu( FPTYPE * dz_dy, const FPTYPE * table, const FPTYPE * table_info, @@ -39,9 +39,49 @@ void tabulate_fusion_grad_grad_cpu( const int nnei, const int last_layer_size); +template +void tabulate_fusion_se_t_cpu( + FPTYPE * out, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em_x, + const FPTYPE * em, + const int nloc, + const int nnei_i, + const int nnei_j, + const int last_layer_size); + +template +void tabulate_fusion_se_t_grad_cpu( + FPTYPE * dy_dem_x, + FPTYPE * dy_dem, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em_x, + const FPTYPE * em, + const FPTYPE * dy, + const int nloc, + const int nnei_i, + const int nnei_j, + const int last_layer_size); + +template +void tabulate_fusion_se_t_grad_grad_cpu( + FPTYPE * dz_dy, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em_x, + const FPTYPE * em, + const FPTYPE * dz_dy_dem_x, + const FPTYPE * dz_dy_dem, + const int nloc, + const int nnei_i, + const int nnei_j, + const int last_layer_size); + #if GOOGLE_CUDA template -void tabulate_fusion_gpu_cuda( +void tabulate_fusion_se_a_gpu_cuda( FPTYPE * out, const FPTYPE * table, const FPTYPE * table_info, @@ -52,7 +92,7 @@ void tabulate_fusion_gpu_cuda( const int last_layer_size); template -void tabulate_fusion_grad_gpu_cuda( +void tabulate_fusion_se_a_grad_gpu_cuda( FPTYPE * dy_dem_x, FPTYPE * dy_dem, const FPTYPE * table, @@ -65,7 +105,7 @@ void tabulate_fusion_grad_gpu_cuda( const int last_layer_size); template -void tabulate_fusion_grad_grad_gpu_cuda( +void tabulate_fusion_se_a_grad_grad_gpu_cuda( FPTYPE * dz_dy, const FPTYPE * table, const FPTYPE * table_info, @@ -76,11 +116,51 @@ void tabulate_fusion_grad_grad_gpu_cuda( const int nloc, const int nnei, const int last_layer_size); + +template +void tabulate_fusion_se_t_gpu_cuda( + FPTYPE * out, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em_x, + const FPTYPE * em, + const int nloc, + const int nnei_i, + const int nnei_j, + const int last_layer_size); + +template +void tabulate_fusion_se_t_grad_gpu_cuda( + FPTYPE * dy_dem_x, + FPTYPE * dy_dem, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em_x, + const FPTYPE * em, + const FPTYPE * dy, + const int nloc, + const int nnei_i, + const int nnei_j, + const int last_layer_size); + +template +void tabulate_fusion_se_t_grad_grad_gpu_cuda( + FPTYPE * dz_dy, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em_x, + const FPTYPE * em, + const FPTYPE * dz_dy_dem_x, + const FPTYPE * dz_dy_dem, + const int nloc, + const int nnei_i, + const int nnei_j, + const int last_layer_size); #endif // GOOGLE_CUDA #if TENSORFLOW_USE_ROCM template -void tabulate_fusion_gpu_rocm( +void tabulate_fusion_se_a_gpu_rocm( FPTYPE * out, const FPTYPE * table, const FPTYPE * table_info, @@ -91,7 +171,7 @@ void tabulate_fusion_gpu_rocm( const int last_layer_size); template -void tabulate_fusion_grad_gpu_rocm( +void tabulate_fusion_se_a_grad_gpu_rocm( FPTYPE * dy_dem_x, FPTYPE * dy_dem, const FPTYPE * table, @@ -104,7 +184,7 @@ void tabulate_fusion_grad_gpu_rocm( const int last_layer_size); template -void tabulate_fusion_grad_grad_gpu_rocm( +void tabulate_fusion_se_a_grad_grad_gpu_rocm( FPTYPE * dz_dy, const FPTYPE * table, const FPTYPE * table_info, @@ -115,6 +195,46 @@ void tabulate_fusion_grad_grad_gpu_rocm( const int nloc, const int nnei, const int last_layer_size); + +template +void tabulate_fusion_se_t_gpu_rocm( + FPTYPE * out, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em_x, + const FPTYPE * em, + const int nloc, + const int nnei_i, + const int nnei_j, + const int last_layer_size); + +template +void tabulate_fusion_se_t_grad_gpu_rocm( + FPTYPE * dy_dem_x, + FPTYPE * dy_dem, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em_x, + const FPTYPE * em, + const FPTYPE * dy, + const int nloc, + const int nnei_i, + const int nnei_j, + const int last_layer_size); + +template +void tabulate_fusion_se_t_grad_grad_gpu_rocm( + FPTYPE * dz_dy, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em_x, + const FPTYPE * em, + const FPTYPE * dz_dy_dem_x, + const FPTYPE * dz_dy_dem, + const int nloc, + const int nnei_i, + const int nnei_j, + const int last_layer_size); #endif // TENSORFLOW_USE_ROCM } diff --git a/source/lib/src/cuda/tabulate.cu b/source/lib/src/cuda/tabulate.cu index 265a2baffe..47ae73577f 100644 --- a/source/lib/src/cuda/tabulate.cu +++ b/source/lib/src/cuda/tabulate.cu @@ -37,6 +37,42 @@ void locate_xx( } } +template +__forceinline__ __device__ +void locate_xx_se_t( + FPTYPE& xx, + int& table_idx, + const FPTYPE& lower, + const FPTYPE& upper, + const FPTYPE& min, + const FPTYPE& max, + const FPTYPE& stride0, + const FPTYPE& stride1) +{ + if (xx < min) { + table_idx = 0; + xx = 0; + } + else if (xx < lower) { + table_idx = (int)((xx - min) / stride1); + xx -= (table_idx * stride1 + min); + } + else if (xx < upper) { + int first_stride = int((lower - min) / stride1); + table_idx = first_stride + (int)((xx - lower) / stride0); + xx -= ((table_idx - first_stride) * stride0 + lower); + } + else if (xx < max) { + int first_stride = int((lower - min) / stride1) + int((upper - lower) / stride0); + table_idx = first_stride + (int)((xx - upper) / stride1); + xx -= ((table_idx - first_stride) * stride1 + upper); + } + else { + table_idx = int((lower - min) / stride1) + int((upper - lower) / stride0) + (int)((max - upper) / stride1) - 1; + xx = 0; + } +} + template __forceinline__ __device__ FPTYPE dot( @@ -60,7 +96,7 @@ template < typename FPTYPE, int MTILE, int KTILE> -__global__ void tabulate_fusion_fifth_order_polynomial( +__global__ void tabulate_fusion_se_a_fifth_order_polynomial( FPTYPE * out, const FPTYPE * table, const FPTYPE * em_x, @@ -111,7 +147,7 @@ template < typename FPTYPE, int MTILE, int KTILE> -__global__ void tabulate_fusion_grad_fifth_order_polynomial( +__global__ void tabulate_fusion_se_a_grad_fifth_order_polynomial( FPTYPE * dy_dem_x, FPTYPE * dy_dem, const FPTYPE * table, @@ -191,7 +227,7 @@ template < typename FPTYPE, int MTILE, int KTILE> -__global__ void tabulate_fusion_grad_grad_fifth_order_polynomial( +__global__ void tabulate_fusion_se_a_grad_grad_fifth_order_polynomial( FPTYPE * dz_dy, const FPTYPE * table, const FPTYPE * em_x, @@ -247,9 +283,191 @@ __global__ void tabulate_fusion_grad_grad_fifth_order_polynomial( } } +template < + typename FPTYPE, + int MTILE, + int KTILE> +__global__ void tabulate_fusion_se_t_fifth_order_polynomial( + FPTYPE * out, + const FPTYPE * table, + const FPTYPE * em_x, + const FPTYPE * em, + const FPTYPE lower, + const FPTYPE upper, + const FPTYPE max, + const FPTYPE stride0, + const FPTYPE stride1, + const int nnei_i, + const int nnei_j, + const int last_layer_size) +{ + const int block_idx = blockIdx.x; // nloc + const int thread_idx = threadIdx.x; // last_layer_size + + FPTYPE sum = 0.f; + for (int ii = 0; ii < nnei_i; ii++) { + FPTYPE ago = __shfl_sync(0xffffffff, em_x[block_idx * nnei_i * nnei_j + ii * nnei_j + nnei_j - 1], 0); + int breakpoint = nnei_j - 1; + bool unloop = false; + for (int jj = 0; jj < nnei_j; jj++) { + FPTYPE xx = em_x[block_idx * nnei_i * nnei_j + ii * nnei_j + jj]; + FPTYPE tmp = xx; + if (xx == ago) { + unloop = true; + breakpoint = jj - 1; + } + int table_idx = 0; + locate_xx_se_t(xx, table_idx, lower, upper, -max, max, stride0, stride1); + FPTYPE var[6]; + var[0] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 0]; + var[1] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 1]; + var[2] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 2]; + var[3] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 3]; + var[4] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 4]; + var[5] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 5]; + FPTYPE res = var[0] + (var[1] + (var[2] + (var[3] + (var[4] + var[5] * xx) * xx) * xx) * xx) * xx; + + sum += (nnei_j - breakpoint) * tmp * res; + if (unloop) break; + } + } + out[block_idx * last_layer_size + thread_idx] = sum; +} + +template < + typename FPTYPE, + int MTILE, + int KTILE> +__global__ void tabulate_fusion_se_t_grad_fifth_order_polynomial( + FPTYPE * dy_dem_x, + FPTYPE * dy_dem, + const FPTYPE * table, + const FPTYPE * em_x, + const FPTYPE * em, + const FPTYPE * dy, + const FPTYPE lower, + const FPTYPE upper, + const FPTYPE max, + const FPTYPE stride0, + const FPTYPE stride1, + const int nnei_i, + const int nnei_j, + const int last_layer_size) +{ + extern __shared__ int _data[]; + const int block_idx = blockIdx.x; // nloc + const int thread_idx = threadIdx.x; // KTILE * WARP_SIZE, usally 128 here~ + int warp_idx = __shfl_sync(0xffffffff, threadIdx.x / WARP_SIZE, 0); + int lane_idx = threadIdx.x % WARP_SIZE; + FPTYPE * iteratorA = (FPTYPE *)&_data[0]; // dy + for (int ii = thread_idx; ii < last_layer_size; ii += blockDim.x) { + iteratorA[ii] = dy[block_idx * last_layer_size + ii]; + } + __syncthreads(); + + for (int ii = 0; ii < nnei_i; ii++) { + FPTYPE ago = __shfl_sync(0xffffffff, em_x[block_idx * nnei_i * nnei_j + ii * nnei_j + nnei_j - 1], 0); + bool unloop = false; + for (int jj = warp_idx; jj < nnei_j; jj += KTILE) { + FPTYPE xx = em_x[block_idx * nnei_i * nnei_j + ii * nnei_j + jj]; + FPTYPE tmp = xx; + if (ago == xx) { + unloop = true; + } + int table_idx = 0; + locate_xx_se_t(xx, table_idx, lower, upper, -max, max, stride0, stride1); + FPTYPE sum = 0.f; + FPTYPE Csub = 0.f; + for (int kk = lane_idx; kk < last_layer_size; kk += WARP_SIZE) { + FPTYPE var[6]; + // load iteratorB through table + var[0] = table[table_idx * last_layer_size * 6 + 6 * kk + 0]; + var[1] = table[table_idx * last_layer_size * 6 + 6 * kk + 1]; + var[2] = table[table_idx * last_layer_size * 6 + 6 * kk + 2]; + var[3] = table[table_idx * last_layer_size * 6 + 6 * kk + 3]; + var[4] = table[table_idx * last_layer_size * 6 + 6 * kk + 4]; + var[5] = table[table_idx * last_layer_size * 6 + 6 * kk + 5]; + FPTYPE res = var[0] + (var[1] + (var[2] + (var[3] + (var[4] + var[5] * xx) * xx) * xx) * xx) * xx; + + sum += iteratorA[kk] * res; + Csub += iteratorA[kk] * tmp * (var[1] + (2 * var[2] + (3 * var[3] + (4 * var[4] + 5 * var[5] * xx) * xx) * xx) * xx); + } + __syncwarp(); + warp_reduce(sum); + warp_reduce(Csub); + if (lane_idx == 0) { + dy_dem [block_idx * nnei_i * nnei_j + ii * nnei_j + jj] = sum; + dy_dem_x[block_idx * nnei_i * nnei_j + ii * nnei_j + jj] = Csub; + } + if (unloop) break; + } + } +} + +template < + typename FPTYPE, + int MTILE, + int KTILE> +__global__ void tabulate_fusion_se_t_grad_grad_fifth_order_polynomial( + FPTYPE * dz_dy, + const FPTYPE * table, + const FPTYPE * em_x, + const FPTYPE * em, + const FPTYPE * dz_dy_dem_x, + const FPTYPE * dz_dy_dem, + const FPTYPE lower, + const FPTYPE upper, + const FPTYPE max, + const FPTYPE stride0, + const FPTYPE stride1, + const int nnei, + const int nnei_j, + const int last_layer_size) +{ + extern __shared__ int _data[]; + const int block_idx = blockIdx.x; // nloc + const int thread_idx = threadIdx.x; // last_layer_size + FPTYPE ago = __shfl_sync(0xffffffff, em_x[block_idx * nnei + nnei - 1], 0); + bool unloop = false; + int breakpoint = nnei - 1; + FPTYPE * iteratorC = (FPTYPE*) &_data[0]; + for (int kk = 0; kk < MTILE; kk++) + iteratorC[kk * last_layer_size + thread_idx] = 0.f; + __syncthreads(); + + for (int ii = 0; ii < nnei; ii++) { + FPTYPE var[6]; + FPTYPE xx = em_x[block_idx * nnei + ii]; + FPTYPE dz_xx = dz_dy_dem_x[block_idx * nnei + ii]; + if (xx == ago) { + unloop = true; + breakpoint = ii; + } + int table_idx = 0; + locate_xx_se_t(xx, table_idx, lower, upper, -max, max, stride0, stride1); + var[0] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 0]; + var[1] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 1]; + var[2] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 2]; + var[3] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 3]; + var[4] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 4]; + var[5] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 5]; + FPTYPE res = var[0] + (var[1] + (var[2] + (var[3] + (var[4] + var[5] * xx) * xx) * xx) * xx) * xx; + FPTYPE res_grad = var[1] + (2 * var[2] + (3 * var[3] + (4 * var[4] + 5 * var[5] * xx) * xx) * xx) * xx; + + for (int kk = 0; kk < MTILE; kk++) { + int em_index = block_idx * nnei * MTILE + ii * MTILE + kk; + iteratorC[kk * last_layer_size + thread_idx] += (nnei - breakpoint) * (em[em_index] * res_grad * dz_xx + dz_dy_dem[em_index] * res); + } + if (unloop) break; + } + for (int ii = 0; ii < MTILE; ii++) { + dz_dy[block_idx * MTILE * last_layer_size + ii * last_layer_size + thread_idx] = iteratorC[ii * last_layer_size + thread_idx]; + } +} + namespace deepmd { template -void tabulate_fusion_gpu_cuda( +void tabulate_fusion_se_a_gpu_cuda( FPTYPE * out, const FPTYPE * table, const FPTYPE * table_info, @@ -260,7 +478,7 @@ void tabulate_fusion_gpu_cuda( const int last_layer_size) { if (nloc <= 0) {return;} - tabulate_fusion_fifth_order_polynomial <<>>( + tabulate_fusion_se_a_fifth_order_polynomial <<>>( out, table, em_x, em, table_info[0], table_info[1], table_info[2], table_info[3], table_info[4], nnei, last_layer_size); DPErrcheck(cudaGetLastError()); @@ -268,7 +486,7 @@ void tabulate_fusion_gpu_cuda( } template -void tabulate_fusion_grad_gpu_cuda( +void tabulate_fusion_se_a_grad_gpu_cuda( FPTYPE * dy_dem_x, FPTYPE * dy_dem, const FPTYPE * table, @@ -288,7 +506,7 @@ void tabulate_fusion_grad_gpu_cuda( dy_dem, 0.0, sizeof(FPTYPE) * nloc * nnei * 4)); - tabulate_fusion_grad_fifth_order_polynomial <<>>( + tabulate_fusion_se_a_grad_fifth_order_polynomial <<>>( dy_dem_x, dy_dem, table, em_x, em, dy, table_info[0], table_info[1], table_info[2], table_info[3], table_info[4], nnei, last_layer_size); DPErrcheck(cudaGetLastError()); @@ -296,7 +514,7 @@ void tabulate_fusion_grad_gpu_cuda( } template -void tabulate_fusion_grad_grad_gpu_cuda( +void tabulate_fusion_se_a_grad_grad_gpu_cuda( FPTYPE * dz_dy, const FPTYPE * table, const FPTYPE * table_info, @@ -312,17 +530,98 @@ void tabulate_fusion_grad_grad_gpu_cuda( DPErrcheck(cudaMemset( dz_dy, 0.0, sizeof(FPTYPE) * nloc * 4 * last_layer_size)); - tabulate_fusion_grad_grad_fifth_order_polynomial <<>>( + tabulate_fusion_se_a_grad_grad_fifth_order_polynomial <<>>( dz_dy, table, em_x, em, dz_dy_dem_x, dz_dy_dem, table_info[0], table_info[1], table_info[2], table_info[3], table_info[4], nnei, last_layer_size); DPErrcheck(cudaGetLastError()); DPErrcheck(cudaDeviceSynchronize()); } -template void tabulate_fusion_gpu_cuda(float * out, const float * table, const float * table_info, const float * em_x, const float * em, const int nloc, const int nnei, const int last_layer_size); -template void tabulate_fusion_gpu_cuda(double * out, const double * table, const double * table_info, const double * em_x, const double * em, const int nloc, const int nnei, const int last_layer_size); -template void tabulate_fusion_grad_gpu_cuda (float * dy_dem_x, float * dy_dem, const float * table, const float * table_info, const float * em_x, const float * em, const float * dy, const int nloc, const int nnei, const int last_layer_size); -template void tabulate_fusion_grad_gpu_cuda (double * dy_dem_x, double * dy_dem, const double * table, const double * table_info, const double * em_x, const double * em, const double * dy, const int nloc, const int nnei, const int last_layer_size); -template void tabulate_fusion_grad_grad_gpu_cuda (float * dz_dy, const float * table, const float * table_info, const float * em_x, const float * em, const float * dz_dy_dem_x, const float * dz_dy_dem, const int nloc, const int nnei, const int last_layer_size); -template void tabulate_fusion_grad_grad_gpu_cuda (double * dz_dy, const double * table, const double * table_info, const double * em_x, const double * em, const double * dz_dy_dem_x, const double * dz_dy_dem, const int nloc, const int nnei, const int last_layer_size); +template +void tabulate_fusion_se_t_gpu_cuda( + FPTYPE * out, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em_x, + const FPTYPE * em, + const int nloc, + const int nnei_i, + const int nnei_j, + const int last_layer_size) +{ + if (nloc <= 0) {return;} + tabulate_fusion_se_t_fifth_order_polynomial <<>>( + out, + table, em_x, em, table_info[0], table_info[1], table_info[2], table_info[3], table_info[4], nnei_i, nnei_j, last_layer_size); + DPErrcheck(cudaGetLastError()); + DPErrcheck(cudaDeviceSynchronize()); +} + +template +void tabulate_fusion_se_t_grad_gpu_cuda( + FPTYPE * dy_dem_x, + FPTYPE * dy_dem, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em_x, + const FPTYPE * em, + const FPTYPE * dy, + const int nloc, + const int nnei_i, + const int nnei_j, + const int last_layer_size) +{ + if (nloc <= 0) {return;} + DPErrcheck(cudaMemset( + dy_dem_x, + 0.0, sizeof(FPTYPE) * nloc * nnei_i * nnei_j)); + DPErrcheck(cudaMemset( + dy_dem, + 0.0, sizeof(FPTYPE) * nloc * nnei_i * nnei_j)); + + tabulate_fusion_se_t_grad_fifth_order_polynomial <<>>( + dy_dem_x, dy_dem, + table, em_x, em, dy, table_info[0], table_info[1], table_info[2], table_info[3], table_info[4], nnei_i, nnei_j, last_layer_size); + DPErrcheck(cudaGetLastError()); + DPErrcheck(cudaDeviceSynchronize()); +} + +template +void tabulate_fusion_se_t_grad_grad_gpu_cuda( + FPTYPE * dz_dy, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em_x, + const FPTYPE * em, + const FPTYPE * dz_dy_dem_x, + const FPTYPE * dz_dy_dem, + const int nloc, + const int nnei_i, + const int nnei_j, + const int last_layer_size) +{ + if (nloc <= 0) {return;} + DPErrcheck(cudaMemset( + dz_dy, + 0.0, sizeof(FPTYPE) * nloc * last_layer_size)); + tabulate_fusion_se_t_grad_grad_fifth_order_polynomial <<>>( + dz_dy, + table, em_x, em, dz_dy_dem_x, dz_dy_dem, table_info[0], table_info[1], table_info[2], table_info[3], table_info[4], nnei_i, nnei_j, last_layer_size); + DPErrcheck(cudaGetLastError()); + DPErrcheck(cudaDeviceSynchronize()); +} + +template void tabulate_fusion_se_a_gpu_cuda(float * out, const float * table, const float * table_info, const float * em_x, const float * em, const int nloc, const int nnei, const int last_layer_size); +template void tabulate_fusion_se_a_gpu_cuda(double * out, const double * table, const double * table_info, const double * em_x, const double * em, const int nloc, const int nnei, const int last_layer_size); +template void tabulate_fusion_se_a_grad_gpu_cuda (float * dy_dem_x, float * dy_dem, const float * table, const float * table_info, const float * em_x, const float * em, const float * dy, const int nloc, const int nnei, const int last_layer_size); +template void tabulate_fusion_se_a_grad_gpu_cuda (double * dy_dem_x, double * dy_dem, const double * table, const double * table_info, const double * em_x, const double * em, const double * dy, const int nloc, const int nnei, const int last_layer_size); +template void tabulate_fusion_se_a_grad_grad_gpu_cuda (float * dz_dy, const float * table, const float * table_info, const float * em_x, const float * em, const float * dz_dy_dem_x, const float * dz_dy_dem, const int nloc, const int nnei, const int last_layer_size); +template void tabulate_fusion_se_a_grad_grad_gpu_cuda (double * dz_dy, const double * table, const double * table_info, const double * em_x, const double * em, const double * dz_dy_dem_x, const double * dz_dy_dem, const int nloc, const int nnei, const int last_layer_size); + +template void tabulate_fusion_se_t_gpu_cuda(float * out, const float * table, const float * table_info, const float * em_x, const float * em, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); +template void tabulate_fusion_se_t_gpu_cuda(double * out, const double * table, const double * table_info, const double * em_x, const double * em, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); +template void tabulate_fusion_se_t_grad_gpu_cuda (float * dy_dem_x, float * dy_dem, const float * table, const float * table_info, const float * em_x, const float * em, const float * dy, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); +template void tabulate_fusion_se_t_grad_gpu_cuda (double * dy_dem_x, double * dy_dem, const double * table, const double * table_info, const double * em_x, const double * em, const double * dy, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); +template void tabulate_fusion_se_t_grad_grad_gpu_cuda (float * dz_dy, const float * table, const float * table_info, const float * em_x, const float * em, const float * dz_dy_dem_x, const float * dz_dy_dem, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); +template void tabulate_fusion_se_t_grad_grad_gpu_cuda (double * dz_dy, const double * table, const double * table_info, const double * em_x, const double * em, const double * dz_dy_dem_x, const double * dz_dy_dem, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); } diff --git a/source/lib/src/rocm/tabulate.hip.cu b/source/lib/src/rocm/tabulate.hip.cu index 050bf1658a..055d52d7b8 100644 --- a/source/lib/src/rocm/tabulate.hip.cu +++ b/source/lib/src/rocm/tabulate.hip.cu @@ -37,6 +37,42 @@ void locate_xx( } } +template +__forceinline__ __device__ +void locate_xx_se_t( + FPTYPE& xx, + int& table_idx, + const FPTYPE& lower, + const FPTYPE& upper, + const FPTYPE& min, + const FPTYPE& max, + const FPTYPE& stride0, + const FPTYPE& stride1) +{ + if (xx < min) { + table_idx = 0; + xx = 0; + } + else if (xx < lower) { + table_idx = (int)((xx - min) / stride1); + xx -= (table_idx * stride1 + min); + } + else if (xx < upper) { + int first_stride = int((lower - min) / stride1); + table_idx = first_stride + (int)((xx - lower) / stride0); + xx -= ((table_idx - first_stride) * stride0 + lower); + } + else if (xx < max) { + int first_stride = int((lower - min) / stride1) + int((upper - lower) / stride0); + table_idx = first_stride + (int)((xx - upper) / stride1); + xx -= ((table_idx - first_stride) * stride1 + upper); + } + else { + table_idx = int((lower - min) / stride1) + int((upper - lower) / stride0) + (int)((max - upper) / stride1) - 1; + xx = 0; + } +} + template __forceinline__ __device__ FPTYPE dot( @@ -60,7 +96,7 @@ template < typename FPTYPE, int MTILE, int KTILE> -__global__ void tabulate_fusion_fifth_order_polynomial( +__global__ void tabulate_fusion_se_a_fifth_order_polynomial( FPTYPE * out, const FPTYPE * table, const FPTYPE * em_x, @@ -115,7 +151,7 @@ template < typename FPTYPE, int MTILE, int KTILE> -__global__ void tabulate_fusion_grad_fifth_order_polynomial( +__global__ void tabulate_fusion_se_a_grad_fifth_order_polynomial( FPTYPE * dy_dem_x, FPTYPE * dy_dem, const FPTYPE * table, @@ -196,7 +232,7 @@ template < typename FPTYPE, int MTILE, int KTILE> -__global__ void tabulate_fusion_grad_grad_fifth_order_polynomial( +__global__ void tabulate_fusion_se_a_grad_grad_fifth_order_polynomial( FPTYPE * dz_dy, const FPTYPE * table, const FPTYPE * em_x, @@ -252,9 +288,192 @@ __global__ void tabulate_fusion_grad_grad_fifth_order_polynomial( } } +template < + typename FPTYPE, + int MTILE, + int KTILE> +__global__ void tabulate_fusion_se_t_fifth_order_polynomial( + FPTYPE * out, + const FPTYPE * table, + const FPTYPE * em_x, + const FPTYPE * em, + const FPTYPE lower, + const FPTYPE upper, + const FPTYPE max, + const FPTYPE stride0, + const FPTYPE stride1, + const int nnei_i, + const int nnei_j, + const int last_layer_size) +{ + HIP_DYNAMIC_SHARED( int, _data) + const int block_idx = blockIdx.x; // nloc + const int thread_idx = threadIdx.x; // last_layer_size + + FPTYPE sum = 0.f; + for (int ii = 0; ii < nnei_i; ii++) { + FPTYPE ago = __shfl(em_x[block_idx * nnei_i * nnei_j + ii * nnei_j + nnei_j - 1], 0); + int breakpoint = nnei_j - 1; + bool unloop = false; + for (int jj = 0; jj < nnei_j; jj++) { + FPTYPE xx = em_x[block_idx * nnei_i * nnei_j + ii * nnei_j + jj]; + FPTYPE tmp = xx; + if (xx == ago) { + unloop = true; + breakpoint = jj - 1; + } + int table_idx = 0; + locate_xx_se_t(xx, table_idx, lower, upper, -max, max, stride0, stride1); + FPTYPE var[6]; + var[0] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 0]; + var[1] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 1]; + var[2] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 2]; + var[3] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 3]; + var[4] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 4]; + var[5] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 5]; + FPTYPE res = var[0] + (var[1] + (var[2] + (var[3] + (var[4] + var[5] * xx) * xx) * xx) * xx) * xx; + + sum += (nnei_j - breakpoint) * tmp * res; + if (unloop) break; + } + } + out[block_idx * last_layer_size + thread_idx] = sum; +} + +template < + typename FPTYPE, + int MTILE, + int KTILE> +__global__ void tabulate_fusion_se_t_grad_fifth_order_polynomial( + FPTYPE * dy_dem_x, + FPTYPE * dy_dem, + const FPTYPE * table, + const FPTYPE * em_x, + const FPTYPE * em, + const FPTYPE * dy, + const FPTYPE lower, + const FPTYPE upper, + const FPTYPE max, + const FPTYPE stride0, + const FPTYPE stride1, + const int nnei_i, + const int nnei_j, + const int last_layer_size) +{ + HIP_DYNAMIC_SHARED( int, _data) + const int block_idx = blockIdx.x; // nloc + const int thread_idx = threadIdx.x; // KTILE * WARP_SIZE, usally 128 here~ + int warp_idx = __shfl(threadIdx.x / 64, 0); + int lane_idx = threadIdx.x % 64; + FPTYPE * iteratorA = (FPTYPE *)&_data[0]; // dy + for (int ii = thread_idx; ii < last_layer_size; ii += blockDim.x) { + iteratorA[ii] = dy[block_idx * last_layer_size + ii]; + } + __syncthreads(); + + for (int ii = 0; ii < nnei_i; ii++) { + FPTYPE ago = __shfl(em_x[block_idx * nnei_i * nnei_j + ii * nnei_j + nnei_j - 1], 0); + bool unloop = false; + for (int jj = warp_idx; jj < nnei_j; jj += KTILE) { + FPTYPE xx = em_x[block_idx * nnei_i * nnei_j + ii * nnei_j + jj]; + FPTYPE tmp = xx; + if (ago == xx) { + unloop = true; + } + int table_idx = 0; + locate_xx_se_t(xx, table_idx, lower, upper, -max, max, stride0, stride1); + FPTYPE sum = 0.f; + FPTYPE Csub = 0.f; + for (int kk = lane_idx; kk < last_layer_size; kk += WARP_SIZE) { + FPTYPE var[6]; + // load iteratorB through table + var[0] = table[table_idx * last_layer_size * 6 + 6 * kk + 0]; + var[1] = table[table_idx * last_layer_size * 6 + 6 * kk + 1]; + var[2] = table[table_idx * last_layer_size * 6 + 6 * kk + 2]; + var[3] = table[table_idx * last_layer_size * 6 + 6 * kk + 3]; + var[4] = table[table_idx * last_layer_size * 6 + 6 * kk + 4]; + var[5] = table[table_idx * last_layer_size * 6 + 6 * kk + 5]; + FPTYPE res = var[0] + (var[1] + (var[2] + (var[3] + (var[4] + var[5] * xx) * xx) * xx) * xx) * xx; + + sum += iteratorA[kk] * res; + Csub += iteratorA[kk] * tmp * (var[1] + (2 * var[2] + (3 * var[3] + (4 * var[4] + 5 * var[5] * xx) * xx) * xx) * xx); + } + __syncthreads(); + warp_reduce(sum); + warp_reduce(Csub); + if (lane_idx == 0) { + dy_dem [block_idx * nnei_i * nnei_j + ii * nnei_j + jj] = sum; + dy_dem_x[block_idx * nnei_i * nnei_j + ii * nnei_j + jj] = Csub; + } + if (unloop) break; + } + } +} + +template < + typename FPTYPE, + int MTILE, + int KTILE> +__global__ void tabulate_fusion_se_t_grad_grad_fifth_order_polynomial( + FPTYPE * dz_dy, + const FPTYPE * table, + const FPTYPE * em_x, + const FPTYPE * em, + const FPTYPE * dz_dy_dem_x, + const FPTYPE * dz_dy_dem, + const FPTYPE lower, + const FPTYPE upper, + const FPTYPE max, + const FPTYPE stride0, + const FPTYPE stride1, + const int nnei, + const int nnei_j, + const int last_layer_size) +{ + extern __shared__ int _data[]; + const int block_idx = blockIdx.x; // nloc + const int thread_idx = threadIdx.x; // last_layer_size + FPTYPE ago = __shfl( em_x[block_idx * nnei + nnei - 1], 0); + bool unloop = false; + int breakpoint = nnei - 1; + FPTYPE * iteratorC = (FPTYPE*) &_data[0]; + for (int kk = 0; kk < MTILE; kk++) + iteratorC[kk * last_layer_size + thread_idx] = 0.f; + __syncthreads(); + + for (int ii = 0; ii < nnei; ii++) { + FPTYPE var[6]; + FPTYPE xx = em_x[block_idx * nnei + ii]; + FPTYPE dz_xx = dz_dy_dem_x[block_idx * nnei + ii]; + if (xx == ago) { + unloop = true; + breakpoint = ii; + } + int table_idx = 0; + locate_xx(xx, table_idx, lower, upper, max, stride0, stride1); + var[0] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 0]; + var[1] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 1]; + var[2] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 2]; + var[3] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 3]; + var[4] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 4]; + var[5] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 5]; + FPTYPE res = var[0] + (var[1] + (var[2] + (var[3] + (var[4] + var[5] * xx) * xx) * xx) * xx) * xx; + FPTYPE res_grad = var[1] + (2 * var[2] + (3 * var[3] + (4 * var[4] + 5 * var[5] * xx) * xx) * xx) * xx; + + for (int kk = 0; kk < MTILE; kk++) { + int em_index = block_idx * nnei * MTILE + ii * MTILE + kk; + iteratorC[kk * last_layer_size + thread_idx] += (nnei - breakpoint) * (em[em_index] * res_grad * dz_xx + dz_dy_dem[em_index] * res); + } + if (unloop) break; + } + for (int ii = 0; ii < MTILE; ii++) { + dz_dy[block_idx * MTILE * last_layer_size + ii * last_layer_size + thread_idx] = iteratorC[ii * last_layer_size + thread_idx]; + } +} + namespace deepmd { template -void tabulate_fusion_gpu_rocm( +void tabulate_fusion_se_a_gpu_rocm( FPTYPE * out, const FPTYPE * table, const FPTYPE * table_info, @@ -265,7 +484,7 @@ void tabulate_fusion_gpu_rocm( const int last_layer_size) { if(nloc <= 0){return;} - hipLaunchKernelGGL(HIP_KERNEL_NAME(tabulate_fusion_fifth_order_polynomial), nloc, last_layer_size, sizeof(FPTYPE) * MM * last_layer_size, 0, + hipLaunchKernelGGL(HIP_KERNEL_NAME(tabulate_fusion_se_a_fifth_order_polynomial), nloc, last_layer_size, sizeof(FPTYPE) * MM * last_layer_size, 0, out, table, em_x, em, table_info[0], table_info[1], table_info[2], table_info[3], table_info[4], nnei, last_layer_size); DPErrcheck(hipGetLastError()); @@ -273,7 +492,7 @@ void tabulate_fusion_gpu_rocm( } template -void tabulate_fusion_grad_gpu_rocm( +void tabulate_fusion_se_a_grad_gpu_rocm( FPTYPE * dy_dem_x, FPTYPE * dy_dem, const FPTYPE * table, @@ -293,7 +512,7 @@ void tabulate_fusion_grad_gpu_rocm( dy_dem, 0.0, sizeof(FPTYPE) * nloc * nnei * 4)); - hipLaunchKernelGGL(HIP_KERNEL_NAME(tabulate_fusion_grad_fifth_order_polynomial), nloc, KK * WARP_SIZE, sizeof(FPTYPE) * MM * last_layer_size, 0, + hipLaunchKernelGGL(HIP_KERNEL_NAME(tabulate_fusion_se_a_grad_fifth_order_polynomial), nloc, KK * WARP_SIZE, sizeof(FPTYPE) * MM * last_layer_size, 0, dy_dem_x, dy_dem, table, em_x, em, dy, table_info[0], table_info[1], table_info[2], table_info[3], table_info[4], nnei, last_layer_size); DPErrcheck(hipGetLastError()); @@ -301,7 +520,7 @@ void tabulate_fusion_grad_gpu_rocm( } template -void tabulate_fusion_grad_grad_gpu_rocm( +void tabulate_fusion_se_a_grad_grad_gpu_rocm( FPTYPE * dz_dy, const FPTYPE * table, const FPTYPE * table_info, @@ -317,17 +536,98 @@ void tabulate_fusion_grad_grad_gpu_rocm( DPErrcheck(hipMemset( dz_dy, 0.0, sizeof(FPTYPE) * nloc * 4 * last_layer_size)); - hipLaunchKernelGGL(HIP_KERNEL_NAME(tabulate_fusion_grad_grad_fifth_order_polynomial), nloc, last_layer_size, sizeof(FPTYPE) * MM * last_layer_size, 0, + hipLaunchKernelGGL(HIP_KERNEL_NAME(tabulate_fusion_se_a_grad_grad_fifth_order_polynomial), nloc, last_layer_size, sizeof(FPTYPE) * MM * last_layer_size, 0, dz_dy, table, em_x, em, dz_dy_dem_x, dz_dy_dem, table_info[0], table_info[1], table_info[2], table_info[3], table_info[4], nnei, last_layer_size); DPErrcheck(hipGetLastError()); DPErrcheck(hipDeviceSynchronize()); } -template void tabulate_fusion_gpu_rocm(float * out, const float * table, const float * table_info, const float * em_x, const float * em, const int nloc, const int nnei, const int last_layer_size); -template void tabulate_fusion_gpu_rocm(double * out, const double * table, const double * table_info, const double * em_x, const double * em, const int nloc, const int nnei, const int last_layer_size); -template void tabulate_fusion_grad_gpu_rocm (float * dy_dem_x, float * dy_dem, const float * table, const float * table_info, const float * em_x, const float * em, const float * dy, const int nloc, const int nnei, const int last_layer_size); -template void tabulate_fusion_grad_gpu_rocm (double * dy_dem_x, double * dy_dem, const double * table, const double * table_info, const double * em_x, const double * em, const double * dy, const int nloc, const int nnei, const int last_layer_size); -template void tabulate_fusion_grad_grad_gpu_rocm (float * dz_dy, const float * table, const float * table_info, const float * em_x, const float * em, const float * dz_dy_dem_x, const float * dz_dy_dem, const int nloc, const int nnei, const int last_layer_size); -template void tabulate_fusion_grad_grad_gpu_rocm (double * dz_dy, const double * table, const double * table_info, const double * em_x, const double * em, const double * dz_dy_dem_x, const double * dz_dy_dem, const int nloc, const int nnei, const int last_layer_size); +template +void tabulate_fusion_se_t_gpu_rocm( + FPTYPE * out, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em_x, + const FPTYPE * em, + const int nloc, + const int nnei_i, + const int nnei_j, + const int last_layer_size) +{ + if(nloc <= 0){return;} + hipLaunchKernelGGL(HIP_KERNEL_NAME(tabulate_fusion_se_t_fifth_order_polynomial), nloc, last_layer_size, 0, 0, + out, + table, em_x, em, table_info[0], table_info[1], table_info[2], table_info[3], table_info[4], nnei_i, nnei_j, last_layer_size); + DPErrcheck(hipGetLastError()); + DPErrcheck(hipDeviceSynchronize()); +} + +template +void tabulate_fusion_se_t_grad_gpu_rocm( + FPTYPE * dy_dem_x, + FPTYPE * dy_dem, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em_x, + const FPTYPE * em, + const FPTYPE * dy, + const int nloc, + const int nnei_i, + const int nnei_j, + const int last_layer_size) +{ + if(nloc <= 0) {return;} + DPErrcheck(hipMemset( + dy_dem_x, + 0.0, sizeof(FPTYPE) * nloc * nnei_i * nnei_j)); + DPErrcheck(hipMemset( + dy_dem, + 0.0, sizeof(FPTYPE) * nloc * nnei_i * nnei_j)); + + hipLaunchKernelGGL(HIP_KERNEL_NAME(tabulate_fusion_se_t_grad_fifth_order_polynomial), nloc, KK * WARP_SIZE, sizeof(FPTYPE) * last_layer_size, 0, + dy_dem_x, dy_dem, + table, em_x, em, dy, table_info[0], table_info[1], table_info[2], table_info[3], table_info[4], nnei_i, nnei_j, last_layer_size); + DPErrcheck(hipGetLastError()); + DPErrcheck(hipDeviceSynchronize()); +} + +template +void tabulate_fusion_se_t_grad_grad_gpu_rocm( + FPTYPE * dz_dy, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em_x, + const FPTYPE * em, + const FPTYPE * dz_dy_dem_x, + const FPTYPE * dz_dy_dem, + const int nloc, + const int nnei_i, + const int nnei_j, + const int last_layer_size) +{ + if(nloc <= 0) {return;} + DPErrcheck(hipMemset( + dz_dy, + 0.0, sizeof(FPTYPE) * nloc * last_layer_size)); + hipLaunchKernelGGL(HIP_KERNEL_NAME(tabulate_fusion_se_t_grad_grad_fifth_order_polynomial), nloc, last_layer_size, sizeof(FPTYPE) * last_layer_size, 0, + dz_dy, + table, em_x, em, dz_dy_dem_x, dz_dy_dem, table_info[0], table_info[1], table_info[2], table_info[3], table_info[4], nnei_i, nnei_j, last_layer_size); + DPErrcheck(hipGetLastError()); + DPErrcheck(hipDeviceSynchronize()); +} + +template void tabulate_fusion_se_a_gpu_rocm(float * out, const float * table, const float * table_info, const float * em_x, const float * em, const int nloc, const int nnei, const int last_layer_size); +template void tabulate_fusion_se_a_gpu_rocm(double * out, const double * table, const double * table_info, const double * em_x, const double * em, const int nloc, const int nnei, const int last_layer_size); +template void tabulate_fusion_se_a_grad_gpu_rocm (float * dy_dem_x, float * dy_dem, const float * table, const float * table_info, const float * em_x, const float * em, const float * dy, const int nloc, const int nnei, const int last_layer_size); +template void tabulate_fusion_se_a_grad_gpu_rocm (double * dy_dem_x, double * dy_dem, const double * table, const double * table_info, const double * em_x, const double * em, const double * dy, const int nloc, const int nnei, const int last_layer_size); +template void tabulate_fusion_se_a_grad_grad_gpu_rocm (float * dz_dy, const float * table, const float * table_info, const float * em_x, const float * em, const float * dz_dy_dem_x, const float * dz_dy_dem, const int nloc, const int nnei, const int last_layer_size); +template void tabulate_fusion_se_a_grad_grad_gpu_rocm (double * dz_dy, const double * table, const double * table_info, const double * em_x, const double * em, const double * dz_dy_dem_x, const double * dz_dy_dem, const int nloc, const int nnei, const int last_layer_size); + +template void tabulate_fusion_se_t_gpu_rocm(float * out, const float * table, const float * table_info, const float * em_x, const float * em, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); +template void tabulate_fusion_se_t_gpu_rocm(double * out, const double * table, const double * table_info, const double * em_x, const double * em, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); +template void tabulate_fusion_se_t_grad_gpu_rocm (float * dy_dem_x, float * dy_dem, const float * table, const float * table_info, const float * em_x, const float * em, const float * dy, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); +template void tabulate_fusion_se_t_grad_gpu_rocm (double * dy_dem_x, double * dy_dem, const double * table, const double * table_info, const double * em_x, const double * em, const double * dy, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); +template void tabulate_fusion_se_t_grad_grad_gpu_rocm (float * dz_dy, const float * table, const float * table_info, const float * em_x, const float * em, const float * dz_dy_dem_x, const float * dz_dy_dem, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); +template void tabulate_fusion_se_t_grad_grad_gpu_rocm (double * dz_dy, const double * table, const double * table_info, const double * em_x, const double * em, const double * dz_dy_dem_x, const double * dz_dy_dem, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); } diff --git a/source/lib/src/tabulate.cc b/source/lib/src/tabulate.cc index 385c68523b..ffacb96fdb 100644 --- a/source/lib/src/tabulate.cc +++ b/source/lib/src/tabulate.cc @@ -42,6 +42,42 @@ inline void locate_xx( } } + +template +inline void locate_xx_se_t( + const FPTYPE& lower, + const FPTYPE& upper, + const FPTYPE& min, + const FPTYPE& max, + const FPTYPE& stride0, + const FPTYPE& stride1, + FPTYPE& xx, + int& table_idx) +{ + if (xx < min) { + table_idx = 0; + xx = 0; + } + else if (xx < lower) { + table_idx = (int)((xx - min) / stride1); + xx -= (table_idx * stride1 + min); + } + else if (xx < upper) { + int first_stride = int((lower - min) / stride1); + table_idx = first_stride + (int)((xx - lower) / stride0); + xx -= ((table_idx - first_stride) * stride0 + lower); + } + else if (xx < max) { + int first_stride = int((lower - min) / stride1) + int((upper - lower) / stride0); + table_idx = first_stride + (int)((xx - upper) / stride1); + xx -= ((table_idx - first_stride) * stride1 + upper); + } + else { + table_idx = int((lower - min) / stride1) + int((upper - lower) / stride0) + (int)((max - upper) / stride1) - 1; + xx = 0; + } +} + template inline FPTYPE dot( FPTYPE a[4], @@ -51,7 +87,7 @@ inline FPTYPE dot( } template -void deepmd::tabulate_fusion_cpu( +void deepmd::tabulate_fusion_se_a_cpu( FPTYPE * out, const FPTYPE * table, const FPTYPE * table_info, @@ -112,7 +148,7 @@ void deepmd::tabulate_fusion_cpu( } template -void deepmd::tabulate_fusion_grad_cpu( +void deepmd::tabulate_fusion_se_a_grad_cpu( FPTYPE * dy_dem_x, FPTYPE * dy_dem, const FPTYPE * table, @@ -187,7 +223,7 @@ void deepmd::tabulate_fusion_grad_cpu( } template -void deepmd::tabulate_fusion_grad_grad_cpu( +void deepmd::tabulate_fusion_se_a_grad_grad_cpu( FPTYPE * dz_dy, const FPTYPE * table, const FPTYPE * table_info, @@ -256,9 +292,207 @@ void deepmd::tabulate_fusion_grad_grad_cpu( } } -template void deepmd::tabulate_fusion_cpu(float * out, const float * table, const float * table_info, const float * em_x, const float * em, const int nloc, const int nnei, const int last_layer_size); -template void deepmd::tabulate_fusion_cpu(double * out, const double * table, const double * table_info, const double * em_x, const double * em, const int nloc, const int nnei, const int last_layer_size); -template void deepmd::tabulate_fusion_grad_cpu (float * dy_dem_x, float * dy_dem, const float * table, const float * table_info, const float * em_x, const float * em, const float * dy, const int nloc, const int nnei, const int last_layer_size); -template void deepmd::tabulate_fusion_grad_cpu (double * dy_dem_x, double * dy_dem, const double * table, const double * table_info, const double * em_x, const double * em, const double * dy, const int nloc, const int nnei, const int last_layer_size); -template void deepmd::tabulate_fusion_grad_grad_cpu(float * dz_dy, const float * table, const float * table_info, const float * em_x, const float * em, const float * dz_dy_dem_x, const float * dz_dy_dem, const int nloc, const int nnei, const int last_layer_size); -template void deepmd::tabulate_fusion_grad_grad_cpu(double * dz_dy, const double * table, const double * table_info, const double * em_x, const double * em, const double * dz_dy_dem_x, const double * dz_dy_dem, const int nloc, const int nnei, const int last_layer_size); +template +void deepmd::tabulate_fusion_se_t_cpu( + FPTYPE * out, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em_x, + const FPTYPE * em, + const int nloc, + const int nnei_i, + const int nnei_j, + const int last_layer_size) +{ + memset(out, 0.0, sizeof(FPTYPE) * nloc * last_layer_size); + const FPTYPE lower = table_info[0]; + const FPTYPE upper = table_info[1]; + const FPTYPE _max = table_info[2]; + const FPTYPE stride0 = table_info[3]; + const FPTYPE stride1 = table_info[4]; + // for every atom, execute a small manual gemm ~ + // FPTYPE * res = new FPTYPE[4 * last_layer_size]; + #pragma omp parallel for + for (int ii = 0; ii < nloc; ii++) { + for (int jj = 0; jj < nnei_i; jj++) { + FPTYPE ago = em_x[ii * nnei_i * nnei_j + jj * nnei_j + nnei_j - 1]; + bool unloop = false; + for (int kk = 0; kk < nnei_j; kk++) { + FPTYPE xx = em_x[ii * nnei_i * nnei_j + jj * nnei_j + kk]; + FPTYPE ll = xx; + if (ago == xx) { + unloop = true; + } + int table_idx = 0; + locate_xx_se_t(lower, upper, -_max, _max, stride0, stride1, xx, table_idx); + for (int mm = 0; mm < last_layer_size; mm++) { + FPTYPE a0 = table[table_idx * last_layer_size * 6 + 6 * mm + 0]; + FPTYPE a1 = table[table_idx * last_layer_size * 6 + 6 * mm + 1]; + FPTYPE a2 = table[table_idx * last_layer_size * 6 + 6 * mm + 2]; + FPTYPE a3 = table[table_idx * last_layer_size * 6 + 6 * mm + 3]; + FPTYPE a4 = table[table_idx * last_layer_size * 6 + 6 * mm + 4]; + FPTYPE a5 = table[table_idx * last_layer_size * 6 + 6 * mm + 5]; + FPTYPE var = a0 + (a1 + (a2 + (a3 + (a4 + a5 * xx) * xx) * xx) * xx) * xx; + if (unloop) { + out[ii * last_layer_size + mm] += (nnei_j - kk) * var * ll; + } + else { + out[ii * last_layer_size + mm] += var * ll; + } + } + if (unloop) break; + } + } + } +} + +template +void deepmd::tabulate_fusion_se_t_grad_cpu( + FPTYPE * dy_dem_x, + FPTYPE * dy_dem, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em_x, + const FPTYPE * em, + const FPTYPE * dy, + const int nloc, + const int nnei_i, + const int nnei_j, + const int last_layer_size) +{ + memset(dy_dem_x, 0.0, sizeof(FPTYPE) * nloc * nnei_i * nnei_j); + memset(dy_dem, 0.0, sizeof(FPTYPE) * nloc * nnei_i * nnei_j); + FPTYPE const lower = table_info[0]; + FPTYPE const upper = table_info[1]; + FPTYPE const _max = table_info[2]; + FPTYPE const stride0 = table_info[3]; + FPTYPE const stride1 = table_info[4]; + // for every atom, execute a small gemm~ + // FPTYPE * res = new FPTYPE[4 * last_layer_size]; + #pragma omp parallel for + for (int ii = 0; ii < nloc; ii++) { + FPTYPE ll = 0; + FPTYPE rr = 0; + for (int jj = 0; jj < nnei_i; jj++) { + FPTYPE ago = em_x[ii * nnei_i * nnei_j + jj * nnei_j + nnei_j - 1]; + bool unloop = false; + for (int kk = 0; kk < nnei_j; kk++) { + // construct the dy/dx + FPTYPE xx = em_x[ii * nnei_i * nnei_j + jj * nnei_j + kk]; + ll = xx; + if (ago == xx) { + unloop = true; + } + int table_idx = 0; + locate_xx_se_t(lower, upper, -_max, _max, stride0, stride1, xx, table_idx); + FPTYPE grad = 0.0; + for (int mm = 0; mm < last_layer_size; mm++) { + rr = dy[ii * last_layer_size + mm]; + FPTYPE a0 = table[table_idx * last_layer_size * 6 + 6 * mm + 0]; + FPTYPE a1 = table[table_idx * last_layer_size * 6 + 6 * mm + 1]; + FPTYPE a2 = table[table_idx * last_layer_size * 6 + 6 * mm + 2]; + FPTYPE a3 = table[table_idx * last_layer_size * 6 + 6 * mm + 3]; + FPTYPE a4 = table[table_idx * last_layer_size * 6 + 6 * mm + 4]; + FPTYPE a5 = table[table_idx * last_layer_size * 6 + 6 * mm + 5]; + FPTYPE res = a0 + (a1 + (a2 + (a3 + (a4 + a5 * xx) * xx) * xx) * xx) * xx; + + if (unloop) { + grad += (a1 + (2 * a2 + (3 * a3 + (4 * a4 + 5 * a5 * xx) * xx) * xx) * xx) * ll * rr * (nnei_j - kk); + dy_dem[ii * nnei_i * nnei_j + jj * nnei_j + kk] += res * rr * (nnei_j - kk); + } + else { + grad += (a1 + (2 * a2 + (3 * a3 + (4 * a4 + 5 * a5 * xx) * xx) * xx) * xx) * ll * rr; + dy_dem[ii * nnei_i * nnei_j + jj * nnei_j + kk] += res * rr; + } + } + dy_dem_x[ii * nnei_i * nnei_j + jj * nnei_j + kk] = grad; + if (unloop) break; + } + } + } +} + +template +void deepmd::tabulate_fusion_se_t_grad_grad_cpu( + FPTYPE * dz_dy, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em_x, + const FPTYPE * em, + const FPTYPE * dz_dy_dem_x, + const FPTYPE * dz_dy_dem, + const int nloc, + const int nnei, + const int nnei_j, + const int last_layer_size) +{ + memset(dz_dy, 0.0, sizeof(FPTYPE) * nloc * 4 * last_layer_size); + const FPTYPE lower = table_info[0]; + const FPTYPE upper = table_info[1]; + const FPTYPE _max = table_info[2]; + const FPTYPE stride0 = table_info[3]; + const FPTYPE stride1 = table_info[4]; + // for every atom, execute a small manual gemm ~ + // FPTYPE * res = new FPTYPE[4 * last_layer_size]; + #pragma omp parallel for + for (int ii = 0; ii < nloc; ii++) { + FPTYPE ll[4]; + FPTYPE hh[4]; + FPTYPE ago = em_x[ii * nnei + nnei - 1]; + bool unloop = false; + for (int jj = 0; jj < nnei; jj++) { + ll[0] = em[ii * nnei * 4 + jj * 4 + 0]; + ll[1] = em[ii * nnei * 4 + jj * 4 + 1]; + ll[2] = em[ii * nnei * 4 + jj * 4 + 2]; + ll[3] = em[ii * nnei * 4 + jj * 4 + 3]; + hh[0] = dz_dy_dem[ii * nnei * 4 + jj * 4 + 0]; + hh[1] = dz_dy_dem[ii * nnei * 4 + jj * 4 + 1]; + hh[2] = dz_dy_dem[ii * nnei * 4 + jj * 4 + 2]; + hh[3] = dz_dy_dem[ii * nnei * 4 + jj * 4 + 3]; + FPTYPE xx = em_x[ii * nnei + jj]; + FPTYPE dz_xx = dz_dy_dem_x[ii * nnei + jj]; + if (ago == xx) { + unloop = true; + } + int table_idx = 0; + locate_xx(lower, upper, _max, stride0, stride1, xx, table_idx); + for (int kk = 0; kk < last_layer_size; kk++) { + FPTYPE a0 = table[table_idx * last_layer_size * 6 + 6 * kk + 0]; + FPTYPE a1 = table[table_idx * last_layer_size * 6 + 6 * kk + 1]; + FPTYPE a2 = table[table_idx * last_layer_size * 6 + 6 * kk + 2]; + FPTYPE a3 = table[table_idx * last_layer_size * 6 + 6 * kk + 3]; + FPTYPE a4 = table[table_idx * last_layer_size * 6 + 6 * kk + 4]; + FPTYPE a5 = table[table_idx * last_layer_size * 6 + 6 * kk + 5]; + FPTYPE var = a0 + (a1 + (a2 + (a3 + (a4 + a5 * xx) * xx) * xx) * xx) * xx; + FPTYPE var_grad = a1 + (2 * a2 + (3 * a3 + (4 * a4 + 5 * a5 * xx) * xx) * xx) * xx; + if (unloop) { + dz_dy[ii * last_layer_size * 4 + 0 * last_layer_size + kk] += (nnei - jj) * (var * hh[0] + dz_xx * var_grad * ll[0]); + dz_dy[ii * last_layer_size * 4 + 1 * last_layer_size + kk] += (nnei - jj) * (var * hh[1] + dz_xx * var_grad * ll[1]); + dz_dy[ii * last_layer_size * 4 + 2 * last_layer_size + kk] += (nnei - jj) * (var * hh[2] + dz_xx * var_grad * ll[2]); + dz_dy[ii * last_layer_size * 4 + 3 * last_layer_size + kk] += (nnei - jj) * (var * hh[3] + dz_xx * var_grad * ll[3]); + } + else { + dz_dy[ii * last_layer_size * 4 + 0 * last_layer_size + kk] += var * hh[0] + dz_xx * var_grad * ll[0]; + dz_dy[ii * last_layer_size * 4 + 1 * last_layer_size + kk] += var * hh[1] + dz_xx * var_grad * ll[1]; + dz_dy[ii * last_layer_size * 4 + 2 * last_layer_size + kk] += var * hh[2] + dz_xx * var_grad * ll[2]; + dz_dy[ii * last_layer_size * 4 + 3 * last_layer_size + kk] += var * hh[3] + dz_xx * var_grad * ll[3]; + } + } + if (unloop) break; + } + } +} + +template void deepmd::tabulate_fusion_se_a_cpu(float * out, const float * table, const float * table_info, const float * em_x, const float * em, const int nloc, const int nnei, const int last_layer_size); +template void deepmd::tabulate_fusion_se_a_cpu(double * out, const double * table, const double * table_info, const double * em_x, const double * em, const int nloc, const int nnei, const int last_layer_size); +template void deepmd::tabulate_fusion_se_a_grad_cpu (float * dy_dem_x, float * dy_dem, const float * table, const float * table_info, const float * em_x, const float * em, const float * dy, const int nloc, const int nnei, const int last_layer_size); +template void deepmd::tabulate_fusion_se_a_grad_cpu (double * dy_dem_x, double * dy_dem, const double * table, const double * table_info, const double * em_x, const double * em, const double * dy, const int nloc, const int nnei, const int last_layer_size); +template void deepmd::tabulate_fusion_se_a_grad_grad_cpu(float * dz_dy, const float * table, const float * table_info, const float * em_x, const float * em, const float * dz_dy_dem_x, const float * dz_dy_dem, const int nloc, const int nnei, const int last_layer_size); +template void deepmd::tabulate_fusion_se_a_grad_grad_cpu(double * dz_dy, const double * table, const double * table_info, const double * em_x, const double * em, const double * dz_dy_dem_x, const double * dz_dy_dem, const int nloc, const int nnei, const int last_layer_size); + +template void deepmd::tabulate_fusion_se_t_cpu(float * out, const float * table, const float * table_info, const float * em_x, const float * em, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); +template void deepmd::tabulate_fusion_se_t_cpu(double * out, const double * table, const double * table_info, const double * em_x, const double * em, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); +template void deepmd::tabulate_fusion_se_t_grad_cpu (float * dy_dem_x, float * dy_dem, const float * table, const float * table_info, const float * em_x, const float * em, const float * dy, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); +template void deepmd::tabulate_fusion_se_t_grad_cpu (double * dy_dem_x, double * dy_dem, const double * table, const double * table_info, const double * em_x, const double * em, const double * dy, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); +template void deepmd::tabulate_fusion_se_t_grad_grad_cpu(float * dz_dy, const float * table, const float * table_info, const float * em_x, const float * em, const float * dz_dy_dem_x, const float * dz_dy_dem, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); +template void deepmd::tabulate_fusion_se_t_grad_grad_cpu(double * dz_dy, const double * table, const double * table_info, const double * em_x, const double * em, const double * dz_dy_dem_x, const double * dz_dy_dem, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); diff --git a/source/lib/tests/test_tabulate.cc b/source/lib/tests/test_tabulate.cc index 43c0ef798e..3fe0b27d78 100644 --- a/source/lib/tests/test_tabulate.cc +++ b/source/lib/tests/test_tabulate.cc @@ -144,10 +144,10 @@ class TestTabulate : public ::testing::Test } }; -TEST_F(TestTabulate, tabulate_fusion_cpu) +TEST_F(TestTabulate, tabulate_fusion_se_a_cpu) { std::vector xyz_scatter(nloc * nnei * last_layer_size); - deepmd::tabulate_fusion_cpu(&xyz_scatter[0], &table[0], &info[0], &em_x[0], &em[0], nloc, nnei, last_layer_size); + deepmd::tabulate_fusion_se_a_cpu(&xyz_scatter[0], &table[0], &info[0], &em_x[0], &em[0], nloc, nnei, last_layer_size); EXPECT_EQ(xyz_scatter.size(), nloc * nnei * last_layer_size); EXPECT_EQ(xyz_scatter.size(), expected_xyz_scatter.size()); for (int jj = 0; jj < xyz_scatter.size(); ++jj){ @@ -155,12 +155,12 @@ TEST_F(TestTabulate, tabulate_fusion_cpu) } } -TEST_F(TestTabulate, tabulate_fusion_grad_cpu) +TEST_F(TestTabulate, tabulate_fusion_se_a_grad_cpu) { std::vector dy_dem_x(em_x.size()); std::vector dy_dem(em.size()); std::vector dy(nloc * nnei * last_layer_size, 1.0); - deepmd::tabulate_fusion_grad_cpu(&dy_dem_x[0], &dy_dem[0], &table[0], &info[0], &em_x[0], &em[0], &dy[0], nloc, nnei, last_layer_size); + deepmd::tabulate_fusion_se_a_grad_cpu(&dy_dem_x[0], &dy_dem[0], &table[0], &info[0], &em_x[0], &em[0], &dy[0], nloc, nnei, last_layer_size); EXPECT_EQ(dy_dem_x.size(), nloc * nnei); EXPECT_EQ(dy_dem.size(), nloc * nnei * 4); EXPECT_EQ(dy_dem_x.size(), expected_dy_dem_x.size()); @@ -174,7 +174,7 @@ TEST_F(TestTabulate, tabulate_fusion_grad_cpu) } #if GOOGLE_CUDA -TEST_F(TestTabulate, tabulate_fusion_gpu_cuda) +TEST_F(TestTabulate, tabulate_fusion_se_a_gpu_cuda) { std::vector xyz_scatter(nloc * nnei * last_layer_size, 0.0); @@ -183,7 +183,7 @@ TEST_F(TestTabulate, tabulate_fusion_gpu_cuda) deepmd::malloc_device_memory_sync(table_dev, table); deepmd::malloc_device_memory_sync(em_x_dev, em_x); deepmd::malloc_device_memory_sync(em_dev, em); - deepmd::tabulate_fusion_gpu_cuda(xyz_scatter_dev, table_dev, &info[0], em_x_dev, em_dev, nloc, nnei, last_layer_size); + deepmd::tabulate_fusion_se_a_gpu_cuda(xyz_scatter_dev, table_dev, &info[0], em_x_dev, em_dev, nloc, nnei, last_layer_size); deepmd::memcpy_device_to_host(xyz_scatter_dev, xyz_scatter); deepmd::delete_device_memory(xyz_scatter_dev); deepmd::delete_device_memory(table_dev); @@ -197,7 +197,7 @@ TEST_F(TestTabulate, tabulate_fusion_gpu_cuda) } } -TEST_F(TestTabulate, tabulate_fusion_grad_gpu_cuda) +TEST_F(TestTabulate, tabulate_fusion_se_a_grad_gpu_cuda) { std::vector dy_dem_x(em_x.size(), 0.0); std::vector dy_dem(em.size(), 0.0); @@ -210,7 +210,7 @@ TEST_F(TestTabulate, tabulate_fusion_grad_gpu_cuda) deepmd::malloc_device_memory_sync(em_x_dev, em_x); deepmd::malloc_device_memory_sync(em_dev, em); deepmd::malloc_device_memory_sync(dy_dev, dy); - deepmd::tabulate_fusion_grad_gpu_cuda(dy_dem_x_dev, dy_dem_dev, table_dev, &info[0], em_x_dev, em_dev, dy_dev, nloc, nnei, last_layer_size); + deepmd::tabulate_fusion_se_a_grad_gpu_cuda(dy_dem_x_dev, dy_dem_dev, table_dev, &info[0], em_x_dev, em_dev, dy_dev, nloc, nnei, last_layer_size); deepmd::memcpy_device_to_host(dy_dem_x_dev, dy_dem_x); deepmd::memcpy_device_to_host(dy_dem_dev, dy_dem); deepmd::delete_device_memory(dy_dem_x_dev); @@ -234,7 +234,7 @@ TEST_F(TestTabulate, tabulate_fusion_grad_gpu_cuda) #endif // GOOGLE_CUDA #if TENSORFLOW_USE_ROCM -TEST_F(TestTabulate, tabulate_fusion_gpu_rocm) +TEST_F(TestTabulate, tabulate_fusion_se_a_gpu_rocm) { std::vector xyz_scatter(nloc * nnei * last_layer_size, 0.0); @@ -243,7 +243,7 @@ TEST_F(TestTabulate, tabulate_fusion_gpu_rocm) deepmd::malloc_device_memory_sync(table_dev, table); deepmd::malloc_device_memory_sync(em_x_dev, em_x); deepmd::malloc_device_memory_sync(em_dev, em); - deepmd::tabulate_fusion_gpu_rocm(xyz_scatter_dev, table_dev, &info[0], em_x_dev, em_dev, nloc, nnei, last_layer_size); + deepmd::tabulate_fusion_se_a_gpu_rocm(xyz_scatter_dev, table_dev, &info[0], em_x_dev, em_dev, nloc, nnei, last_layer_size); deepmd::memcpy_device_to_host(xyz_scatter_dev, xyz_scatter); deepmd::delete_device_memory(xyz_scatter_dev); deepmd::delete_device_memory(table_dev); @@ -257,7 +257,7 @@ TEST_F(TestTabulate, tabulate_fusion_gpu_rocm) } } -TEST_F(TestTabulate, tabulate_fusion_grad_gpu_rocm) +TEST_F(TestTabulate, tabulate_fusion_se_a_grad_gpu_rocm) { std::vector dy_dem_x(em_x.size(), 0.0); std::vector dy_dem(em.size(), 0.0); @@ -270,7 +270,7 @@ TEST_F(TestTabulate, tabulate_fusion_grad_gpu_rocm) deepmd::malloc_device_memory_sync(em_x_dev, em_x); deepmd::malloc_device_memory_sync(em_dev, em); deepmd::malloc_device_memory_sync(dy_dev, dy); - deepmd::tabulate_fusion_grad_gpu_rocm(dy_dem_x_dev, dy_dem_dev, table_dev, &info[0], em_x_dev, em_dev, dy_dev, nloc, nnei, last_layer_size); + deepmd::tabulate_fusion_se_a_grad_gpu_rocm(dy_dem_x_dev, dy_dem_dev, table_dev, &info[0], em_x_dev, em_dev, dy_dev, nloc, nnei, last_layer_size); deepmd::memcpy_device_to_host(dy_dem_x_dev, dy_dem_x); deepmd::memcpy_device_to_host(dy_dem_dev, dy_dem); deepmd::delete_device_memory(dy_dem_x_dev); diff --git a/source/op/_tabulate_grad.py b/source/op/_tabulate_grad.py index f7be9445c7..855d9a470e 100644 --- a/source/op/_tabulate_grad.py +++ b/source/op/_tabulate_grad.py @@ -9,11 +9,23 @@ # from deepmd.DescrptSeATabulate import last_layer_size @ops.RegisterGradient("TabulateFusion") -def _tabulate_fusion_grad_cc (op, dy): - dy_dx, dy_df = op_module.tabulate_fusion_grad(op.inputs[0], op.inputs[1], op.inputs[2], op.inputs[3], dy, op.outputs[0]) +@ops.RegisterGradient("TabulateFusionSeA") +def _tabulate_fusion_se_a_grad_cc (op, dy): + dy_dx, dy_df = op_module.tabulate_fusion_se_a_grad(op.inputs[0], op.inputs[1], op.inputs[2], op.inputs[3], dy, op.outputs[0]) return [None, None, dy_dx, dy_df] @ops.RegisterGradient("TabulateFusionGrad") -def _tabulate_fusion_grad_grad_cc (op, dy, dy_): - dz_dy = op_module.tabulate_fusion_grad_grad(op.inputs[0], op.inputs[1], op.inputs[2], op.inputs[3], dy, dy_, op.inputs[5]) +@ops.RegisterGradient("TabulateFusionSeAGrad") +def _tabulate_fusion_se_a_grad_grad_cc (op, dy, dy_): + dz_dy = op_module.tabulate_fusion_se_a_grad_grad(op.inputs[0], op.inputs[1], op.inputs[2], op.inputs[3], dy, dy_, op.inputs[5]) + return [None, None, None, None, dz_dy, None] + +@ops.RegisterGradient("TabulateFusionSeT") +def _tabulate_fusion_se_t_grad_cc (op, dy): + dy_dx, dy_df = op_module.tabulate_fusion_se_t_grad(op.inputs[0], op.inputs[1], op.inputs[2], op.inputs[3], dy, op.outputs[0]) + return [None, None, dy_dx, dy_df] + +@ops.RegisterGradient("TabulateFusionSeTGrad") +def _tabulate_fusion_se_t_grad_grad_cc (op, dy, dy_): + dz_dy = op_module.tabulate_fusion_se_t_grad_grad(op.inputs[0], op.inputs[1], op.inputs[2], op.inputs[3], dy, dy_, op.inputs[5]) return [None, None, None, None, dz_dy, None] \ No newline at end of file diff --git a/source/op/tabulate_multi_device.cc b/source/op/tabulate_multi_device.cc index 3d3019b188..c19c88e48b 100644 --- a/source/op/tabulate_multi_device.cc +++ b/source/op/tabulate_multi_device.cc @@ -32,10 +32,72 @@ REGISTER_OP("TabulateFusionGradGrad") .Input("descriptor: T") .Output("dz_dy: T"); +REGISTER_OP("TabulateFusionSeA") + .Attr("T: {float, double} = DT_DOUBLE") + .Input("table: T") + .Input("table_info: T") + .Input("em_x: T") + .Input("em: T") + .Attr("last_layer_size: int") + .Output("descriptor: T"); + +REGISTER_OP("TabulateFusionSeAGrad") + .Attr("T: {float, double} = DT_DOUBLE") + .Input("table: T") + .Input("table_info: T") + .Input("em_x: T") + .Input("em: T") + .Input("dy: T") + .Input("descriptor: T") + .Output("dy_dem_x: T") + .Output("dy_dem: T"); + +REGISTER_OP("TabulateFusionSeAGradGrad") + .Attr("T: {float, double}") + .Input("table: T") + .Input("table_info: T") + .Input("em_x: T") + .Input("em: T") + .Input("dz_dy_dem_x: T") + .Input("dz_dy_dem: T") + .Input("descriptor: T") + .Output("dz_dy: T"); + +REGISTER_OP("TabulateFusionSeT") + .Attr("T: {float, double} = DT_DOUBLE") + .Input("table: T") + .Input("table_info: T") + .Input("em_x: T") + .Input("em: T") + .Attr("last_layer_size: int") + .Output("descriptor: T"); + +REGISTER_OP("TabulateFusionSeTGrad") + .Attr("T: {float, double} = DT_DOUBLE") + .Input("table: T") + .Input("table_info: T") + .Input("em_x: T") + .Input("em: T") + .Input("dy: T") + .Input("descriptor: T") + .Output("dy_dem_x: T") + .Output("dy_dem: T"); + +REGISTER_OP("TabulateFusionSeTGradGrad") + .Attr("T: {float, double}") + .Input("table: T") + .Input("table_info: T") + .Input("em_x: T") + .Input("em: T") + .Input("dz_dy_dem_x: T") + .Input("dz_dy_dem: T") + .Input("descriptor: T") + .Output("dz_dy: T"); + template -class TabulateFusionOp : public OpKernel { +class TabulateFusionSeAOp : public OpKernel { public: - explicit TabulateFusionOp(OpKernelConstruction* context) : OpKernel(context) { + explicit TabulateFusionSeAOp(OpKernelConstruction* context) : OpKernel(context) { OP_REQUIRES_OK(context, context->GetAttr("last_layer_size", &last_layer_size)); } void Compute(OpKernelContext* context) override { @@ -78,19 +140,19 @@ class TabulateFusionOp : public OpKernel { if (device == "GPU") { #if GOOGLE_CUDA - deepmd::tabulate_fusion_gpu_cuda( + deepmd::tabulate_fusion_se_a_gpu_cuda( descriptor, table, table_info, em_x, em, nloc, nnei, last_layer_size); #endif // GOOGLE_CUDA #if TENSORFLOW_USE_ROCM - deepmd::tabulate_fusion_gpu_rocm( + deepmd::tabulate_fusion_se_a_gpu_rocm( descriptor, table, table_info, em_x, em, nloc, nnei, last_layer_size); #endif // TENSORFLOW_USE_ROCM } else if (device == "CPU") { - deepmd::tabulate_fusion_cpu( + deepmd::tabulate_fusion_se_a_cpu( descriptor, table, table_info, em_x, em, nloc, nnei, last_layer_size); } @@ -101,9 +163,9 @@ class TabulateFusionOp : public OpKernel { }; template -class TabulateFusionGradOp : public OpKernel { +class TabulateFusionSeAGradOp : public OpKernel { public: - explicit TabulateFusionGradOp(OpKernelConstruction* context) : OpKernel(context) {} + explicit TabulateFusionSeAGradOp(OpKernelConstruction* context) : OpKernel(context) {} void Compute(OpKernelContext* context) override { deepmd::safe_compute(context, [this](OpKernelContext* context) {this->_Compute(context);}); } @@ -150,19 +212,19 @@ class TabulateFusionGradOp : public OpKernel { if (device == "GPU") { #if GOOGLE_CUDA - deepmd::tabulate_fusion_grad_gpu_cuda( + deepmd::tabulate_fusion_se_a_grad_gpu_cuda( dy_dem_x, dy_dem, table, table_info, em_x, em, dy, nloc, nnei, last_layer_size); #endif // GOOGLE_CUDA #if TENSORFLOW_USE_ROCM - deepmd::tabulate_fusion_grad_gpu_rocm( + deepmd::tabulate_fusion_se_a_grad_gpu_rocm( dy_dem_x, dy_dem, table, table_info, em_x, em, dy, nloc, nnei, last_layer_size); #endif // TENSORFLOW_USE_ROCM } else if (device == "CPU") { - deepmd::tabulate_fusion_grad_cpu( + deepmd::tabulate_fusion_se_a_grad_cpu( dy_dem_x, dy_dem, table, table_info, em_x, em, dy, nloc, nnei, last_layer_size); } @@ -172,9 +234,9 @@ class TabulateFusionGradOp : public OpKernel { }; template -class TabulateFusionGradGradOp : public OpKernel { +class TabulateFusionSeAGradGradOp : public OpKernel { public: - explicit TabulateFusionGradGradOp(OpKernelConstruction* context) : OpKernel(context) {} + explicit TabulateFusionSeAGradGradOp(OpKernelConstruction* context) : OpKernel(context) {} void Compute(OpKernelContext* context) override { // Grab the input tensor int context_input_index = 0; @@ -213,19 +275,19 @@ class TabulateFusionGradGradOp : public OpKernel { if (device == "GPU") { #if GOOGLE_CUDA - deepmd::tabulate_fusion_grad_grad_gpu_cuda( + deepmd::tabulate_fusion_se_a_grad_grad_gpu_cuda( dz_dy, table, table_info, em_x, em, dz_dy_dem_x, dz_dy_dem, nloc, nnei, last_layer_size); #endif // GOOGLE_CUDA #if TENSORFLOW_USE_ROCM - deepmd::tabulate_fusion_grad_grad_gpu_rocm( + deepmd::tabulate_fusion_se_a_grad_grad_gpu_rocm( dz_dy, table, table_info, em_x, em, dz_dy_dem_x, dz_dy_dem, nloc, nnei, last_layer_size); #endif // TENSORFLOW_USE_ROCM OP_REQUIRES (context, (last_layer_size <= 1024), errors::InvalidArgument ("In the process of model compression, the size of the last layer of embedding net must be less than 1024!")); } else if (device == "CPU") { - deepmd::tabulate_fusion_grad_grad_cpu( + deepmd::tabulate_fusion_se_a_grad_grad_cpu( dz_dy, table, table_info, em_x, em, dz_dy_dem_x, dz_dy_dem, nloc, nnei, last_layer_size); } @@ -234,30 +296,270 @@ class TabulateFusionGradGradOp : public OpKernel { std::string device; }; -#define REGISTER_CPU(T) \ -REGISTER_KERNEL_BUILDER( \ - Name("TabulateFusion").Device(DEVICE_CPU).TypeConstraint("T").HostMemory("table_info"), \ - TabulateFusionOp); \ -REGISTER_KERNEL_BUILDER( \ - Name("TabulateFusionGrad").Device(DEVICE_CPU).TypeConstraint("T").HostMemory("table_info"), \ - TabulateFusionGradOp); \ -REGISTER_KERNEL_BUILDER( \ - Name("TabulateFusionGradGrad").Device(DEVICE_CPU).TypeConstraint("T").HostMemory("table_info"), \ - TabulateFusionGradGradOp); +template +class TabulateFusionSeTOp : public OpKernel { + public: + explicit TabulateFusionSeTOp(OpKernelConstruction* context) : OpKernel(context) { + OP_REQUIRES_OK(context, context->GetAttr("last_layer_size", &last_layer_size)); + } + void Compute(OpKernelContext* context) override { + deepmd::safe_compute(context, [this](OpKernelContext* context) {this->_Compute(context);}); + } + + void _Compute(OpKernelContext* context) { + // Grab the input tensor + int context_input_index = 0; + const Tensor& table_tensor = context->input(context_input_index++); + const Tensor& table_info_tensor = context->input(context_input_index++); + const Tensor& em_x_tensor = context->input(context_input_index++); + const Tensor& em_tensor = context->input(context_input_index++); + // set size of the sample + OP_REQUIRES (context, (table_tensor.shape().dims() == 2), errors::InvalidArgument ("Dim of table should be 2")); + OP_REQUIRES (context, (em_x_tensor.shape().dims() == 2), errors::InvalidArgument ("Dim of em_x_tensor should be 2")); + OP_REQUIRES (context, (em_tensor.shape().dims() == 3), errors::InvalidArgument ("Dim of em_tensor should be 3")); + TensorShape descriptor_shape; + descriptor_shape.AddDim (em_tensor.shape().dim_size(0)); + descriptor_shape.AddDim (last_layer_size); + int context_output_index = 0; + Tensor* descriptor_tensor = NULL; + OP_REQUIRES_OK(context, context->allocate_output( + context_output_index++, + descriptor_shape, + &descriptor_tensor)); + DeviceFunctor() ( + device, + context->eigen_device() + ); + // flat the tensors + FPTYPE * descriptor = descriptor_tensor->flat().data(); + const FPTYPE * table = table_tensor.flat().data(); + const FPTYPE * table_info = table_info_tensor.flat().data(); + const FPTYPE * em_x = em_x_tensor.flat().data(); + const FPTYPE * em = em_tensor.flat().data(); + const int nloc = em_tensor.shape().dim_size(0); + const int nnei_i = em_tensor.shape().dim_size(1); + const int nnei_j = em_tensor.shape().dim_size(2); + + if (device == "GPU") { + #if GOOGLE_CUDA + deepmd::tabulate_fusion_se_t_gpu_cuda( + descriptor, + table, table_info, em_x, em, nloc, nnei_i, nnei_j, last_layer_size); + #endif // GOOGLE_CUDA + + #if TENSORFLOW_USE_ROCM + deepmd::tabulate_fusion_se_t_gpu_rocm( + descriptor, + table, table_info, em_x, em, nloc, nnei_i, nnei_j, last_layer_size); + #endif // TENSORFLOW_USE_ROCM + } + else if (device == "CPU") { + deepmd::tabulate_fusion_se_t_cpu( + descriptor, + table, table_info, em_x, em, nloc, nnei_i, nnei_j, last_layer_size); + } + } +private: + int last_layer_size; + std::string device; +}; + +template +class TabulateFusionSeTGradOp : public OpKernel { + public: + explicit TabulateFusionSeTGradOp(OpKernelConstruction* context) : OpKernel(context) {} + void Compute(OpKernelContext* context) override { + deepmd::safe_compute(context, [this](OpKernelContext* context) {this->_Compute(context);}); + } + + void _Compute(OpKernelContext* context) { + // Grab the input tensor + int context_input_index = 0; + const Tensor& table_tensor = context->input(context_input_index++); + const Tensor& table_info_tensor = context->input(context_input_index++); + const Tensor& em_x_tensor = context->input(context_input_index++); + const Tensor& em_tensor = context->input(context_input_index++); + const Tensor& dy_tensor = context->input(context_input_index++); + const Tensor& descriptor_tensor = context->input(context_input_index++); + // set size of the sample + OP_REQUIRES (context, (dy_tensor.shape().dims() == 2), errors::InvalidArgument ("Dim of dy_tensor should be 2")); + int context_output_index = 0; + Tensor* dy_dem_x_tensor = NULL; + OP_REQUIRES_OK(context, context->allocate_output( + context_output_index++, + em_x_tensor.shape(), + &dy_dem_x_tensor)); + Tensor* dy_dem_tensor = NULL; + OP_REQUIRES_OK(context, context->allocate_output( + context_output_index++, + em_tensor.shape(), + &dy_dem_tensor)); + DeviceFunctor() ( + device, + context->eigen_device() + ); + + // flat the tensors + FPTYPE * dy_dem_x = dy_dem_x_tensor->flat().data(); + FPTYPE * dy_dem = dy_dem_tensor->flat().data(); + const FPTYPE * descriptor = descriptor_tensor.flat().data(); + const FPTYPE * table = table_tensor.flat().data(); + const FPTYPE * table_info = table_info_tensor.flat().data(); + const FPTYPE * em_x = em_x_tensor.flat().data(); + const FPTYPE * em = em_tensor.flat().data(); + const FPTYPE * dy = dy_tensor.flat().data(); + const int nloc = em_tensor.shape().dim_size(0); + const int nnei_i = em_tensor.shape().dim_size(1); + const int nnei_j = em_tensor.shape().dim_size(2); + const int last_layer_size = descriptor_tensor.shape().dim_size(1); + + if (device == "GPU") { + #if GOOGLE_CUDA + deepmd::tabulate_fusion_se_t_grad_gpu_cuda( + dy_dem_x, dy_dem, + table, table_info, em_x, em, dy, nloc, nnei_i, nnei_j, last_layer_size); + #endif // GOOGLE_CUDA + + #if TENSORFLOW_USE_ROCM + deepmd::tabulate_fusion_se_t_grad_gpu_rocm( + dy_dem_x, dy_dem, + table, table_info, em_x, em, dy, nloc, nnei_i, nnei_j, last_layer_size); + #endif // TENSORFLOW_USE_ROCM + } + else if (device == "CPU") { + deepmd::tabulate_fusion_se_t_grad_cpu( + dy_dem_x, dy_dem, + table, table_info, em_x, em, dy, nloc, nnei_i, nnei_j, last_layer_size); + } + } +private: + std::string device; +}; + +template +class TabulateFusionSeTGradGradOp : public OpKernel { + public: + explicit TabulateFusionSeTGradGradOp(OpKernelConstruction* context) : OpKernel(context) {} + void Compute(OpKernelContext* context) override { + // Grab the input tensor + int context_input_index = 0; + const Tensor& table_tensor = context->input(context_input_index++); + const Tensor& table_info_tensor = context->input(context_input_index++); + const Tensor& em_x_tensor = context->input(context_input_index++); + const Tensor& em_tensor = context->input(context_input_index++); + const Tensor& dz_dy_dem_x_tensor = context->input(context_input_index++); + const Tensor& dz_dy_dem_tensor = context->input(context_input_index++); + const Tensor& descriptor_tensor = context->input(context_input_index++); + // set size of the sample + OP_REQUIRES (context, (dz_dy_dem_x_tensor.shape().dims() == 2), errors::InvalidArgument ("Dim of input should be 2")); + OP_REQUIRES (context, (dz_dy_dem_tensor.shape().dims() == 3), errors::InvalidArgument ("Dim of input should be 3")); + int context_output_index = 0; + Tensor* dz_dy_tensor = NULL; + OP_REQUIRES_OK(context, context->allocate_output( + context_output_index++, + descriptor_tensor.shape(), + &dz_dy_tensor)); + DeviceFunctor() ( + device, + context->eigen_device() + ); + + // flat the tensors + FPTYPE * dz_dy = dz_dy_tensor->flat().data(); + const FPTYPE * table = table_tensor.flat().data(); + const FPTYPE * table_info = table_info_tensor.flat().data(); + const FPTYPE * em_x = em_x_tensor.flat().data(); + const FPTYPE * em = em_tensor.flat().data(); + const FPTYPE * dz_dy_dem_x = dz_dy_dem_x_tensor.flat().data(); + const FPTYPE * dz_dy_dem = dz_dy_dem_tensor.flat().data(); + const int nloc = em_tensor.shape().dim_size(0); + const int nnei_i = em_tensor.shape().dim_size(1); + const int nnei_j = em_tensor.shape().dim_size(2); + const int last_layer_size = descriptor_tensor.shape().dim_size(2); + + if (device == "GPU") { + #if GOOGLE_CUDA + deepmd::tabulate_fusion_se_t_grad_grad_gpu_cuda( + dz_dy, + table, table_info, em_x, em, dz_dy_dem_x, dz_dy_dem, nloc, nnei_i, nnei_j, last_layer_size); + #endif // GOOGLE_CUDA + #if TENSORFLOW_USE_ROCM + deepmd::tabulate_fusion_se_t_grad_grad_gpu_rocm( + dz_dy, + table, table_info, em_x, em, dz_dy_dem_x, dz_dy_dem, nloc, nnei_i, nnei_j, last_layer_size); + #endif // TENSORFLOW_USE_ROCM + OP_REQUIRES (context, (last_layer_size <= 1024), errors::InvalidArgument ("In the process of model compression, the size of the last layer of embedding net must be less than 1024!")); + } + else if (device == "CPU") { + deepmd::tabulate_fusion_se_t_grad_grad_cpu( + dz_dy, + table, table_info, em_x, em, dz_dy_dem_x, dz_dy_dem, nloc, nnei_i, nnei_j, last_layer_size); + } + } +private: + std::string device; +}; + +#define REGISTER_CPU(T) \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusion").Device(DEVICE_CPU).TypeConstraint("T"), \ + TabulateFusionSeAOp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionGrad").Device(DEVICE_CPU).TypeConstraint("T"), \ + TabulateFusionSeAGradOp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionGradGrad").Device(DEVICE_CPU).TypeConstraint("T"), \ + TabulateFusionSeAGradGradOp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionSeA").Device(DEVICE_CPU).TypeConstraint("T"), \ + TabulateFusionSeAOp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionSeAGrad").Device(DEVICE_CPU).TypeConstraint("T"), \ + TabulateFusionSeAGradOp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionSeAGradGrad").Device(DEVICE_CPU).TypeConstraint("T"), \ + TabulateFusionSeAGradGradOp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionSeT").Device(DEVICE_CPU).TypeConstraint("T"), \ + TabulateFusionSeTOp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionSeTGrad").Device(DEVICE_CPU).TypeConstraint("T"), \ + TabulateFusionSeTGradOp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionSeTGradGrad").Device(DEVICE_CPU).TypeConstraint("T"), \ + TabulateFusionSeTGradGradOp); REGISTER_CPU(float); REGISTER_CPU(double); #if GOOGLE_CUDA || TENSORFLOW_USE_ROCM -#define REGISTER_GPU(T) \ -REGISTER_KERNEL_BUILDER( \ - Name("TabulateFusion").Device(DEVICE_GPU).TypeConstraint("T").HostMemory("table_info"), \ - TabulateFusionOp); \ -REGISTER_KERNEL_BUILDER( \ - Name("TabulateFusionGrad").Device(DEVICE_GPU).TypeConstraint("T").HostMemory("table_info"), \ - TabulateFusionGradOp); \ -REGISTER_KERNEL_BUILDER( \ - Name("TabulateFusionGradGrad").Device(DEVICE_GPU).TypeConstraint("T").HostMemory("table_info"), \ - TabulateFusionGradGradOp); +#define REGISTER_GPU(T) \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusion").Device(DEVICE_GPU).TypeConstraint("T").HostMemory("table_info"), \ + TabulateFusionSeAOp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionGrad").Device(DEVICE_GPU).TypeConstraint("T").HostMemory("table_info"), \ + TabulateFusionSeAGradOp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionGradGrad").Device(DEVICE_GPU).TypeConstraint("T").HostMemory("table_info"), \ + TabulateFusionSeAGradGradOp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionSeA").Device(DEVICE_GPU).TypeConstraint("T").HostMemory("table_info"), \ + TabulateFusionSeAOp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionSeAGrad").Device(DEVICE_GPU).TypeConstraint("T").HostMemory("table_info"), \ + TabulateFusionSeAGradOp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionSeAGradGrad").Device(DEVICE_GPU).TypeConstraint("T").HostMemory("table_info"), \ + TabulateFusionSeAGradGradOp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionSeT").Device(DEVICE_GPU).TypeConstraint("T").HostMemory("table_info"), \ + TabulateFusionSeTOp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionSeTGrad").Device(DEVICE_GPU).TypeConstraint("T").HostMemory("table_info"), \ + TabulateFusionSeTGradOp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionSeTGradGrad").Device(DEVICE_GPU).TypeConstraint("T").HostMemory("table_info"), \ + TabulateFusionSeTGradGradOp); REGISTER_GPU(float); REGISTER_GPU(double); #endif // GOOGLE_CUDA || TENSORFLOW_USE_ROCM diff --git a/source/tests/infer/deepdipole.pbtxt b/source/tests/infer/deepdipole.pbtxt index b503c29336..8968da9409 100644 --- a/source/tests/infer/deepdipole.pbtxt +++ b/source/tests/infer/deepdipole.pbtxt @@ -5966,7 +5966,7 @@ node { dtype: DT_STRING tensor_shape { } - string_val: "1.0" + string_val: "1.1" } } } diff --git a/source/tests/infer/deepdipole_fake.pbtxt b/source/tests/infer/deepdipole_fake.pbtxt index 5b41d69f20..0ad18beae8 100644 --- a/source/tests/infer/deepdipole_fake.pbtxt +++ b/source/tests/infer/deepdipole_fake.pbtxt @@ -180,7 +180,7 @@ node { dtype: DT_STRING tensor_shape { } - string_val: "1.0" + string_val: "1.1" } } } diff --git a/source/tests/infer/deepdipole_new.pbtxt b/source/tests/infer/deepdipole_new.pbtxt index 4a76d9d79d..ef697e2f5c 100644 --- a/source/tests/infer/deepdipole_new.pbtxt +++ b/source/tests/infer/deepdipole_new.pbtxt @@ -180,7 +180,7 @@ node { dtype: DT_STRING tensor_shape { } - string_val: "1.0" + string_val: "1.1" } } } diff --git a/source/tests/infer/deeppolar.pbtxt b/source/tests/infer/deeppolar.pbtxt index 49b9645b68..22d9bbc71d 100644 --- a/source/tests/infer/deeppolar.pbtxt +++ b/source/tests/infer/deeppolar.pbtxt @@ -6194,7 +6194,7 @@ node { dtype: DT_STRING tensor_shape { } - string_val: "1.0" + string_val: "1.1" } } } diff --git a/source/tests/infer/deeppolar_new.pbtxt b/source/tests/infer/deeppolar_new.pbtxt index f680add745..aa74204142 100644 --- a/source/tests/infer/deeppolar_new.pbtxt +++ b/source/tests/infer/deeppolar_new.pbtxt @@ -180,7 +180,7 @@ node { dtype: DT_STRING tensor_shape { } - string_val: "1.0" + string_val: "1.1" } } } diff --git a/source/tests/infer/deeppot-1.pbtxt b/source/tests/infer/deeppot-1.pbtxt index 0819df4b9e..1dbd60b2e1 100644 --- a/source/tests/infer/deeppot-1.pbtxt +++ b/source/tests/infer/deeppot-1.pbtxt @@ -8891,7 +8891,7 @@ node { dtype: DT_STRING tensor_shape { } - string_val: "1.0" + string_val: "1.1" } } } diff --git a/source/tests/infer/deeppot-r.pbtxt b/source/tests/infer/deeppot-r.pbtxt index c307be00f0..2bf26d40c5 100644 --- a/source/tests/infer/deeppot-r.pbtxt +++ b/source/tests/infer/deeppot-r.pbtxt @@ -8545,7 +8545,7 @@ node { dtype: DT_STRING tensor_shape { } - string_val: "1.0" + string_val: "1.1" } } } diff --git a/source/tests/infer/deeppot.pbtxt b/source/tests/infer/deeppot.pbtxt index c7c49e2483..47eac4825f 100644 --- a/source/tests/infer/deeppot.pbtxt +++ b/source/tests/infer/deeppot.pbtxt @@ -8891,7 +8891,7 @@ node { dtype: DT_STRING tensor_shape { } - string_val: "1.0" + string_val: "1.1" } } } diff --git a/source/tests/infer/dipolecharge_d.pbtxt b/source/tests/infer/dipolecharge_d.pbtxt index 6be963119f..7ea390dcb8 100644 --- a/source/tests/infer/dipolecharge_d.pbtxt +++ b/source/tests/infer/dipolecharge_d.pbtxt @@ -7119,7 +7119,7 @@ node { dtype: DT_STRING tensor_shape { } - string_val: "1.0" + string_val: "1.1" } } } diff --git a/source/tests/infer/dipolecharge_e.pbtxt b/source/tests/infer/dipolecharge_e.pbtxt index ec9412a111..891ec09d68 100644 --- a/source/tests/infer/dipolecharge_e.pbtxt +++ b/source/tests/infer/dipolecharge_e.pbtxt @@ -12418,7 +12418,7 @@ node { dtype: DT_STRING tensor_shape { } - string_val: "1.0" + string_val: "1.1" } } } @@ -56051,7 +56051,7 @@ node { dtype: DT_STRING tensor_shape { } - string_val: "1.0" + string_val: "1.1" } } } diff --git a/source/tests/test_model_compression.py b/source/tests/test_model_compression_se_a.py similarity index 100% rename from source/tests/test_model_compression.py rename to source/tests/test_model_compression_se_a.py diff --git a/source/tests/test_model_compression_se_t.py b/source/tests/test_model_compression_se_t.py new file mode 100644 index 0000000000..5d1efc08a2 --- /dev/null +++ b/source/tests/test_model_compression_se_t.py @@ -0,0 +1,420 @@ +import os,sys,platform,shutil,dpdata,json +import numpy as np +import unittest +import subprocess as sp + +from deepmd.infer import DeepPot +from deepmd.env import MODEL_VERSION +# from deepmd.entrypoints.compress import compress +from common import j_loader, tests_path + +from deepmd.env import GLOBAL_NP_FLOAT_PRECISION +if GLOBAL_NP_FLOAT_PRECISION == np.float32 : + default_places = 4 +else : + default_places = 10 + +def _file_delete(file) : + if os.path.isdir(file): + os.rmdir(file) + elif os.path.isfile(file): + os.remove(file) + +def _subprocess_run(command): + popen = sp.Popen(command.split(), shell=False, stdout=sp.PIPE, stderr=sp.STDOUT) + for line in iter(popen.stdout.readline, b''): + if hasattr(line, 'decode'): + line = line.decode('utf-8') + line = line.rstrip() + print(line) + popen.wait() + return popen.returncode + +def _init_models(): + data_file = str(tests_path / os.path.join("model_compression", "data")) + frozen_model = str(tests_path / "dp-original-se-t.pb") + compressed_model = str(tests_path / "dp-compressed-se-t.pb") + INPUT = str(tests_path / "input.json") + jdata = j_loader(str(tests_path / os.path.join("model_compression", "input.json"))) + jdata["model"]["descriptor"] = {} + jdata["model"]["descriptor"]["type"] = "se_e3" + jdata["model"]["descriptor"]["sel"] = [46, 92] + jdata["model"]["descriptor"]["rcut_smth"] = 0.5 + jdata["model"]["descriptor"]["rcut"] = 6.0 + jdata["model"]["descriptor"]["neuron"] = [4,8,16] + jdata["model"]["descriptor"]["resnet_dt"] = False + jdata["model"]["descriptor"]["seed"] = 1 + jdata["training"]["training_data"]["systems"] = data_file + jdata["training"]["validation_data"]["systems"] = data_file + with open(INPUT, "w") as fp: + json.dump(jdata, fp, indent=4) + + ret = _subprocess_run("dp train " + INPUT) + np.testing.assert_equal(ret, 0, 'DP train failed!') + ret = _subprocess_run("dp freeze -o " + frozen_model) + np.testing.assert_equal(ret, 0, 'DP freeze failed!') + ret = _subprocess_run("dp compress " + " -i " + frozen_model + " -o " + compressed_model) + np.testing.assert_equal(ret, 0, 'DP model compression failed!') + return INPUT, frozen_model, compressed_model + +INPUT, FROZEN_MODEL, COMPRESSED_MODEL = _init_models() + +class TestDeepPotAPBC(unittest.TestCase) : + @classmethod + def setUpClass(self): + self.dp_original = DeepPot(FROZEN_MODEL) + self.dp_compressed = DeepPot(COMPRESSED_MODEL) + self.coords = np.array([12.83, 2.56, 2.18, + 12.09, 2.87, 2.74, + 00.25, 3.32, 1.68, + 3.36, 3.00, 1.81, + 3.51, 2.51, 2.60, + 4.27, 3.22, 1.56]) + self.atype = [0, 1, 1, 0, 1, 1] + self.box = np.array([13., 0., 0., 0., 13., 0., 0., 0., 13.]) + + def test_attrs(self): + self.assertEqual(self.dp_original.get_ntypes(), 2) + self.assertAlmostEqual(self.dp_original.get_rcut(), 6.0, places = default_places) + self.assertEqual(self.dp_original.get_type_map(), ['O', 'H']) + self.assertEqual(self.dp_original.get_dim_fparam(), 0) + self.assertEqual(self.dp_original.get_dim_aparam(), 0) + + self.assertEqual(self.dp_compressed.get_ntypes(), 2) + self.assertAlmostEqual(self.dp_compressed.get_rcut(), 6.0, places = default_places) + self.assertEqual(self.dp_compressed.get_type_map(), ['O', 'H']) + self.assertEqual(self.dp_compressed.get_dim_fparam(), 0) + self.assertEqual(self.dp_compressed.get_dim_aparam(), 0) + + def test_1frame(self): + ee0, ff0, vv0 = self.dp_original.eval(self.coords, self.box, self.atype, atomic = False) + ee1, ff1, vv1 = self.dp_compressed.eval(self.coords, self.box, self.atype, atomic = False) + # check shape of the returns + nframes = 1 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) + + def test_1frame_atm(self): + ee0, ff0, vv0, ae0, av0 = self.dp_original.eval(self.coords, self.box, self.atype, atomic = True) + ee1, ff1, vv1, ae1, av1 = self.dp_compressed.eval(self.coords, self.box, self.atype, atomic = True) + # check shape of the returns + nframes = 1 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ae0.shape, (nframes,natoms,1)) + self.assertEqual(av0.shape, (nframes,natoms,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + self.assertEqual(ae1.shape, (nframes,natoms,1)) + self.assertEqual(av1.shape, (nframes,natoms,9)) + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ae0, ae1, default_places) + np.testing.assert_almost_equal(av0, av1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) + + def test_2frame_atm(self): + coords2 = np.concatenate((self.coords, self.coords)) + box2 = np.concatenate((self.box, self.box)) + ee0, ff0, vv0, ae0, av0 = self.dp_original.eval(coords2, box2, self.atype, atomic = True) + ee1, ff1, vv1, ae1, av1 = self.dp_compressed.eval(coords2, box2, self.atype, atomic = True) + # check shape of the returns + nframes = 2 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ae0.shape, (nframes,natoms,1)) + self.assertEqual(av0.shape, (nframes,natoms,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + self.assertEqual(ae1.shape, (nframes,natoms,1)) + self.assertEqual(av1.shape, (nframes,natoms,9)) + + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ae0, ae1, default_places) + np.testing.assert_almost_equal(av0, av1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) + + +class TestDeepPotANoPBC(unittest.TestCase) : + @classmethod + def setUpClass(self): + self.dp_original = DeepPot(FROZEN_MODEL) + self.dp_compressed = DeepPot(COMPRESSED_MODEL) + self.coords = np.array([12.83, 2.56, 2.18, + 12.09, 2.87, 2.74, + 00.25, 3.32, 1.68, + 3.36, 3.00, 1.81, + 3.51, 2.51, 2.60, + 4.27, 3.22, 1.56]) + self.atype = [0, 1, 1, 0, 1, 1] + self.box = None + + def test_1frame(self): + ee0, ff0, vv0 = self.dp_original.eval(self.coords, self.box, self.atype, atomic = False) + ee1, ff1, vv1 = self.dp_compressed.eval(self.coords, self.box, self.atype, atomic = False) + # check shape of the returns + nframes = 1 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) + + def test_1frame_atm(self): + ee0, ff0, vv0, ae0, av0 = self.dp_original.eval(self.coords, self.box, self.atype, atomic = True) + ee1, ff1, vv1, ae1, av1 = self.dp_compressed.eval(self.coords, self.box, self.atype, atomic = True) + # check shape of the returns + nframes = 1 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ae0.shape, (nframes,natoms,1)) + self.assertEqual(av0.shape, (nframes,natoms,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + self.assertEqual(ae1.shape, (nframes,natoms,1)) + self.assertEqual(av1.shape, (nframes,natoms,9)) + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ae0, ae1, default_places) + np.testing.assert_almost_equal(av0, av1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) + + def test_2frame_atm(self): + coords2 = np.concatenate((self.coords, self.coords)) + ee0, ff0, vv0, ae0, av0 = self.dp_original.eval(coords2, self.box, self.atype, atomic = True) + ee1, ff1, vv1, ae1, av1 = self.dp_compressed.eval(coords2, self.box, self.atype, atomic = True) + # check shape of the returns + nframes = 2 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ae0.shape, (nframes,natoms,1)) + self.assertEqual(av0.shape, (nframes,natoms,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + self.assertEqual(ae1.shape, (nframes,natoms,1)) + self.assertEqual(av1.shape, (nframes,natoms,9)) + + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ae0, ae1, default_places) + np.testing.assert_almost_equal(av0, av1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) + + +class TestDeepPotALargeBoxNoPBC(unittest.TestCase) : + @classmethod + def setUpClass(self): + self.dp_original = DeepPot(FROZEN_MODEL) + self.dp_compressed = DeepPot(COMPRESSED_MODEL) + self.coords = np.array([12.83, 2.56, 2.18, + 12.09, 2.87, 2.74, + 00.25, 3.32, 1.68, + 3.36, 3.00, 1.81, + 3.51, 2.51, 2.60, + 4.27, 3.22, 1.56]) + self.atype = [0, 1, 1, 0, 1, 1] + self.box = np.array([19., 0., 0., 0., 13., 0., 0., 0., 13.]) + + def test_1frame(self): + ee0, ff0, vv0 = self.dp_original.eval(self.coords, self.box, self.atype, atomic = False) + ee1, ff1, vv1 = self.dp_compressed.eval(self.coords, self.box, self.atype, atomic = False) + # check shape of the returns + nframes = 1 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) + + def test_1frame_atm(self): + ee0, ff0, vv0, ae0, av0 = self.dp_original.eval(self.coords, self.box, self.atype, atomic = True) + ee1, ff1, vv1, ae1, av1 = self.dp_compressed.eval(self.coords, self.box, self.atype, atomic = True) + # check shape of the returns + nframes = 1 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ae0.shape, (nframes,natoms,1)) + self.assertEqual(av0.shape, (nframes,natoms,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + self.assertEqual(ae1.shape, (nframes,natoms,1)) + self.assertEqual(av1.shape, (nframes,natoms,9)) + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ae0, ae1, default_places) + np.testing.assert_almost_equal(av0, av1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) + + def test_ase(self): + from ase import Atoms + from deepmd.calculator import DP + water0 = Atoms('OHHOHH', + positions=self.coords.reshape((-1,3)), + cell=self.box.reshape((3,3)), + calculator=DP(FROZEN_MODEL)) + water1 = Atoms('OHHOHH', + positions=self.coords.reshape((-1,3)), + cell=self.box.reshape((3,3)), + calculator=DP(COMPRESSED_MODEL)) + ee0 = water0.get_potential_energy() + ff0 = water0.get_forces() + ee1 = water1.get_potential_energy() + ff1 = water1.get_forces() + nframes = 1 + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + +class TestDeepPotAPBCExcludeTypes(unittest.TestCase) : + @classmethod + def setUpClass(self): + self.dp_original = DeepPot(FROZEN_MODEL) + self.dp_compressed = DeepPot(COMPRESSED_MODEL) + self.coords = np.array([12.83, 2.56, 2.18, + 12.09, 2.87, 2.74, + 00.25, 3.32, 1.68, + 3.36, 3.00, 1.81, + 3.51, 2.51, 2.60, + 4.27, 3.22, 1.56]) + self.atype = [0, 1, 1, 0, 1, 1] + self.box = np.array([13., 0., 0., 0., 13., 0., 0., 0., 13.]) + + @classmethod + def tearDownClass(self): + _file_delete(INPUT) + _file_delete(FROZEN_MODEL) + _file_delete(COMPRESSED_MODEL) + _file_delete("out.json") + _file_delete("compress.json") + _file_delete("checkpoint") + _file_delete("model.ckpt.meta") + _file_delete("model.ckpt.index") + _file_delete("model.ckpt.data-00000-of-00001") + _file_delete("model.ckpt-100.meta") + _file_delete("model.ckpt-100.index") + _file_delete("model.ckpt-100.data-00000-of-00001") + _file_delete("model-compression/checkpoint") + _file_delete("model-compression/model.ckpt.meta") + _file_delete("model-compression/model.ckpt.index") + _file_delete("model-compression/model.ckpt.data-00000-of-00001") + _file_delete("model-compression") + + def test_attrs(self): + self.assertEqual(self.dp_original.get_ntypes(), 2) + self.assertAlmostEqual(self.dp_original.get_rcut(), 6.0, places = default_places) + self.assertEqual(self.dp_original.get_type_map(), ['O', 'H']) + self.assertEqual(self.dp_original.get_dim_fparam(), 0) + self.assertEqual(self.dp_original.get_dim_aparam(), 0) + + self.assertEqual(self.dp_compressed.get_ntypes(), 2) + self.assertAlmostEqual(self.dp_compressed.get_rcut(), 6.0, places = default_places) + self.assertEqual(self.dp_compressed.get_type_map(), ['O', 'H']) + self.assertEqual(self.dp_compressed.get_dim_fparam(), 0) + self.assertEqual(self.dp_compressed.get_dim_aparam(), 0) + + def test_1frame(self): + ee0, ff0, vv0 = self.dp_original.eval(self.coords, self.box, self.atype, atomic = False) + ee1, ff1, vv1 = self.dp_compressed.eval(self.coords, self.box, self.atype, atomic = False) + # check shape of the returns + nframes = 1 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) + + def test_1frame_atm(self): + ee0, ff0, vv0, ae0, av0 = self.dp_original.eval(self.coords, self.box, self.atype, atomic = True) + ee1, ff1, vv1, ae1, av1 = self.dp_compressed.eval(self.coords, self.box, self.atype, atomic = True) + # check shape of the returns + nframes = 1 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ae0.shape, (nframes,natoms,1)) + self.assertEqual(av0.shape, (nframes,natoms,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + self.assertEqual(ae1.shape, (nframes,natoms,1)) + self.assertEqual(av1.shape, (nframes,natoms,9)) + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ae0, ae1, default_places) + np.testing.assert_almost_equal(av0, av1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) + + def test_2frame_atm(self): + coords2 = np.concatenate((self.coords, self.coords)) + box2 = np.concatenate((self.box, self.box)) + ee0, ff0, vv0, ae0, av0 = self.dp_original.eval(coords2, box2, self.atype, atomic = True) + ee1, ff1, vv1, ae1, av1 = self.dp_compressed.eval(coords2, box2, self.atype, atomic = True) + # check shape of the returns + nframes = 2 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ae0.shape, (nframes,natoms,1)) + self.assertEqual(av0.shape, (nframes,natoms,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + self.assertEqual(ae1.shape, (nframes,natoms,1)) + self.assertEqual(av1.shape, (nframes,natoms,9)) + + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ae0, ae1, default_places) + np.testing.assert_almost_equal(av0, av1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) From 2e6be16248f523bf989d361e6d571e50b6bbe0f5 Mon Sep 17 00:00:00 2001 From: Denghui Lu Date: Fri, 22 Oct 2021 07:52:34 +0800 Subject: [PATCH 093/161] enable model compression for dipole and polar type fitting net (#1228) * enable model compression for dipole and polar type fitting net * delete debug code --- deepmd/descriptor/se.py | 2 +- deepmd/fit/dipole.py | 21 ++++++++++++++++++--- deepmd/fit/ener.py | 13 ++++++------- deepmd/fit/polar.py | 36 ++++++++++++++++++++++++++++++++---- deepmd/train/trainer.py | 8 ++++---- 5 files changed, 61 insertions(+), 19 deletions(-) diff --git a/deepmd/descriptor/se.py b/deepmd/descriptor/se.py index 72bf908ecb..c7470568b6 100644 --- a/deepmd/descriptor/se.py +++ b/deepmd/descriptor/se.py @@ -96,7 +96,7 @@ def init_variables(self, suffix : str = "", ) -> None: """ - Init the embedding net variables with the given dict + Init the embedding net variables with the given frozen model Parameters ---------- diff --git a/deepmd/fit/dipole.py b/deepmd/fit/dipole.py index 81c26db9c9..6c115e3fb3 100644 --- a/deepmd/fit/dipole.py +++ b/deepmd/fit/dipole.py @@ -6,6 +6,7 @@ from deepmd.common import add_data_requirement, get_activation_func, get_precision, ACTIVATION_FN_DICT, PRECISION_DICT, docstring_parameter from deepmd.utils.argcheck import list_to_doc from deepmd.utils.network import one_layer, one_layer_rand_seed_shift +from deepmd.utils.graph import get_fitting_net_variables from deepmd.descriptor import DescrptSeA from deepmd.env import global_cvt_2_tf_float @@ -75,6 +76,7 @@ def __init__ (self, self.dim_rot_mat_1 = descrpt.get_dim_rot_mat_1() self.dim_rot_mat = self.dim_rot_mat_1 * 3 self.useBN = False + self.fitting_net_variables = None def get_sel_type(self) -> int: """ @@ -139,12 +141,12 @@ def build (self, layer = inputs_i for ii in range(0,len(self.n_neuron)) : if ii >= 1 and self.n_neuron[ii] == self.n_neuron[ii-1] : - layer+= one_layer(layer, self.n_neuron[ii], name='layer_'+str(ii)+'_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, use_timestep = self.resnet_dt, activation_fn = self.fitting_activation_fn, precision = self.fitting_precision, uniform_seed = self.uniform_seed) + layer+= one_layer(layer, self.n_neuron[ii], name='layer_'+str(ii)+'_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, use_timestep = self.resnet_dt, activation_fn = self.fitting_activation_fn, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables) else : - layer = one_layer(layer, self.n_neuron[ii], name='layer_'+str(ii)+'_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, activation_fn = self.fitting_activation_fn, precision = self.fitting_precision, uniform_seed = self.uniform_seed) + layer = one_layer(layer, self.n_neuron[ii], name='layer_'+str(ii)+'_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, activation_fn = self.fitting_activation_fn, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables) if (not self.uniform_seed) and (self.seed is not None): self.seed += self.seed_shift # (nframes x natoms) x naxis - final_layer = one_layer(layer, self.dim_rot_mat_1, activation_fn = None, name='final_layer_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, precision = self.fitting_precision, uniform_seed = self.uniform_seed) + final_layer = one_layer(layer, self.dim_rot_mat_1, activation_fn = None, name='final_layer_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables) if (not self.uniform_seed) and (self.seed is not None): self.seed += self.seed_shift # (nframes x natoms) x 1 * naxis final_layer = tf.reshape(final_layer, [tf.shape(inputs)[0] * natoms[2+type_i], 1, self.dim_rot_mat_1]) @@ -163,3 +165,16 @@ def build (self, tf.summary.histogram('fitting_net_output', outs) return tf.cast(tf.reshape(outs, [-1]), GLOBAL_TF_FLOAT_PRECISION) # return tf.reshape(outs, [tf.shape(inputs)[0] * natoms[0] * 3 // 3]) + + def init_variables(self, + model_file: str + ) -> None: + """ + Init the fitting net variables with the given frozen model + + Parameters + ---------- + model_file : str + The input frozen model file + """ + self.fitting_net_variables = get_fitting_net_variables(model_file) \ No newline at end of file diff --git a/deepmd/fit/ener.py b/deepmd/fit/ener.py index 37900a70d8..0afcf26de2 100644 --- a/deepmd/fit/ener.py +++ b/deepmd/fit/ener.py @@ -9,6 +9,7 @@ from deepmd.descriptor import DescrptLocFrame from deepmd.descriptor import DescrptSeA from deepmd.utils.type_embed import embed_atom_type +from deepmd.utils.graph import get_fitting_net_variables from deepmd.env import global_cvt_2_tf_float from deepmd.env import GLOBAL_TF_FLOAT_PRECISION @@ -148,7 +149,6 @@ def __init__ (self, self.aparam_std = None self.aparam_inv_std = None - self.compress = False self.fitting_net_variables = None def get_numb_fparam(self) -> int: @@ -484,15 +484,14 @@ def build (self, def init_variables(self, - fitting_net_variables: dict + model_file: str ) -> None: """ - Init the fitting net variables with the given dict + Init the fitting net variables with the given frozen model Parameters ---------- - fitting_net_variables - The input dict which stores the fitting net variables + model_file : str + The input frozen model file """ - self.compress = True - self.fitting_net_variables = fitting_net_variables \ No newline at end of file + self.fitting_net_variables = get_fitting_net_variables(model_file) \ No newline at end of file diff --git a/deepmd/fit/polar.py b/deepmd/fit/polar.py index c82e08f214..65b1ff6aef 100644 --- a/deepmd/fit/polar.py +++ b/deepmd/fit/polar.py @@ -6,6 +6,7 @@ from deepmd.common import add_data_requirement, get_activation_func, get_precision, ACTIVATION_FN_DICT, PRECISION_DICT, docstring_parameter from deepmd.utils.argcheck import list_to_doc from deepmd.utils.network import one_layer, one_layer_rand_seed_shift +from deepmd.utils.graph import get_fitting_net_variables from deepmd.descriptor import DescrptLocFrame from deepmd.descriptor import DescrptSeA @@ -192,6 +193,7 @@ def __init__ (self, self.dim_rot_mat_1 = descrpt.get_dim_rot_mat_1() self.dim_rot_mat = self.dim_rot_mat_1 * 3 self.useBN = False + self.fitting_net_variables = None def get_sel_type(self) -> List[int]: """ @@ -322,9 +324,9 @@ def build (self, layer = inputs_i for ii in range(0,len(self.n_neuron)) : if ii >= 1 and self.n_neuron[ii] == self.n_neuron[ii-1] : - layer+= one_layer(layer, self.n_neuron[ii], name='layer_'+str(ii)+'_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, use_timestep = self.resnet_dt, activation_fn = self.fitting_activation_fn, precision = self.fitting_precision, uniform_seed = self.uniform_seed) + layer+= one_layer(layer, self.n_neuron[ii], name='layer_'+str(ii)+'_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, use_timestep = self.resnet_dt, activation_fn = self.fitting_activation_fn, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables) else : - layer = one_layer(layer, self.n_neuron[ii], name='layer_'+str(ii)+'_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, activation_fn = self.fitting_activation_fn, precision = self.fitting_precision, uniform_seed = self.uniform_seed) + layer = one_layer(layer, self.n_neuron[ii], name='layer_'+str(ii)+'_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, activation_fn = self.fitting_activation_fn, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables) if (not self.uniform_seed) and (self.seed is not None): self.seed += self.seed_shift if self.fit_diag : bavg = np.zeros(self.dim_rot_mat_1) @@ -332,7 +334,7 @@ def build (self, # bavg[1] = self.avgeig[1] # bavg[2] = self.avgeig[2] # (nframes x natoms) x naxis - final_layer = one_layer(layer, self.dim_rot_mat_1, activation_fn = None, name='final_layer_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, bavg = bavg, precision = self.fitting_precision, uniform_seed = self.uniform_seed) + final_layer = one_layer(layer, self.dim_rot_mat_1, activation_fn = None, name='final_layer_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, bavg = bavg, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables) if (not self.uniform_seed) and (self.seed is not None): self.seed += self.seed_shift # (nframes x natoms) x naxis final_layer = tf.reshape(final_layer, [tf.shape(inputs)[0] * natoms[2+type_i], self.dim_rot_mat_1]) @@ -344,7 +346,7 @@ def build (self, # bavg[1*self.dim_rot_mat_1+1] = self.avgeig[1] # bavg[2*self.dim_rot_mat_1+2] = self.avgeig[2] # (nframes x natoms) x (naxis x naxis) - final_layer = one_layer(layer, self.dim_rot_mat_1*self.dim_rot_mat_1, activation_fn = None, name='final_layer_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, bavg = bavg, precision = self.fitting_precision, uniform_seed = self.uniform_seed) + final_layer = one_layer(layer, self.dim_rot_mat_1*self.dim_rot_mat_1, activation_fn = None, name='final_layer_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, bavg = bavg, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables) if (not self.uniform_seed) and (self.seed is not None): self.seed += self.seed_shift # (nframes x natoms) x naxis x naxis final_layer = tf.reshape(final_layer, [tf.shape(inputs)[0] * natoms[2+type_i], self.dim_rot_mat_1, self.dim_rot_mat_1]) @@ -371,6 +373,19 @@ def build (self, tf.summary.histogram('fitting_net_output', outs) return tf.cast(tf.reshape(outs, [-1]), GLOBAL_TF_FLOAT_PRECISION) + def init_variables(self, + model_file: str + ) -> None: + """ + Init the fitting net variables with the given frozen model + + Parameters + ---------- + model_file : str + The input frozen model file + """ + self.fitting_net_variables = get_fitting_net_variables(model_file) + class GlobalPolarFittingSeA () : """ @@ -480,4 +495,17 @@ def build (self, outs = tf.reduce_sum(outs, axis = 1) tf.summary.histogram('fitting_net_output', outs) return tf.reshape(outs, [-1]) + + def init_variables(self, + model_file: str + ) -> None: + """ + Init the fitting net variables with the given frozen model + + Parameters + ---------- + model_file : str + The input frozen model file + """ + self.polar_fitting.init_variables(model_file) diff --git a/deepmd/train/trainer.py b/deepmd/train/trainer.py index a8d85d86e6..16d1234112 100644 --- a/deepmd/train/trainer.py +++ b/deepmd/train/trainer.py @@ -20,7 +20,7 @@ from deepmd.utils.neighbor_stat import NeighborStat from deepmd.utils.sess import run_sess from deepmd.utils.type_embed import TypeEmbedNet -from deepmd.utils.graph import get_tensor_by_name, get_embedding_net_variables, get_fitting_net_variables +from deepmd.utils.graph import get_tensor_by_name from tensorflow.python.client import timeline from deepmd.env import op_module @@ -283,7 +283,7 @@ def build (self, # architecture to call neighbor stat else : self.descrpt.enable_compression(self.model_param['compress']["min_nbor_dist"], self.model_param['compress']['model_file'], self.model_param['compress']['table_config'][0], self.model_param['compress']['table_config'][1], self.model_param['compress']['table_config'][2], self.model_param['compress']['table_config'][3]) - self.fitting.init_variables(get_fitting_net_variables(self.model_param['compress']['model_file'])) + self.fitting.init_variables(self.model_param['compress']['model_file']) if self.is_compress or self.model_type == 'compressed_model': tf.constant("compressed_model", name = 'model_type', dtype = tf.string) @@ -661,11 +661,11 @@ def _init_from_frz_model(self): # initialize fitting net with the given compressed frozen model if self.model_type == 'original_model': self.descrpt.init_variables(self.run_opt.init_frz_model) - self.fitting.init_variables(get_fitting_net_variables(self.run_opt.init_frz_model)) + self.fitting.init_variables(self.run_opt.init_frz_model) tf.constant("original_model", name = 'model_type', dtype = tf.string) elif self.model_type == 'compressed_model': self.frz_model = self.run_opt.init_frz_model - self.fitting.init_variables(get_fitting_net_variables(self.frz_model)) + self.fitting.init_variables(self.frz_model) tf.constant("compressed_model", name = 'model_type', dtype = tf.string) else: raise RuntimeError("Unknown model type %s" % self.model_type) From 70061f0abf87b1cc048c174fd72b32f8dd8c12a3 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sun, 24 Oct 2021 20:14:05 -0400 Subject: [PATCH 094/161] bump pip version (#1237) --- .github/workflows/test_python.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test_python.yml b/.github/workflows/test_python.yml index 27f8e866d6..3c957e7758 100644 --- a/.github/workflows/test_python.yml +++ b/.github/workflows/test_python.yml @@ -65,6 +65,7 @@ jobs: - run: | sudo apt update sudo apt install gcc-${{ matrix.gcc }} g++-${{ matrix.gcc }} + - run: python -m pip install -U pip>=21.3.1 - run: pip install -e .[cpu,test] codecov env: CC: gcc-${{ matrix.gcc }} From 3130caac9c0ee831f6a11d2ede6e3144d145896a Mon Sep 17 00:00:00 2001 From: Jingchao Zhang <6353250+JingchaoZhang@users.noreply.github.com> Date: Sun, 24 Oct 2021 20:33:36 -0400 Subject: [PATCH 095/161] Update training.md (#1238) another typo. Co-authored-by: Han Wang --- doc/train/training.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/train/training.md b/doc/train/training.md index b9c6bd4cb8..939afb7df0 100644 --- a/doc/train/training.md +++ b/doc/train/training.md @@ -26,7 +26,7 @@ DEEPMD INFO system natoms bch_sz n_bc DEEPMD INFO ../data_water/data_3 192 1 80 1.000 T DEEPMD INFO -------------------------------------------------------------------------------------- ``` -The DeePMD-kit prints detailed informaiton on the training and validation data sets. The data sets are defined by `"training_data"` and `"validation_data"` defined in the `"training"` section of the input script. The training data set is composed by three data systems, while the validation data set is composed by one data system. The number of atoms, batch size, number of batches in the system and the probability of using the system are all shown on the screen. The last column presents if the periodic boundary condition is assumed for the system. +The DeePMD-kit prints detailed information on the training and validation data sets. The data sets are defined by `"training_data"` and `"validation_data"` defined in the `"training"` section of the input script. The training data set is composed by three data systems, while the validation data set is composed by one data system. The number of atoms, batch size, number of batches in the system and the probability of using the system are all shown on the screen. The last column presents if the periodic boundary condition is assumed for the system. During the training, the error of the model is tested every `disp_freq` training steps with the batch used to train the model and with `numb_btch` batches from the validating data. The training error and validation error are printed correspondingly in the file `disp_file` (default is `lcurve.out`). The batch size can be set in the input script by the key `batch_size` in the corresponding sections for training and validation data set. An example of the output ```bash @@ -59,4 +59,4 @@ plt.show() Checkpoints will be written to files with prefix `save_ckpt` every `save_freq` training steps. ## Warning -It is warned that the example water data (in folder `examples/water/data`) is of very limited amount, is provided only for testing purpose, and should not be used to train a productive model. \ No newline at end of file +It is warned that the example water data (in folder `examples/water/data`) is of very limited amount, is provided only for testing purpose, and should not be used to train a productive model. From 6c41aa3d464d9433791f166af1848588dec6267d Mon Sep 17 00:00:00 2001 From: Jingchao Zhang <6353250+JingchaoZhang@users.noreply.github.com> Date: Sun, 24 Oct 2021 20:34:25 -0400 Subject: [PATCH 096/161] Update dpdata.md (#1229) typo update Co-authored-by: Han Wang --- doc/data/dpdata.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/data/dpdata.md b/doc/data/dpdata.md index 2e540ea5c2..f7fc3822ee 100644 --- a/doc/data/dpdata.md +++ b/doc/data/dpdata.md @@ -1,6 +1,6 @@ # Prepare data with dpdata -One can use the a convenient tool [`dpdata`](https://github.com/deepmodeling/dpdata) to convert data directly from the output of first priciple packages to the DeePMD-kit format. +One can use the a convenient tool [`dpdata`](https://github.com/deepmodeling/dpdata) to convert data directly from the output of first principle packages to the DeePMD-kit format. To install one can execute ```bash From 1a8fd7367d34bd939ee86b8b35cfaad5cc0eac23 Mon Sep 17 00:00:00 2001 From: Denghui Lu Date: Tue, 26 Oct 2021 08:17:32 +0800 Subject: [PATCH 097/161] Fix compress training bug within the dp train --init-frz-model interface (#1233) * fix compress training bug within the dp train --init-frz-model interface * address comments * rename _transfer_graph_def function within freeze.py --- deepmd/entrypoints/freeze.py | 41 +++++++++++++++++++++++++++-- deepmd/entrypoints/transfer.py | 21 ++------------- deepmd/env.py | 36 ++++++++++++++++++++++++++ deepmd/utils/graph.py | 47 ++++++++++++++++++++++++++-------- 4 files changed, 113 insertions(+), 32 deletions(-) diff --git a/deepmd/entrypoints/freeze.py b/deepmd/entrypoints/freeze.py index afbb7659d4..9d17786e1a 100755 --- a/deepmd/entrypoints/freeze.py +++ b/deepmd/entrypoints/freeze.py @@ -7,9 +7,9 @@ """ import logging -from deepmd.env import tf -from deepmd.env import op_module +from deepmd.env import tf, FITTING_NET_PATTERN from deepmd.utils.sess import run_sess +from deepmd.utils.graph import get_pattern_nodes_from_graph_def from os.path import abspath # load grad of force module @@ -21,6 +21,36 @@ log = logging.getLogger(__name__) +def _transfer_fitting_net_trainable_variables(sess, old_graph_def, raw_graph_def): + old_pattern = FITTING_NET_PATTERN + raw_pattern = FITTING_NET_PATTERN\ + .replace('idt', 'idt+_\d+')\ + .replace('bias', 'bias+_\d+')\ + .replace('matrix', 'matrix+_\d+') + old_graph_nodes = get_pattern_nodes_from_graph_def( + old_graph_def, + old_pattern + ) + try : + raw_graph_def = tf.graph_util.convert_variables_to_constants( + sess, # The session is used to retrieve the weights + raw_graph_def, # The graph_def is used to retrieve the nodes + [n + '_1' for n in old_graph_nodes], # The output node names are used to select the usefull nodes + ) + except AssertionError: + # if there's no additional nodes + return old_graph_def + + raw_graph_nodes = get_pattern_nodes_from_graph_def( + raw_graph_def, + raw_pattern + ) + for node in old_graph_def.node: + if node.name not in old_graph_nodes.keys(): + continue + tensor = tf.make_ndarray(raw_graph_nodes[node.name + '_1']) + node.attr["value"].tensor.tensor_content = tensor.tostring() + return old_graph_def def _make_node_names(model_type: str, modifier_type: Optional[str] = None) -> List[str]: """Get node names based on model type. @@ -205,6 +235,13 @@ def freeze( output_node_list, # The output node names are used to select the usefull nodes ) + # If we need to transfer the fitting net variables + output_graph_def = _transfer_fitting_net_trainable_variables( + sess, + output_graph_def, + input_graph_def + ) + # Finally we serialize and dump the output graph to the filesystem with tf.gfile.GFile(output_graph, "wb") as f: f.write(output_graph_def.SerializeToString()) diff --git a/deepmd/entrypoints/transfer.py b/deepmd/entrypoints/transfer.py index 3a755fd61a..47509551a8 100644 --- a/deepmd/entrypoints/transfer.py +++ b/deepmd/entrypoints/transfer.py @@ -1,7 +1,7 @@ """Module used for transfering parameters between models.""" from typing import Dict, Optional, Sequence, Tuple -from deepmd.env import tf +from deepmd.env import tf, TRANSFER_PATTERN import re import numpy as np import logging @@ -225,24 +225,7 @@ def load_transform_node(graph: tf.Graph) -> Dict[str, tf.Tensor]: Dict[str, tf.Tensor] mapping on graph node names and corresponding tensors """ - transform_node_pattern = re.compile( - r"filter_type_\d+/matrix_\d+_\d+|" - r"filter_type_\d+/bias_\d+_\d+|" - r"filter_type_\d+/idt_\d+_\d+|" - r"layer_\d+_type_\d+/matrix|" - r"layer_\d+_type_\d+/bias|" - r"layer_\d+_type_\d+/idt|" - r"final_layer_type_\d+/matrix|" - r"descrpt_attr/t_avg|" - r"descrpt_attr/t_std|" - r"final_layer_type_\d+/bias|" - r"fitting_attr/t_fparam_avg|" - r"fitting_attr/t_fparam_istd|" - r"fitting_attr/t_aparam_avg|" - r"fitting_attr/t_aparam_istd|" - r"model_attr/t_tab_info|" - r"model_attr/t_tab_data|" - ) + transform_node_pattern = re.compile(TRANSFER_PATTERN) transform_node = {} for node in graph.node: diff --git a/deepmd/env.py b/deepmd/env.py index 92287d8aa5..6e6543697e 100644 --- a/deepmd/env.py +++ b/deepmd/env.py @@ -2,6 +2,7 @@ import logging import os +import re import platform from configparser import ConfigParser from imp import reload @@ -35,10 +36,45 @@ "reset_default_tf_session_config", "op_module", "op_grads_module", + "TRANSFER_PATTERN", + "FITTING_NET_PATTERN", + "EMBEDDING_NET_PATTERN", ] SHARED_LIB_MODULE = "op" +EMBEDDING_NET_PATTERN = str( + r"filter_type_\d+/matrix_\d+_\d+|" + r"filter_type_\d+/bias_\d+_\d+|" + r"filter_type_\d+/idt_\d+_\d+|" + r"filter_type_all/matrix_\d+_\d+|" + r"filter_type_all/matrix_\d+_\d+_\d+|" + r"filter_type_all/bias_\d+_\d+|" + r"filter_type_all/bias_\d+_\d+_\d+|" + r"filter_type_all/idt_\d+_\d+|" +) + +FITTING_NET_PATTERN = str( + r"layer_\d+_type_\d+/matrix|" + r"layer_\d+_type_\d+/bias|" + r"layer_\d+_type_\d+/idt|" + r"final_layer_type_\d+/matrix|" + r"final_layer_type_\d+/bias|" +) + +TRANSFER_PATTERN = \ + EMBEDDING_NET_PATTERN + \ + FITTING_NET_PATTERN + \ + str( + r"descrpt_attr/t_avg|" + r"descrpt_attr/t_std|" + r"fitting_attr/t_fparam_avg|" + r"fitting_attr/t_fparam_istd|" + r"fitting_attr/t_aparam_avg|" + r"fitting_attr/t_aparam_istd|" + r"model_attr/t_tab_info|" + r"model_attr/t_tab_data|" +) def set_env_if_empty(key: str, value: str, verbose: bool = True): """Set environment variable only if it is empty. diff --git a/deepmd/utils/graph.py b/deepmd/utils/graph.py index 6766750e4b..031454e4b9 100644 --- a/deepmd/utils/graph.py +++ b/deepmd/utils/graph.py @@ -1,7 +1,7 @@ import re import numpy as np from typing import Tuple, Dict -from deepmd.env import tf +from deepmd.env import tf, EMBEDDING_NET_PATTERN, FITTING_NET_PATTERN from deepmd.utils.sess import run_sess from deepmd.utils.errors import GraphWithoutTensorError @@ -112,6 +112,30 @@ def get_tensor_by_type(node, return tensor +def get_pattern_nodes_from_graph_def(graph_def: tf.GraphDef, pattern: str) -> Dict: + """ + Get the pattern nodes with the given tf.GraphDef object + + Parameters + ---------- + graph_def + The input tf.GraphDef object + pattern + The node pattern within the graph_def + + Returns + ---------- + Dict + The fitting net nodes within the given tf.GraphDef object + """ + nodes = {} + pattern = re.compile(pattern) + for node in graph_def.node: + if re.fullmatch(pattern, node.name) != None: + nodes[node.name] = node.attr["value"].tensor + return nodes + + def get_embedding_net_nodes_from_graph_def(graph_def: tf.GraphDef, suffix: str = "") -> Dict: """ Get the embedding net nodes with the given tf.GraphDef object @@ -128,11 +152,16 @@ def get_embedding_net_nodes_from_graph_def(graph_def: tf.GraphDef, suffix: str = Dict The embedding net nodes within the given tf.GraphDef object """ - embedding_net_nodes = {} - embedding_net_pattern = f"filter_type_\d+{suffix}/matrix_\d+_\d+|filter_type_\d+{suffix}/bias_\d+_\d+|filter_type_\d+{suffix}/idt_\d+_\d+|filter_type_all{suffix}/matrix_\d+_\d+|filter_type_all{suffix}/matrix_\d+_\d+_\d+|filter_type_all{suffix}/bias_\d+_\d+|filter_type_all{suffix}/bias_\d+_\d+_\d+|filter_type_all{suffix}/idt_\d+_\d+" - for node in graph_def.node: - if re.fullmatch(embedding_net_pattern, node.name) != None: - embedding_net_nodes[node.name] = node.attr["value"].tensor + # embedding_net_pattern = f"filter_type_\d+{suffix}/matrix_\d+_\d+|filter_type_\d+{suffix}/bias_\d+_\d+|filter_type_\d+{suffix}/idt_\d+_\d+|filter_type_all{suffix}/matrix_\d+_\d+|filter_type_all{suffix}/matrix_\d+_\d+_\d+|filter_type_all{suffix}/bias_\d+_\d+|filter_type_all{suffix}/bias_\d+_\d+_\d+|filter_type_all{suffix}/idt_\d+_\d+" + if suffix is not "": + embedding_net_pattern = EMBEDDING_NET_PATTERN\ + .replace('/idt', suffix + '/idt')\ + .replace('/bias', suffix + '/bias')\ + .replace('/matrix', suffix + '/matrix') + else: + embedding_net_pattern = EMBEDDING_NET_PATTERN + + embedding_net_nodes = get_pattern_nodes_from_graph_def(graph_def, embedding_net_pattern) for key in embedding_net_nodes.keys(): assert key.find('bias') > 0 or key.find( 'matrix') > 0, "currently, only support weight matrix and bias matrix at the tabulation op!" @@ -222,11 +251,7 @@ def get_fitting_net_nodes_from_graph_def(graph_def: tf.GraphDef) -> Dict: Dict The fitting net nodes within the given tf.GraphDef object """ - fitting_net_nodes = {} - fitting_net_pattern = "layer_\d+_type_\d+/matrix+|layer_\d+_type_\d+/bias+|layer_\d+_type_\d+/idt+|final_layer_type_\d+/matrix+|final_layer_type_\d+/bias" - for node in graph_def.node: - if re.fullmatch(fitting_net_pattern, node.name) != None: - fitting_net_nodes[node.name] = node.attr["value"].tensor + fitting_net_nodes = get_pattern_nodes_from_graph_def(graph_def, FITTING_NET_PATTERN) for key in fitting_net_nodes.keys(): assert key.find('bias') > 0 or key.find('matrix') > 0 or key.find( 'idt') > 0, "currently, only support weight matrix, bias and idt at the model compression process!" From 956143d9bbaecc731b2838c42364d80a092c240e Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Wed, 27 Oct 2021 11:42:56 -0400 Subject: [PATCH 098/161] skip musllinux build tensorflow doesn't support it --- .github/workflows/build_wheel.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_wheel.yml b/.github/workflows/build_wheel.yml index ce72d9cc41..bc6a375c81 100644 --- a/.github/workflows/build_wheel.yml +++ b/.github/workflows/build_wheel.yml @@ -28,7 +28,7 @@ jobs: CIBW_BUILD: "cp36-* cp37-* cp38-* cp39-*" CIBW_MANYLINUX_X86_64_IMAGE: ghcr.io/deepmodeling/manylinux2010_x86_64_tensorflow CIBW_BEFORE_BUILD: pip install tensorflow - CIBW_SKIP: "*-win32 *-manylinux_i686" + CIBW_SKIP: "*-win32 *-manylinux_i686 *-musllinux*" run: | python -m cibuildwheel --output-dir wheelhouse - uses: actions/upload-artifact@v2 From 75501ac5814c430320942f12893de6ef10ae4f03 Mon Sep 17 00:00:00 2001 From: Jingchao Zhang <6353250+JingchaoZhang@users.noreply.github.com> Date: Wed, 27 Oct 2021 21:00:50 -0400 Subject: [PATCH 099/161] Merge pull request #1241 from JingchaoZhang/patch-3 Fix typo in training.md --- doc/train/training.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/train/training.md b/doc/train/training.md index 939afb7df0..42b2f90fab 100644 --- a/doc/train/training.md +++ b/doc/train/training.md @@ -38,7 +38,7 @@ During the training, the error of the model is tested every `disp_freq` training 400 1.36e+01 1.32e+01 1.07e-02 2.07e-03 4.29e-01 4.19e-01 1.0e-03 500 1.07e+01 1.05e+01 2.45e-03 4.11e-03 3.38e-01 3.31e-01 1.0e-03 ``` -The file contains 8 columns, form right to left, are the training step, the validation loss, training loss, root mean square (RMS) validation error of energy, RMS training error of energy, RMS validation error of force, RMS training error of force and the learning rate. The RMS error (RMSE) of the energy is normalized by number of atoms in the system. One can visualize this file by a simple Python script: +The file contains 8 columns, form left to right, are the training step, the validation loss, training loss, root mean square (RMS) validation error of energy, RMS training error of energy, RMS validation error of force, RMS training error of force and the learning rate. The RMS error (RMSE) of the energy is normalized by number of atoms in the system. One can visualize this file by a simple Python script: ```py import numpy as np From edb8bd9f3697d73316568ffb062a2b839f092500 Mon Sep 17 00:00:00 2001 From: Denghui Lu Date: Thu, 28 Oct 2021 09:02:26 +0800 Subject: [PATCH 100/161] add init-frz-model support for se-t type descriptor (#1245) --- source/lib/src/cuda/tabulate.cu | 67 +++++++++++++--------------- source/lib/src/rocm/tabulate.hip.cu | 64 ++++++++++++-------------- source/lib/src/tabulate.cc | 69 +++++++++++------------------ source/op/tabulate_multi_device.cc | 4 +- 4 files changed, 89 insertions(+), 115 deletions(-) diff --git a/source/lib/src/cuda/tabulate.cu b/source/lib/src/cuda/tabulate.cu index 47ae73577f..4cc6b112ee 100644 --- a/source/lib/src/cuda/tabulate.cu +++ b/source/lib/src/cuda/tabulate.cu @@ -420,49 +420,43 @@ __global__ void tabulate_fusion_se_t_grad_grad_fifth_order_polynomial( const FPTYPE max, const FPTYPE stride0, const FPTYPE stride1, - const int nnei, + const int nnei_i, const int nnei_j, const int last_layer_size) { - extern __shared__ int _data[]; - const int block_idx = blockIdx.x; // nloc + const int block_idx = blockIdx.x; // nloc const int thread_idx = threadIdx.x; // last_layer_size - FPTYPE ago = __shfl_sync(0xffffffff, em_x[block_idx * nnei + nnei - 1], 0); - bool unloop = false; - int breakpoint = nnei - 1; - FPTYPE * iteratorC = (FPTYPE*) &_data[0]; - for (int kk = 0; kk < MTILE; kk++) - iteratorC[kk * last_layer_size + thread_idx] = 0.f; - __syncthreads(); - for (int ii = 0; ii < nnei; ii++) { - FPTYPE var[6]; - FPTYPE xx = em_x[block_idx * nnei + ii]; - FPTYPE dz_xx = dz_dy_dem_x[block_idx * nnei + ii]; - if (xx == ago) { - unloop = true; - breakpoint = ii; - } - int table_idx = 0; - locate_xx_se_t(xx, table_idx, lower, upper, -max, max, stride0, stride1); - var[0] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 0]; - var[1] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 1]; - var[2] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 2]; - var[3] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 3]; - var[4] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 4]; - var[5] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 5]; - FPTYPE res = var[0] + (var[1] + (var[2] + (var[3] + (var[4] + var[5] * xx) * xx) * xx) * xx) * xx; - FPTYPE res_grad = var[1] + (2 * var[2] + (3 * var[3] + (4 * var[4] + 5 * var[5] * xx) * xx) * xx) * xx; + FPTYPE sum = 0.f; + for (int ii = 0; ii < nnei_i; ii++) { + FPTYPE ago = __shfl_sync(0xffffffff, em_x[block_idx * nnei_i * nnei_j + ii * nnei_j + nnei_j - 1], 0); + bool unloop = false; + for (int jj = 0; ii < nnei_j; jj++) { + FPTYPE xx = em_x[block_idx * nnei_i * nnei_j + ii * nnei_j + jj]; + FPTYPE tmp = xx; + FPTYPE dz_xx = dz_dy_dem_x[block_idx * nnei_i * nnei_j + ii * nnei_j + jj]; + FPTYPE dz_em = dz_dy_dem[block_idx * nnei_i * nnei_j + ii * nnei_j + jj]; + FPTYPE var[6]; + if (ago == xx) { + unloop = true; + } - for (int kk = 0; kk < MTILE; kk++) { - int em_index = block_idx * nnei * MTILE + ii * MTILE + kk; - iteratorC[kk * last_layer_size + thread_idx] += (nnei - breakpoint) * (em[em_index] * res_grad * dz_xx + dz_dy_dem[em_index] * res); + int table_idx = 0; + locate_xx_se_t(xx, table_idx, lower, upper, -max, max, stride0, stride1); + var[0] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 0]; + var[1] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 1]; + var[2] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 2]; + var[3] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 3]; + var[4] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 4]; + var[5] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 5]; + FPTYPE res = var[0] + (var[1] + (var[2] + (var[3] + (var[4] + var[5] * xx) * xx) * xx) * xx) * xx; + FPTYPE res_grad = var[1] + (2 * var[2] + (3 * var[3] + (4 * var[4] + 5 * var[5] * xx) * xx) * xx) * xx; + + sum += (tmp * res_grad * dz_xx + dz_em * res); + if (unloop) break; } - if (unloop) break; - } - for (int ii = 0; ii < MTILE; ii++) { - dz_dy[block_idx * MTILE * last_layer_size + ii * last_layer_size + thread_idx] = iteratorC[ii * last_layer_size + thread_idx]; } + dz_dy[block_idx * last_layer_size + thread_idx] = sum; } namespace deepmd { @@ -604,7 +598,8 @@ void tabulate_fusion_se_t_grad_grad_gpu_cuda( DPErrcheck(cudaMemset( dz_dy, 0.0, sizeof(FPTYPE) * nloc * last_layer_size)); - tabulate_fusion_se_t_grad_grad_fifth_order_polynomial <<>>( + + tabulate_fusion_se_t_grad_grad_fifth_order_polynomial <<>>( dz_dy, table, em_x, em, dz_dy_dem_x, dz_dy_dem, table_info[0], table_info[1], table_info[2], table_info[3], table_info[4], nnei_i, nnei_j, last_layer_size); DPErrcheck(cudaGetLastError()); diff --git a/source/lib/src/rocm/tabulate.hip.cu b/source/lib/src/rocm/tabulate.hip.cu index 055d52d7b8..6b6270c18f 100644 --- a/source/lib/src/rocm/tabulate.hip.cu +++ b/source/lib/src/rocm/tabulate.hip.cu @@ -430,45 +430,39 @@ __global__ void tabulate_fusion_se_t_grad_grad_fifth_order_polynomial( const int nnei_j, const int last_layer_size) { - extern __shared__ int _data[]; - const int block_idx = blockIdx.x; // nloc + const int block_idx = blockIdx.x; // nloc const int thread_idx = threadIdx.x; // last_layer_size - FPTYPE ago = __shfl( em_x[block_idx * nnei + nnei - 1], 0); - bool unloop = false; - int breakpoint = nnei - 1; - FPTYPE * iteratorC = (FPTYPE*) &_data[0]; - for (int kk = 0; kk < MTILE; kk++) - iteratorC[kk * last_layer_size + thread_idx] = 0.f; - __syncthreads(); - for (int ii = 0; ii < nnei; ii++) { - FPTYPE var[6]; - FPTYPE xx = em_x[block_idx * nnei + ii]; - FPTYPE dz_xx = dz_dy_dem_x[block_idx * nnei + ii]; - if (xx == ago) { - unloop = true; - breakpoint = ii; - } - int table_idx = 0; - locate_xx(xx, table_idx, lower, upper, max, stride0, stride1); - var[0] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 0]; - var[1] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 1]; - var[2] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 2]; - var[3] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 3]; - var[4] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 4]; - var[5] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 5]; - FPTYPE res = var[0] + (var[1] + (var[2] + (var[3] + (var[4] + var[5] * xx) * xx) * xx) * xx) * xx; - FPTYPE res_grad = var[1] + (2 * var[2] + (3 * var[3] + (4 * var[4] + 5 * var[5] * xx) * xx) * xx) * xx; + FPTYPE sum = 0.f; + for (int ii = 0; ii < nnei_i; ii++) { + FPTYPE ago = __shfl(em_x[block_idx * nnei_i * nnei_j + ii * nnei_j + nnei_j - 1], 0); + bool unloop = false; + for (int jj = 0; ii < nnei_j; jj++) { + FPTYPE xx = em_x[block_idx * nnei_i * nnei_j + ii * nnei_j + jj]; + FPTYPE tmp = xx; + FPTYPE dz_xx = dz_dy_dem_x[block_idx * nnei_i * nnei_j + ii * nnei_j + jj]; + FPTYPE dz_em = dz_dy_dem[block_idx * nnei_i * nnei_j + ii * nnei_j + jj]; + FPTYPE var[6]; + if (ago == xx) { + unloop = true; + } - for (int kk = 0; kk < MTILE; kk++) { - int em_index = block_idx * nnei * MTILE + ii * MTILE + kk; - iteratorC[kk * last_layer_size + thread_idx] += (nnei - breakpoint) * (em[em_index] * res_grad * dz_xx + dz_dy_dem[em_index] * res); + int table_idx = 0; + locate_xx_se_t(xx, table_idx, lower, upper, -max, max, stride0, stride1); + var[0] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 0]; + var[1] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 1]; + var[2] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 2]; + var[3] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 3]; + var[4] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 4]; + var[5] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 5]; + FPTYPE res = var[0] + (var[1] + (var[2] + (var[3] + (var[4] + var[5] * xx) * xx) * xx) * xx) * xx; + FPTYPE res_grad = var[1] + (2 * var[2] + (3 * var[3] + (4 * var[4] + 5 * var[5] * xx) * xx) * xx) * xx; + + sum += (tmp * res_grad * dz_xx + dz_em * res); + if (unloop) break; } - if (unloop) break; - } - for (int ii = 0; ii < MTILE; ii++) { - dz_dy[block_idx * MTILE * last_layer_size + ii * last_layer_size + thread_idx] = iteratorC[ii * last_layer_size + thread_idx]; } + dz_dy[block_idx * last_layer_size + thread_idx] = sum; } namespace deepmd { @@ -610,7 +604,7 @@ void tabulate_fusion_se_t_grad_grad_gpu_rocm( DPErrcheck(hipMemset( dz_dy, 0.0, sizeof(FPTYPE) * nloc * last_layer_size)); - hipLaunchKernelGGL(HIP_KERNEL_NAME(tabulate_fusion_se_t_grad_grad_fifth_order_polynomial), nloc, last_layer_size, sizeof(FPTYPE) * last_layer_size, 0, + hipLaunchKernelGGL(HIP_KERNEL_NAME(tabulate_fusion_se_t_grad_grad_fifth_order_polynomial), nloc, last_layer_size, 0, 0, dz_dy, table, em_x, em, dz_dy_dem_x, dz_dy_dem, table_info[0], table_info[1], table_info[2], table_info[3], table_info[4], nnei_i, nnei_j, last_layer_size); DPErrcheck(hipGetLastError()); diff --git a/source/lib/src/tabulate.cc b/source/lib/src/tabulate.cc index ffacb96fdb..840284b93b 100644 --- a/source/lib/src/tabulate.cc +++ b/source/lib/src/tabulate.cc @@ -422,11 +422,11 @@ void deepmd::tabulate_fusion_se_t_grad_grad_cpu( const FPTYPE * dz_dy_dem_x, const FPTYPE * dz_dy_dem, const int nloc, - const int nnei, + const int nnei_i, const int nnei_j, const int last_layer_size) { - memset(dz_dy, 0.0, sizeof(FPTYPE) * nloc * 4 * last_layer_size); + memset(dz_dy, 0.0, sizeof(FPTYPE) * nloc * last_layer_size); const FPTYPE lower = table_info[0]; const FPTYPE upper = table_info[1]; const FPTYPE _max = table_info[2]; @@ -436,49 +436,34 @@ void deepmd::tabulate_fusion_se_t_grad_grad_cpu( // FPTYPE * res = new FPTYPE[4 * last_layer_size]; #pragma omp parallel for for (int ii = 0; ii < nloc; ii++) { - FPTYPE ll[4]; - FPTYPE hh[4]; - FPTYPE ago = em_x[ii * nnei + nnei - 1]; - bool unloop = false; - for (int jj = 0; jj < nnei; jj++) { - ll[0] = em[ii * nnei * 4 + jj * 4 + 0]; - ll[1] = em[ii * nnei * 4 + jj * 4 + 1]; - ll[2] = em[ii * nnei * 4 + jj * 4 + 2]; - ll[3] = em[ii * nnei * 4 + jj * 4 + 3]; - hh[0] = dz_dy_dem[ii * nnei * 4 + jj * 4 + 0]; - hh[1] = dz_dy_dem[ii * nnei * 4 + jj * 4 + 1]; - hh[2] = dz_dy_dem[ii * nnei * 4 + jj * 4 + 2]; - hh[3] = dz_dy_dem[ii * nnei * 4 + jj * 4 + 3]; - FPTYPE xx = em_x[ii * nnei + jj]; - FPTYPE dz_xx = dz_dy_dem_x[ii * nnei + jj]; - if (ago == xx) { - unloop = true; - } - int table_idx = 0; - locate_xx(lower, upper, _max, stride0, stride1, xx, table_idx); - for (int kk = 0; kk < last_layer_size; kk++) { - FPTYPE a0 = table[table_idx * last_layer_size * 6 + 6 * kk + 0]; - FPTYPE a1 = table[table_idx * last_layer_size * 6 + 6 * kk + 1]; - FPTYPE a2 = table[table_idx * last_layer_size * 6 + 6 * kk + 2]; - FPTYPE a3 = table[table_idx * last_layer_size * 6 + 6 * kk + 3]; - FPTYPE a4 = table[table_idx * last_layer_size * 6 + 6 * kk + 4]; - FPTYPE a5 = table[table_idx * last_layer_size * 6 + 6 * kk + 5]; - FPTYPE var = a0 + (a1 + (a2 + (a3 + (a4 + a5 * xx) * xx) * xx) * xx) * xx; - FPTYPE var_grad = a1 + (2 * a2 + (3 * a3 + (4 * a4 + 5 * a5 * xx) * xx) * xx) * xx; - if (unloop) { - dz_dy[ii * last_layer_size * 4 + 0 * last_layer_size + kk] += (nnei - jj) * (var * hh[0] + dz_xx * var_grad * ll[0]); - dz_dy[ii * last_layer_size * 4 + 1 * last_layer_size + kk] += (nnei - jj) * (var * hh[1] + dz_xx * var_grad * ll[1]); - dz_dy[ii * last_layer_size * 4 + 2 * last_layer_size + kk] += (nnei - jj) * (var * hh[2] + dz_xx * var_grad * ll[2]); - dz_dy[ii * last_layer_size * 4 + 3 * last_layer_size + kk] += (nnei - jj) * (var * hh[3] + dz_xx * var_grad * ll[3]); + for (int jj = 0; jj < nnei_i; jj++) { + FPTYPE ago = em_x[ii * nnei_i * nnei_j + jj * nnei_j + nnei_j - 1]; + bool unloop = false; + for (int kk = 0; kk < nnei_j; kk++) { + FPTYPE xx = em_x[ii * nnei_i * nnei_j + jj * nnei_j + kk]; + FPTYPE tmp = xx; + FPTYPE dz_em = dz_dy_dem [ii * nnei_i * nnei_j + jj * nnei_j + kk]; + FPTYPE dz_xx = dz_dy_dem_x[ii * nnei_i * nnei_j + jj * nnei_j + kk]; + + if (ago == xx) { + unloop = true; } - else { - dz_dy[ii * last_layer_size * 4 + 0 * last_layer_size + kk] += var * hh[0] + dz_xx * var_grad * ll[0]; - dz_dy[ii * last_layer_size * 4 + 1 * last_layer_size + kk] += var * hh[1] + dz_xx * var_grad * ll[1]; - dz_dy[ii * last_layer_size * 4 + 2 * last_layer_size + kk] += var * hh[2] + dz_xx * var_grad * ll[2]; - dz_dy[ii * last_layer_size * 4 + 3 * last_layer_size + kk] += var * hh[3] + dz_xx * var_grad * ll[3]; + int table_idx = 0; + locate_xx_se_t(lower, upper, -_max, _max, stride0, stride1, xx, table_idx); + for (int mm = 0; mm < last_layer_size; mm++) { + FPTYPE a0 = table[table_idx * last_layer_size * 6 + 6 * mm + 0]; + FPTYPE a1 = table[table_idx * last_layer_size * 6 + 6 * mm + 1]; + FPTYPE a2 = table[table_idx * last_layer_size * 6 + 6 * mm + 2]; + FPTYPE a3 = table[table_idx * last_layer_size * 6 + 6 * mm + 3]; + FPTYPE a4 = table[table_idx * last_layer_size * 6 + 6 * mm + 4]; + FPTYPE a5 = table[table_idx * last_layer_size * 6 + 6 * mm + 5]; + FPTYPE var = a0 + (a1 + (a2 + (a3 + (a4 + a5 * xx) * xx) * xx) * xx) * xx; + FPTYPE var_grad = a1 + (2 * a2 + (3 * a3 + (4 * a4 + 5 * a5 * xx) * xx) * xx) * xx; + + dz_dy[ii * last_layer_size + mm] += var * dz_em + dz_xx * var_grad * tmp; } + if (unloop) break; } - if (unloop) break; } } } diff --git a/source/op/tabulate_multi_device.cc b/source/op/tabulate_multi_device.cc index c19c88e48b..aba6efc5b5 100644 --- a/source/op/tabulate_multi_device.cc +++ b/source/op/tabulate_multi_device.cc @@ -472,10 +472,10 @@ class TabulateFusionSeTGradGradOp : public OpKernel { const FPTYPE * em = em_tensor.flat().data(); const FPTYPE * dz_dy_dem_x = dz_dy_dem_x_tensor.flat().data(); const FPTYPE * dz_dy_dem = dz_dy_dem_tensor.flat().data(); - const int nloc = em_tensor.shape().dim_size(0); + const int nloc = em_tensor.shape().dim_size(0); const int nnei_i = em_tensor.shape().dim_size(1); const int nnei_j = em_tensor.shape().dim_size(2); - const int last_layer_size = descriptor_tensor.shape().dim_size(2); + const int last_layer_size = descriptor_tensor.shape().dim_size(1); if (device == "GPU") { #if GOOGLE_CUDA From d57a53a2161f7c8933b542898be357f24998d65d Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Thu, 28 Oct 2021 19:50:41 -0400 Subject: [PATCH 101/161] deprecate `numb_test` (#1249) * deprecate `numb_test` See #1243. * bugfix --- deepmd/utils/argcheck.py | 2 -- deepmd/utils/compat.py | 46 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/deepmd/utils/argcheck.py b/deepmd/utils/argcheck.py index db31469c29..36e9eb2ee6 100644 --- a/deepmd/utils/argcheck.py +++ b/deepmd/utils/argcheck.py @@ -605,7 +605,6 @@ def training_args(): # ! modified by Ziyao: data configuration isolated. doc_seed = 'The random seed for getting frames from the training data set.' doc_disp_file = 'The file for printing learning curve.' doc_disp_freq = 'The frequency of printing learning curve.' - doc_numb_test = 'Number of frames used for the test during training.' doc_save_freq = 'The frequency of saving check point.' doc_save_ckpt = 'The file name of saving check point.' doc_disp_training = 'Displaying verbose information during training.' @@ -626,7 +625,6 @@ def training_args(): # ! modified by Ziyao: data configuration isolated. Argument("seed", [int,None], optional=True, doc=doc_seed), Argument("disp_file", str, optional=True, default='lcurve.out', doc=doc_disp_file), Argument("disp_freq", int, optional=True, default=1000, doc=doc_disp_freq), - Argument("numb_test", [list,int,str], optional=True, default=1, doc=doc_numb_test), Argument("save_freq", int, optional=True, default=1000, doc=doc_save_freq), Argument("save_ckpt", str, optional=True, default='model.ckpt', doc=doc_save_ckpt), Argument("disp_training", bool, optional=True, default=True, doc=doc_disp_training), diff --git a/deepmd/utils/compat.py b/deepmd/utils/compat.py index 66387bf0b8..bae778e426 100644 --- a/deepmd/utils/compat.py +++ b/deepmd/utils/compat.py @@ -316,6 +316,44 @@ def _warning_input_v1_v2(fname: Optional[Union[str, Path]]): warnings.warn(msg) +def deprecate_numb_test(jdata: Dict[str, Any], + warning: bool = True, + dump: Optional[Union[str, Path]] = None) -> Dict[str, Any]: + """Deprecate `numb_test` since v2.1. It has taken no effect since v2.0. + + See `#1243 `_. + + Parameters + ---------- + jdata : Dict[str, Any] + loaded json/yaml file + warning : bool, optional + whether to show deprecation warning, by default True + dump : Optional[Union[str, Path]], optional + whether to dump converted file, by default None + + Returns + ------- + Dict[str, Any] + converted output + """ + try: + jdata.get("training", {}).pop("numb_test") + except KeyError: + pass + else: + if warning: + warnings.warn( + "The argument training->numb_test has been deprecated since v2.0.0. " + "Use training->validation_data->batch_size instead." + ) + + if dump is not None: + with open(dump, "w") as fp: + json.dump(jdata, fp, indent=4) + return jdata + + def updata_deepmd_input(jdata: Dict[str, Any], warning: bool = True, dump: Optional[Union[str, Path]] = None) -> Dict[str, Any]: @@ -327,10 +365,12 @@ def is_deepmd_v1_input(jdata): if is_deepmd_v0_input(jdata): jdata = convert_input_v0_v1(jdata, warning, None) - jdata = convert_input_v1_v2(jdata, False, dump) + jdata = convert_input_v1_v2(jdata, False, None) + jdata = deprecate_numb_test(jdata, False, dump) elif is_deepmd_v1_input(jdata): - jdata = convert_input_v1_v2(jdata, warning, dump) + jdata = convert_input_v1_v2(jdata, warning, None) + jdata = deprecate_numb_test(jdata, False, dump) else: - pass + jdata = deprecate_numb_test(jdata, warning, dump) return jdata From 036265957e38ce758971001d8caaccf72815db82 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Fri, 29 Oct 2021 22:04:50 -0400 Subject: [PATCH 102/161] fix Python bugs of loc_frame descriptor (#1253) Fix #1248 (only the first part). --- deepmd/descriptor/loc_frame.py | 1 + deepmd/entrypoints/train.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/deepmd/descriptor/loc_frame.py b/deepmd/descriptor/loc_frame.py index a664384ba6..3a178ff494 100644 --- a/deepmd/descriptor/loc_frame.py +++ b/deepmd/descriptor/loc_frame.py @@ -9,6 +9,7 @@ from deepmd.utils.sess import run_sess from .descriptor import Descriptor +@Descriptor.register("loc_frame") class DescrptLocFrame (Descriptor) : """Defines a local frame at each atom, and the compute the descriptor as local coordinates under this frame. diff --git a/deepmd/entrypoints/train.py b/deepmd/entrypoints/train.py index 98090c18af..f3a9faca46 100755 --- a/deepmd/entrypoints/train.py +++ b/deepmd/entrypoints/train.py @@ -337,7 +337,7 @@ def update_sel(jdata): if descrpt_data['type'] == 'hybrid': for ii in range(len(descrpt_data['list'])): descrpt_data['list'][ii] = update_one_sel(jdata, descrpt_data['list'][ii]) - else: + elif descrpt_data['type'] != 'loc_frame': descrpt_data = update_one_sel(jdata, descrpt_data) jdata['model']['descriptor'] = descrpt_data return jdata From 09d13915488fb7abb707eb644b89e69f67f32af3 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Wed, 3 Nov 2021 07:30:28 +0800 Subject: [PATCH 103/161] fix bug of loc_frame descriptor when using lammps (#1255) * fix bug of loc_frame descriptor, due to the change of nei list convention introduced in v2 * fix change of format Co-authored-by: Han Wang --- source/op/descrpt.cc | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/source/op/descrpt.cc b/source/op/descrpt.cc index 7fdf81d986..3731804fef 100644 --- a/source/op/descrpt.cc +++ b/source/op/descrpt.cc @@ -213,22 +213,20 @@ class DescrptOp : public OpKernel { std::vector nlist_map; bool b_nlist_map = false; if (nei_mode == 3) { - int * pilist, *pjrange, *pjlist; + int *pilist, *pnumneigh, **pfirstneigh; memcpy (&pilist, &mesh(4), sizeof(int *)); - memcpy (&pjrange, &mesh(8), sizeof(int *)); - memcpy (&pjlist, &mesh(12), sizeof(int *)); + memcpy (&pnumneigh, &mesh(8), sizeof(int *)); + memcpy (&pfirstneigh, &mesh(12), sizeof(int **)); int inum = mesh(1); assert (inum == nloc); d_nlist_a.resize (inum); d_nlist_r.resize (inum); - for (unsigned ii = 0; ii < inum; ++ii){ - d_nlist_r.reserve (pjrange[inum] / inum + 10); - } for (unsigned ii = 0; ii < inum; ++ii){ int i_idx = pilist[ii]; - for (unsigned jj = pjrange[ii]; jj < pjrange[ii+1]; ++jj){ - int j_idx = pjlist[jj]; - d_nlist_r[i_idx].push_back (j_idx); + d_nlist_r[i_idx].reserve(pnumneigh[ii]); + for (unsigned jj = 0; jj < pnumneigh[ii]; ++jj){ + int j_idx = pfirstneigh[ii][jj]; + d_nlist_r[i_idx].push_back(j_idx); } } } From 4e15b0d14ca122d5f4c1a93f5fb3ca7569c206cb Mon Sep 17 00:00:00 2001 From: ZhengdQin <46387172+ZhengdQin@users.noreply.github.com> Date: Thu, 4 Nov 2021 08:25:31 +0800 Subject: [PATCH 104/161] Update transfer.py (#1246) fix the np.frombuffer in dp transfer --- deepmd/entrypoints/transfer.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/deepmd/entrypoints/transfer.py b/deepmd/entrypoints/transfer.py index 47509551a8..85664ac83c 100644 --- a/deepmd/entrypoints/transfer.py +++ b/deepmd/entrypoints/transfer.py @@ -130,8 +130,9 @@ def transform_graph(raw_graph: tf.Graph, old_graph: tf.Graph) -> tf.Graph: if raw_graph_dtype == np.float16: if old_graph_dtype == np.float64 or old_graph_dtype == np.float32: if (len(tensor_shape) != 1) or (tensor_shape[0] != 1): - tensor = np.frombuffer(old_node.tensor_content, dtype = raw_graph_dtype) - cp_attr.from_array(tensor, tf.float16, shape = tensor_shape) + tensor = np.frombuffer(old_node.tensor_content, dtype = old_graph_dtype) + tensor = tensor.astype(raw_graph_dtype) + cp_attr.from_str(tensor) else: tensor = load_tensor(old_node, old_graph_dtype, raw_graph_dtype) cp_attr.from_array(tensor, tf.float16, [1]) @@ -143,7 +144,8 @@ def transform_graph(raw_graph: tf.Graph, old_graph: tf.Graph) -> tf.Graph: elif raw_graph_dtype == np.float64 or raw_graph_dtype == np.float32: if old_graph_dtype == np.float64 or old_graph_dtype == np.float32: if (len(tensor_shape) != 1) or (tensor_shape[0] != 1): - tensor = np.frombuffer(old_node.tensor_content, dtype = raw_graph_dtype) + tensor = np.frombuffer(old_node.tensor_content, dtype = old_graph_dtype) + tensor = tensor.astype(raw_graph_dtype) cp_attr.from_str(tensor) else: tensor = load_tensor(old_node, old_graph_dtype, raw_graph_dtype) From 36275c81adfe3078364f7deded6e41782ee5e19b Mon Sep 17 00:00:00 2001 From: Denghui Lu Date: Wed, 10 Nov 2021 10:47:08 +0800 Subject: [PATCH 105/161] Accelerate model compression (#1274) * accelerate model compression * remove redundancy --- source/lib/src/cuda/tabulate.cu | 112 +++++++++++++++++--------------- 1 file changed, 61 insertions(+), 51 deletions(-) diff --git a/source/lib/src/cuda/tabulate.cu b/source/lib/src/cuda/tabulate.cu index 4cc6b112ee..2037764c03 100644 --- a/source/lib/src/cuda/tabulate.cu +++ b/source/lib/src/cuda/tabulate.cu @@ -9,7 +9,7 @@ template __forceinline__ __device__ -void locate_xx( +void locate_xx_se_a( FPTYPE& xx, int& table_idx, const FPTYPE& lower, @@ -73,6 +73,24 @@ void locate_xx_se_t( } } +template +__forceinline__ __device__ +void load_polynomial_params( + FPTYPE var[6], + const FPTYPE* table, + const int& table_idx, + const int& idx, + const int& last_layer_size) +{ + var[0] = table[table_idx * last_layer_size * 6 + idx * 6 + 0]; + var[1] = table[table_idx * last_layer_size * 6 + idx * 6 + 1]; + var[2] = table[table_idx * last_layer_size * 6 + idx * 6 + 2]; + var[3] = table[table_idx * last_layer_size * 6 + idx * 6 + 3]; + var[4] = table[table_idx * last_layer_size * 6 + idx * 6 + 4]; + var[5] = table[table_idx * last_layer_size * 6 + idx * 6 + 5]; +} + + template __forceinline__ __device__ FPTYPE dot( @@ -116,27 +134,26 @@ __global__ void tabulate_fusion_se_a_fifth_order_polynomial( int breakpoint = nnei - 1; FPTYPE sum[MTILE] = {0.f}; + int mark_table_idx = 0; + FPTYPE var[6]; for (int ii = 0; ii < nnei; ii++) { - FPTYPE var[6]; FPTYPE xx = em_x[block_idx * nnei + ii]; if (xx == ago) { unloop = true; breakpoint = ii; } int table_idx = 0; - locate_xx(xx, table_idx, lower, upper, max, stride0, stride1); - var[0] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 0]; - var[1] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 1]; - var[2] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 2]; - var[3] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 3]; - var[4] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 4]; - var[5] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 5]; + locate_xx_se_a(xx, table_idx, lower, upper, max, stride0, stride1); + if (table_idx != mark_table_idx) { + load_polynomial_params(var, table, table_idx, thread_idx, last_layer_size); + } FPTYPE res = var[0] + (var[1] + (var[2] + (var[3] + (var[4] + var[5] * xx) * xx) * xx) * xx) * xx; for (int kk = 0; kk < MTILE; kk++) { sum[kk] += (nnei - breakpoint) * em[block_idx * nnei * MTILE + ii * MTILE + kk] * res; } if (unloop) break; + mark_table_idx = table_idx; } for (int ii = 0; ii < MTILE; ii++) { out[block_idx * MTILE * last_layer_size + ii * last_layer_size + thread_idx] = sum[ii]; @@ -185,27 +202,28 @@ __global__ void tabulate_fusion_se_a_grad_fifth_order_polynomial( } int table_idx = 0; - locate_xx(xx, table_idx, lower, upper, max, stride0, stride1); - FPTYPE sum[MTILE] = {0.f}; + FPTYPE reg_em[MTILE] = { + em[block_idx * nnei * MTILE + ii * 4 + 0], + em[block_idx * nnei * MTILE + ii * 4 + 1], + em[block_idx * nnei * MTILE + ii * 4 + 2], + em[block_idx * nnei * MTILE + ii * 4 + 3] + }; FPTYPE Csub = 0.f; + FPTYPE sum[MTILE] = {0.f}; + locate_xx_se_a(xx, table_idx, lower, upper, max, stride0, stride1); + + FPTYPE var[6]; for (int jj = lane_idx; jj < last_layer_size; jj += WARP_SIZE) { - FPTYPE var[6]; - // load iteratorB through table - var[0] = table[table_idx * last_layer_size * 6 + 6 * jj + 0]; - var[1] = table[table_idx * last_layer_size * 6 + 6 * jj + 1]; - var[2] = table[table_idx * last_layer_size * 6 + 6 * jj + 2]; - var[3] = table[table_idx * last_layer_size * 6 + 6 * jj + 3]; - var[4] = table[table_idx * last_layer_size * 6 + 6 * jj + 4]; - var[5] = table[table_idx * last_layer_size * 6 + 6 * jj + 5]; + load_polynomial_params(var, table, table_idx, jj, last_layer_size); FPTYPE res = var[0] + (var[1] + (var[2] + (var[3] + (var[4] + var[5] * xx) * xx) * xx) * xx) * xx; for (int kk = 0; kk < MTILE; kk++) { sum[kk] += (nnei - breakpoint) * iteratorA[kk * last_layer_size + jj] * res; } - res = em[block_idx * nnei * MTILE + ii * 4 + 0] * iteratorA[0 * last_layer_size + jj]; - res += em[block_idx * nnei * MTILE + ii * 4 + 1] * iteratorA[1 * last_layer_size + jj]; - res += em[block_idx * nnei * MTILE + ii * 4 + 2] * iteratorA[2 * last_layer_size + jj]; - res += em[block_idx * nnei * MTILE + ii * 4 + 3] * iteratorA[3 * last_layer_size + jj]; + res = reg_em[0] * iteratorA[0 * last_layer_size + jj]; + res += reg_em[1] * iteratorA[1 * last_layer_size + jj]; + res += reg_em[2] * iteratorA[2 * last_layer_size + jj]; + res += reg_em[3] * iteratorA[3 * last_layer_size + jj]; Csub += (nnei - breakpoint) * (var[1] + (2 * var[2] + (3 * var[3] + (4 * var[4] + 5 * var[5] * xx) * xx) * xx) * xx) * res; } __syncwarp(); @@ -253,8 +271,9 @@ __global__ void tabulate_fusion_se_a_grad_grad_fifth_order_polynomial( iteratorC[kk * last_layer_size + thread_idx] = 0.f; __syncthreads(); + int mark_table_idx = -1; + FPTYPE var[6]; for (int ii = 0; ii < nnei; ii++) { - FPTYPE var[6]; FPTYPE xx = em_x[block_idx * nnei + ii]; FPTYPE dz_xx = dz_dy_dem_x[block_idx * nnei + ii]; if (xx == ago) { @@ -262,13 +281,11 @@ __global__ void tabulate_fusion_se_a_grad_grad_fifth_order_polynomial( breakpoint = ii; } int table_idx = 0; - locate_xx(xx, table_idx, lower, upper, max, stride0, stride1); - var[0] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 0]; - var[1] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 1]; - var[2] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 2]; - var[3] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 3]; - var[4] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 4]; - var[5] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 5]; + locate_xx_se_a(xx, table_idx, lower, upper, max, stride0, stride1); + if (table_idx != mark_table_idx) { + load_polynomial_params(var, table, table_idx, thread_idx, last_layer_size); + } + FPTYPE res = var[0] + (var[1] + (var[2] + (var[3] + (var[4] + var[5] * xx) * xx) * xx) * xx) * xx; FPTYPE res_grad = var[1] + (2 * var[2] + (3 * var[3] + (4 * var[4] + 5 * var[5] * xx) * xx) * xx) * xx; @@ -276,6 +293,7 @@ __global__ void tabulate_fusion_se_a_grad_grad_fifth_order_polynomial( int em_index = block_idx * nnei * MTILE + ii * MTILE + kk; iteratorC[kk * last_layer_size + thread_idx] += (nnei - breakpoint) * (em[em_index] * res_grad * dz_xx + dz_dy_dem[em_index] * res); } + mark_table_idx = table_idx; if (unloop) break; } for (int ii = 0; ii < MTILE; ii++) { @@ -309,6 +327,8 @@ __global__ void tabulate_fusion_se_t_fifth_order_polynomial( FPTYPE ago = __shfl_sync(0xffffffff, em_x[block_idx * nnei_i * nnei_j + ii * nnei_j + nnei_j - 1], 0); int breakpoint = nnei_j - 1; bool unloop = false; + FPTYPE var[6]; + int mark_table_idx = -1; for (int jj = 0; jj < nnei_j; jj++) { FPTYPE xx = em_x[block_idx * nnei_i * nnei_j + ii * nnei_j + jj]; FPTYPE tmp = xx; @@ -318,16 +338,13 @@ __global__ void tabulate_fusion_se_t_fifth_order_polynomial( } int table_idx = 0; locate_xx_se_t(xx, table_idx, lower, upper, -max, max, stride0, stride1); - FPTYPE var[6]; - var[0] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 0]; - var[1] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 1]; - var[2] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 2]; - var[3] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 3]; - var[4] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 4]; - var[5] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 5]; + if (table_idx != mark_table_idx) { + load_polynomial_params(var, table, table_idx, thread_idx, last_layer_size); + } FPTYPE res = var[0] + (var[1] + (var[2] + (var[3] + (var[4] + var[5] * xx) * xx) * xx) * xx) * xx; sum += (nnei_j - breakpoint) * tmp * res; + mark_table_idx = table_idx; if (unloop) break; } } @@ -380,13 +397,7 @@ __global__ void tabulate_fusion_se_t_grad_fifth_order_polynomial( FPTYPE Csub = 0.f; for (int kk = lane_idx; kk < last_layer_size; kk += WARP_SIZE) { FPTYPE var[6]; - // load iteratorB through table - var[0] = table[table_idx * last_layer_size * 6 + 6 * kk + 0]; - var[1] = table[table_idx * last_layer_size * 6 + 6 * kk + 1]; - var[2] = table[table_idx * last_layer_size * 6 + 6 * kk + 2]; - var[3] = table[table_idx * last_layer_size * 6 + 6 * kk + 3]; - var[4] = table[table_idx * last_layer_size * 6 + 6 * kk + 4]; - var[5] = table[table_idx * last_layer_size * 6 + 6 * kk + 5]; + load_polynomial_params(var, table, table_idx, kk, last_layer_size); FPTYPE res = var[0] + (var[1] + (var[2] + (var[3] + (var[4] + var[5] * xx) * xx) * xx) * xx) * xx; sum += iteratorA[kk] * res; @@ -431,6 +442,7 @@ __global__ void tabulate_fusion_se_t_grad_grad_fifth_order_polynomial( for (int ii = 0; ii < nnei_i; ii++) { FPTYPE ago = __shfl_sync(0xffffffff, em_x[block_idx * nnei_i * nnei_j + ii * nnei_j + nnei_j - 1], 0); bool unloop = false; + int mark_table_idx = -1; for (int jj = 0; ii < nnei_j; jj++) { FPTYPE xx = em_x[block_idx * nnei_i * nnei_j + ii * nnei_j + jj]; FPTYPE tmp = xx; @@ -443,16 +455,14 @@ __global__ void tabulate_fusion_se_t_grad_grad_fifth_order_polynomial( int table_idx = 0; locate_xx_se_t(xx, table_idx, lower, upper, -max, max, stride0, stride1); - var[0] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 0]; - var[1] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 1]; - var[2] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 2]; - var[3] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 3]; - var[4] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 4]; - var[5] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 5]; + if (table_idx != mark_table_idx) { + load_polynomial_params(var, table, table_idx, thread_idx, last_layer_size); + } FPTYPE res = var[0] + (var[1] + (var[2] + (var[3] + (var[4] + var[5] * xx) * xx) * xx) * xx) * xx; FPTYPE res_grad = var[1] + (2 * var[2] + (3 * var[3] + (4 * var[4] + 5 * var[5] * xx) * xx) * xx) * xx; sum += (tmp * res_grad * dz_xx + dz_em * res); + mark_table_idx = table_idx; if (unloop) break; } } From 43fc8b3b5c3260ed4193c6467b57ef91e99e749e Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Tue, 9 Nov 2021 21:47:42 -0500 Subject: [PATCH 106/161] Use c++14 for TF 2.7 (#1275) --- source/CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index f05ab8df15..a3d62c09b4 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -46,7 +46,6 @@ endif(GIT_FOUND) # global defines list (APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/) -set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-ignored-attributes") # model version file(READ ${PROJECT_SOURCE_DIR}/config/MODEL_VER MODEL_VERSION) @@ -81,6 +80,12 @@ endif (USE_ROCM_TOOLKIT) # find tensorflow, I need tf abi info find_package(tensorflow REQUIRED) +if (TENSORFLOW_VERSION GREATER_EQUAL 2.7) + set (CMAKE_CXX_STANDARD 14) +else() + set (CMAKE_CXX_STANDARD 11) +endif() + # find threads find_package(Threads) From 77ce65153c31650e0e199a64270197c524dabe71 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Wed, 10 Nov 2021 20:35:04 -0500 Subject: [PATCH 107/161] fix SyntaxWarning in graph.py (#1278) /home/runner/work/deepmd-kit/deepmd-kit/deepmd/utils/graph.py:156: SyntaxWarning: "is not" with a literal. Did you mean "!="? --- deepmd/utils/graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deepmd/utils/graph.py b/deepmd/utils/graph.py index 031454e4b9..e6ff47b21f 100644 --- a/deepmd/utils/graph.py +++ b/deepmd/utils/graph.py @@ -153,7 +153,7 @@ def get_embedding_net_nodes_from_graph_def(graph_def: tf.GraphDef, suffix: str = The embedding net nodes within the given tf.GraphDef object """ # embedding_net_pattern = f"filter_type_\d+{suffix}/matrix_\d+_\d+|filter_type_\d+{suffix}/bias_\d+_\d+|filter_type_\d+{suffix}/idt_\d+_\d+|filter_type_all{suffix}/matrix_\d+_\d+|filter_type_all{suffix}/matrix_\d+_\d+_\d+|filter_type_all{suffix}/bias_\d+_\d+|filter_type_all{suffix}/bias_\d+_\d+_\d+|filter_type_all{suffix}/idt_\d+_\d+" - if suffix is not "": + if suffix != "": embedding_net_pattern = EMBEDDING_NET_PATTERN\ .replace('/idt', suffix + '/idt')\ .replace('/bias', suffix + '/bias')\ From abec07e2ebde5a1cd8c534139329778b0f276546 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Thu, 11 Nov 2021 18:45:09 -0500 Subject: [PATCH 108/161] add a citation badge (#1280) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 177cb5071f..6e9d43648b 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ ======== [![GitHub release](https://img.shields.io/github/release/deepmodeling/deepmd-kit.svg?maxAge=86400)](https://github.com/deepmodeling/deepmd-kit/releases) [![doi:10.1016/j.cpc.2018.03.016](https://img.shields.io/badge/DOI-10.1016%2Fj.cpc.2018.03.016-blue)](https://doi.org/10.1016/j.cpc.2020.107206) +![Citations](https://citations.njzjz.win/10.1016/j.cpc.2018.03.016) [![offline packages](https://img.shields.io/github/downloads/deepmodeling/deepmd-kit/total?label=offline%20packages)](https://github.com/deepmodeling/deepmd-kit/releases) [![conda install](https://img.shields.io/badge/downloads-9k%20total-green.svg?style=round-square&label=conda%20install)](https://anaconda.org/deepmodeling/deepmd-kit) [![pip install](https://img.shields.io/pypi/dm/deepmd-kit?label=pip%20install)](https://pypi.org/project/deepmd-kit) From 4af4ea5d964769d2b07ffd471abb0e8518f9824d Mon Sep 17 00:00:00 2001 From: liangadam <45301969+liangadam@users.noreply.github.com> Date: Mon, 15 Nov 2021 08:40:05 +0800 Subject: [PATCH 109/161] Added all activation functions for model compression. (#1283) * Improved activation function. * Improved activation function. * Added description of available activation functions. * Added description of available activation functions. * Added description of available activation functions. * Added description of available activation functions. * Update train-input.rst * Update train-input.rst * Update train-input.rst * Added description of available activation functions. * Added description of available activation functions. * Update train-input.rst * Added description of available activation functions. * Added description of available activation functions. * Added description of available activation functions. * Added description of available activation functions. * Update train-input.rst --- deepmd/utils/tabulate.py | 8 ++++++ doc/freeze/compress.md | 11 ++++++++ source/op/unaggregated_grad.cc | 46 ++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/deepmd/utils/tabulate.py b/deepmd/utils/tabulate.py index f0dc571142..1e2cdcd40c 100644 --- a/deepmd/utils/tabulate.py +++ b/deepmd/utils/tabulate.py @@ -68,6 +68,14 @@ def __init__(self, self.functype = 1 elif activation_fn == ACTIVATION_FN_DICT["gelu"]: self.functype = 2 + elif activation_fn == ACTIVATION_FN_DICT["relu"]: + self.functype = 3 + elif activation_fn == ACTIVATION_FN_DICT["relu6"]: + self.functype = 4 + elif activation_fn == ACTIVATION_FN_DICT["softplus"]: + self.functype = 5 + elif activation_fn == ACTIVATION_FN_DICT["sigmoid"]: + self.functype = 6 else: raise RuntimeError("Unknown actication function type!") self.activation_fn = activation_fn diff --git a/doc/freeze/compress.md b/doc/freeze/compress.md index ed55db0c4b..362ae22840 100644 --- a/doc/freeze/compress.md +++ b/doc/freeze/compress.md @@ -83,3 +83,14 @@ The model compression interface requires the version of deepmd-kit used in origi **Acceptable descriptor type** Note only descriptors with `se_e2_a` or `se_e3` type are supported by the model compression feature. Hybrid mixed with above descriptors is also supported. + + +**Available activation functions for descriptor:** +- tanh +- gelu +- relu +- relu6 +- softplus +- sigmoid + + diff --git a/source/op/unaggregated_grad.cc b/source/op/unaggregated_grad.cc index 89c14a84fb..c5f872ab42 100644 --- a/source/op/unaggregated_grad.cc +++ b/source/op/unaggregated_grad.cc @@ -52,6 +52,36 @@ FPTYPE grad(const FPTYPE xbar, const FPTYPE y, const int functype) //functype=t const FPTYPE var = tanh(SQRT_2_PI * (xbar + GGELU * xbar * xbar * xbar)); return 0.5 * SQRT_2_PI * xbar * (1 - var * var) * (3 * GGELU * xbar * xbar + 1) + 0.5 * var + 0.5; } + case 3: + { + if(xbar<=0) + { + return 0; + } + else + { + return 1; + } + } + case 4: + { + if(xbar<=0 || xbar>=6) + { + return 0; + } + else + { + return 1; + } + } + case 5: + { + return 1.0-1.0/(1.0+exp(xbar)); + } + case 6: + { + return y*(1-y); + } default: return -1; } @@ -71,6 +101,22 @@ FPTYPE grad_grad(const FPTYPE xbar, const FPTYPE y, const int functype) const FPTYPE var2 = SQRT_2_PI * (1 - var1 * var1) * (3 * GGELU * xbar * xbar + 1); return 3 * GGELU * SQRT_2_PI * xbar * xbar * (1 - var1 * var1) - SQRT_2_PI * xbar * var2 * (3 * GGELU * xbar * xbar + 1) * var1 + var2; } + case 3: + { + return 0; + } + case 4: + { + return 0; + } + case 5: + { + return exp(xbar)/((1+exp(xbar))*(1+exp(xbar))); + } + case 6: + { + return y*(1-y)*(1-2*y); + } default: return -1; } From 95e321f6d9651a4745b0f9f2e88d89c5dda5537d Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Thu, 18 Nov 2021 20:39:55 -0500 Subject: [PATCH 110/161] change googletest from master to main (#1292) See https://github.com/google/googletest/issues/3663 --- source/cmake/googletest.cmake.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/cmake/googletest.cmake.in b/source/cmake/googletest.cmake.in index c6247af53c..37b622bf30 100644 --- a/source/cmake/googletest.cmake.in +++ b/source/cmake/googletest.cmake.in @@ -5,7 +5,7 @@ project(googletest-download NONE) include(ExternalProject) ExternalProject_Add(googletest GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG master + GIT_TAG main SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src" BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build" CONFIGURE_COMMAND "" From b9e51e81596b21addab375cd7e8a58423c0dfed3 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sun, 21 Nov 2021 09:02:27 -0500 Subject: [PATCH 111/161] updata_deepmd_input when compress (#1297) This ensures the old model can be compressed by the new program. --- deepmd/entrypoints/compress.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deepmd/entrypoints/compress.py b/deepmd/entrypoints/compress.py index 8cb8070594..030224d7de 100644 --- a/deepmd/entrypoints/compress.py +++ b/deepmd/entrypoints/compress.py @@ -102,6 +102,7 @@ def compress( int(frequency), ] jdata["training"]["save_ckpt"] = "model-compression/model.ckpt" + jdata = updata_deepmd_input(jdata) jdata = normalize(jdata) # check the descriptor info of the input file @@ -146,4 +147,4 @@ def _check_compress_type(model_file): t_model_type = None if t_model_type == "compressed_model": - raise RuntimeError("The input frozen model %s has already been compressed! Please do not compress the model repeatedly. " % model_file) \ No newline at end of file + raise RuntimeError("The input frozen model %s has already been compressed! Please do not compress the model repeatedly. " % model_file) From 3b96011132517b505efc1c0391d2a2b05fdad1f0 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sun, 21 Nov 2021 19:43:00 -0500 Subject: [PATCH 112/161] update guidelines for the number of threads (#1291) * update guidelines for the number of threads Setting `OMP_NUM_THREADS` to 3 may be faster than 6. It needs to be confirmed. * add warnings if not adjusting threads --- deepmd/env.py | 8 ++++++++ doc/train/parallel-training.md | 2 ++ doc/train/training-advanced.md | 14 ++++++++++++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/deepmd/env.py b/deepmd/env.py index 6e6543697e..214302ab6b 100644 --- a/deepmd/env.py +++ b/deepmd/env.py @@ -126,6 +126,14 @@ def set_tf_default_nthreads(): `TF_INTRA_OP_PARALLELISM_THREADS` and `TF_INTER_OP_PARALLELISM_THREADS` control TF configuration of multithreading. """ + if "OMP_NUM_THREADS" not in os.environ or \ + "TF_INTRA_OP_PARALLELISM_THREADS" not in os.environ or \ + "TF_INTER_OP_PARALLELISM_THREADS" not in os.environ: + logging.warning( + "To get the best performance, it is recommended to adjust " + "the number of threads by setting the environment variables " + "OMP_NUM_THREADS, TF_INTRA_OP_PARALLELISM_THREADS, and " + "TF_INTER_OP_PARALLELISM_THREADS.") set_env_if_empty("TF_INTRA_OP_PARALLELISM_THREADS", "0", verbose=False) set_env_if_empty("TF_INTER_OP_PARALLELISM_THREADS", "0", verbose=False) diff --git a/doc/train/parallel-training.md b/doc/train/parallel-training.md index b252446971..7fecd364c2 100644 --- a/doc/train/parallel-training.md +++ b/doc/train/parallel-training.md @@ -39,6 +39,8 @@ CUDA_VISIBLE_DEVICES=4,5,6,7 horovodrun -np 4 \ Need to mention, environment variable `CUDA_VISIBLE_DEVICES` must be set to control parallelism on the occupied host where one process is bound to one GPU card. +Note that `OMP_NUM_THREADS`, `TF_INTRA_OP_PARALLELISM_THREADS`, and `TF_INTER_OP_PARALLELISM_THREADS` should be carefully adjusted to achieve the best performance. + When using MPI with Horovod, `horovodrun` is a simple wrapper around `mpirun`. In the case where fine-grained control over options passed to `mpirun`, [`mpirun` can be invoked directly](https://horovod.readthedocs.io/en/stable/mpi_include.html), and it will be detected automatically by Horovod, e.g., ```bash CUDA_VISIBLE_DEVICES=4,5,6,7 mpirun -l -launcher=fork -hosts=localhost -np 4 \ diff --git a/doc/train/training-advanced.md b/doc/train/training-advanced.md index ea9e1e8075..082569712f 100644 --- a/doc/train/training-advanced.md +++ b/doc/train/training-advanced.md @@ -111,16 +111,26 @@ optional arguments: **`--init-frz-model frozen_model.pb`**, initializes the training with an existing model that is stored in `frozen_model.pb`. -On some resources limited machines, one may want to control the number of threads used by DeePMD-kit. This is achieved by three environmental variables: `OMP_NUM_THREADS`, `TF_INTRA_OP_PARALLELISM_THREADS` and `TF_INTER_OP_PARALLELISM_THREADS`. `OMP_NUM_THREADS` controls the multithreading of DeePMD-kit implemented operations. `TF_INTRA_OP_PARALLELISM_THREADS` and `TF_INTER_OP_PARALLELISM_THREADS` controls `intra_op_parallelism_threads` and `inter_op_parallelism_threads`, which are Tensorflow configurations for multithreading. An explanation is found [here](https://stackoverflow.com/questions/41233635/meaning-of-inter-op-parallelism-threads-and-intra-op-parallelism-threads). +To get the best performance, one should control the number of threads used by DeePMD-kit. This is achieved by three environmental variables: `OMP_NUM_THREADS`, `TF_INTRA_OP_PARALLELISM_THREADS` and `TF_INTER_OP_PARALLELISM_THREADS`. `OMP_NUM_THREADS` controls the multithreading of DeePMD-kit implemented operations. `TF_INTRA_OP_PARALLELISM_THREADS` and `TF_INTER_OP_PARALLELISM_THREADS` controls `intra_op_parallelism_threads` and `inter_op_parallelism_threads`, which are Tensorflow configurations for multithreading. An explanation is found [here](https://www.intel.com/content/www/us/en/developer/articles/technical/maximize-tensorflow-performance-on-cpu-considerations-and-recommendations-for-inference.html). For example if you wish to use 3 cores of 2 CPUs on one node, you may set the environmental variables and run DeePMD-kit as follows: ```bash -export OMP_NUM_THREADS=6 +export OMP_NUM_THREADS=3 export TF_INTRA_OP_PARALLELISM_THREADS=3 export TF_INTER_OP_PARALLELISM_THREADS=2 dp train input.json ``` +For a node with 128 cores, it is recommended to start with the following variables: + +```bash +export OMP_NUM_THREADS=16 +export TF_INTRA_OP_PARALLELISM_THREADS=16 +export TF_INTER_OP_PARALLELISM_THREADS=8 +``` + +It is encouraged to adjust the configurations after empirical testing. + One can set other environmental variables: | Environment variables | Allowed value | Default value | Usage | From 9c517ebca47af22bf0500aea6f60b5e807e20192 Mon Sep 17 00:00:00 2001 From: Han Wang <92130845+wanghan-iapcm@users.noreply.github.com> Date: Mon, 22 Nov 2021 10:36:17 +0800 Subject: [PATCH 113/161] fix typo updata->update (#1301) Co-authored-by: Han Wang --- deepmd/entrypoints/compress.py | 4 ++-- deepmd/entrypoints/train.py | 4 ++-- deepmd/utils/compat.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/deepmd/entrypoints/compress.py b/deepmd/entrypoints/compress.py index 030224d7de..bb3f2241e9 100644 --- a/deepmd/entrypoints/compress.py +++ b/deepmd/entrypoints/compress.py @@ -8,7 +8,7 @@ from deepmd.common import j_loader from deepmd.env import tf, GLOBAL_ENER_FLOAT_PRECISION from deepmd.utils.argcheck import normalize -from deepmd.utils.compat import updata_deepmd_input +from deepmd.utils.compat import update_deepmd_input from deepmd.utils.errors import GraphTooLargeError, GraphWithoutTensorError from deepmd.utils.graph import get_tensor_by_name @@ -102,7 +102,7 @@ def compress( int(frequency), ] jdata["training"]["save_ckpt"] = "model-compression/model.ckpt" - jdata = updata_deepmd_input(jdata) + jdata = update_deepmd_input(jdata) jdata = normalize(jdata) # check the descriptor info of the input file diff --git a/deepmd/entrypoints/train.py b/deepmd/entrypoints/train.py index f3a9faca46..093c95fbff 100755 --- a/deepmd/entrypoints/train.py +++ b/deepmd/entrypoints/train.py @@ -16,7 +16,7 @@ from deepmd.train.trainer import DPTrainer from deepmd.utils import random as dp_random from deepmd.utils.argcheck import normalize -from deepmd.utils.compat import updata_deepmd_input +from deepmd.utils.compat import update_deepmd_input from deepmd.utils.data_system import DeepmdDataSystem from deepmd.utils.sess import run_sess from deepmd.utils.neighbor_stat import NeighborStat @@ -83,7 +83,7 @@ def train( # load json database jdata = j_loader(INPUT) - jdata = updata_deepmd_input(jdata, warning=True, dump="input_v2_compat.json") + jdata = update_deepmd_input(jdata, warning=True, dump="input_v2_compat.json") jdata = normalize(jdata) diff --git a/deepmd/utils/compat.py b/deepmd/utils/compat.py index bae778e426..8a048a13a2 100644 --- a/deepmd/utils/compat.py +++ b/deepmd/utils/compat.py @@ -354,7 +354,7 @@ def deprecate_numb_test(jdata: Dict[str, Any], return jdata -def updata_deepmd_input(jdata: Dict[str, Any], +def update_deepmd_input(jdata: Dict[str, Any], warning: bool = True, dump: Optional[Union[str, Path]] = None) -> Dict[str, Any]: def is_deepmd_v0_input(jdata): From f40e14e80008cd2207ba0da50ff66ca8a9d10ca7 Mon Sep 17 00:00:00 2001 From: Denghui Lu Date: Tue, 23 Nov 2021 08:22:43 +0800 Subject: [PATCH 114/161] Enable mixed precision support for deepmd-kit (#1285) * enable mixed precision support for dp * set the default embedding net & fitting net precision * add doc for mixed precision * fix typo * fix UT bug * use input script to control the mixed precision workflow * add tf version check for mixed precision * Update training-advanced.md * fix typo * fix TF_VERSION control * fix TF_VERSION comparison * enable mixed precision for hybrid descriptor * Update network.py * use parameter to control the network mixed precision output precision * add example for mixed precision training workflow * fix lint errors --- deepmd/descriptor/descriptor.py | 18 +++++ deepmd/descriptor/hybrid.py | 14 ++++ deepmd/descriptor/se_a.py | 18 ++++- deepmd/env.py | 1 + deepmd/fit/dipole.py | 22 ++++-- deepmd/fit/ener.py | 26 ++++++-- deepmd/fit/polar.py | 35 ++++++++-- deepmd/train/trainer.py | 28 +++++++- deepmd/utils/argcheck.py | 20 ++++++ deepmd/utils/network.py | 28 +++++++- doc/train/training-advanced.md | 11 +++ examples/water/se_e2_a_mixed_prec/input.json | 70 ++++++++++++++++++++ requirements.txt | 1 + 13 files changed, 273 insertions(+), 19 deletions(-) create mode 100644 examples/water/se_e2_a_mixed_prec/input.json diff --git a/deepmd/descriptor/descriptor.py b/deepmd/descriptor/descriptor.py index d179660a9d..231f3abe1e 100644 --- a/deepmd/descriptor/descriptor.py +++ b/deepmd/descriptor/descriptor.py @@ -262,6 +262,24 @@ def enable_compression(self, raise NotImplementedError( "Descriptor %s doesn't support compression!" % type(self).__name__) + def enable_mixed_precision(self, mixed_prec: dict = None) -> None: + """ + Reveive the mixed precision setting. + + Parameters + ---------- + mixed_prec + The mixed precision setting used in the embedding net + + Notes + ----- + This method is called by others when the descriptor supported compression. + """ + raise NotImplementedError( + "Descriptor %s doesn't support mixed precision training!" + % type(self).__name__ + ) + @abstractmethod def prod_force_virial(self, atom_ener: tf.Tensor, diff --git a/deepmd/descriptor/hybrid.py b/deepmd/descriptor/hybrid.py index 9d8967faee..39cb9fed0e 100644 --- a/deepmd/descriptor/hybrid.py +++ b/deepmd/descriptor/hybrid.py @@ -264,6 +264,20 @@ def enable_compression(self, for idx, ii in enumerate(self.descrpt_list): ii.enable_compression(min_nbor_dist, model_file, table_extrapolate, table_stride_1, table_stride_2, check_frequency, suffix=f"{suffix}_{idx}") + + def enable_mixed_precision(self, mixed_prec : dict = None) -> None: + """ + Reveive the mixed precision setting. + + Parameters + ---------- + mixed_prec + The mixed precision setting used in the embedding net + """ + for idx, ii in enumerate(self.descrpt_list): + ii.enable_mixed_precision(mixed_prec) + + def init_variables(self, model_file : str, suffix : str = "", diff --git a/deepmd/descriptor/se_a.py b/deepmd/descriptor/se_a.py index 74b12a412a..91843a47d3 100644 --- a/deepmd/descriptor/se_a.py +++ b/deepmd/descriptor/se_a.py @@ -160,6 +160,7 @@ def __init__ (self, self.davg = None self.compress = False self.embedding_net_variables = None + self.mixed_prec = None self.place_holders = {} nei_type = np.array([]) for ii in range(self.ntypes): @@ -348,6 +349,18 @@ def enable_compression(self, self.dstd = get_tensor_by_name_from_graph(graph, 'descrpt_attr%s/t_std' % suffix) + def enable_mixed_precision(self, mixed_prec : dict = None) -> None: + """ + Reveive the mixed precision setting. + + Parameters + ---------- + mixed_prec + The mixed precision setting used in the embedding net + """ + self.mixed_prec = mixed_prec + self.filter_precision = get_precision(mixed_prec['output_prec']) + def build (self, coord_ : tf.Tensor, @@ -708,7 +721,8 @@ def _filter_lower( seed = self.seed, trainable = trainable, uniform_seed = self.uniform_seed, - initial_variables = self.embedding_net_variables) + initial_variables = self.embedding_net_variables, + mixed_prec = self.mixed_prec) if (not self.uniform_seed) and (self.seed is not None): self.seed += self.seed_shift else: # we can safely return the final xyz_scatter filled with zero directly @@ -735,6 +749,8 @@ def _filter( name='linear', reuse=None, trainable = True): + if self.mixed_prec is not None: + inputs = tf.cast(inputs, get_precision(self.mixed_prec['compute_prec'])) nframes = tf.shape(tf.reshape(inputs, [-1, natoms[0], self.ndescrpt]))[0] # natom x (nei x 4) shape = inputs.get_shape().as_list() diff --git a/deepmd/env.py b/deepmd/env.py index 214302ab6b..3adfc40528 100644 --- a/deepmd/env.py +++ b/deepmd/env.py @@ -39,6 +39,7 @@ "TRANSFER_PATTERN", "FITTING_NET_PATTERN", "EMBEDDING_NET_PATTERN", + "TF_VERSION" ] SHARED_LIB_MODULE = "op" diff --git a/deepmd/fit/dipole.py b/deepmd/fit/dipole.py index 6c115e3fb3..7c2d5dea86 100644 --- a/deepmd/fit/dipole.py +++ b/deepmd/fit/dipole.py @@ -77,6 +77,7 @@ def __init__ (self, self.dim_rot_mat = self.dim_rot_mat_1 * 3 self.useBN = False self.fitting_net_variables = None + self.mixed_prec = None def get_sel_type(self) -> int: """ @@ -141,12 +142,12 @@ def build (self, layer = inputs_i for ii in range(0,len(self.n_neuron)) : if ii >= 1 and self.n_neuron[ii] == self.n_neuron[ii-1] : - layer+= one_layer(layer, self.n_neuron[ii], name='layer_'+str(ii)+'_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, use_timestep = self.resnet_dt, activation_fn = self.fitting_activation_fn, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables) + layer+= one_layer(layer, self.n_neuron[ii], name='layer_'+str(ii)+'_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, use_timestep = self.resnet_dt, activation_fn = self.fitting_activation_fn, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables, mixed_prec = self.mixed_prec) else : - layer = one_layer(layer, self.n_neuron[ii], name='layer_'+str(ii)+'_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, activation_fn = self.fitting_activation_fn, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables) + layer = one_layer(layer, self.n_neuron[ii], name='layer_'+str(ii)+'_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, activation_fn = self.fitting_activation_fn, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables, mixed_prec = self.mixed_prec) if (not self.uniform_seed) and (self.seed is not None): self.seed += self.seed_shift # (nframes x natoms) x naxis - final_layer = one_layer(layer, self.dim_rot_mat_1, activation_fn = None, name='final_layer_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables) + final_layer = one_layer(layer, self.dim_rot_mat_1, activation_fn = None, name='final_layer_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables, mixed_prec = self.mixed_prec, final_layer = True) if (not self.uniform_seed) and (self.seed is not None): self.seed += self.seed_shift # (nframes x natoms) x 1 * naxis final_layer = tf.reshape(final_layer, [tf.shape(inputs)[0] * natoms[2+type_i], 1, self.dim_rot_mat_1]) @@ -177,4 +178,17 @@ def init_variables(self, model_file : str The input frozen model file """ - self.fitting_net_variables = get_fitting_net_variables(model_file) \ No newline at end of file + self.fitting_net_variables = get_fitting_net_variables(model_file) + + + def enable_mixed_precision(self, mixed_prec : dict = None) -> None: + """ + Reveive the mixed precision setting. + + Parameters + ---------- + mixed_prec + The mixed precision setting used in the embedding net + """ + self.mixed_prec = mixed_prec + self.fitting_precision = get_precision(mixed_prec['output_prec']) \ No newline at end of file diff --git a/deepmd/fit/ener.py b/deepmd/fit/ener.py index 0afcf26de2..e6b0d0a763 100644 --- a/deepmd/fit/ener.py +++ b/deepmd/fit/ener.py @@ -150,6 +150,7 @@ def __init__ (self, self.aparam_inv_std = None self.fitting_net_variables = None + self.mixed_prec = None def get_numb_fparam(self) -> int: """ @@ -293,7 +294,8 @@ def _build_lower( precision = self.fitting_precision, trainable = self.trainable[ii], uniform_seed = self.uniform_seed, - initial_variables = self.fitting_net_variables) + initial_variables = self.fitting_net_variables, + mixed_prec = self.mixed_prec) else : layer = one_layer( layer, @@ -305,7 +307,8 @@ def _build_lower( precision = self.fitting_precision, trainable = self.trainable[ii], uniform_seed = self.uniform_seed, - initial_variables = self.fitting_net_variables) + initial_variables = self.fitting_net_variables, + mixed_prec = self.mixed_prec) if (not self.uniform_seed) and (self.seed is not None): self.seed += self.seed_shift final_layer = one_layer( layer, @@ -318,7 +321,9 @@ def _build_lower( precision = self.fitting_precision, trainable = self.trainable[-1], uniform_seed = self.uniform_seed, - initial_variables = self.fitting_net_variables) + initial_variables = self.fitting_net_variables, + mixed_prec = self.mixed_prec, + final_layer = True) if (not self.uniform_seed) and (self.seed is not None): self.seed += self.seed_shift return final_layer @@ -494,4 +499,17 @@ def init_variables(self, model_file : str The input frozen model file """ - self.fitting_net_variables = get_fitting_net_variables(model_file) \ No newline at end of file + self.fitting_net_variables = get_fitting_net_variables(model_file) + + + def enable_mixed_precision(self, mixed_prec : dict = None) -> None: + """ + Reveive the mixed precision setting. + + Parameters + ---------- + mixed_prec + The mixed precision setting used in the embedding net + """ + self.mixed_prec = mixed_prec + self.fitting_precision = get_precision(mixed_prec['output_prec']) \ No newline at end of file diff --git a/deepmd/fit/polar.py b/deepmd/fit/polar.py index 65b1ff6aef..5f6ddd7525 100644 --- a/deepmd/fit/polar.py +++ b/deepmd/fit/polar.py @@ -79,7 +79,7 @@ def build (self, else : layer = one_layer(layer, self.n_neuron[ii], name='layer_'+str(ii)+'_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, activation_fn = self.fitting_activation_fn, precision = self.fitting_precision) # (nframes x natoms) x 9 - final_layer = one_layer(layer, 9, activation_fn = None, name='final_layer_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, precision = self.fitting_precision) + final_layer = one_layer(layer, 9, activation_fn = None, name='final_layer_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, precision = self.fitting_precision, final_layer = True) # (nframes x natoms) x 3 x 3 final_layer = tf.reshape(final_layer, [tf.shape(inputs)[0] * natoms[2+type_i], 3, 3]) # (nframes x natoms) x 3 x 3 @@ -194,6 +194,7 @@ def __init__ (self, self.dim_rot_mat = self.dim_rot_mat_1 * 3 self.useBN = False self.fitting_net_variables = None + self.mixed_prec = None def get_sel_type(self) -> List[int]: """ @@ -324,9 +325,9 @@ def build (self, layer = inputs_i for ii in range(0,len(self.n_neuron)) : if ii >= 1 and self.n_neuron[ii] == self.n_neuron[ii-1] : - layer+= one_layer(layer, self.n_neuron[ii], name='layer_'+str(ii)+'_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, use_timestep = self.resnet_dt, activation_fn = self.fitting_activation_fn, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables) + layer+= one_layer(layer, self.n_neuron[ii], name='layer_'+str(ii)+'_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, use_timestep = self.resnet_dt, activation_fn = self.fitting_activation_fn, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables, mixed_prec = self.mixed_prec) else : - layer = one_layer(layer, self.n_neuron[ii], name='layer_'+str(ii)+'_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, activation_fn = self.fitting_activation_fn, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables) + layer = one_layer(layer, self.n_neuron[ii], name='layer_'+str(ii)+'_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, activation_fn = self.fitting_activation_fn, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables, mixed_prec = self.mixed_prec) if (not self.uniform_seed) and (self.seed is not None): self.seed += self.seed_shift if self.fit_diag : bavg = np.zeros(self.dim_rot_mat_1) @@ -334,7 +335,7 @@ def build (self, # bavg[1] = self.avgeig[1] # bavg[2] = self.avgeig[2] # (nframes x natoms) x naxis - final_layer = one_layer(layer, self.dim_rot_mat_1, activation_fn = None, name='final_layer_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, bavg = bavg, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables) + final_layer = one_layer(layer, self.dim_rot_mat_1, activation_fn = None, name='final_layer_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, bavg = bavg, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables, mixed_prec = self.mixed_prec, final_layer = True) if (not self.uniform_seed) and (self.seed is not None): self.seed += self.seed_shift # (nframes x natoms) x naxis final_layer = tf.reshape(final_layer, [tf.shape(inputs)[0] * natoms[2+type_i], self.dim_rot_mat_1]) @@ -346,7 +347,7 @@ def build (self, # bavg[1*self.dim_rot_mat_1+1] = self.avgeig[1] # bavg[2*self.dim_rot_mat_1+2] = self.avgeig[2] # (nframes x natoms) x (naxis x naxis) - final_layer = one_layer(layer, self.dim_rot_mat_1*self.dim_rot_mat_1, activation_fn = None, name='final_layer_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, bavg = bavg, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables) + final_layer = one_layer(layer, self.dim_rot_mat_1*self.dim_rot_mat_1, activation_fn = None, name='final_layer_type_'+str(type_i)+suffix, reuse=reuse, seed = self.seed, bavg = bavg, precision = self.fitting_precision, uniform_seed = self.uniform_seed, initial_variables = self.fitting_net_variables, mixed_prec = self.mixed_prec, final_layer = True) if (not self.uniform_seed) and (self.seed is not None): self.seed += self.seed_shift # (nframes x natoms) x naxis x naxis final_layer = tf.reshape(final_layer, [tf.shape(inputs)[0] * natoms[2+type_i], self.dim_rot_mat_1, self.dim_rot_mat_1]) @@ -387,6 +388,19 @@ def init_variables(self, self.fitting_net_variables = get_fitting_net_variables(model_file) + def enable_mixed_precision(self, mixed_prec : dict = None) -> None: + """ + Reveive the mixed precision setting. + + Parameters + ---------- + mixed_prec + The mixed precision setting used in the embedding net + """ + self.mixed_prec = mixed_prec + self.fitting_precision = get_precision(mixed_prec['output_prec']) + + class GlobalPolarFittingSeA () : """ Fit the system polarizability with descriptor se_a @@ -509,3 +523,14 @@ def init_variables(self, """ self.polar_fitting.init_variables(model_file) + + def enable_mixed_precision(self, mixed_prec : dict = None) -> None: + """ + Reveive the mixed precision setting. + + Parameters + ---------- + mixed_prec + The mixed precision setting used in the embedding net + """ + self.polar_fitting.enable_mixed_precision(mixed_prec) diff --git a/deepmd/train/trainer.py b/deepmd/train/trainer.py index 16d1234112..0e1ed6d3e8 100644 --- a/deepmd/train/trainer.py +++ b/deepmd/train/trainer.py @@ -7,6 +7,8 @@ import shutil import google.protobuf.message import numpy as np +from packaging.version import Version + from deepmd.env import tf from deepmd.env import get_tf_session_config from deepmd.env import GLOBAL_TF_FLOAT_PRECISION @@ -23,13 +25,13 @@ from deepmd.utils.graph import get_tensor_by_name from tensorflow.python.client import timeline -from deepmd.env import op_module +from deepmd.env import op_module, TF_VERSION from deepmd.utils.errors import GraphWithoutTensorError # load grad of force module import deepmd.op -from deepmd.common import j_must_have, ClassArg, data_requirement +from deepmd.common import j_must_have, ClassArg, data_requirement, get_precision log = logging.getLogger(__name__) @@ -227,6 +229,13 @@ def _init_param(self, jdata): self.tensorboard = self.run_opt.is_chief and tr_data.get('tensorboard', False) self.tensorboard_log_dir = tr_data.get('tensorboard_log_dir', 'log') self.tensorboard_freq = tr_data.get('tensorboard_freq', 1) + self.mixed_prec = tr_data.get('mixed_precision', None) + if self.mixed_prec is not None: + if (self.mixed_prec['compute_prec'] != 'float16' or self.mixed_prec['output_prec'] != 'float32'): + raise RuntimeError( + "Unsupported mixed precision option [output_prec, compute_prec]: [%s, %s], " + " Supported: [float32, float16], Please set mixed precision option correctly!" + % (self.mixed_prec['output_prec'], self.mixed_prec['compute_prec'])) # self.sys_probs = tr_data['sys_probs'] # self.auto_prob_style = tr_data['auto_prob'] self.useBN = False @@ -289,6 +298,10 @@ def build (self, tf.constant("compressed_model", name = 'model_type', dtype = tf.string) else: tf.constant("original_model", name = 'model_type', dtype = tf.string) + + if self.mixed_prec is not None: + self.descrpt.enable_mixed_precision(self.mixed_prec) + self.fitting.enable_mixed_precision(self.mixed_prec) self._build_lr() self._build_network(data) @@ -332,6 +345,8 @@ def _build_network(self, data): self.place_holders, suffix = "test") + if self.mixed_prec is not None: + self.l2_l = tf.cast(self.l2_l, get_precision(self.mixed_prec['output_prec'])) log.info("built network") def _build_training(self): @@ -345,6 +360,15 @@ def _build_training(self): optimizer = self.run_opt._HVD.DistributedOptimizer(optimizer) else: optimizer = tf.train.AdamOptimizer(learning_rate = self.learning_rate) + if self.mixed_prec is not None: + _TF_VERSION = Version(TF_VERSION) + # check the TF_VERSION, when TF < 1.12, mixed precision is not allowed + if _TF_VERSION < Version('1.12.0'): + raise RuntimeError("TensorFlow version %s is not compatible with the mixed precision setting. Please consider upgrading your TF version!" % TF_VERSION) + elif _TF_VERSION < Version('2.4.0'): + optimizer = tf.train.experimental.enable_mixed_precision_graph_rewrite(optimizer) + else: + optimizer = tf.mixed_precision.enable_mixed_precision_graph_rewrite(optimizer) apply_op = optimizer.minimize(loss=self.l2_l, global_step=self.global_step, var_list=trainable_variables, diff --git a/deepmd/utils/argcheck.py b/deepmd/utils/argcheck.py index 36e9eb2ee6..847eccc52e 100644 --- a/deepmd/utils/argcheck.py +++ b/deepmd/utils/argcheck.py @@ -600,6 +600,24 @@ def validation_data_args(): # ! added by Ziyao: new specification style for dat sub_fields=args, sub_variants=[], doc=doc_validation_data) +def mixed_precision_args(): # ! added by Denghui. + doc_output_prec = 'The precision for mixed precision params. " \ + "The trainable variables precision during the mixed precision training process, " \ + "supported options are float32 only currently.' + doc_compute_prec = 'The precision for mixed precision compute. " \ + "The compute precision during the mixed precision training process, "" \ + "supported options are float16 only currently.' + + args = [ + Argument("output_prec", str, optional=True, default="float32", doc=doc_output_prec), + Argument("compute_prec", str, optional=False, default="float16", doc=doc_compute_prec), + ] + + doc_mixed_precision = "Configurations of mixed precision." + return Argument("mixed_precision", dict, optional=True, + sub_fields=args, sub_variants=[], doc=doc_mixed_precision) + + def training_args(): # ! modified by Ziyao: data configuration isolated. doc_numb_steps = 'Number of training batch. Each training uses one batch of data.' doc_seed = 'The random seed for getting frames from the training data set.' @@ -617,10 +635,12 @@ def training_args(): # ! modified by Ziyao: data configuration isolated. arg_training_data = training_data_args() arg_validation_data = validation_data_args() + mixed_precision_data = mixed_precision_args() args = [ arg_training_data, arg_validation_data, + mixed_precision_data, Argument("numb_steps", int, optional=False, doc=doc_numb_steps, alias=["stop_batch"]), Argument("seed", [int,None], optional=True, doc=doc_seed), Argument("disp_file", str, optional=True, default='lcurve.out', doc=doc_disp_file), diff --git a/deepmd/utils/network.py b/deepmd/utils/network.py index 5c78031167..c82721becb 100644 --- a/deepmd/utils/network.py +++ b/deepmd/utils/network.py @@ -2,6 +2,7 @@ from deepmd.env import tf from deepmd.env import GLOBAL_TF_FLOAT_PRECISION +from deepmd.common import get_precision def one_layer_rand_seed_shift(): return 3 @@ -19,7 +20,12 @@ def one_layer(inputs, trainable = True, useBN = False, uniform_seed = False, - initial_variables = None): + initial_variables = None, + mixed_prec = None, + final_layer = False): + # For good accuracy, the last layer of the fitting network uses a higher precision neuron network. + if mixed_prec is not None and final_layer: + inputs = tf.cast(inputs, get_precision(mixed_prec['output_prec'])) with tf.variable_scope(name, reuse=reuse): shape = inputs.get_shape().as_list() w_initializer = tf.random_normal_initializer( @@ -44,6 +50,12 @@ def one_layer(inputs, b_initializer, trainable = trainable) variable_summaries(b, 'bias') + + if mixed_prec is not None and not final_layer: + inputs = tf.cast(inputs, get_precision(mixed_prec['compute_prec'])) + w = tf.cast(w, get_precision(mixed_prec['compute_prec'])) + b = tf.cast(b, get_precision(mixed_prec['compute_prec'])) + hidden = tf.matmul(inputs, w) + b if activation_fn != None and use_timestep : idt_initializer = tf.random_normal_initializer( @@ -65,6 +77,8 @@ def one_layer(inputs, # return activation_fn(hidden_bn) else: if use_timestep : + if mixed_prec is not None and not final_layer: + idt = tf.cast(idt, get_precision(mixed_prec['compute_prec'])) return tf.reshape(activation_fn(hidden), [-1, outputs_size]) * idt else : return tf.reshape(activation_fn(hidden), [-1, outputs_size]) @@ -93,7 +107,8 @@ def embedding_net(xx, seed = None, trainable = True, uniform_seed = False, - initial_variables = None): + initial_variables = None, + mixed_prec = None): r"""The embedding network. The embedding network function :math:`\mathcal{N}` is constructed by is the @@ -146,6 +161,8 @@ def embedding_net(xx, Only for the purpose of backward compatibility, retrieves the old behavior of using the random seed initial_variables : dict The input dict which stores the embedding net variables + mixed_prec + The input dict which stores the mixed precision setting for the embedding net References @@ -185,6 +202,10 @@ def embedding_net(xx, trainable = trainable) variable_summaries(b, 'bias_'+str(ii)+name_suffix) + if mixed_prec is not None: + xx = tf.cast(xx, get_precision(mixed_prec['compute_prec'])) + w = tf.cast(w, get_precision(mixed_prec['compute_prec'])) + b = tf.cast(b, get_precision(mixed_prec['compute_prec'])) hidden = tf.reshape(activation_fn(tf.matmul(xx, w) + b), [-1, outputs_size[ii]]) if resnet_dt : idt_initializer = tf.random_normal_initializer( @@ -201,6 +222,8 @@ def embedding_net(xx, idt_initializer, trainable = trainable) variable_summaries(idt, 'idt_'+str(ii)+name_suffix) + if mixed_prec is not None: + idt = tf.cast(idt, get_precision(mixed_prec['compute_prec'])) if outputs_size[ii] == outputs_size[ii-1]: if resnet_dt : @@ -214,7 +237,6 @@ def embedding_net(xx, xx = tf.concat([xx,xx], 1) + hidden else: xx = hidden - return xx def variable_summaries(var: tf.Variable, name: str): diff --git a/doc/train/training-advanced.md b/doc/train/training-advanced.md index 082569712f..865b12e460 100644 --- a/doc/train/training-advanced.md +++ b/doc/train/training-advanced.md @@ -36,6 +36,10 @@ Other training parameters are given in the `training` section. "batch_size": 1, "numb_btch": 3 }, + "mixed_precision": { + "output_prec": "float32", + "compute_prec": "float16" + }, "numb_step": 1000000, "seed": 1, @@ -75,6 +79,13 @@ The sections `"training_data"` and `"validation_data"` give the training dataset * `"auto:N"`: automatically determines the batch size so that the `batch_size` times the number of atoms in the system is no less than `N`. * The key `numb_batch` in `validate_data` gives the number of batches of model validation. Note that the batches may not be from the same system +The section `mixed_precision` specifies the mixed precision settings, which will enable the mixed precision training workflow for deepmd-kit. The keys are explained below: +* `output_prec` precision used in the output tensors, only `float32` is supported currently. +* `compute_prec` precision used in the computing tensors, only `float16` is supported currently. +Note there are severial limitations about the mixed precision training: +* Only 'se_e2_a' type descriptor is supported by the mixed precision training workflow. +* The precision of embedding net and fitting net are forced to be set to `float32`. + Other keys in the `training` section are explained below: * `numb_step` The number of training steps. * `seed` The random seed for getting frames from the training data set. diff --git a/examples/water/se_e2_a_mixed_prec/input.json b/examples/water/se_e2_a_mixed_prec/input.json new file mode 100644 index 0000000000..889abedabf --- /dev/null +++ b/examples/water/se_e2_a_mixed_prec/input.json @@ -0,0 +1,70 @@ +{ + "_comment": " model parameters", + "model": { + "type_map": ["O", "H"], + "descriptor" :{ + "type": "se_e2_a", + "sel": [46, 92], + "rcut_smth": 0.50, + "rcut": 6.00, + "neuron": [25, 50, 100], + "resnet_dt": false, + "axis_neuron": 16, + "seed": 1, + "_comment": " that's all" + }, + "fitting_net" : { + "neuron": [240, 240, 240], + "resnet_dt": true, + "seed": 1, + "_comment": " that's all" + }, + "_comment": " that's all" + }, + + "learning_rate" :{ + "type": "exp", + "decay_steps": 5000, + "start_lr": 0.001, + "stop_lr": 3.51e-8, + "_comment": "that's all" + }, + + "loss" :{ + "type": "ener", + "start_pref_e": 0.02, + "limit_pref_e": 1, + "start_pref_f": 1000, + "limit_pref_f": 1, + "start_pref_v": 0, + "limit_pref_v": 0, + "_comment": " that's all" + }, + + "training" : { + "training_data": { + "systems": ["../data/data_0/", "../data/data_1/", "../data/data_2/"], + "batch_size": "auto", + "_comment": "that's all" + }, + "validation_data":{ + "systems": ["../data/data_3"], + "batch_size": 1, + "numb_btch": 3, + "_comment": "that's all" + }, + "mixed_precision": { + "compute_prec": "float16", + "output_prec": "float32" + }, + "numb_steps": 1000000, + "seed": 10, + "disp_file": "lcurve.out", + "disp_freq": 100, + "save_freq": 1000, + "_comment": "that's all" + }, + + "_comment": "that's all" +} + diff --git a/requirements.txt b/requirements.txt index f3ead805b8..06b71f825c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,4 @@ python-hostlist >= 1.21 typing_extensions; python_version < "3.7" h5py wcmatch +packaging From 3d93fd00b5a87e21de732c6a540cc911f6c9b9aa Mon Sep 17 00:00:00 2001 From: Denghui Lu Date: Tue, 23 Nov 2021 08:35:33 +0800 Subject: [PATCH 115/161] add embedding network dimension check of model compression (#1303) --- deepmd/descriptor/se_a.py | 8 ++++++++ deepmd/descriptor/se_t.py | 9 +++++++++ deepmd/utils/tabulate.py | 4 ---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/deepmd/descriptor/se_a.py b/deepmd/descriptor/se_a.py index 91843a47d3..30bb1bfbd9 100644 --- a/deepmd/descriptor/se_a.py +++ b/deepmd/descriptor/se_a.py @@ -334,6 +334,14 @@ def enable_compression(self, if (self.ntypes * self.ntypes - len(self.exclude_types) == 0): raise RuntimeError("empty embedding-net are not supported in model compression!") + for ii in range(len(self.filter_neuron) - 1): + if self.filter_neuron[ii] * 2 != self.filter_neuron[ii + 1]: + raise RecursionError( + "Model Compression error: descriptor neuron [%s] is not supported by model compression! " + "The size of the next layer of the neural network must be twice the size of the previous layer." + % ','.join([str(item) for item in self.filter_neuron]) + ) + self.compress = True self.table = DPTabulate( self, self.filter_neuron, model_file, self.type_one_side, self.exclude_types, self.compress_activation_fn, suffix=suffix) diff --git a/deepmd/descriptor/se_t.py b/deepmd/descriptor/se_t.py index d5ffdf3970..08a6e620c2 100644 --- a/deepmd/descriptor/se_t.py +++ b/deepmd/descriptor/se_t.py @@ -259,6 +259,15 @@ def enable_compression(self, assert ( not self.filter_resnet_dt ), "Model compression error: descriptor resnet_dt must be false!" + + for ii in range(len(self.filter_neuron) - 1): + if self.filter_neuron[ii] * 2 != self.filter_neuron[ii + 1]: + raise RecursionError( + "Model Compression error: descriptor neuron [%s] is not supported by model compression! " + "The size of the next layer of the neural network must be twice the size of the previous layer." + % ','.join([str(item) for item in self.filter_neuron]) + ) + self.compress = True self.table = DPTabulate( self, self.filter_neuron, model_file, activation_fn = self.filter_activation_fn, suffix=suffix) diff --git a/deepmd/utils/tabulate.py b/deepmd/utils/tabulate.py index 1e2cdcd40c..b9372cad2f 100644 --- a/deepmd/utils/tabulate.py +++ b/deepmd/utils/tabulate.py @@ -1,4 +1,3 @@ -import re import math import logging import numpy as np @@ -9,12 +8,9 @@ from deepmd.env import tf from deepmd.env import op_module from deepmd.common import ACTIVATION_FN_DICT -from deepmd.utils.sess import run_sess from deepmd.utils.graph import get_tensor_by_name_from_graph, load_graph_def from deepmd.utils.graph import get_embedding_net_nodes_from_graph_def from deepmd.descriptor import Descriptor -from tensorflow.python.platform import gfile -from tensorflow.python.framework import tensor_util log = logging.getLogger(__name__) From 1bcb917436fcfd649d121f21c4ef04db300c69c0 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 22 Nov 2021 19:37:15 -0500 Subject: [PATCH 116/161] add importlib_metadata as dependency (#1308) Same as https://github.com/deepmodeling/dpdata/pull/227. --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 06b71f825c..8cd273f803 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,7 @@ pyyaml dargs >= 0.2.6 python-hostlist >= 1.21 typing_extensions; python_version < "3.7" +importlib_metadata>=1.4; python_version < "3.8" h5py wcmatch packaging From 338ba31905da0ff714a0ed485a74d0a824b5d41c Mon Sep 17 00:00:00 2001 From: Zeyu Li <47965866+ZeyuLi0123@users.noreply.github.com> Date: Tue, 23 Nov 2021 08:53:35 +0800 Subject: [PATCH 117/161] fix bugs about parameters of memset (#1302) Co-authored-by: pkulzy <47965866+pkulzy@users.noreply.github.com> --- source/lib/include/gpu_cuda.h | 2 +- source/lib/include/gpu_rocm.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/lib/include/gpu_cuda.h b/source/lib/include/gpu_cuda.h index 8a2b617c95..cc23969857 100644 --- a/source/lib/include/gpu_cuda.h +++ b/source/lib/include/gpu_cuda.h @@ -154,7 +154,7 @@ void delete_device_memory( template void memset_device_memory( FPTYPE * device, - const FPTYPE var, + const int var, const int size) { DPErrcheck(cudaMemset(device, var, sizeof(FPTYPE) * size)); diff --git a/source/lib/include/gpu_rocm.h b/source/lib/include/gpu_rocm.h index b6439c3bb8..84e05efbd2 100644 --- a/source/lib/include/gpu_rocm.h +++ b/source/lib/include/gpu_rocm.h @@ -110,7 +110,7 @@ void delete_device_memory( template void memset_device_memory( FPTYPE * device, - const FPTYPE var, + const int var, const int size) { DPErrcheck(hipMemset(device,var,sizeof(FPTYPE)*size)); From 843a3c5ab34930c948ba4687c85cedef86f65176 Mon Sep 17 00:00:00 2001 From: Denghui Lu Date: Tue, 23 Nov 2021 09:28:10 +0800 Subject: [PATCH 118/161] Fix model compression bug when fparam or aparam is not zero (#1306) * fix model compression bug when fparam and aparam are not zero * Update ener.py --- deepmd/fit/ener.py | 30 +++++++++++++++++++++++++++--- deepmd/train/trainer.py | 3 +++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/deepmd/fit/ener.py b/deepmd/fit/ener.py index e6b0d0a763..f82b68f41b 100644 --- a/deepmd/fit/ener.py +++ b/deepmd/fit/ener.py @@ -9,7 +9,7 @@ from deepmd.descriptor import DescrptLocFrame from deepmd.descriptor import DescrptSeA from deepmd.utils.type_embed import embed_atom_type -from deepmd.utils.graph import get_fitting_net_variables +from deepmd.utils.graph import get_fitting_net_variables, load_graph_def, get_tensor_by_name_from_graph from deepmd.env import global_cvt_2_tf_float from deepmd.env import GLOBAL_TF_FLOAT_PRECISION @@ -502,7 +502,31 @@ def init_variables(self, self.fitting_net_variables = get_fitting_net_variables(model_file) - def enable_mixed_precision(self, mixed_prec : dict = None) -> None: + def enable_compression(self, + model_file: str, + suffix: str = "" + ) -> None: + """ + Set the fitting net attributes from the frozen model_file when fparam or aparam is not zero + + Parameters + ---------- + model_file : str + The input frozen model file + suffix : str, optional + The suffix of the scope + """ + if self.numb_fparam > 0 or self.numb_aparam > 0: + graph, _ = load_graph_def(model_file) + if self.numb_fparam > 0: + self.fparam_avg = get_tensor_by_name_from_graph(graph, 'fitting_attr%s/t_fparam_avg' % suffix) + self.fparam_inv_std = get_tensor_by_name_from_graph(graph, 'fitting_attr%s/t_fparam_istd' % suffix) + if self.numb_aparam > 0: + self.aparam_avg = get_tensor_by_name_from_graph(graph, 'fitting_attr%s/t_aparam_avg' % suffix) + self.aparam_inv_std = get_tensor_by_name_from_graph(graph, 'fitting_attr%s/t_aparam_istd' % suffix) + + + def enable_mixed_precision(self, mixed_prec: dict = None) -> None: """ Reveive the mixed precision setting. @@ -512,4 +536,4 @@ def enable_mixed_precision(self, mixed_prec : dict = None) -> None: The mixed precision setting used in the embedding net """ self.mixed_prec = mixed_prec - self.fitting_precision = get_precision(mixed_prec['output_prec']) \ No newline at end of file + self.fitting_precision = get_precision(mixed_prec['output_prec']) diff --git a/deepmd/train/trainer.py b/deepmd/train/trainer.py index 0e1ed6d3e8..ea75b30bd1 100644 --- a/deepmd/train/trainer.py +++ b/deepmd/train/trainer.py @@ -293,6 +293,9 @@ def build (self, else : self.descrpt.enable_compression(self.model_param['compress']["min_nbor_dist"], self.model_param['compress']['model_file'], self.model_param['compress']['table_config'][0], self.model_param['compress']['table_config'][1], self.model_param['compress']['table_config'][2], self.model_param['compress']['table_config'][3]) self.fitting.init_variables(self.model_param['compress']['model_file']) + # for fparam or aparam settings in 'ener' type fitting net + if self.fitting_type == 'ener': + self.fitting.enable_compression(self.model_param['compress']['model_file']) if self.is_compress or self.model_type == 'compressed_model': tf.constant("compressed_model", name = 'model_type', dtype = tf.string) From 3869a5b5dcc041c9fa329f9f5df91338a80e6aa6 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Fri, 26 Nov 2021 22:49:03 -0500 Subject: [PATCH 119/161] add space between words in messages (#1312) --- deepmd/env.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/deepmd/env.py b/deepmd/env.py index 3adfc40528..f8088c69d4 100644 --- a/deepmd/env.py +++ b/deepmd/env.py @@ -233,7 +233,7 @@ def get_module(module_name: str) -> "ModuleType": "This deepmd-kit package was compiled with " "CXX11_ABI_FLAG=%d, but TensorFlow runtime was compiled " "with CXX11_ABI_FLAG=%d. These two library ABIs are " - "incompatible and thus an error is raised when loading %s." + "incompatible and thus an error is raised when loading %s. " "You need to rebuild deepmd-kit against this TensorFlow " "runtime." % ( TF_CXX11_ABI_FLAG, @@ -262,9 +262,9 @@ def get_module(module_name: str) -> "ModuleType": tf.version.VERSION, )) from e raise RuntimeError( - "This deepmd-kit package is inconsitent with TensorFlow" - "Runtime, thus an error is raised when loading %s." - "You need to rebuild deepmd-kit against this TensorFlow" + "This deepmd-kit package is inconsitent with TensorFlow " + "Runtime, thus an error is raised when loading %s. " + "You need to rebuild deepmd-kit against this TensorFlow " "runtime." % ( module_name, )) from e From 743c1e618fa785397929e98aac7fb52ec495325c Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Fri, 26 Nov 2021 22:49:38 -0500 Subject: [PATCH 120/161] do not print virial error with nopbc data (#1314) fix #1286 --- deepmd/entrypoints/test.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/deepmd/entrypoints/test.py b/deepmd/entrypoints/test.py index 877c6948ae..1cd23534a4 100644 --- a/deepmd/entrypoints/test.py +++ b/deepmd/entrypoints/test.py @@ -262,8 +262,9 @@ def test_ener( log.info(f"Energy RMSE : {rmse_e:e} eV") log.info(f"Energy RMSE/Natoms : {rmse_ea:e} eV") log.info(f"Force RMSE : {rmse_f:e} eV/A") - log.info(f"Virial RMSE : {rmse_v:e} eV") - log.info(f"Virial RMSE/Natoms : {rmse_va:e} eV") + if data.pbc: + log.info(f"Virial RMSE : {rmse_v:e} eV") + log.info(f"Virial RMSE/Natoms : {rmse_va:e} eV") if has_atom_ener: log.info(f"Atomic ener RMSE : {rmse_ae:e} eV") From 833c059b2a926cad0b08e8653306d275dc668f68 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sun, 28 Nov 2021 20:38:58 -0500 Subject: [PATCH 121/161] provide an option to skip neighbor stat (#1313) * provide an option to skip neighbor stat Resolves #1088. Also fixes a bug in `update_sel`. * fix tests --- deepmd/entrypoints/main.py | 5 +++++ deepmd/entrypoints/train.py | 9 +++++++-- doc/train/training-advanced.md | 3 +++ source/tests/test_train.py | 4 ++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/deepmd/entrypoints/main.py b/deepmd/entrypoints/main.py index 4f772526c8..632d35fd71 100644 --- a/deepmd/entrypoints/main.py +++ b/deepmd/entrypoints/main.py @@ -170,6 +170,11 @@ def parse_args(args: Optional[List[str]] = None): default=None, help="Initialize the training from the frozen model.", ) + parser_train.add_argument( + "--skip-neighbor-stat", + action="store_true", + help="Skip calculating neighbor statistics. Sel checking, automatic sel, and model compression will be disabled.", + ) # * freeze script ****************************************************************** parser_frz = subparsers.add_parser( diff --git a/deepmd/entrypoints/train.py b/deepmd/entrypoints/train.py index 093c95fbff..42e21d2ba4 100755 --- a/deepmd/entrypoints/train.py +++ b/deepmd/entrypoints/train.py @@ -38,6 +38,7 @@ def train( log_level: int, log_path: Optional[str], is_compress: bool = False, + skip_neighbor_stat: bool = False, **kwargs, ): """Run DeePMD model training. @@ -62,6 +63,8 @@ def train( logging file path or None if logs are to be output only to stdout is_compress: bool indicates whether in the model compress mode + skip_neighbor_stat : bool, default=False + skip checking neighbor statistics Raises ------ @@ -87,7 +90,7 @@ def train( jdata = normalize(jdata) - if not is_compress: + if not is_compress and not skip_neighbor_stat: jdata = update_sel(jdata) with open(output, "w") as fp: @@ -333,10 +336,12 @@ def update_one_sel(jdata, descriptor): def update_sel(jdata): + log.info("Calculate neighbor statistics... (add --skip-neighbor-stat to skip this step)") descrpt_data = jdata['model']['descriptor'] if descrpt_data['type'] == 'hybrid': for ii in range(len(descrpt_data['list'])): - descrpt_data['list'][ii] = update_one_sel(jdata, descrpt_data['list'][ii]) + if descrpt_data['list'][ii]['type'] != 'loc_frame': + descrpt_data['list'][ii] = update_one_sel(jdata, descrpt_data['list'][ii]) elif descrpt_data['type'] != 'loc_frame': descrpt_data = update_one_sel(jdata, descrpt_data) jdata['model']['descriptor'] = descrpt_data diff --git a/doc/train/training-advanced.md b/doc/train/training-advanced.md index 865b12e460..7b4c475ae7 100644 --- a/doc/train/training-advanced.md +++ b/doc/train/training-advanced.md @@ -114,6 +114,7 @@ optional arguments: --init-frz-model INIT_FRZ_MODEL Initialize the training from the frozen model. + --skip-neighbor-stat Skip calculating neighbor statistics. Sel checking, automatic sel, and model compression will be disabled. (default: False) ``` **`--init-model model.ckpt`**, initializes the model training with an existing model that is stored in the checkpoint `model.ckpt`, the network architectures should match. @@ -122,6 +123,8 @@ optional arguments: **`--init-frz-model frozen_model.pb`**, initializes the training with an existing model that is stored in `frozen_model.pb`. +**`--skip-neighbor-stat`** will skip calculating neighbor statistics if one is concerned about performance. Some features will be disabled. + To get the best performance, one should control the number of threads used by DeePMD-kit. This is achieved by three environmental variables: `OMP_NUM_THREADS`, `TF_INTRA_OP_PARALLELISM_THREADS` and `TF_INTER_OP_PARALLELISM_THREADS`. `OMP_NUM_THREADS` controls the multithreading of DeePMD-kit implemented operations. `TF_INTRA_OP_PARALLELISM_THREADS` and `TF_INTER_OP_PARALLELISM_THREADS` controls `intra_op_parallelism_threads` and `inter_op_parallelism_threads`, which are Tensorflow configurations for multithreading. An explanation is found [here](https://www.intel.com/content/www/us/en/developer/articles/technical/maximize-tensorflow-performance-on-cpu-considerations-and-recommendations-for-inference.html). For example if you wish to use 3 cores of 2 CPUs on one node, you may set the environmental variables and run DeePMD-kit as follows: diff --git a/source/tests/test_train.py b/source/tests/test_train.py index 21892fb8fe..2b7fd16d18 100644 --- a/source/tests/test_train.py +++ b/source/tests/test_train.py @@ -53,10 +53,12 @@ def test_update_sel_hybrid(self, sel_mock): 'type' : 'hybrid', 'list' : [ { + 'type': 'se_e2_a', 'rcut': 6, 'sel': "auto" }, { + 'type': 'se_e2_a', 'rcut': 6, 'sel': "auto:1.5" } @@ -70,10 +72,12 @@ def test_update_sel_hybrid(self, sel_mock): 'type' : 'hybrid', 'list' : [ { + 'type': 'se_e2_a', 'rcut': 6, 'sel': [12,24] }, { + 'type': 'se_e2_a', 'rcut': 6, 'sel': [16,32] } From 29e59d3a3ca58a6ee8ebd2a6ecaee7fce81e8fd6 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 29 Nov 2021 02:59:36 -0500 Subject: [PATCH 122/161] Fix test errors with TensorFlow 2.7 (#1315) * fix C++ tests with TF 2.7 * tensorflow package was renamed * add RPATH * revert; add protobuf * revert change to action * revert 574d5e19c24d99e5b341ff7496c4c178344f48fe --- .github/workflows/build_cc.yml | 1 + source/api_cc/tests/CMakeLists.txt | 12 +++++++++++- source/cmake/Findtensorflow.cmake | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_cc.yml b/.github/workflows/build_cc.yml index 115bdb017f..36bb0201e1 100644 --- a/.github/workflows/build_cc.yml +++ b/.github/workflows/build_cc.yml @@ -23,3 +23,4 @@ jobs: DP_VARIANT: ${{ matrix.variant }} CC: gcc-7 CXX: g++-7 + CONDA_OVERRIDE_CUDA: 11.3 diff --git a/source/api_cc/tests/CMakeLists.txt b/source/api_cc/tests/CMakeLists.txt index 1a5b56fca0..caa62c82f8 100644 --- a/source/api_cc/tests/CMakeLists.txt +++ b/source/api_cc/tests/CMakeLists.txt @@ -9,7 +9,6 @@ add_definitions ("-DHIGH_PREC") enable_testing() -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") # model version file(READ ${PROJECT_SOURCE_DIR}/../../config/MODEL_VER MODEL_VERSION) @@ -42,6 +41,11 @@ add_library(${opname} SHARED ${OP_SRC}) list (APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/../../cmake/) find_package(tensorflow REQUIRED) +if (TENSORFLOW_VERSION GREATER_EQUAL 2.7) + set (CMAKE_CXX_STANDARD 14) +else() + set (CMAKE_CXX_STANDARD 11) +endif() include_directories(${TensorFlow_INCLUDE_DIRS}) find_package(Threads) @@ -102,6 +106,12 @@ else() target_link_libraries(runUnitTests gtest gtest_main ${libname} ${apiname} ${opname} pthread ${TensorFlow_LIBRARY} rt coverage_config) endif() +find_package(Protobuf) +if(Protobuf_FOUND) + include_directories(${Protobuf_INCLUDE_DIRS}) + target_link_libraries(runUnitTests ${Protobuf_LIBRARIES}) +endif() + add_test( runUnitTests runUnitTests ) find_package(GTest) diff --git a/source/cmake/Findtensorflow.cmake b/source/cmake/Findtensorflow.cmake index cdc66f997c..43fc97894c 100644 --- a/source/cmake/Findtensorflow.cmake +++ b/source/cmake/Findtensorflow.cmake @@ -15,7 +15,7 @@ if (BUILD_CPP_IF AND INSTALL_TENSORFLOW) # Here we try to install libtensorflow_cc using conda install. if (USE_CUDA_TOOLKIT) - set (VARIANT gpu) + set (VARIANT cuda) else () set (VARIANT cpu) endif () From 6ce4225e0133e9dd606bc12190903614b9c272a3 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Tue, 30 Nov 2021 18:48:49 -0500 Subject: [PATCH 123/161] add an error message to compress/freeze (#1319) As reported by #1318, the message was not covered here. --- deepmd/entrypoints/compress.py | 10 +++++++++- deepmd/entrypoints/freeze.py | 9 ++++++++- deepmd/utils/errors.py | 2 +- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/deepmd/entrypoints/compress.py b/deepmd/entrypoints/compress.py index bb3f2241e9..caf19cef63 100644 --- a/deepmd/entrypoints/compress.py +++ b/deepmd/entrypoints/compress.py @@ -137,7 +137,15 @@ def compress( # stage 2: freeze the model log.info("\n\n") log.info("stage 2: freeze the model") - freeze(checkpoint_folder=checkpoint_folder, output=output, node_names=None) + try: + freeze(checkpoint_folder=checkpoint_folder, output=output, node_names=None) + except GraphTooLargeError as e: + raise RuntimeError( + "The uniform step size of the tabulation's first table is %f, " + "which is too small. This leads to a very large graph size, " + "exceeding protobuf's limitation (2 GB). You should try to " + "increase the step size." % step + ) from e def _check_compress_type(model_file): try: diff --git a/deepmd/entrypoints/freeze.py b/deepmd/entrypoints/freeze.py index 9d17786e1a..8b0acf0e98 100755 --- a/deepmd/entrypoints/freeze.py +++ b/deepmd/entrypoints/freeze.py @@ -200,7 +200,14 @@ def freeze( # We retrieve the protobuf graph definition graph = tf.get_default_graph() - input_graph_def = graph.as_graph_def() + try: + input_graph_def = graph.as_graph_def() + except google.protobuf.message.DecodeError as e: + raise GraphTooLargeError( + "The graph size exceeds 2 GB, the hard limitation of protobuf." + " Then a DecodeError was raised by protobuf. You should " + "reduce the size of your model." + ) from e nodes = [n.name for n in input_graph_def.node] # We start a session and restore the graph weights diff --git a/deepmd/utils/errors.py b/deepmd/utils/errors.py index c4579c43a1..3fb35fae00 100644 --- a/deepmd/utils/errors.py +++ b/deepmd/utils/errors.py @@ -1,5 +1,5 @@ class GraphTooLargeError(Exception): - pass + """The graph is too large, exceeding protobuf's hard limit of 2GB.""" class GraphWithoutTensorError(Exception): pass From 67d3567f3a18994fb7d699eddfab173518c100a6 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Wed, 1 Dec 2021 08:22:19 -0500 Subject: [PATCH 124/161] redirect `print_summary` to LAMMPS log (#1324) This commit redirects `print_summary` output to LAMMPS log function, so it will be also shown in `log.lammps`. --- source/lmp/pair_deepmd.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/lmp/pair_deepmd.cpp b/source/lmp/pair_deepmd.cpp index 5cb058e1e8..eec4a503b5 100644 --- a/source/lmp/pair_deepmd.cpp +++ b/source/lmp/pair_deepmd.cpp @@ -267,6 +267,12 @@ void PairDeepMD::print_summary(const string pre) const { if (comm->me == 0){ + // capture cout to a string, then call LAMMPS's utils::logmesg + // https://stackoverflow.com/a/4043813/9567349 + std::stringstream buffer; + std::streambuf *sbuf = std::cout.rdbuf(); + std::cout.rdbuf(buffer.rdbuf()); + cout << "Summary of lammps deepmd module ..." << endl; cout << pre << ">>> Info of deepmd-kit:" << endl; deep_pot.print_summary(pre); @@ -279,6 +285,9 @@ PairDeepMD::print_summary(const string pre) const cout << pre << "build float prec: " << STR_FLOAT_PREC << endl; cout << pre << "build with tf inc: " << STR_TensorFlow_INCLUDE_DIRS << endl; cout << pre << "build with tf lib: " << STR_TensorFlow_LIBRARY << endl; + + std::cout.rdbuf(sbuf); + utils::logmesg(lmp, buffer.str()); } } From 6af84975b1d83bdebaf39efb818723b1fdb1518c Mon Sep 17 00:00:00 2001 From: Denghui Lu Date: Thu, 2 Dec 2021 08:11:34 +0800 Subject: [PATCH 125/161] fix bug of hip model compression (#1325) --- source/lib/src/rocm/tabulate.hip.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/lib/src/rocm/tabulate.hip.cu b/source/lib/src/rocm/tabulate.hip.cu index 6b6270c18f..10bc849db4 100644 --- a/source/lib/src/rocm/tabulate.hip.cu +++ b/source/lib/src/rocm/tabulate.hip.cu @@ -426,7 +426,7 @@ __global__ void tabulate_fusion_se_t_grad_grad_fifth_order_polynomial( const FPTYPE max, const FPTYPE stride0, const FPTYPE stride1, - const int nnei, + const int nnei_i, const int nnei_j, const int last_layer_size) { From bcd5a3b09d432f9c4cea2e7fed82f8b1f951749e Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Wed, 1 Dec 2021 19:13:58 -0500 Subject: [PATCH 126/161] unify C++ errors and pass message to LAMMPS (#1326) This commits unifies all C++ errors (including TF) to `deepmd::deepmd_exception` and passes the error message to LAMMPS. --- source/api_cc/include/common.h | 12 ++++++++++- source/api_cc/src/DeepPot.cc | 12 +++++------ source/api_cc/src/DeepTensor.cc | 2 +- source/api_cc/src/common.cc | 6 +++--- source/lib/include/errors.h | 9 ++++++--- source/lmp/pair_deepmd.cpp | 36 +++++++++++++++++++++++++++++++++ 6 files changed, 63 insertions(+), 14 deletions(-) diff --git a/source/api_cc/include/common.h b/source/api_cc/include/common.h index 6d24f9d5c3..96f6057358 100644 --- a/source/api_cc/include/common.h +++ b/source/api_cc/include/common.h @@ -6,6 +6,7 @@ #include "version.h" #include "neighbor_list.h" #include "AtomMap.h" +#include "errors.h" #include "tensorflow/core/platform/env.h" #include "tensorflow/core/public/session.h" @@ -115,8 +116,17 @@ void get_env_nthreads(int & num_intra_nthreads, int & num_inter_nthreads); +/** @struct deepmd::deepmd_exception + **/ + +/** +* @brief Throw exception if TensorFlow doesn't work. +**/ struct -tf_exception: public std::exception { +tf_exception: public deepmd::deepmd_exception { +public: + tf_exception(): deepmd::deepmd_exception("TensorFlow Error!") {}; + tf_exception(const std::string& msg): deepmd::deepmd_exception(std::string("TensorFlow Error: ") + msg) {}; }; /** diff --git a/source/api_cc/src/DeepPot.cc b/source/api_cc/src/DeepPot.cc index a8890498e5..d1126d3844 100644 --- a/source/api_cc/src/DeepPot.cc +++ b/source/api_cc/src/DeepPot.cc @@ -220,7 +220,7 @@ init (const std::string & model, const int & gpu_rank, const std::string & file_ model_version = "0.0"; } if(! model_compatable(model_version)){ - throw std::runtime_error( + throw deepmd::deepmd_exception( "incompatable model: version " + model_version + " in graph, but version " + global_model_version + " supported "); @@ -307,10 +307,10 @@ validate_fparam_aparam(const int & nloc, const std::vector &aparam)const { if (fparam.size() != dfparam) { - throw std::runtime_error("the dim of frame parameter provided is not consistent with what the model uses"); + throw deepmd::deepmd_exception("the dim of frame parameter provided is not consistent with what the model uses"); } if (aparam.size() != daparam * nloc) { - throw std::runtime_error("the dim of atom parameter provided is not consistent with what the model uses"); + throw deepmd::deepmd_exception("the dim of atom parameter provided is not consistent with what the model uses"); } } @@ -552,7 +552,7 @@ init (const std::vector & models, const int & gpu_rank, const std:: model_type = get_scalar("model_attr/model_type"); model_version = get_scalar("model_attr/model_version"); if(! model_compatable(model_version)){ - throw std::runtime_error( + throw deepmd::deepmd_exception( "incompatable model: version " + model_version + " in graph, but version " + global_model_version + " supported "); @@ -636,10 +636,10 @@ validate_fparam_aparam(const int & nloc, const std::vector &aparam)const { if (fparam.size() != dfparam) { - throw std::runtime_error("the dim of frame parameter provided is not consistent with what the model uses"); + throw deepmd::deepmd_exception("the dim of frame parameter provided is not consistent with what the model uses"); } if (aparam.size() != daparam * nloc) { - throw std::runtime_error("the dim of atom parameter provided is not consistent with what the model uses"); + throw deepmd::deepmd_exception("the dim of atom parameter provided is not consistent with what the model uses"); } } diff --git a/source/api_cc/src/DeepTensor.cc b/source/api_cc/src/DeepTensor.cc index e6ec684f28..419c97aa65 100644 --- a/source/api_cc/src/DeepTensor.cc +++ b/source/api_cc/src/DeepTensor.cc @@ -44,7 +44,7 @@ init (const std::string & model, model_type = get_scalar("model_attr/model_type"); model_version = get_scalar("model_attr/model_version"); if(! model_compatable(model_version)){ - throw std::runtime_error( + throw deepmd::deepmd_exception( "incompatable model: version " + model_version + " in graph, but version " + global_model_version + " supported "); diff --git a/source/api_cc/src/common.cc b/source/api_cc/src/common.cc index 92883f04fe..8693c14b5c 100644 --- a/source/api_cc/src/common.cc +++ b/source/api_cc/src/common.cc @@ -27,10 +27,10 @@ model_compatable( std::vector words_mv = split(model_version, "."); std::vector words_gmv = split(global_model_version, "."); if(words_mv.size() != 2){ - throw std::runtime_error("invalid graph model version string " + model_version); + throw deepmd::deepmd_exception("invalid graph model version string " + model_version); } if(words_gmv.size() != 2){ - throw std::runtime_error("invalid supported model version string " + global_model_version); + throw deepmd::deepmd_exception("invalid supported model version string " + global_model_version); } int model_version_major = atoi(words_mv[0].c_str()); int model_version_minor = atoi(words_mv[1].c_str()); @@ -201,7 +201,7 @@ deepmd:: check_status(const tensorflow::Status& status) { if (!status.ok()) { std::cout << status.ToString() << std::endl; - throw deepmd::tf_exception(); + throw deepmd::tf_exception(status.ToString()); } } diff --git a/source/lib/include/errors.h b/source/lib/include/errors.h index fe0a21fc50..29329768ea 100644 --- a/source/lib/include/errors.h +++ b/source/lib/include/errors.h @@ -4,6 +4,9 @@ #include namespace deepmd{ + /** + * @brief General DeePMD-kit exception. Throw if anything doesn't work. + **/ struct deepmd_exception: public std::runtime_error { public: @@ -12,9 +15,9 @@ namespace deepmd{ }; struct - deepmd_exception_oom: public std::runtime_error{ + deepmd_exception_oom: public deepmd_exception{ public: - deepmd_exception_oom(): runtime_error("DeePMD-kit OOM!") {}; - deepmd_exception_oom(const std::string& msg): runtime_error(std::string("DeePMD-kit OOM: ") + msg) {}; + deepmd_exception_oom(): deepmd_exception("DeePMD-kit OOM!") {}; + deepmd_exception_oom(const std::string& msg): deepmd_exception(std::string("DeePMD-kit OOM: ") + msg) {}; }; }; \ No newline at end of file diff --git a/source/lmp/pair_deepmd.cpp b/source/lmp/pair_deepmd.cpp index eec4a503b5..17ea766ae2 100644 --- a/source/lmp/pair_deepmd.cpp +++ b/source/lmp/pair_deepmd.cpp @@ -379,7 +379,11 @@ void PairDeepMD::compute(int eflag, int vflag) //cvflag_atom is the right flag for the cvatom matrix if ( ! (eflag_atom || cvflag_atom) ) { #ifdef HIGH_PREC + try { deep_pot.compute (dener, dforce, dvirial, dcoord, dtype, dbox, nghost, lmp_list, ago, fparam, daparam); + } catch(deepmd::deepmd_exception& e) { + error->all(FLERR, e.what()); + } #else vector dcoord_(dcoord.size()); vector dbox_(dbox.size()); @@ -388,7 +392,11 @@ void PairDeepMD::compute(int eflag, int vflag) vector dforce_(dforce.size(), 0); vector dvirial_(dvirial.size(), 0); double dener_ = 0; + try { deep_pot.compute (dener_, dforce_, dvirial_, dcoord_, dtype, dbox_, nghost, lmp_list, ago, fparam, daparam); + } catch(deepmd::deepmd_exception& e) { + error->all(FLERR, e.what()); + } for (unsigned dd = 0; dd < dforce.size(); ++dd) dforce[dd] = dforce_[dd]; for (unsigned dd = 0; dd < dvirial.size(); ++dd) dvirial[dd] = dvirial_[dd]; dener = dener_; @@ -410,7 +418,11 @@ void PairDeepMD::compute(int eflag, int vflag) vector deatom_(dforce.size(), 0); vector dvatom_(dforce.size(), 0); double dener_ = 0; + try { deep_pot.compute (dener_, dforce_, dvirial_, deatom_, dvatom_, dcoord_, dtype, dbox_, nghost, lmp_list, ago, fparam, daparam); + } catch(deepmd::deepmd_exception& e) { + error->all(FLERR, e.what()); + } for (unsigned dd = 0; dd < dforce.size(); ++dd) dforce[dd] = dforce_[dd]; for (unsigned dd = 0; dd < dvirial.size(); ++dd) dvirial[dd] = dvirial_[dd]; for (unsigned dd = 0; dd < deatom.size(); ++dd) deatom[dd] = deatom_[dd]; @@ -452,7 +464,11 @@ void PairDeepMD::compute(int eflag, int vflag) vector all_energy; vector> all_atom_energy; vector> all_atom_virial; + try { deep_pot_model_devi.compute(all_energy, all_force, all_virial, all_atom_energy, all_atom_virial, dcoord, dtype, dbox, nghost, lmp_list, ago, fparam, daparam); + } catch(deepmd::deepmd_exception& e) { + error->all(FLERR, e.what()); + } // deep_pot_model_devi.compute_avg (dener, all_energy); // deep_pot_model_devi.compute_avg (dforce, all_force); // deep_pot_model_devi.compute_avg (dvirial, all_virial); @@ -478,7 +494,11 @@ void PairDeepMD::compute(int eflag, int vflag) vector> all_virial_; vector> all_atom_energy_; vector> all_atom_virial_; + try { deep_pot_model_devi.compute(all_energy_, all_force_, all_virial_, all_atom_energy_, all_atom_virial_, dcoord_, dtype, dbox_, nghost, lmp_list, ago, fparam, daparam); + } catch(deepmd::deepmd_exception& e) { + error->all(FLERR, e.what()); + } // deep_pot_model_devi.compute_avg (dener_, all_energy_); // deep_pot_model_devi.compute_avg (dforce_, all_force_); // deep_pot_model_devi.compute_avg (dvirial_, all_virial_); @@ -688,7 +708,11 @@ void PairDeepMD::compute(int eflag, int vflag) else { if (numb_models == 1) { #ifdef HIGH_PREC + try { deep_pot.compute (dener, dforce, dvirial, dcoord, dtype, dbox); + } catch(deepmd::deepmd_exception& e) { + error->all(FLERR, e.what()); + } #else vector dcoord_(dcoord.size()); vector dbox_(dbox.size()); @@ -697,7 +721,11 @@ void PairDeepMD::compute(int eflag, int vflag) vector dforce_(dforce.size(), 0); vector dvirial_(dvirial.size(), 0); double dener_ = 0; + try { deep_pot.compute (dener_, dforce_, dvirial_, dcoord_, dtype, dbox_); + } catch(deepmd::deepmd_exception& e) { + error->all(FLERR, e.what()); + } for (unsigned dd = 0; dd < dforce.size(); ++dd) dforce[dd] = dforce_[dd]; for (unsigned dd = 0; dd < dvirial.size(); ++dd) dvirial[dd] = dvirial_[dd]; dener = dener_; @@ -793,15 +821,23 @@ void PairDeepMD::settings(int narg, char **arg) } numb_models = models.size(); if (numb_models == 1) { + try { deep_pot.init (arg[0], get_node_rank(), get_file_content(arg[0])); + } catch(deepmd::deepmd_exception& e) { + error->all(FLERR, e.what()); + } cutoff = deep_pot.cutoff (); numb_types = deep_pot.numb_types(); dim_fparam = deep_pot.dim_fparam(); dim_aparam = deep_pot.dim_aparam(); } else { + try { deep_pot.init (arg[0], get_node_rank(), get_file_content(arg[0])); deep_pot_model_devi.init(models, get_node_rank(), get_file_content(models)); + } catch(deepmd::deepmd_exception& e) { + error->all(FLERR, e.what()); + } cutoff = deep_pot_model_devi.cutoff(); numb_types = deep_pot_model_devi.numb_types(); dim_fparam = deep_pot_model_devi.dim_fparam(); From 0393766376f33199a6fed8bc72b8c3d9abdd7899 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Wed, 1 Dec 2021 19:14:21 -0500 Subject: [PATCH 127/161] optimize `DPTabulate._build_lower` method (#1323) This commit optimizes the execution time of `_build_lower` from 1s to 0.1s. --- deepmd/utils/tabulate.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/deepmd/utils/tabulate.py b/deepmd/utils/tabulate.py index b9372cad2f..cf0f17571e 100644 --- a/deepmd/utils/tabulate.py +++ b/deepmd/utils/tabulate.py @@ -177,26 +177,26 @@ def build(self, def _build_lower(self, net, xx, idx, upper, lower, stride0, stride1, extrapolate): vv, dd, d2 = self._make_data(xx, idx) self.data[net] = np.zeros([self.nspline, 6 * self.last_layer_size], dtype = self.data_type) - # for jj in tqdm(range(self.nspline), desc = 'DEEPMD INFO |-> deepmd.utils.tabulate\t\t\t' + net + ', tabulating'): - for jj in range(self.nspline): - for kk in range(self.last_layer_size): - if isinstance(self.descrpt, deepmd.descriptor.DescrptSeA): - if jj < int((upper - lower) / stride0): - tt = stride0 - else: - tt = stride1 - elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeT): - if jj > int((lower - extrapolate * lower) / stride1) and jj < (int((lower - extrapolate * lower) / stride1) + int((upper - lower) / stride0)): - tt = stride0 - else: - tt = stride1 - hh = vv[jj + 1][kk] - vv[jj][kk] - self.data[net][jj][kk * 6 + 0] = vv[jj][kk] - self.data[net][jj][kk * 6 + 1] = dd[jj][kk] - self.data[net][jj][kk * 6 + 2] = 0.5 * d2[jj][kk] - self.data[net][jj][kk * 6 + 3] = (1 / (2 * tt * tt * tt)) * (20 * hh - (8 * dd[jj + 1][kk] + 12 * dd[jj][kk]) * tt - (3 * d2[jj][kk] - d2[jj + 1][kk]) * tt * tt) - self.data[net][jj][kk * 6 + 4] = (1 / (2 * tt * tt * tt * tt)) * (-30 * hh + (14 * dd[jj + 1][kk] + 16 * dd[jj][kk]) * tt + (3 * d2[jj][kk] - 2 * d2[jj + 1][kk]) * tt * tt) - self.data[net][jj][kk * 6 + 5] = (1 / (2 * tt * tt * tt * tt * tt)) * (12 * hh - 6 * (dd[jj + 1][kk] + dd[jj][kk]) * tt + (d2[jj + 1][kk] - d2[jj][kk]) * tt * tt) + + # tt.shape: [self.nspline, self.last_layer_size] + if isinstance(self.descrpt, deepmd.descriptor.DescrptSeA): + tt = np.full((self.nspline, self.last_layer_size), stride1) + tt[:int((upper - lower) / stride0), :] = stride0 + elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeT): + tt = np.full((self.nspline, self.last_layer_size), stride1) + tt[int((lower - extrapolate * lower) / stride1) + 1:(int((lower - extrapolate * lower) / stride1) + int((upper - lower) / stride0)), :] = stride0 + else: + raise RuntimeError("Unsupported descriptor") + + # hh.shape: [self.nspline, self.last_layer_size] + hh = vv[1:self.nspline+1, :self.last_layer_size] - vv[:self.nspline, :self.last_layer_size] + + self.data[net][:, :6 * self.last_layer_size:6] = vv[:self.nspline, :self.last_layer_size] + self.data[net][:, 1:6 * self.last_layer_size:6] = dd[:self.nspline, :self.last_layer_size] + self.data[net][:, 2:6 * self.last_layer_size:6] = 0.5 * d2[:self.nspline, :self.last_layer_size] + self.data[net][:, 3:6 * self.last_layer_size:6] = (1 / (2 * tt * tt * tt)) * (20 * hh - (8 * dd[1:self.nspline+1, :self.last_layer_size] + 12 * dd[:self.nspline, :self.last_layer_size]) * tt - (3 * d2[:self.nspline, :self.last_layer_size] - d2[1:self.nspline+1, :self.last_layer_size]) * tt * tt) + self.data[net][:, 4:6 * self.last_layer_size:6] = (1 / (2 * tt * tt * tt * tt)) * (-30 * hh + (14 * dd[1:self.nspline+1, :self.last_layer_size] + 16 * dd[:self.nspline, :self.last_layer_size]) * tt + (3 * d2[:self.nspline, :self.last_layer_size] - 2 * d2[1:self.nspline+1, :self.last_layer_size]) * tt * tt) + self.data[net][:, 5:6 * self.last_layer_size:6] = (1 / (2 * tt * tt * tt * tt * tt)) * (12 * hh - 6 * (dd[1:self.nspline+1, :self.last_layer_size] + dd[:self.nspline, :self.last_layer_size]) * tt + (d2[1:self.nspline+1, :self.last_layer_size] - d2[:self.nspline, :self.last_layer_size]) * tt * tt) def _load_sub_graph(self): sub_graph_def = tf.GraphDef() From 5076263a6f1a08fda826b1602c17e1e20f63fd29 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Thu, 2 Dec 2021 19:00:09 -0500 Subject: [PATCH 128/161] fix a typo in install_lammps.md (#1328) --- doc/install/install-lammps.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/install/install-lammps.md b/doc/install/install-lammps.md index d7c979ef39..64687068a4 100644 --- a/doc/install/install-lammps.md +++ b/doc/install/install-lammps.md @@ -58,7 +58,7 @@ make -j4 make install ``` -If everything works fine, you will end up with an executable `${deepmd_root}/lmp`. +If everything works fine, you will end up with an executable `${deepmd_root}/bin/lmp`. ```bash -${deepmd_root}/lmp -h +${deepmd_root}/bin/lmp -h ``` From 3c505732e5cefc19ef2d7171723f994ddcfaa3e6 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 13 Dec 2021 19:17:01 -0500 Subject: [PATCH 129/161] prevents rcut_smth larger than rcut (#1354) See #1341. --- deepmd/descriptor/se_a.py | 2 ++ deepmd/descriptor/se_r.py | 2 ++ deepmd/descriptor/se_t.py | 2 ++ 3 files changed, 6 insertions(+) diff --git a/deepmd/descriptor/se_a.py b/deepmd/descriptor/se_a.py index 30bb1bfbd9..3b8790911f 100644 --- a/deepmd/descriptor/se_a.py +++ b/deepmd/descriptor/se_a.py @@ -120,6 +120,8 @@ def __init__ (self, """ Constructor """ + if rcut < rcut_smth: + raise RuntimeError("rcut_smth (%f) should be no more than rcut (%f)!" % (rcut_smth, rcut)) self.sel_a = sel self.rcut_r = rcut self.rcut_r_smth = rcut_smth diff --git a/deepmd/descriptor/se_r.py b/deepmd/descriptor/se_r.py index 4d4a39074b..429590c07f 100644 --- a/deepmd/descriptor/se_r.py +++ b/deepmd/descriptor/se_r.py @@ -83,6 +83,8 @@ def __init__ (self, # .add("activation_function", str, default = "tanh") \ # .add("precision", str, default = "default") # class_data = args.parse(jdata) + if rcut < rcut_smth: + raise RuntimeError("rcut_smth (%f) should be no more than rcut (%f)!" % (rcut_smth, rcut)) self.sel_r = sel self.rcut = rcut self.rcut_smth = rcut_smth diff --git a/deepmd/descriptor/se_t.py b/deepmd/descriptor/se_t.py index 08a6e620c2..84ce07eddf 100644 --- a/deepmd/descriptor/se_t.py +++ b/deepmd/descriptor/se_t.py @@ -67,6 +67,8 @@ def __init__ (self, """ Constructor """ + if rcut < rcut_smth: + raise RuntimeError("rcut_smth (%f) should be no more than rcut (%f)!" % (rcut_smth, rcut)) self.sel_a = sel self.rcut_r = rcut self.rcut_r_smth = rcut_smth From 2223ff355e6d594997ff0dc21f1a93004ac9af96 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Tue, 14 Dec 2021 19:59:12 -0500 Subject: [PATCH 130/161] Fix cell and virial transpose bug in dp_ipi (#1353) * Fix cell and virial transpose bug in dp_ipi i-Pi assumes column-major storage to store cell and virial. Fix #1351. See i-Pi manual: https://ipi-code.org/i-pi/_static/manual.pdf See also i-pi/i-pi#48 and i-pi/i-pi#196. * fix dbox * fix --- source/ipi/driver.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/ipi/driver.cc b/source/ipi/driver.cc index 61ed851317..d147f286ff 100644 --- a/source/ipi/driver.cc +++ b/source/ipi/driver.cc @@ -158,7 +158,7 @@ int main(int argc, char * argv[]) readbuffer_ (&socket, (char *)(cell_h), 9*sizeof(double)); readbuffer_ (&socket, (char *)(cell_ih), 9*sizeof(double)); for (int dd = 0; dd < 9; ++dd){ - dbox[dd] = cell_h[dd] * cvt_len; + dbox[dd] = cell_h[(dd%3)*3+(dd/3)] * cvt_len; } region.reinitBox (&dbox[0]); @@ -209,7 +209,7 @@ int main(int argc, char * argv[]) msg_buff[ii] = dforce[ii] * icvt_f; } for (int ii = 0; ii < 9; ++ii){ - virial[ii] = dvirial[ii] * icvt_ener * (1.0); + virial[ii] = dvirial[(ii%3)*3+(ii/3)] * icvt_ener * (1.0); } if (b_verb) std::cout << "# energy of sys. : " << std::scientific << std::setprecision(10) << dener << std::endl; writebuffer_ (&socket, msg_forceready, MSGLEN); From 7f3c218a903dd0ca6fa98fc79009db06e351c3d7 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sun, 19 Dec 2021 02:28:17 -0500 Subject: [PATCH 131/161] enable OpenMP for `prod_force` and `prod_virial` (#1360) * enable OpenMP for `prod_force` and `prod_virial` About 1 ms can be saved in each training step. * bugfix * spawn the threads only once --- source/lib/src/prod_force.cc | 6 ++++++ source/lib/src/prod_force_grad.cc | 2 ++ source/lib/src/prod_virial.cc | 6 ++++++ source/lib/src/prod_virial_grad.cc | 2 ++ 4 files changed, 16 insertions(+) diff --git a/source/lib/src/prod_force.cc b/source/lib/src/prod_force.cc index e9784d3409..b1286e7b14 100644 --- a/source/lib/src/prod_force.cc +++ b/source/lib/src/prod_force.cc @@ -36,14 +36,17 @@ prod_force_a_cpu( memset(force, 0.0, sizeof(FPTYPE) * nall * 3); // compute force of a frame + #pragma omp parallel for (int i_idx = 0; i_idx < nloc; ++i_idx) { // deriv wrt center atom + #pragma omp single for (int aa = 0; aa < ndescrpt; ++aa) { force[i_idx * 3 + 0] -= net_deriv[i_idx * ndescrpt + aa] * env_deriv[i_idx * ndescrpt * 3 + aa * 3 + 0]; force[i_idx * 3 + 1] -= net_deriv[i_idx * ndescrpt + aa] * env_deriv[i_idx * ndescrpt * 3 + aa * 3 + 1]; force[i_idx * 3 + 2] -= net_deriv[i_idx * ndescrpt + aa] * env_deriv[i_idx * ndescrpt * 3 + aa * 3 + 2]; } // deriv wrt neighbors + #pragma omp for for (int jj = 0; jj < nnei; ++jj) { int j_idx = nlist[i_idx * nnei + jj]; if (j_idx < 0) continue; @@ -105,15 +108,18 @@ prod_force_r_cpu( } // compute force of a frame + #pragma omp parallel for (int ii = 0; ii < nloc; ++ii){ int i_idx = ii; // deriv wrt center atom + #pragma omp single for (int aa = 0; aa < ndescrpt; ++aa){ force[i_idx * 3 + 0] -= net_deriv[i_idx * ndescrpt + aa] * env_deriv[i_idx * ndescrpt * 3 + aa * 3 + 0]; force[i_idx * 3 + 1] -= net_deriv[i_idx * ndescrpt + aa] * env_deriv[i_idx * ndescrpt * 3 + aa * 3 + 1]; force[i_idx * 3 + 2] -= net_deriv[i_idx * ndescrpt + aa] * env_deriv[i_idx * ndescrpt * 3 + aa * 3 + 2]; } // deriv wrt neighbors + #pragma omp for for (int jj = 0; jj < nnei; ++jj){ int j_idx = nlist[i_idx * nnei + jj]; // if (j_idx > nloc) j_idx = j_idx % nloc; diff --git a/source/lib/src/prod_force_grad.cc b/source/lib/src/prod_force_grad.cc index 110bf790f4..78bad3c9ca 100644 --- a/source/lib/src/prod_force_grad.cc +++ b/source/lib/src/prod_force_grad.cc @@ -42,6 +42,7 @@ prod_force_grad_a_cpu( } // compute grad of one frame + #pragma omp parallel for for (int ii = 0; ii < nloc; ++ii){ int i_idx = ii; @@ -120,6 +121,7 @@ prod_force_grad_r_cpu( } // compute grad of one frame + #pragma omp parallel for for (int ii = 0; ii < nloc; ++ii){ int i_idx = ii; diff --git a/source/lib/src/prod_virial.cc b/source/lib/src/prod_virial.cc index f1c598c807..d715cf9e5b 100644 --- a/source/lib/src/prod_virial.cc +++ b/source/lib/src/prod_virial.cc @@ -44,6 +44,7 @@ prod_virial_a_cpu( } // compute virial of a frame + #pragma omp parallel for for (int ii = 0; ii < nloc; ++ii){ int i_idx = ii; @@ -58,7 +59,9 @@ prod_virial_a_cpu( for (int dd0 = 0; dd0 < 3; ++dd0){ for (int dd1 = 0; dd1 < 3; ++dd1){ FPTYPE tmp_v = pref * rij[i_idx * nnei * 3 + jj * 3 + dd1] * env_deriv[i_idx * ndescrpt * 3 + aa * 3 + dd0]; + #pragma omp atomic virial[dd0 * 3 + dd1] -= tmp_v; + #pragma omp atomic atom_virial[j_idx * 9 + dd0 * 3 + dd1] -= tmp_v; } } @@ -120,6 +123,7 @@ prod_virial_r_cpu( } // compute virial of a frame + #pragma omp parallel for for (int ii = 0; ii < nloc; ++ii){ int i_idx = ii; @@ -131,7 +135,9 @@ prod_virial_r_cpu( for (int dd0 = 0; dd0 < 3; ++dd0){ for (int dd1 = 0; dd1 < 3; ++dd1){ FPTYPE tmp_v = pref * rij[i_idx * nnei * 3 + jj * 3 + dd1] * env_deriv[i_idx * ndescrpt * 3 + jj * 3 + dd0]; + #pragma omp atomic virial[dd0 * 3 + dd1] -= tmp_v; + #pragma omp atomic atom_virial[j_idx * 9 + dd0 * 3 + dd1] -= tmp_v; } } diff --git a/source/lib/src/prod_virial_grad.cc b/source/lib/src/prod_virial_grad.cc index 8e225c0793..0f8495c90e 100644 --- a/source/lib/src/prod_virial_grad.cc +++ b/source/lib/src/prod_virial_grad.cc @@ -41,6 +41,7 @@ prod_virial_grad_a_cpu( } // compute grad of one frame + #pragma omp parallel for for (int ii = 0; ii < nloc; ++ii){ int i_idx = ii; @@ -117,6 +118,7 @@ prod_virial_grad_r_cpu( } // compute grad of one frame + #pragma omp parallel for for (int ii = 0; ii < nloc; ++ii){ int i_idx = ii; From 6611dfdb17e8f9b35be4dcaf2cdfc4bbd83d5b6b Mon Sep 17 00:00:00 2001 From: Jia-Xin Zhu <53895049+ChiahsinChu@users.noreply.github.com> Date: Tue, 21 Dec 2021 14:21:33 +0800 Subject: [PATCH 132/161] fix bug in DipoleFittingSeA: (#1363) self.sel_type is set wrongly when input value is None Co-authored-by: Han Wang Co-authored-by: Jia-Xin Zhu --- deepmd/fit/dipole.py | 1 - 1 file changed, 1 deletion(-) diff --git a/deepmd/fit/dipole.py b/deepmd/fit/dipole.py index 7c2d5dea86..56dc777631 100644 --- a/deepmd/fit/dipole.py +++ b/deepmd/fit/dipole.py @@ -67,7 +67,6 @@ def __init__ (self, self.sel_type = sel_type if self.sel_type is None: self.sel_type = [ii for ii in range(self.ntypes)] - self.sel_type = sel_type self.seed = seed self.uniform_seed = uniform_seed self.seed_shift = one_layer_rand_seed_shift() From ffd2a76fdfd0d6dcb7c1e2329acc19d81f75f0ac Mon Sep 17 00:00:00 2001 From: tuoping <80671886+tuoping@users.noreply.github.com> Date: Tue, 21 Dec 2021 14:44:53 +0800 Subject: [PATCH 133/161] fix typo in doc/troubleshooting/howtoset_netsize.md (#1300) Co-authored-by: Han Wang --- doc/troubleshooting/howtoset_netsize.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/troubleshooting/howtoset_netsize.md b/doc/troubleshooting/howtoset_netsize.md index 800abb9a08..3bb07dcb80 100644 --- a/doc/troubleshooting/howtoset_netsize.md +++ b/doc/troubleshooting/howtoset_netsize.md @@ -62,7 +62,7 @@ Embedding-net size | Energy L2err(eV) | Energy L2err/Natoms(eV) | Force L2err(eV [10,20,40] | 4.263060e-02 | 1.665258e-04 | 8.955818e-02 [5,10,20] | 4.994913e-02 | 1.951138e-04 | 9.007786e-02 [4,8,16] | 1.022157e-01 | 3.992802e-04 | 9.532119e-02 -[3,9,12] | 1.362098e-01 | 5.320695e-04 | 1.073860e-01 +[3,6,12] | 1.362098e-01 | 5.320695e-04 | 1.073860e-01 [2,4,8] | 7.061800e-02 | 2.758515e-04 | 9.126418e-02 [1,2,4] && seed = 1 | 9.843161e-02 | 3.844985e-04 | 9.348505e-02 [1,2,4] && seed = 2 | 9.404335e-02 | 3.673568e-04 | 9.304089e-02 From 1c1756aa3b0721556f9c9f653ffb90cc295bf163 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Thu, 23 Dec 2021 18:45:55 -0500 Subject: [PATCH 134/161] Update issue templates (#1368) * Create config.yml * Delete request-for-help.md * Update bug-report.md * Update and rename bug-report.md to bug-report.yml * Update bug-report.yml * Update bug-report.yml * Update bug-report.yml * Update bug-report.yml * Update bug-report.yml * Update and rename feature-request.md to feature-request.yml * Update feature-request.yml * Update feature-request.yml * Update and rename parameters.md to parameters.yml * Update bug-report.yml * Update feature-request.yml * Update parameters.yml * Update and rename generic-issue.md to generic-issue.yml * Update generic-issue.yml * Update bug-report.yml * Update config.yml * Update bug-report.yml --- .github/ISSUE_TEMPLATE/bug-report.md | 27 --------- .github/ISSUE_TEMPLATE/bug-report.yml | 69 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 5 ++ .github/ISSUE_TEMPLATE/feature-request.md | 21 ------- .github/ISSUE_TEMPLATE/feature-request.yml | 33 +++++++++++ .github/ISSUE_TEMPLATE/generic-issue.md | 23 -------- .github/ISSUE_TEMPLATE/generic-issue.yml | 47 +++++++++++++++ .github/ISSUE_TEMPLATE/parameters.md | 25 -------- .github/ISSUE_TEMPLATE/parameters.yml | 33 +++++++++++ .github/ISSUE_TEMPLATE/request-for-help.md | 21 ------- 10 files changed, 187 insertions(+), 117 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug-report.md create mode 100644 .github/ISSUE_TEMPLATE/bug-report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml delete mode 100644 .github/ISSUE_TEMPLATE/feature-request.md create mode 100644 .github/ISSUE_TEMPLATE/feature-request.yml delete mode 100644 .github/ISSUE_TEMPLATE/generic-issue.md create mode 100644 .github/ISSUE_TEMPLATE/generic-issue.yml delete mode 100644 .github/ISSUE_TEMPLATE/parameters.md create mode 100644 .github/ISSUE_TEMPLATE/parameters.yml delete mode 100644 .github/ISSUE_TEMPLATE/request-for-help.md diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md deleted file mode 100644 index 80c0ef7a13..0000000000 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -name: Bug report -about: Create a bug report to help us eliminate issues and improve deepmd-kit. If - this doesn’t look right, [choose a different type](https://github.com/deepmodeling/deepmd-kit/issues/new/choose). -title: "[BUG] _Replace With Suitable Title_" -labels: bug -assignees: '' - ---- - -**Summary** - - - -**Deepmd-kit version, installation way, input file, running commands, error log, etc.** - - - - - -**Steps to Reproduce** - - - -**Further Information, Files, and Links** - - diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 0000000000..e13eb9daf7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,69 @@ +name: Bug report +description: Create a bug report to help us eliminate issues and improve deepmd-kit. +title: "[BUG] _Replace With Suitable Title_" +labels: bug +assignees: [] +body: + - type: textarea + id: summary + attributes: + label: Bug summary + description: Please provide a clear and concise description of what the bug is. + placeholder: + value: + validations: + required: true + - type: input + id: version + attributes: + label: DeePMD-kit Version + description: "`dp --version`" + validations: + required: true + - type: input + id: tf-version + attributes: + label: TensorFlow Version + description: "The version will be printed when running DeePMD-kit." + validations: + required: true + - type: dropdown + id: installation-way + attributes: + label: How did you download the software? + options: + - Offline packages + - conda + - docker + - pip + - Built from source + - Others (write below) + validations: + required: true + - type: textarea + id: log + attributes: + label: Input Files, Running Commands, Error Log, etc. + description: "Please provide necessary information including input file, running commands, error log , etc., AS DETAILED AS POSSIBLE to help locate and reproduce your problem. WARNING: Do not use image to show error log! Paste texts in a code block instead." + placeholder: + value: + validations: + required: true + - type: textarea + id: reproduce + attributes: + label: Steps to Reproduce + description: "Describe the steps required to (quickly) reproduce the issue. You can attach (small) files to the section below or add URLs where to download an archive with all necessary files. Please try to create an input set that is as minimal and small as possible and reproduces the bug as quickly as possible. **NOTE:** the less effort and time it takes to reproduce your reported bug, the more likely it becomes, that somebody will look into it and fix the problem." + placeholder: + value: + validations: + required: true + - type: textarea + id: further + attributes: + label: Further Information, Files, and Links + description: Put any additional information here, attach relevant text or image files and URLs to external sites, e.g. relevant publications + placeholder: + value: + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..4c4c6cb750 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Request for Help + url: https://github.com/deepmodeling/deepmd-kit/discussions/491 + about: If you have a usage question diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md deleted file mode 100644 index d099345615..0000000000 --- a/.github/ISSUE_TEMPLATE/feature-request.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project. If this doesn’t work right, [choose a different - type]( https://github.com/deepmodeling/deepmd-kit/issues/new/choose) -title: "[Feature Request] _Replace with Title_" -labels: enhancement -assignees: '' - ---- - -**Summary** - - - -**Detailed Description** - - - -**Further Information, Files, and Links** - - diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 0000000000..ec76a91927 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,33 @@ +name: Feature request +description: Suggest an idea for this project. +title: "[Feature Request] _Replace with Title_" +labels: enhancement +assignees: [] +body: + - type: textarea + id: summary + attributes: + label: Summary + description: "Please provide a brief and concise description of the suggested feature or change" + placeholder: + value: + validations: + required: true + - type: textarea + id: details + attributes: + label: Detailed Description + description: "Please explain how you would like to see deepmd-kit enhanced, what feature(s) you are looking for, what specific problems this will solve. If possible, provide references to relevant background information like publications or web pages, and whether you are planning to implement the enhancement yourself or would like to participate in the implementation. If applicable add a reference to an existing bug report or issue that this will address." + placeholder: + value: + validations: + required: true + - type: textarea + id: further + attributes: + label: Further Information, Files, and Links + description: Put any additional information here, attach relevant text or image files and URLs to external sites, e.g. relevant publications + placeholder: + value: + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/generic-issue.md b/.github/ISSUE_TEMPLATE/generic-issue.md deleted file mode 100644 index 00b0913afb..0000000000 --- a/.github/ISSUE_TEMPLATE/generic-issue.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -name: Generic issue -about: For issues that do not fit any of the other categories. If this doesn’t work - right, [choose a different type]( https://github.com/deepmodeling/deepmd-kit/issues/new/choose). -title: _Replace With a Descriptive Title_ -labels: wontfix -assignees: '' - ---- - -**Summary** - - - -**Deepmd-kit Version, Python Version, Tensorflow Version, GCC Version and Cuda Version.** - - - - - -**Details** - - diff --git a/.github/ISSUE_TEMPLATE/generic-issue.yml b/.github/ISSUE_TEMPLATE/generic-issue.yml new file mode 100644 index 0000000000..5fe3cd8851 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/generic-issue.yml @@ -0,0 +1,47 @@ +name: Generic issue +description: For issues that do not fit any of the other categories. +title: _Replace With a Descriptive Title_ +labels: wontfix +assignees: [] +body: + - type: textarea + id: summary + attributes: + label: Summary + description: "Please provide a clear and concise description of what the question is." + placeholder: + value: + validations: + required: true + - type: input + id: version + attributes: + label: DeePMD-kit Version + description: "`dp --version`" + validations: + required: true + - type: input + id: tf-version + attributes: + label: TensorFlow Version + description: "The version will be printed when running DeePMD-kit." + validations: + required: true + - type: textarea + id: other-version + attributes: + label: Python Version, CUDA Version, GCC Version, LAMMPS Version, etc + description: "If applicable, specify what platform you are running on." + placeholder: + value: + validations: + required: false + - type: textarea + id: details + attributes: + label: Details + description: "Please explain the issue in detail here." + placeholder: + value: + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/parameters.md b/.github/ISSUE_TEMPLATE/parameters.md deleted file mode 100644 index ff9188f06e..0000000000 --- a/.github/ISSUE_TEMPLATE/parameters.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -name: Parameters -about: Make a suggestion for a change of input parameters or a new output to deepmd-kit. - If this doesn’t work right, [choose a different type]( https://github.com/deepmodeling/deepmd-kit/issues/new/choose). -title: "[Parameters] _Replace With Suitable Title_" -labels: documentation, enhancement -assignees: '' - ---- - -**Summary** - - - -**Summary** - - - -**Detailed Description** - - - -**Further Information, Files, and Links** - - diff --git a/.github/ISSUE_TEMPLATE/parameters.yml b/.github/ISSUE_TEMPLATE/parameters.yml new file mode 100644 index 0000000000..df7397d782 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/parameters.yml @@ -0,0 +1,33 @@ +name: Parameters +description: Make a suggestion for a change of input parameters or a new output to deepmd-kit. +title: "[Parameters] _Replace With Suitable Title_" +labels: documentation, enhancement +assignees: [] +body: + - type: textarea + id: summary + attributes: + label: Summary + description: "Please provide a clear and concise description of what your request is." + placeholder: + value: + validations: + required: true + - type: textarea + id: details + attributes: + label: Detailed Description + description: "Please explain how you would like to see deepmd-kit enhanced. Specify your material system, and exactly what behaviors or properties you are looking for, or what specific problems this will solve. If possible, provide references to relevant background information like publications or web pages, and whether you are planning to implement the enhancement yourself or would like to participate in the implementation." + placeholder: + value: + validations: + required: true + - type: textarea + id: further + attributes: + label: Further Information, Files, and Links + description: Put any additional information here, attach relevant text or image files and URLs to external sites, e.g. relevant publications + placeholder: + value: + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/request-for-help.md b/.github/ISSUE_TEMPLATE/request-for-help.md deleted file mode 100644 index daeb6a85a6..0000000000 --- a/.github/ISSUE_TEMPLATE/request-for-help.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: Request for Help -about: Don't post help requests here, go to [discussions](https://github.com/deepmodeling/deepmd-kit/discussions) - instead. -title: '' -labels: '' -assignees: '' - ---- - -Before asking questions, you can - -search the previous issues or discussions -check the [document](https://deepmd.readthedocs.io/en/stable), especially [training parameters](https://deepmd.readthedocs.io/en/stable/train-input.html). - -Please **do not** post requests for help (e.g. with installing or using deepmd-kit) here. -Instead go to [discussions](https://github.com/deepmodeling/deepmd-kit/discussions). - -This issue tracker is for tracking deepmd-kit development related issues only. - -Thanks for your cooperation. From 50373796af632fcb43c199f419437db163e8e35f Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Thu, 23 Dec 2021 18:46:31 -0500 Subject: [PATCH 135/161] [ImgBot] Optimize images (#1369) /examples/water/gmx/rdf.png -- 167.16kb -> 141.24kb (15.5%) Signed-off-by: ImgBotApp Co-authored-by: ImgBotApp Co-authored-by: Han Wang From 70c0e73622febf579aba86a60a50a76f8a400a89 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Thu, 23 Dec 2021 18:49:47 -0500 Subject: [PATCH 136/161] fix cxx standard for LAMMPS (#1379) --- source/lmp/env.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/lmp/env.sh.in b/source/lmp/env.sh.in index 4bfe613f16..b419154a32 100644 --- a/source/lmp/env.sh.in +++ b/source/lmp/env.sh.in @@ -6,6 +6,6 @@ TF_INCLUDE_DIRS=`echo $TENSORFLOW_INCLUDE_DIRS | sed "s/;/ -I/g"` TF_LIBRARY_PATH=`echo $TENSORFLOW_LIBRARY_PATH | sed "s/;/ -L/g"` TF_RPATH=`echo $TENSORFLOW_LIBRARY_PATH | sed "s/;/ -Wl,-rpath=/g"` -NNP_INC=" -std=c++11 -D@prec_def@ @TTM_DEF@ -DLAMMPS_VERSION_NUMBER=@LAMMPS_VERSION_NUMBER@ -I$TF_INCLUDE_DIRS -I$DEEPMD_ROOT/include/ " +NNP_INC=" -std=c++@CMAKE_CXX_STANDARD@ -D@prec_def@ @TTM_DEF@ -DLAMMPS_VERSION_NUMBER=@LAMMPS_VERSION_NUMBER@ -I$TF_INCLUDE_DIRS -I$DEEPMD_ROOT/include/ " NNP_PATH=" -L$TF_LIBRARY_PATH -L$DEEPMD_ROOT/lib" NNP_LIB=" -Wl,--no-as-needed -l@LIB_DEEPMD_CC@@variant_name@ -ltensorflow_cc -ltensorflow_framework -Wl,-rpath=$TF_RPATH -Wl,-rpath=$DEEPMD_ROOT/lib" From dbc61e21163bc42e8b7901315a53504873351777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yifan=20Li=E6=9D=8E=E4=B8=80=E5=B8=86?= Date: Fri, 24 Dec 2021 07:28:57 -0500 Subject: [PATCH 137/161] explicitly set neighbor request to full in compute deeptensor/atom to fix bug #1381 (#1382) --- source/lmp/compute_deeptensor_atom.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/lmp/compute_deeptensor_atom.cpp b/source/lmp/compute_deeptensor_atom.cpp index 85f7d0a332..e29897266a 100644 --- a/source/lmp/compute_deeptensor_atom.cpp +++ b/source/lmp/compute_deeptensor_atom.cpp @@ -70,7 +70,7 @@ void ComputeDeeptensorAtom::init() neighbor->requests[irequest]->half = 0; neighbor->requests[irequest]->pair = 0; neighbor->requests[irequest]->compute = 1; - // neighbor->requests[irequest]->full = 1; + neighbor->requests[irequest]->full = 1; neighbor->requests[irequest]->occasional = 1; } From 1a51b5dfb421a31fe197859b3c68107ea35138b6 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Tue, 28 Dec 2021 02:00:07 -0500 Subject: [PATCH 138/161] fix NameError (#1385) --- deepmd/entrypoints/freeze.py | 1 + 1 file changed, 1 insertion(+) diff --git a/deepmd/entrypoints/freeze.py b/deepmd/entrypoints/freeze.py index 8b0acf0e98..172c765646 100755 --- a/deepmd/entrypoints/freeze.py +++ b/deepmd/entrypoints/freeze.py @@ -7,6 +7,7 @@ """ import logging +import google.protobuf.message from deepmd.env import tf, FITTING_NET_PATTERN from deepmd.utils.sess import run_sess from deepmd.utils.graph import get_pattern_nodes_from_graph_def From 082a199b9cb55e7d4136b6ebd9de0a556af65294 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Thu, 30 Dec 2021 23:35:48 -0500 Subject: [PATCH 139/161] fix network precision under specific situation (#1391) * fix filter network precision * fix fitting energy precision * fix one more precision * cast back precision * add a decorator to cast precision * fix typo * fix compatibility issue with TF 1.8 * fix missing self * improve docstrings --- deepmd/common.py | 79 +++++++++++++++++++++++++++++++++++++++ deepmd/descriptor/se.py | 5 +++ deepmd/descriptor/se_a.py | 12 +++--- deepmd/descriptor/se_r.py | 10 ++--- deepmd/descriptor/se_t.py | 6 +-- deepmd/fit/dipole.py | 10 +++-- deepmd/fit/ener.py | 19 +++++----- deepmd/fit/fitting.py | 8 ++++ deepmd/fit/polar.py | 10 +++-- 9 files changed, 126 insertions(+), 33 deletions(-) create mode 100644 deepmd/fit/fitting.py diff --git a/deepmd/common.py b/deepmd/common.py index 9968cff39c..695fee1a93 100644 --- a/deepmd/common.py +++ b/deepmd/common.py @@ -20,6 +20,7 @@ import yaml from deepmd.env import op_module, tf +from tensorflow.python.framework import tensor_util from deepmd.env import GLOBAL_TF_FLOAT_PRECISION, GLOBAL_NP_FLOAT_PRECISION from deepmd.utils.sess import run_sess from deepmd.utils.errors import GraphWithoutTensorError @@ -487,3 +488,81 @@ def get_np_precision(precision: "_PRECISION") -> np.dtype: return np.float64 else: raise RuntimeError(f"{precision} is not a valid precision") + + +def safe_cast_tensor(input: tf.Tensor, + from_precision: tf.DType, + to_precision: tf.DType) -> tf.Tensor: + """Convert a Tensor from a precision to another precision. + + If input is not a Tensor or without the specific precision, the method will not + cast it. + + Parameters + ---------- + input: tf.Tensor + input tensor + precision : tf.DType + Tensor data type that casts to + + Returns + ------- + tf.Tensor + casted Tensor + """ + if tensor_util.is_tensor(input) and input.dtype == from_precision: + return tf.cast(input, to_precision) + return input + + +def cast_precision(func: Callable) -> Callable: + """A decorator that casts and casts back the input + and output tensor of a method. + + The decorator should be used in a classmethod. + + The decorator will do the following thing: + (1) It casts input Tensors from `GLOBAL_TF_FLOAT_PRECISION` + to precision defined by property `precision`. + (2) It casts output Tensors from `precision` to + `GLOBAL_TF_FLOAT_PRECISION`. + (3) It checks inputs and outputs and only casts when + input or output is a Tensor and its dtype matches + `GLOBAL_TF_FLOAT_PRECISION` and `precision`, respectively. + If it does not match (e.g. it is an integer), the decorator + will do nothing on it. + + Parameters + ---------- + precision : tf.DType + Tensor data type that casts to + + Returns + ------- + Callable + a decorator that casts and casts back the input and + output tensor of a method + + Examples + -------- + >>> class A: + ... @property + ... def precision(self): + ... return tf.float32 + ... + ... @cast_precision + ... def f(x: tf.Tensor, y: tf.Tensor) -> tf.Tensor: + ... return x ** 2 + y + """ + def wrapper(self, *args, **kwargs): + # only convert tensors + returned_tensor = func( + self, + *[safe_cast_tensor(vv, GLOBAL_TF_FLOAT_PRECISION, self.precision) for vv in args], + **{kk: safe_cast_tensor(vv, GLOBAL_TF_FLOAT_PRECISION, self.precision) for kk, vv in kwargs.items()}, + ) + if isinstance(returned_tensor, tuple): + return tuple((safe_cast_tensor(vv, self.precision, GLOBAL_TF_FLOAT_PRECISION) for vv in returned_tensor)) + else: + return safe_cast_tensor(returned_tensor, self.precision, GLOBAL_TF_FLOAT_PRECISION) + return wrapper diff --git a/deepmd/descriptor/se.py b/deepmd/descriptor/se.py index c7470568b6..c42a7fb46d 100644 --- a/deepmd/descriptor/se.py +++ b/deepmd/descriptor/se.py @@ -106,3 +106,8 @@ def init_variables(self, The suffix of the scope """ self.embedding_net_variables = get_embedding_net_variables(model_file, suffix = suffix) + + @property + def precision(self) -> tf.DType: + """Precision of filter network.""" + return self.filter_precision diff --git a/deepmd/descriptor/se_a.py b/deepmd/descriptor/se_a.py index 3b8790911f..db2980b882 100644 --- a/deepmd/descriptor/se_a.py +++ b/deepmd/descriptor/se_a.py @@ -3,7 +3,7 @@ from typing import Tuple, List, Dict, Any from deepmd.env import tf -from deepmd.common import get_activation_func, get_precision, ACTIVATION_FN_DICT, PRECISION_DICT, docstring_parameter +from deepmd.common import get_activation_func, get_precision, ACTIVATION_FN_DICT, PRECISION_DICT, docstring_parameter, cast_precision from deepmd.utils.argcheck import list_to_doc from deepmd.env import GLOBAL_TF_FLOAT_PRECISION from deepmd.env import GLOBAL_NP_FLOAT_PRECISION @@ -558,7 +558,7 @@ def _pass_filter(self, [ 0, start_index* self.ndescrpt], [-1, natoms[2+type_i]* self.ndescrpt] ) inputs_i = tf.reshape(inputs_i, [-1, self.ndescrpt]) - layer, qmat = self._filter(tf.cast(inputs_i, self.filter_precision), type_i, name='filter_type_'+str(type_i)+suffix, natoms=natoms, reuse=reuse, trainable = trainable, activation_fn = self.filter_activation_fn) + layer, qmat = self._filter(inputs_i, type_i, name='filter_type_'+str(type_i)+suffix, natoms=natoms, reuse=reuse, trainable = trainable, activation_fn = self.filter_activation_fn) layer = tf.reshape(layer, [tf.shape(inputs)[0], natoms[2+type_i] * self.get_dim_out()]) qmat = tf.reshape(qmat, [tf.shape(inputs)[0], natoms[2+type_i] * self.get_dim_rot_mat_1() * 3]) output.append(layer) @@ -568,7 +568,7 @@ def _pass_filter(self, inputs_i = inputs inputs_i = tf.reshape(inputs_i, [-1, self.ndescrpt]) type_i = -1 - layer, qmat = self._filter(tf.cast(inputs_i, self.filter_precision), type_i, name='filter_type_all'+suffix, natoms=natoms, reuse=reuse, trainable = trainable, activation_fn = self.filter_activation_fn, type_embedding=type_embedding) + layer, qmat = self._filter(inputs_i, type_i, name='filter_type_all'+suffix, natoms=natoms, reuse=reuse, trainable = trainable, activation_fn = self.filter_activation_fn, type_embedding=type_embedding) layer = tf.reshape(layer, [tf.shape(inputs)[0], natoms[0] * self.get_dim_out()]) qmat = tf.reshape(qmat, [tf.shape(inputs)[0], natoms[0] * self.get_dim_rot_mat_1() * 3]) output.append(layer) @@ -704,7 +704,6 @@ def _filter_lower( # with (natom x nei_type_i) x 1 xyz_scatter = tf.reshape(tf.slice(inputs_reshape, [0,0],[-1,1]),[-1,1]) if type_embedding is not None: - type_embedding = tf.cast(type_embedding, self.filter_precision) xyz_scatter = self._concat_type_embedding( xyz_scatter, nframes, natoms, type_embedding) if self.compress: @@ -736,7 +735,7 @@ def _filter_lower( if (not self.uniform_seed) and (self.seed is not None): self.seed += self.seed_shift else: # we can safely return the final xyz_scatter filled with zero directly - return tf.cast(tf.fill((natom, 4, outputs_size[-1]), 0.), GLOBAL_TF_FLOAT_PRECISION) + return tf.cast(tf.fill((natom, 4, outputs_size[-1]), 0.), self.filter_precision) # natom x nei_type_i x out_size xyz_scatter = tf.reshape(xyz_scatter, (-1, shape_i[1]//4, outputs_size[-1])) # When using tf.reshape(inputs_i, [-1, shape_i[1]//4, 4]) below @@ -747,6 +746,7 @@ def _filter_lower( return tf.matmul(tf.reshape(inputs_i, [natom, shape_i[1]//4, 4]), xyz_scatter, transpose_a = True) + @cast_precision def _filter( self, inputs, @@ -759,8 +759,6 @@ def _filter( name='linear', reuse=None, trainable = True): - if self.mixed_prec is not None: - inputs = tf.cast(inputs, get_precision(self.mixed_prec['compute_prec'])) nframes = tf.shape(tf.reshape(inputs, [-1, natoms[0], self.ndescrpt]))[0] # natom x (nei x 4) shape = inputs.get_shape().as_list() diff --git a/deepmd/descriptor/se_r.py b/deepmd/descriptor/se_r.py index 429590c07f..79ca850c83 100644 --- a/deepmd/descriptor/se_r.py +++ b/deepmd/descriptor/se_r.py @@ -2,7 +2,7 @@ from typing import Tuple, List from deepmd.env import tf -from deepmd.common import get_activation_func, get_precision, ACTIVATION_FN_DICT, PRECISION_DICT, docstring_parameter +from deepmd.common import get_activation_func, get_precision, ACTIVATION_FN_DICT, PRECISION_DICT, docstring_parameter, cast_precision from deepmd.utils.argcheck import list_to_doc from deepmd.env import GLOBAL_TF_FLOAT_PRECISION from deepmd.env import GLOBAL_NP_FLOAT_PRECISION @@ -392,7 +392,7 @@ def _pass_filter(self, [ 0, start_index* self.ndescrpt], [-1, natoms[2+type_i]* self.ndescrpt] ) inputs_i = tf.reshape(inputs_i, [-1, self.ndescrpt]) - layer = self._filter_r(tf.cast(inputs_i, self.filter_precision), type_i, name='filter_type_'+str(type_i)+suffix, natoms=natoms, reuse=reuse, trainable = trainable, activation_fn = self.filter_activation_fn) + layer = self._filter_r(self.filter_precision, type_i, name='filter_type_'+str(type_i)+suffix, natoms=natoms, reuse=reuse, trainable = trainable, activation_fn = self.filter_activation_fn) layer = tf.reshape(layer, [tf.shape(inputs)[0], natoms[2+type_i] * self.get_dim_out()]) output.append(layer) start_index += natoms[2+type_i] @@ -400,7 +400,7 @@ def _pass_filter(self, inputs_i = inputs inputs_i = tf.reshape(inputs_i, [-1, self.ndescrpt]) type_i = -1 - layer = self._filter_r(tf.cast(inputs_i, self.filter_precision), type_i, name='filter_type_all'+suffix, natoms=natoms, reuse=reuse, trainable = trainable, activation_fn = self.filter_activation_fn) + layer = self._filter_r(inputs_i, type_i, name='filter_type_all'+suffix, natoms=natoms, reuse=reuse, trainable = trainable, activation_fn = self.filter_activation_fn) layer = tf.reshape(layer, [tf.shape(inputs)[0], natoms[0] * self.get_dim_out()]) output.append(layer) output = tf.concat(output, axis = 1) @@ -450,7 +450,7 @@ def _compute_std (self,sumv2, sumv, sumn) : val = 1e-2 return val - + @cast_precision def _filter_r(self, inputs, type_input, @@ -495,7 +495,7 @@ def _filter_r(self, xyz_scatter = tf.reshape(xyz_scatter, (-1, shape_i[1], outputs_size[-1])) else: natom = tf.shape(inputs)[0] - xyz_scatter = tf.cast(tf.fill((natom, shape_i[1], outputs_size[-1]), 0.), GLOBAL_TF_FLOAT_PRECISION) + xyz_scatter = tf.cast(tf.fill((natom, shape_i[1], outputs_size[-1]), 0.), self.filter_precision) xyz_scatter_total.append(xyz_scatter) # natom x nei x outputs_size diff --git a/deepmd/descriptor/se_t.py b/deepmd/descriptor/se_t.py index 84ce07eddf..d7dd845cb6 100644 --- a/deepmd/descriptor/se_t.py +++ b/deepmd/descriptor/se_t.py @@ -2,7 +2,7 @@ from typing import Tuple, List from deepmd.env import tf -from deepmd.common import get_activation_func, get_precision, ACTIVATION_FN_DICT, PRECISION_DICT, docstring_parameter +from deepmd.common import get_activation_func, get_precision, ACTIVATION_FN_DICT, PRECISION_DICT, docstring_parameter, cast_precision from deepmd.utils.argcheck import list_to_doc from deepmd.env import GLOBAL_TF_FLOAT_PRECISION from deepmd.env import GLOBAL_NP_FLOAT_PRECISION @@ -448,7 +448,7 @@ def _pass_filter(self, inputs_i = inputs inputs_i = tf.reshape(inputs_i, [-1, self.ndescrpt]) type_i = -1 - layer, qmat = self._filter(tf.cast(inputs_i, self.filter_precision), type_i, name='filter_type_all'+suffix, natoms=natoms, reuse=reuse, trainable = trainable, activation_fn = self.filter_activation_fn) + layer, qmat = self._filter(inputs_i, type_i, name='filter_type_all'+suffix, natoms=natoms, reuse=reuse, trainable = trainable, activation_fn = self.filter_activation_fn) layer = tf.reshape(layer, [tf.shape(inputs)[0], natoms[0] * self.get_dim_out()]) # qmat = tf.reshape(qmat, [tf.shape(inputs)[0], natoms[0] * self.get_dim_rot_mat_1() * 3]) output.append(layer) @@ -509,7 +509,7 @@ def _compute_std (self,sumv2, sumv, sumn) : val = 1e-2 return val - + @cast_precision def _filter(self, inputs, type_input, diff --git a/deepmd/fit/dipole.py b/deepmd/fit/dipole.py index 56dc777631..44e84dac1f 100644 --- a/deepmd/fit/dipole.py +++ b/deepmd/fit/dipole.py @@ -3,16 +3,17 @@ from typing import Tuple, List from deepmd.env import tf -from deepmd.common import add_data_requirement, get_activation_func, get_precision, ACTIVATION_FN_DICT, PRECISION_DICT, docstring_parameter +from deepmd.common import add_data_requirement, get_activation_func, get_precision, ACTIVATION_FN_DICT, PRECISION_DICT, docstring_parameter, cast_precision from deepmd.utils.argcheck import list_to_doc from deepmd.utils.network import one_layer, one_layer_rand_seed_shift from deepmd.utils.graph import get_fitting_net_variables from deepmd.descriptor import DescrptSeA +from deepmd.fit.fitting import Fitting from deepmd.env import global_cvt_2_tf_float from deepmd.env import GLOBAL_TF_FLOAT_PRECISION -class DipoleFittingSeA () : +class DipoleFittingSeA (Fitting) : """ Fit the atomic dipole with descriptor se_a @@ -90,6 +91,7 @@ def get_out_size(self) -> int: """ return 3 + @cast_precision def build (self, input_d : tf.Tensor, rot_mat : tf.Tensor, @@ -121,7 +123,7 @@ def build (self, The atomic dipole. """ start_index = 0 - inputs = tf.cast(tf.reshape(input_d, [-1, self.dim_descrpt * natoms[0]]), self.fitting_precision) + inputs = tf.reshape(input_d, [-1, self.dim_descrpt * natoms[0]]) rot_mat = tf.reshape(rot_mat, [-1, self.dim_rot_mat * natoms[0]]) count = 0 @@ -163,7 +165,7 @@ def build (self, count += 1 tf.summary.histogram('fitting_net_output', outs) - return tf.cast(tf.reshape(outs, [-1]), GLOBAL_TF_FLOAT_PRECISION) + return tf.reshape(outs, [-1]) # return tf.reshape(outs, [tf.shape(inputs)[0] * natoms[0] * 3 // 3]) def init_variables(self, diff --git a/deepmd/fit/ener.py b/deepmd/fit/ener.py index f82b68f41b..dca4e0d353 100644 --- a/deepmd/fit/ener.py +++ b/deepmd/fit/ener.py @@ -3,18 +3,17 @@ from typing import Tuple, List from deepmd.env import tf -from deepmd.common import ClassArg, add_data_requirement, get_activation_func, get_precision, ACTIVATION_FN_DICT, PRECISION_DICT, docstring_parameter +from deepmd.common import add_data_requirement, get_activation_func, get_precision, ACTIVATION_FN_DICT, PRECISION_DICT, docstring_parameter, cast_precision from deepmd.utils.argcheck import list_to_doc from deepmd.utils.network import one_layer, one_layer_rand_seed_shift -from deepmd.descriptor import DescrptLocFrame -from deepmd.descriptor import DescrptSeA from deepmd.utils.type_embed import embed_atom_type from deepmd.utils.graph import get_fitting_net_variables, load_graph_def, get_tensor_by_name_from_graph +from deepmd.fit.fitting import Fitting from deepmd.env import global_cvt_2_tf_float from deepmd.env import GLOBAL_TF_FLOAT_PRECISION -class EnerFitting (): +class EnerFitting (Fitting): r"""Fitting the energy of the system. The force and the virial can also be trained. The potential energy :math:`E` is a fitting network function of the descriptor :math:`\mathcal{D}`: @@ -132,7 +131,7 @@ def __init__ (self, self.atom_ener = [] for at, ae in enumerate(atom_ener): if ae is not None: - self.atom_ener.append(tf.constant(ae, GLOBAL_TF_FLOAT_PRECISION, name = "atom_%d_ener" % at)) + self.atom_ener.append(tf.constant(ae, self.fitting_precision, name = "atom_%d_ener" % at)) else: self.atom_ener.append(None) self.useBN = False @@ -329,7 +328,7 @@ def _build_lower( return final_layer - + @cast_precision def build (self, inputs : tf.Tensor, natoms : tf.Tensor, @@ -399,10 +398,10 @@ def build (self, trainable = False, initializer = tf.constant_initializer(self.aparam_inv_std)) - inputs = tf.cast(tf.reshape(inputs, [-1, self.dim_descrpt * natoms[0]]), self.fitting_precision) + inputs = tf.reshape(inputs, [-1, self.dim_descrpt * natoms[0]]) if len(self.atom_ener): # only for atom_ener - inputs_zero = tf.zeros_like(inputs, dtype=GLOBAL_TF_FLOAT_PRECISION) + inputs_zero = tf.zeros_like(inputs, dtype=self.fitting_precision) if bias_atom_e is not None : @@ -468,7 +467,7 @@ def build (self, axis=1 ) self.dim_descrpt = self.dim_descrpt + type_shape[1] - inputs = tf.cast(tf.reshape(inputs, [-1, self.dim_descrpt * natoms[0]]), self.fitting_precision) + inputs = tf.reshape(inputs, [-1, self.dim_descrpt * natoms[0]]) final_layer = self._build_lower( 0, natoms[0], inputs, fparam, aparam, @@ -485,7 +484,7 @@ def build (self, outs = tf.reshape(outs, [-1]) tf.summary.histogram('fitting_net_output', outs) - return tf.cast(tf.reshape(outs, [-1]), GLOBAL_TF_FLOAT_PRECISION) + return tf.reshape(outs, [-1]) def init_variables(self, diff --git a/deepmd/fit/fitting.py b/deepmd/fit/fitting.py new file mode 100644 index 0000000000..69c2c96e52 --- /dev/null +++ b/deepmd/fit/fitting.py @@ -0,0 +1,8 @@ +from deepmd.env import tf +from deepmd.utils import Plugin, PluginVariant + +class Fitting: + @property + def precision(self) -> tf.DType: + """Precision of fitting network.""" + return self.fitting_precision diff --git a/deepmd/fit/polar.py b/deepmd/fit/polar.py index 5f6ddd7525..e5632fadbb 100644 --- a/deepmd/fit/polar.py +++ b/deepmd/fit/polar.py @@ -3,12 +3,13 @@ from typing import Tuple, List from deepmd.env import tf -from deepmd.common import add_data_requirement, get_activation_func, get_precision, ACTIVATION_FN_DICT, PRECISION_DICT, docstring_parameter +from deepmd.common import add_data_requirement, cast_precision, get_activation_func, get_precision, ACTIVATION_FN_DICT, PRECISION_DICT, docstring_parameter from deepmd.utils.argcheck import list_to_doc from deepmd.utils.network import one_layer, one_layer_rand_seed_shift from deepmd.utils.graph import get_fitting_net_variables from deepmd.descriptor import DescrptLocFrame from deepmd.descriptor import DescrptSeA +from deepmd.fit.fitting import Fitting from deepmd.env import global_cvt_2_tf_float from deepmd.env import GLOBAL_TF_FLOAT_PRECISION @@ -102,7 +103,7 @@ def build (self, return tf.cast(tf.reshape(outs, [-1]), GLOBAL_TF_FLOAT_PRECISION) -class PolarFittingSeA () : +class PolarFittingSeA (Fitting) : """ Fit the atomic polarizability with descriptor se_a """ @@ -274,6 +275,7 @@ def compute_input_stats(self, for itype in range(len(self.sel_type)): self.constant_matrix[itype] = np.mean(np.diagonal(atom_polar[itype].reshape((3,3)))) + @cast_precision def build (self, input_d : tf.Tensor, rot_mat : tf.Tensor, @@ -305,7 +307,7 @@ def build (self, The atomic polarizability """ start_index = 0 - inputs = tf.cast(tf.reshape(input_d, [-1, self.dim_descrpt * natoms[0]]), self.fitting_precision) + inputs = tf.reshape(input_d, [-1, self.dim_descrpt * natoms[0]]) rot_mat = tf.reshape(rot_mat, [-1, self.dim_rot_mat * natoms[0]]) count = 0 @@ -372,7 +374,7 @@ def build (self, count += 1 tf.summary.histogram('fitting_net_output', outs) - return tf.cast(tf.reshape(outs, [-1]), GLOBAL_TF_FLOAT_PRECISION) + return tf.reshape(outs, [-1]) def init_variables(self, model_file: str From 7498fbb7c3452d2c8e47f2cf4857df4fce312d2f Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sun, 2 Jan 2022 09:20:41 -0500 Subject: [PATCH 140/161] add unittest for `deepmd.common.cast_precision` (#1394) --- source/tests/test_common.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/source/tests/test_common.py b/source/tests/test_common.py index 0f052663b2..adbde971e9 100644 --- a/source/tests/test_common.py +++ b/source/tests/test_common.py @@ -3,7 +3,8 @@ import unittest from pathlib import Path -from deepmd.common import expand_sys_str +from deepmd.common import expand_sys_str, cast_precision, GLOBAL_TF_FLOAT_PRECISION +from deepmd.env import tf # compute relative path # https://stackoverflow.com/questions/38083555/using-pathlibs-relative-to-for-directories-on-the-same-level @@ -46,3 +47,30 @@ def test_expand(self): ret = expand_sys_str('test_sys') ret.sort() self.assertEqual(ret, self.expected_out) + + +class TestCastPrecision(unittest.TestCase): + """This class tests `deepmd.common.cast_precision`.""" + @property + def precision(self): + if GLOBAL_TF_FLOAT_PRECISION == tf.float32: + return tf.float64 + return tf.float32 + + def test_cast_precision(self): + x = tf.zeros(1, dtype=GLOBAL_TF_FLOAT_PRECISION) + y = tf.zeros(1, dtype=tf.int64) + self.assertEqual(x.dtype, GLOBAL_TF_FLOAT_PRECISION) + self.assertEqual(y.dtype, tf.int64) + x, y, z = self._inner_method(x, y) + self.assertEqual(x.dtype, GLOBAL_TF_FLOAT_PRECISION) + self.assertEqual(y.dtype, tf.int64) + self.assertIsInstance(z, bool) + + @cast_precision + def _inner_method(self, x: tf.Tensor, y: tf.Tensor, z: bool = False) -> tf.Tensor: + # y and z should not be cast here + self.assertEqual(x.dtype, self.precision) + self.assertEqual(y.dtype, tf.int64) + self.assertIsInstance(z, bool) + return x, y, z From c5f88f3ba76064d37faf75f43d1b82718f5f77f1 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sun, 2 Jan 2022 09:27:00 -0500 Subject: [PATCH 141/161] initialize input virial vector to zero (#1397) Fix #1123. --- source/api_cc/src/DeepPot.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/api_cc/src/DeepPot.cc b/source/api_cc/src/DeepPot.cc index d1126d3844..8bf212e520 100644 --- a/source/api_cc/src/DeepPot.cc +++ b/source/api_cc/src/DeepPot.cc @@ -61,6 +61,8 @@ run_model (ENERGYTYPE & dener, for (unsigned ii = 0; ii < nall * 3; ++ii){ dforce[ii] = of(ii); } + // set dvirial to zero, prevent input vector is not zero (#1123) + std::fill(dvirial.begin(), dvirial.end(), 0.); for (int ii = 0; ii < nall; ++ii) { dvirial[0] += 1.0 * oav(9*ii+0); dvirial[1] += 1.0 * oav(9*ii+1); @@ -136,6 +138,8 @@ static void run_model (ENERGYTYPE & dener, for (int ii = 0; ii < nall * 9; ++ii) { datom_virial[ii] = oav(ii); } + // set dvirial to zero, prevent input vector is not zero (#1123) + std::fill(dvirial.begin(), dvirial.end(), 0.); for (int ii = 0; ii < nall; ++ii) { dvirial[0] += 1.0 * datom_virial[9*ii+0]; dvirial[1] += 1.0 * datom_virial[9*ii+1]; From 6f740ee96b475f72a2b8af5ab007a3ade3f5995d Mon Sep 17 00:00:00 2001 From: Denghui Lu Date: Fri, 7 Jan 2022 15:49:26 +0800 Subject: [PATCH 142/161] fix nvcc warning when using cuda-11.x toolkit (#1401) --- source/lib/src/cuda/CMakeLists.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/source/lib/src/cuda/CMakeLists.txt b/source/lib/src/cuda/CMakeLists.txt index eb1d1893bb..6941ba9532 100644 --- a/source/lib/src/cuda/CMakeLists.txt +++ b/source/lib/src/cuda/CMakeLists.txt @@ -36,8 +36,7 @@ if (${CUDA_VERSION_MAJOR} GREATER "11") ) elseif (${CUDA_VERSION_MAJOR} STREQUAL "11" AND ${CUDA_VERSION_MINOR} GREATER "0") # nvcc flags - set(CUDA_NVCC_FLAGS -gencode arch=compute_50,code=sm_50; - -gencode arch=compute_52,code=sm_52; # Tesla M40, Tesla M40, Quadro M6000... + set(CUDA_NVCC_FLAGS -gencode arch=compute_52,code=sm_52; # Tesla M40, Tesla M40, Quadro M6000... -gencode arch=compute_53,code=sm_53; -gencode arch=compute_60,code=sm_60; # Pascal – GP100/Tesla P100 – DGX-1 (Generic Pascal) -gencode arch=compute_61,code=sm_61; # Pascal - GTX 1080, GTX 1070, GTX 1060, GTX 1050, GTX 1030, Titan Xp, Tesla P40, Tesla P4, Discrete GPU on the NVIDIA Drive PX2 @@ -49,8 +48,7 @@ elseif (${CUDA_VERSION_MAJOR} STREQUAL "11" AND ${CUDA_VERSION_MINOR} GREATER "0 ) elseif (${CUDA_VERSION_MAJOR} STREQUAL "11" AND ${CUDA_VERSION_MINOR} STREQUAL "0") # nvcc flags - set(CUDA_NVCC_FLAGS -gencode arch=compute_50,code=sm_50; - -gencode arch=compute_52,code=sm_52; # Tesla M40, Tesla M40, Quadro M6000... + set(CUDA_NVCC_FLAGS -gencode arch=compute_52,code=sm_52; # Tesla M40, Tesla M40, Quadro M6000... -gencode arch=compute_53,code=sm_53; -gencode arch=compute_60,code=sm_60; # Pascal – GP100/Tesla P100 – DGX-1 (Generic Pascal) -gencode arch=compute_61,code=sm_61; # Pascal - GTX 1080, GTX 1070, GTX 1060, GTX 1050, GTX 1030, Titan Xp, Tesla P40, Tesla P4, Discrete GPU on the NVIDIA Drive PX2 From 0d302880abe4f9be9d26e2540487acbde4b488a1 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sun, 9 Jan 2022 21:48:27 -0500 Subject: [PATCH 143/161] bump LAMMPS version to `stable_29Sep2021_update2` (#1279) * bump default LAMMPS version to stable_29Sep2021_update1 bump LAMMPS to latest stable version * bump to `update2` --- doc/install/install-lammps.md | 18 +++++++++--------- source/install/build_cc.sh | 2 +- source/install/build_lammps.sh | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/doc/install/install-lammps.md b/doc/install/install-lammps.md index 64687068a4..55a264cdbb 100644 --- a/doc/install/install-lammps.md +++ b/doc/install/install-lammps.md @@ -12,12 +12,12 @@ make lammps DeePMD-kit will generate a module called `USER-DEEPMD` in the `build` directory. If you need low precision version, move `env_low.sh` to `env.sh` in the directory. Now download the LAMMPS code (`29Oct2020` or later), and uncompress it: ```bash cd /some/workspace -wget https://github.com/lammps/lammps/archive/stable_29Sep2021.tar.gz -tar xf stable_29Sep2021.tar.gz +wget https://github.com/lammps/lammps/archive/stable_29Sep2021_update2.tar.gz +tar xf stable_29Sep2021_update2.tar.gz ``` -The source code of LAMMPS is stored in directory `lammps-stable_29Sep2021`. Now go into the LAMMPS code and copy the DeePMD-kit module like this +The source code of LAMMPS is stored in directory `lammps-stable_29Sep2021_update2`. Now go into the LAMMPS code and copy the DeePMD-kit module like this ```bash -cd lammps-stable_29Sep2021/src/ +cd lammps-stable_29Sep2021_update2/src/ cp -r $deepmd_source_dir/source/build/USER-DEEPMD . ``` Now build LAMMPS @@ -43,13 +43,13 @@ Starting from `8Apr2021`, LAMMPS also provides a plugin mode, allowing one build Now download the LAMMPS code (`8Apr2021` or later), and uncompress it: ```bash cd /some/workspace -wget https://github.com/lammps/lammps/archive/stable_29Sep2021.tar.gz -tar xf stable_29Sep2021.tar.gz +wget https://github.com/lammps/lammps/archive/stable_29Sep2021_update2.tar.gz +tar xf stable_29Sep2021_update2.tar.gz ``` -The source code of LAMMPS is stored in directory `lammps-stable_29Sep2021`. Now go into the LAMMPS code and create a directory called `build` +The source code of LAMMPS is stored in directory `lammps-stable_29Sep2021_update2`. Now go into the LAMMPS code and create a directory called `build` ```bash -mkdir -p lammps-stable_29Sep2021/build/ -cd lammps-stable_29Sep2021/build/ +mkdir -p lammps-stable_29Sep2021_update2/build/ +cd lammps-stable_29Sep2021_update2/build/ ``` Now build LAMMPS. Note that `PLUGIN` and `KSPACE` package must be enabled, and `BUILD_SHARED_LIBS` must be set to `yes`. You can install any other package you want. ```bash diff --git a/source/install/build_cc.sh b/source/install/build_cc.sh index c7e7686812..a4da73651f 100755 --- a/source/install/build_cc.sh +++ b/source/install/build_cc.sh @@ -20,7 +20,7 @@ NPROC=$(nproc --all) BUILD_TMP_DIR=${SCRIPT_PATH}/../build mkdir -p ${BUILD_TMP_DIR} cd ${BUILD_TMP_DIR} -cmake -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} -DINSTALL_TENSORFLOW=TRUE ${CUDA_ARGS} -DLAMMPS_VERSION=stable_29Sep2021 -DUSE_TTM=TRUE .. +cmake -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} -DINSTALL_TENSORFLOW=TRUE ${CUDA_ARGS} -DLAMMPS_VERSION=stable_29Sep2021_update2 -DUSE_TTM=TRUE .. make -j${NPROC} make install diff --git a/source/install/build_lammps.sh b/source/install/build_lammps.sh index 399847ae25..8ab3475e64 100755 --- a/source/install/build_lammps.sh +++ b/source/install/build_lammps.sh @@ -15,7 +15,7 @@ BUILD_TMP_DIR=${SCRIPT_PATH}/../build_lammps mkdir -p ${BUILD_TMP_DIR} cd ${BUILD_TMP_DIR} # download LAMMMPS -LAMMPS_VERSION=stable_29Sep2021 +LAMMPS_VERSION=stable_29Sep2021_update2 if [ ! -d "lammps-${LAMMPS_VERSION}" ] then curl -L -o lammps.tar.gz https://github.com/lammps/lammps/archive/refs/tags/${LAMMPS_VERSION}.tar.gz From 1f4dc3a6f85943d5f0a57197d6bb91eb24d34462 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 10 Jan 2022 19:28:35 -0500 Subject: [PATCH 144/161] remove `api_cc/include/custom_op.h` (#1405) This file is duplicate of `op/custom_op.h`. It's not used by files outside the `op` directory. --- source/api_cc/include/custom_op.h | 34 ------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 source/api_cc/include/custom_op.h diff --git a/source/api_cc/include/custom_op.h b/source/api_cc/include/custom_op.h deleted file mode 100644 index bd27821fb3..0000000000 --- a/source/api_cc/include/custom_op.h +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include -#include - -#include "tensorflow/core/platform/env.h" -#include "tensorflow/core/framework/op.h" -#include "tensorflow/core/public/session.h" -#include "tensorflow/core/public/version.h" -#include "tensorflow/core/framework/op_kernel.h" -#include "tensorflow/core/framework/shape_inference.h" -#include -#include - -using namespace tensorflow; -using CPUDevice = Eigen::ThreadPoolDevice; -using GPUDevice = Eigen::GpuDevice; - -// functions used in custom ops -struct DeviceFunctor { - void operator()( - std::string& device, - const CPUDevice& d) - { - device = "CPU"; - } - #if GOOGLE_CUDA || TENSORFLOW_USE_ROCM - void operator()( - std::string& device, - const GPUDevice& d) - { - device = "GPU"; - } - #endif // GOOGLE_CUDA || TENSORFLOW_USE_ROCM -}; \ No newline at end of file From 1fa9e637728b81809b9293d0f5c8e7053747971f Mon Sep 17 00:00:00 2001 From: Denghui Lu Date: Wed, 12 Jan 2022 07:40:57 +0800 Subject: [PATCH 145/161] add UT for se_3 type descriptor (#1404) * fix nvcc warning when using cuda-11.x toolkit * add UT for se_3 type descriptor --- source/lib/src/cuda/tabulate.cu | 2 +- source/lib/src/rocm/tabulate.hip.cu | 2 +- ...test_tabulate.cc => test_tabulate_se_a.cc} | 14 +- source/lib/tests/test_tabulate_se_t.cc | 189 ++++++++++++++++++ 4 files changed, 198 insertions(+), 9 deletions(-) rename source/lib/tests/{test_tabulate.cc => test_tabulate_se_a.cc} (99%) create mode 100644 source/lib/tests/test_tabulate_se_t.cc diff --git a/source/lib/src/cuda/tabulate.cu b/source/lib/src/cuda/tabulate.cu index 2037764c03..02032a45cf 100644 --- a/source/lib/src/cuda/tabulate.cu +++ b/source/lib/src/cuda/tabulate.cu @@ -334,7 +334,7 @@ __global__ void tabulate_fusion_se_t_fifth_order_polynomial( FPTYPE tmp = xx; if (xx == ago) { unloop = true; - breakpoint = jj - 1; + breakpoint = jj; } int table_idx = 0; locate_xx_se_t(xx, table_idx, lower, upper, -max, max, stride0, stride1); diff --git a/source/lib/src/rocm/tabulate.hip.cu b/source/lib/src/rocm/tabulate.hip.cu index 10bc849db4..edc6bcc4f5 100644 --- a/source/lib/src/rocm/tabulate.hip.cu +++ b/source/lib/src/rocm/tabulate.hip.cu @@ -320,7 +320,7 @@ __global__ void tabulate_fusion_se_t_fifth_order_polynomial( FPTYPE tmp = xx; if (xx == ago) { unloop = true; - breakpoint = jj - 1; + breakpoint = jj; } int table_idx = 0; locate_xx_se_t(xx, table_idx, lower, upper, -max, max, stride0, stride1); diff --git a/source/lib/tests/test_tabulate.cc b/source/lib/tests/test_tabulate_se_a.cc similarity index 99% rename from source/lib/tests/test_tabulate.cc rename to source/lib/tests/test_tabulate_se_a.cc index 3fe0b27d78..8903711904 100644 --- a/source/lib/tests/test_tabulate.cc +++ b/source/lib/tests/test_tabulate_se_a.cc @@ -5,7 +5,7 @@ #include #include "utilities.h" -class TestTabulate : public ::testing::Test +class TestTabulateSeA : public ::testing::Test { protected: // em_x = tf.random.uniform([4, 16], minval=0, maxval=0.2, dtype = tf.float64) @@ -144,7 +144,7 @@ class TestTabulate : public ::testing::Test } }; -TEST_F(TestTabulate, tabulate_fusion_se_a_cpu) +TEST_F(TestTabulateSeA, tabulate_fusion_se_a_cpu) { std::vector xyz_scatter(nloc * nnei * last_layer_size); deepmd::tabulate_fusion_se_a_cpu(&xyz_scatter[0], &table[0], &info[0], &em_x[0], &em[0], nloc, nnei, last_layer_size); @@ -155,7 +155,7 @@ TEST_F(TestTabulate, tabulate_fusion_se_a_cpu) } } -TEST_F(TestTabulate, tabulate_fusion_se_a_grad_cpu) +TEST_F(TestTabulateSeA, tabulate_fusion_se_a_grad_cpu) { std::vector dy_dem_x(em_x.size()); std::vector dy_dem(em.size()); @@ -174,7 +174,7 @@ TEST_F(TestTabulate, tabulate_fusion_se_a_grad_cpu) } #if GOOGLE_CUDA -TEST_F(TestTabulate, tabulate_fusion_se_a_gpu_cuda) +TEST_F(TestTabulateSeA, tabulate_fusion_se_a_gpu_cuda) { std::vector xyz_scatter(nloc * nnei * last_layer_size, 0.0); @@ -197,7 +197,7 @@ TEST_F(TestTabulate, tabulate_fusion_se_a_gpu_cuda) } } -TEST_F(TestTabulate, tabulate_fusion_se_a_grad_gpu_cuda) +TEST_F(TestTabulateSeA, tabulate_fusion_se_a_grad_gpu_cuda) { std::vector dy_dem_x(em_x.size(), 0.0); std::vector dy_dem(em.size(), 0.0); @@ -234,7 +234,7 @@ TEST_F(TestTabulate, tabulate_fusion_se_a_grad_gpu_cuda) #endif // GOOGLE_CUDA #if TENSORFLOW_USE_ROCM -TEST_F(TestTabulate, tabulate_fusion_se_a_gpu_rocm) +TEST_F(TestTabulateSeA, tabulate_fusion_se_a_gpu_rocm) { std::vector xyz_scatter(nloc * nnei * last_layer_size, 0.0); @@ -257,7 +257,7 @@ TEST_F(TestTabulate, tabulate_fusion_se_a_gpu_rocm) } } -TEST_F(TestTabulate, tabulate_fusion_se_a_grad_gpu_rocm) +TEST_F(TestTabulateSeA, tabulate_fusion_se_a_grad_gpu_rocm) { std::vector dy_dem_x(em_x.size(), 0.0); std::vector dy_dem(em.size(), 0.0); diff --git a/source/lib/tests/test_tabulate_se_t.cc b/source/lib/tests/test_tabulate_se_t.cc new file mode 100644 index 0000000000..b4e728a5c6 --- /dev/null +++ b/source/lib/tests/test_tabulate_se_t.cc @@ -0,0 +1,189 @@ +#include +#include +#include "device.h" +#include "tabulate.h" +#include +#include "utilities.h" + +class TestTabulateSeT : public ::testing::Test +{ +protected: + // em_x = tf.random.uniform([4, 16], minval=0, maxval=0.2, dtype = tf.float64) + std::vector info = { + -2.1000000000000000e+01, 2.1000000000000000e+01, 1.0500000000000000e+02, 1.0000000000000000e+00, 1.0000000000000000e+01, -1.0000000000000000e+00 + }; + std::vector em_x = { + 9.3816147034272368e-01, -1.6703373029862567e-01, -4.4294526064601734e-02, -2.8798505489184573e-01, -1.6703373029862567e-01, 9.2489218226366088e-01, -2.8928196536572048e-01, -4.7833509099876154e-01, -4.4294526064601734e-02, -2.8928196536572048e-01, 5.7034320185695120e-01, 1.8771147911830000e-01, -2.8798505489184573e-01, -4.7833509099876154e-01, 1.8771147911830000e-01, 4.0174654365823070e-01, 8.4370316144902313e-01, -3.7813146789689916e-02, -3.6989397568296523e-01, -4.0554075086539937e-01, -3.7813146789689916e-02, 6.5766402633747112e-01, -4.2312966361682885e-01, 1.2685067374257861e-01, -3.6989397568296523e-01, -4.2312966361682885e-01, 6.0171576901660107e-01, 9.8283160997298613e-02, -4.0554075086539937e-01, 1.2685067374257861e-01, 9.8283160997298613e-02, 2.1324148100625978e-01, 9.7843596341516559e-01, -1.0492833888237871e-01, -1.0538688914576379e-01, -2.0453551592353389e-01, -1.0492833888237871e-01, 7.7943976693565231e-01, -1.5898500035781410e-01, 9.4834209331437741e-02, -1.0538688914576379e-01, -1.5898500035781410e-01, 7.4778071691708869e-01, -6.1895255142095873e-01, -2.0453551592353389e-01, 9.4834209331437741e-02, -6.1895255142095873e-01, 6.0844713798743799e-01, 1.0079020879244640e+00, -2.3855984150631487e-01, -3.4608276043004524e-02, -4.7448768267289088e-01, -2.3855984150631487e-01, 4.9732018171028253e-01, -3.1320787082485729e-01, -1.4528004145602180e-01, -3.4608276043004524e-02, -3.1320787082485729e-01, 4.7696729363954582e-01, 1.1723268074231248e-01, -4.7448768267289088e-01, -1.4528004145602180e-01, 1.1723268074231248e-01, 4.0511515406019899e-01, 6.9317482874286218e-01, 3.8721526993960850e-02, -1.4829415254252801e-01, 1.9079858574793401e-01, 3.8721526993960850e-02, 4.0694636061668399e-01, -1.6669745680958750e-01, -2.9455183336619600e-01, -1.4829415254252801e-01, -1.6669745680958750e-01, 3.5115749833010762e-01, 2.7972274527006624e-02, 1.9079858574793401e-01, -2.9455183336619600e-01, 2.7972274527006624e-02, 2.9261590797274251e-01, 1.0547679530528609e+00, -7.5579498870314032e-01, -2.5907966401834215e-01, 2.4868586817732710e-01, -7.5579498870314032e-01, 7.7943976693565231e-01, -2.7840391808748116e-02, -1.2197364072902048e-02, -2.5907966401834215e-01, -2.7840391808748116e-02, 2.9514791871283574e-01, -1.4724344774699041e-01, 2.4868586817732710e-01, -1.2197364072902048e-02, -1.4724344774699041e-01, 2.7333766016385419e-01, 7.1494810971572931e-01, -3.8965690625377569e-01, -2.0579233200987346e-01, 1.5638053130676866e-01, -3.8965690625377569e-01, 5.7034320185695120e-01, -2.4759451701376567e-01, 1.7075608253389668e-01, -2.0579233200987346e-01, -2.4759451701376567e-01, 4.4489736273181785e-01, -3.3903230422862907e-01, 1.5638053130676866e-01, 1.7075608253389668e-01, -3.3903230422862907e-01, 2.7328888249045513e-01, 8.2256346358859145e-01, -3.0201999278197073e-01, -1.0847299712994765e-01, -3.3355086180245408e-01, -3.0201999278197073e-01, 6.2584346461620799e-01, -2.3661062787307036e-02, 2.5559368174587482e-02, -1.0847299712994765e-01, -2.3661062787307036e-02, 6.2471079378938721e-01, -3.9923912886685187e-01, -3.3355086180245408e-01, 2.5559368174587482e-02, -3.9923912886685187e-01, 4.9732018171028253e-01, 7.4300380743635475e-01, -4.4058918654051710e-01, -2.5375568912718455e-01, 5.6029289688609013e-02, -4.4058918654051710e-01, 4.3025970692640908e-01, -1.0172468432781301e-02, -1.2941908849275471e-01, -2.5375568912718455e-01, -1.0172468432781301e-02, 4.2845986148404269e-01, -1.7460159217638957e-01, 5.6029289688609013e-02, -1.2941908849275471e-01, -1.7460159217638957e-01, 3.8138264541081690e-01, 7.4620920788925238e-01, -1.6424881399213448e-01, -2.2361648073503249e-01, -4.0424642786821852e-01, -1.6424881399213448e-01, 6.6730350814323314e-01, -2.1317459925340326e-01, 1.9376435628360764e-01, -2.2361648073503249e-01, -2.1317459925340326e-01, 5.8089947575573275e-01, -1.0277026325170427e-01, -4.0424642786821852e-01, 1.9376435628360764e-01, -1.0277026325170427e-01, 3.1673915325970592e-01, 9.3159182283013242e-01, 2.7352164674733859e-01, -3.0194015433346399e-01, -1.6581739923723970e-01, 2.7352164674733859e-01, 8.7811025837608414e-01, -5.3657994020118693e-01, -1.4168666065928759e-02, -3.0194015433346399e-01, -5.3657994020118693e-01, 5.7772908002379919e-01, -2.6766718990342830e-01, -1.6581739923723970e-01, -1.4168666065928759e-02, -2.6766718990342830e-01, 4.3044918224444273e-01, 7.5776646946384441e-01, -2.8544634168978411e-01, -4.6917826735271817e-01, 9.0648108100258265e-02, -2.8544634168978411e-01, 6.3333781479517670e-01, -1.5635743535006455e-01, -3.2225585549698127e-01, -4.6917826735271817e-01, -1.5635743535006455e-01, 5.8894646017949193e-01, -4.7549586643753167e-02, 9.0648108100258265e-02, -3.2225585549698127e-01, -4.7549586643753167e-02, 5.1470686148396338e-01, 8.9120731219577032e-01, -4.8913932884415684e-01, -2.8535162253723745e-01, -3.9481172797096048e-01, -4.8913932884415684e-01, 6.8161288899055572e-01, 5.3745624046182272e-02, -9.6415050020146331e-03, -2.8535162253723745e-01, 5.3745624046182272e-02, 3.7216282686452884e-01, 1.8327616930599899e-01, -3.9481172797096048e-01, -9.6415050020146331e-03, 1.8327616930599899e-01, 2.9889755032428134e-01, 6.6730350814323314e-01, -1.9772856508212072e-01, -1.3214864503616511e-01, 1.9656713013350754e-01, -1.9772856508212072e-01, 6.4107564273521156e-01, -3.6750949174506781e-01, -2.1514707877261979e-01, -1.3214864503616511e-01, -3.6750949174506781e-01, 4.7696729363954582e-01, -1.0605540579882382e-01, 1.9656713013350754e-01, -2.1514707877261979e-01, -1.0605540579882382e-01, 2.8720166306787565e-01, 9.3159182283013242e-01, -3.9105219662031382e-01, -4.6012852922712744e-01, -3.7107529450742310e-01, -3.9105219662031382e-01, 6.4624598213814299e-01, -1.9218513692914521e-01, 8.7864237420793134e-02, -4.6012852922712744e-01, -1.9218513692914521e-01, 5.5411629355023162e-01, 1.5181004010991156e-01, -3.7107529450742310e-01, 8.7864237420793134e-02, 1.5181004010991156e-01, 5.4717905839342551e-01, 5.5411629355023162e-01, -1.3154982153268135e-01, -2.2683561534265623e-02, -8.5067568027022145e-02, -1.3154982153268135e-01, 4.6158852919583448e-01, -7.4818007595887706e-02, -2.3446129520432515e-01, -2.2683561534265623e-02, -7.4818007595887706e-02, 4.0511515406019899e-01, -2.4964155806145566e-01, -8.5067568027022145e-02, -2.3446129520432515e-01, -2.4964155806145566e-01, 3.9586735852137039e-01, 9.4162575876886123e-01, 1.6551527461893110e-02, -1.7195190048425002e-01, -8.5321232935839153e-02, 1.6551527461893110e-02, 7.4304915761252677e-01, -1.1678199974326212e-01, -4.3729941122496446e-01, -1.7195190048425002e-01, -1.1678199974326212e-01, 6.9317482874286218e-01, 1.9191976680315659e-01, -8.5321232935839153e-02, -4.3729941122496446e-01, 1.9191976680315659e-01, 2.8206822618179617e-01, 7.5776646946384441e-01, 1.3081288362678634e-02, -3.2517003355282742e-01, -3.5562946132636442e-01, 1.3081288362678634e-02, 7.0400669442030783e-01, -2.7774712576896132e-01, -1.7472226352059492e-01, -3.2517003355282742e-01, -2.7774712576896132e-01, 5.1099339330908866e-01, 7.5918257186359903e-02, -3.5562946132636442e-01, -1.7472226352059492e-01, 7.5918257186359903e-02, 2.8288909669360418e-01, 7.4778071691708869e-01, -3.9041097955700099e-01, -5.2895674526793196e-03, -4.0620032081707269e-01, -3.9041097955700099e-01, 6.0650459602198470e-01, -1.5236577918073632e-01, 1.3182011644234659e-03, -5.2895674526793196e-03, -1.5236577918073632e-01, 5.8972872609679527e-01, -1.4295182509075030e-01, -4.0620032081707269e-01, 1.3182011644234659e-03, -1.4295182509075030e-01, 4.2821165206248513e-01, 6.2471079378938699e-01, 6.0802406816920937e-02, -3.5127267686929931e-01, -3.3963258237386684e-01, 6.0802406816920937e-02, 6.0313886404423811e-01, -2.6977044122265748e-01, -1.6348825415331092e-01, -3.5127267686929931e-01, -2.6977044122265748e-01, 5.4552055268521205e-01, -1.9316799462722023e-02, -3.3963258237386684e-01, -1.6348825415331092e-01, -1.9316799462722023e-02, 4.8171669294486208e-01, 7.0578073898484561e-01, -9.8967741792306413e-02, 1.2502659893584156e-01, -3.7971201998874682e-01, -9.8967741792306413e-02, 7.0400669442030783e-01, -6.5060917634508969e-02, -1.0206531635166906e-01, 1.2502659893584156e-01, -6.5060917634508969e-02, 6.0506774685063136e-01, -2.9019848494979322e-01, -3.7971201998874682e-01, -1.0206531635166906e-01, -2.9019848494979322e-01, 3.3340397458978077e-01, 9.7231560474448697e-01, -4.8839770953582246e-02, -5.2649040695832883e-01, 7.6239831325479152e-02, -4.8839770953582246e-02, 8.5066067412859170e-01, -2.8381943351894323e-01, -4.3719342578830916e-01, -5.2649040695832883e-01, -2.8381943351894323e-01, 7.8694914200240895e-01, -6.9042842925044076e-02, 7.6239831325479152e-02, -4.3719342578830916e-01, -6.9042842925044076e-02, 3.1673915325970592e-01, 1.1146471781363385e+00, -4.1950872828895774e-01, -7.5099287814360732e-02, -7.0747093773604913e-02, -4.1950872828895774e-01, 8.7811025837608414e-01, -7.1680109826753424e-01, 1.5469221891377744e-01, -7.5099287814360732e-02, -7.1680109826753424e-01, 7.8157368152678353e-01, -1.8674982324145417e-01, -7.0747093773604913e-02, 1.5469221891377744e-01, -1.8674982324145417e-01, 6.3333781479517670e-01, 9.4162575876886123e-01, -3.5756088285386567e-01, -4.9692587682641537e-02, -1.9259678748208950e-01, -3.5756088285386567e-01, 5.7304538706875918e-01, -1.7263416631604137e-01, 1.7497761971314219e-01, -4.9692587682641537e-02, -1.7263416631604137e-01, 5.4717905839342551e-01, 1.8011611616770615e-01, -1.9259678748208950e-01, 1.7497761971314219e-01, 1.8011611616770615e-01, 1.6303735520554410e-01, 1.1146471781363385e+00, -2.9955742667885321e-01, -4.6620550078880341e-01, -3.1378159981378489e-01, -2.9955742667885321e-01, 8.8424257114190075e-01, -2.5441067597713185e-01, -2.1591071501682862e-01, -4.6620550078880341e-01, -2.5441067597713185e-01, 7.7959145539751795e-01, -1.2407187853083543e-01, -3.1378159981378489e-01, -2.1591071501682862e-01, -1.2407187853083543e-01, 5.8972872609679527e-01, 6.0506774685063081e-01, -7.0161191563646669e-02, -3.3988044762842473e-01, -1.9107644999487627e-01, -7.0161191563646669e-02, 4.3045264342637135e-01, 2.0824077812721109e-02, -2.5586718835495170e-01, -3.3988044762842473e-01, 2.0824077812721109e-02, 4.0694636061668399e-01, -3.6902580613623392e-02, -1.9107644999487627e-01, -2.5586718835495170e-01, -3.6902580613623392e-02, 3.5713610126062301e-01, 1.0507172480981881e+00, -5.7238843768987546e-01, -8.6532548307818979e-02, -2.4797217908729449e-01, -5.7238843768987546e-01, 6.4845065425155790e-01, -2.3798654086704824e-01, -9.0951164465072995e-02, -8.6532548307818979e-02, -2.3798654086704824e-01, 4.3025970692640908e-01, 5.1916371214171878e-02, -2.4797217908729449e-01, -9.0951164465072995e-02, 5.1916371214171878e-02, 3.5115749833010762e-01, 9.7070728754011626e-01, -1.9667254906106676e-01, 1.3881360342029997e-01, 1.8332147125431175e-01, -1.9667254906106676e-01, 6.4107564273521156e-01, 6.9919471349729312e-02, -2.0671318712854408e-01, 1.3881360342029997e-01, 6.9919471349729312e-02, 1.7842412973820965e-01, -2.8800113566320312e-02, 1.8332147125431175e-01, -2.0671318712854408e-01, -2.8800113566320312e-02, 8.7697980056291452e-02, 9.9699133925078010e-01, -1.9566669897090611e-01, 1.1472638955603826e-02, -3.8137297906451501e-01, -1.9566669897090611e-01, 7.8694914200240895e-01, -4.8668226019268873e-01, -4.3398812393252877e-02, 1.1472638955603826e-02, -4.8668226019268873e-01, 4.4489736273181785e-01, -5.7511361787363047e-02, -3.8137297906451501e-01, -4.3398812393252877e-02, -5.7511361787363047e-02, 2.9261590797274251e-01, 5.8721566479597598e-01, 2.1741339413236024e-01, -4.6310740433823661e-01, 1.5212653882669683e-01, 2.1741339413236024e-01, 5.7772908002379919e-01, -1.1309986042713593e-01, -4.6297902780444065e-02, -4.6310740433823661e-01, -1.1309986042713593e-01, 5.3524434793006614e-01, 7.7916319803791656e-02, 1.5212653882669683e-01, -4.6297902780444065e-02, 7.7916319803791656e-02, 3.3070548705408681e-01, 6.4845065425155790e-01, -1.1573648783331206e-01, -1.1295787224086569e-02, -3.2976819109974104e-01, -1.1573648783331206e-01, 5.7304538706875918e-01, -4.4057056903958502e-01, 1.5118880665101278e-01, -1.1295787224086569e-02, -4.4057056903958502e-01, 5.0847535644168274e-01, -2.7508898922066710e-01, -3.2976819109974104e-01, 1.5118880665101278e-01, -2.7508898922066710e-01, 4.6158852919583448e-01, 9.2489218226366110e-01, 1.9293109981997125e-01, -4.6858170396716431e-01, 4.2486849070578564e-02, 1.9293109981997125e-01, 8.8759600097388758e-01, -9.3159319915253995e-03, -2.1166189641553734e-01, -4.6858170396716431e-01, -9.3159319915253995e-03, 5.8089947575573275e-01, -3.8498930024537814e-01, 4.2486849070578564e-02, -2.1166189641553734e-01, -3.8498930024537814e-01, 4.0608640117736616e-01, 9.0810287860174088e-01, -4.5026066211118843e-01, -2.4234797312679049e-01, -1.1635894709323868e-01, -4.5026066211118843e-01, 8.3522648701948565e-01, 7.0167369514544164e-02, -3.0380780947210834e-01, -2.4234797312679049e-01, 7.0167369514544164e-02, 2.8658902506762818e-01, 4.2786039330881952e-02, -1.1635894709323868e-01, -3.0380780947210834e-01, 4.2786039330881952e-02, 2.2990742509977113e-01, 8.7859873068865801e-01, -4.0521290114598696e-01, -3.4537197184419777e-02, -5.1541416274527041e-01, -4.0521290114598696e-01, 7.7073679568840003e-01, -5.2766218816177624e-01, 7.2009349057603700e-02, -3.4537197184419777e-02, -5.2766218816177624e-01, 5.3524434793006614e-01, 1.2774094128859217e-01, -5.1541416274527041e-01, 7.2009349057603700e-02, 1.2774094128859217e-01, 4.2821165206248513e-01, 8.0017606793538776e-01, -3.6273937320056748e-01, -4.8244644816054216e-01, 1.9000791267873704e-01, -3.6273937320056748e-01, 5.6353888423218390e-01, 3.7095579553517985e-02, 1.0807739396133707e-01, -4.8244644816054216e-01, 3.7095579553517985e-02, 4.2845986148404269e-01, -1.2901615367558902e-01, 1.9000791267873704e-01, 1.0807739396133707e-01, -1.2901615367558902e-01, 2.3908528931221024e-01, 8.7859873068865801e-01, -5.7342075379041779e-01, -2.2560792912032243e-01, 1.3840168053297142e-01, -5.7342075379041779e-01, 6.2584346461620799e-01, -1.0147381298988209e-01, -1.8792520225770581e-01, -2.2560792912032243e-01, -1.0147381298988209e-01, 4.9480400405457242e-01, -1.3243403061658676e-01, 1.3840168053297142e-01, -1.8792520225770581e-01, -1.3243403061658676e-01, 2.5543672306163845e-01, 9.7843596341516559e-01, -3.0135276617413775e-02, -6.3204064126440895e-01, 7.7858338769721036e-02, -3.0135276617413775e-02, 7.3888148744521942e-01, -2.8105601448216994e-01, -1.4222291428374428e-01, -6.3204064126440895e-01, -2.8105601448216994e-01, 5.3087899217465850e-01, -1.9954226724405721e-03, 7.7858338769721036e-02, -1.4222291428374428e-01, -1.9954226724405721e-03, 3.7962008200748842e-01, 7.3888148744521942e-01, -3.9876854953397894e-01, 7.9344112776459683e-02, 3.0881146913585039e-02, -3.9876854953397894e-01, 5.8894646017949193e-01, -2.9566251514384462e-01, -2.1875193893336647e-01, 7.9344112776459683e-02, -2.9566251514384462e-01, 2.1324148100625978e-01, 1.1337428718710466e-01, 3.0881146913585039e-02, -2.1875193893336647e-01, 1.1337428718710466e-01, 1.3167057692769979e-01, 7.2588662899153644e-01, -4.0491835492648354e-01, 3.8294782214220321e-02, -1.6245502019487457e-01, -4.0491835492648354e-01, 6.9978773302195119e-01, -2.6130189173261692e-01, -1.4963646270159869e-01, 3.8294782214220321e-02, -2.6130189173261692e-01, 4.9396599356834686e-01, -2.0453500637835592e-01, -1.6245502019487457e-01, -1.4963646270159869e-01, -2.0453500637835592e-01, 4.3044918224444273e-01, 5.4829965643085543e-01, -8.6258333909827428e-02, -9.0863809928147868e-02, -2.5335540346466701e-01, -8.6258333909827428e-02, 4.8256071581125803e-01, -1.5824234665042425e-01, -6.2251154159746636e-02, -9.0863809928147868e-02, -1.5824234665042425e-01, 3.3070548705408681e-01, -1.2171470598287978e-01, -2.5335540346466701e-01, -6.2251154159746636e-02, -1.2171470598287978e-01, 2.9993528142734771e-01, 9.9699133925078010e-01, -5.5720924928656934e-02, -3.7563354786645231e-01, -1.7003176037775222e-01, -5.5720924928656934e-02, 7.1322781206304275e-01, -1.4028476150114413e-01, -4.0725518739947821e-02, -3.7563354786645231e-01, -1.4028476150114413e-01, 6.5766402633747112e-01, -3.7470308102778649e-01, -1.7003176037775222e-01, -4.0725518739947821e-02, -3.7470308102778649e-01, 4.5518325168409662e-01, 7.2306489407047936e-01, -5.5652304518705331e-01, -1.7548443714837653e-01, -9.7244252488182109e-02, -5.5652304518705331e-01, 5.8721566479597631e-01, 1.5466175158937293e-02, 2.3395338180958331e-01, -1.7548443714837653e-01, 1.5466175158937293e-02, 5.1099339330908866e-01, 1.2309721775318454e-01, -9.7244252488182109e-02, 2.3395338180958331e-01, 1.2309721775318454e-01, 2.9948736341201787e-01, 8.9120731219577032e-01, 1.3828785543997540e-01, -6.5808954835297839e-01, 2.3928953196716229e-02, 1.3828785543997540e-01, 8.8759600097388758e-01, -1.6580688159465298e-01, -1.0200920121572279e-01, -6.5808954835297839e-01, -1.6580688159465298e-01, 6.0844713798743799e-01, -1.8482739703288106e-01, 2.3928953196716229e-02, -1.0200920121572279e-01, -1.8482739703288106e-01, 2.7328888249045513e-01, 9.7231560474448697e-01, -1.5360824814848673e-01, -3.0175556482836241e-01, 1.4427139165494557e-01, -1.5360824814848673e-01, 7.7073679568840003e-01, -4.4616437520485569e-01, -4.3386642987381790e-02, -3.0175556482836241e-01, -4.4616437520485569e-01, 6.8161288899055572e-01, -3.3586190220507495e-01, 1.4427139165494557e-01, -4.3386642987381790e-02, -3.3586190220507495e-01, 3.7736584257608707e-01, 8.4370316144902313e-01, -3.4786444025064711e-01, -4.9627086468803983e-01, -3.0284479791966981e-02, -3.4786444025064711e-01, 7.7959145539751795e-01, 5.4105959194424263e-02, -5.6892292757652424e-02, -4.9627086468803983e-01, 5.4105959194424263e-02, 3.7736584257608707e-01, 1.4233877887889113e-01, -3.0284479791966981e-02, -5.6892292757652424e-02, 1.4233877887889113e-01, 2.4312804865275472e-01, 9.3816147034272368e-01, -2.0860974495653162e-01, -3.7166959734666666e-01, 5.6250602465564153e-03, -2.0860974495653162e-01, 9.0810287860174088e-01, -9.1069253660888416e-02, -6.4962854816729276e-01, -3.7166959734666666e-01, -9.1069253660888416e-02, 8.1523507511884086e-01, -6.6856471628686520e-02, 5.6250602465564153e-03, -6.4962854816729276e-01, -6.6856471628686520e-02, 5.4817010902737695e-01, 8.3348751379486785e-01, -5.7386182749623121e-02, -1.2725671252595133e-01, -2.6056306991901751e-01, -5.7386182749623121e-02, 5.2909436738615123e-01, -4.2596706647156624e-01, -1.0919830944239709e-02, -1.2725671252595133e-01, -4.2596706647156624e-01, 3.7962008200748842e-01, 7.0344098964105775e-02, -2.6056306991901751e-01, -1.0919830944239709e-02, 7.0344098964105775e-02, 2.2679728457111192e-01, 1.0079020879244640e+00, -6.4116619730398172e-01, 1.1959784878009472e-02, -3.5036660259057062e-01, -6.4116619730398172e-01, 7.2588662899153644e-01, 1.5457663848526185e-01, -1.3123176159456695e-01, 1.1959784878009472e-02, 1.5457663848526185e-01, 7.2306489407047936e-01, 1.1120004060087613e-01, -3.5036660259057062e-01, -1.3123176159456695e-01, 1.1120004060087613e-01, 6.5291435452372704e-01, 6.5291435452372693e-01, -2.0669331449151485e-01, -1.0009057137401539e-01, -1.0458759498524857e-01, -2.0669331449151485e-01, 4.8256071581125803e-01, -1.0975185704675450e-01, -7.1400789900385064e-02, -1.0009057137401539e-01, -1.0975185704675450e-01, 3.1774836143047369e-01, -1.3420181173883666e-01, -1.0458759498524857e-01, -7.1400789900385064e-02, -1.3420181173883666e-01, 1.7842412973820965e-01, 7.4304915761252677e-01, -3.7474512353359690e-02, -2.0605417465948622e-01, 2.8786202774685965e-02, -3.7474512353359690e-02, 6.4624598213814299e-01, -1.8088533245882277e-01, -2.7294935111478841e-01, -2.0605417465948622e-01, -1.8088533245882277e-01, 4.5625158241504560e-01, -2.5573952710441156e-01, 2.8786202774685965e-02, -2.7294935111478841e-01, -2.5573952710441156e-01, 4.3045264342637135e-01, 8.1523507511884286e-01, -1.1206588973386064e-01, -2.9038245483951325e-01, -9.4044512167176214e-02, -1.1206588973386064e-01, 6.9069450025674883e-01, -3.6666406927828260e-01, -2.6295004305146680e-01, -2.9038245483951325e-01, -3.6666406927828260e-01, 6.0313886404423811e-01, -1.1364153715298203e-01, -9.4044512167176214e-02, -2.6295004305146680e-01, -1.1364153715298203e-01, 5.0847535644168274e-01, 7.8157368152678375e-01, -3.6689153089953208e-01, -1.6343042599874896e-01, 2.0681260422479955e-01, -3.6689153089953208e-01, 7.5302376148517924e-01, -3.9141809592987054e-01, -4.9792782459804974e-01, -1.6343042599874896e-01, -3.9141809592987054e-01, 4.8171669294486208e-01, 1.8620912874547166e-01, 2.0681260422479955e-01, -4.9792782459804974e-01, 1.8620912874547166e-01, 4.5625158241504560e-01, 8.5066067412859170e-01, -6.6383544595733118e-02, -4.9833493744041324e-01, 3.5985625651793141e-02, -6.6383544595733118e-02, 7.6738392766662211e-01, -1.9489169085491792e-01, -6.4543919151000315e-02, -4.9833493744041324e-01, -1.9489169085491792e-01, 5.4829965643085543e-01, -2.7028089635457442e-01, 3.5985625651793141e-02, -6.4543919151000315e-02, -2.7028089635457442e-01, 3.9586735852137039e-01, 8.8424257114190075e-01, -5.0968059146564315e-01, -9.5512640917474467e-03, -7.2900503108297598e-02, -5.0968059146564315e-01, 7.0578073898484561e-01, -7.9000528792048164e-02, -3.4471532290325835e-01, -9.5512640917474467e-03, -7.9000528792048164e-02, 6.9978773302195119e-01, -2.3508453941888172e-01, -7.2900503108297598e-02, -3.4471532290325835e-01, -2.3508453941888172e-01, 5.1462854065795050e-01, 8.3522648701948565e-01, -4.4716582952086292e-01, 5.4799614348689352e-02, -2.2914542894364148e-02, -4.4716582952086292e-01, 8.3348751379486763e-01, -6.7279352271601633e-01, -3.4916341927887895e-02, 5.4799614348689352e-02, -6.7279352271601633e-01, 7.6738392766662211e-01, -1.6809648395494664e-01, -2.2914542894364148e-02, -3.4916341927887895e-02, -1.6809648395494664e-01, 7.1322781206304275e-01, 9.7070728754011626e-01, -2.5224986479466716e-01, 5.5579119983494740e-02, -3.6768489956717509e-01, -2.5224986479466716e-01, 5.3965948062451941e-01, -4.4192312892226593e-01, 3.1513756645368173e-02, 5.5579119983494740e-02, -4.4192312892226593e-01, 5.3087899217465850e-01, 7.9885677144886064e-02, -3.6768489956717509e-01, 3.1513756645368173e-02, 7.9885677144886064e-02, 1.6104012331629208e-01, 8.6130335545327363e-01, -9.6129435640763802e-02, -1.4801620948352356e-02, 7.8150102857998158e-02, -9.6129435640763802e-02, 6.7625303314120611e-01, -5.5027301017164576e-02, -1.7520377876997034e-01, -1.4801620948352356e-02, -5.5027301017164576e-02, 3.1774836143047369e-01, -1.2376278479081343e-01, 7.8150102857998158e-02, -1.7520377876997034e-01, -1.2376278479081343e-01, 1.0841353470308304e-01, 7.4620920788925238e-01, -1.6394859485203073e-01, -2.0029713033714130e-01, -1.8645272306202978e-01, -1.6394859485203073e-01, 5.3965948062451941e-01, -3.8230461309700781e-01, 1.3847290923401767e-01, -2.0029713033714130e-01, -3.8230461309700781e-01, 5.1462854065795050e-01, -1.8561874788646704e-01, -1.8645272306202978e-01, 1.3847290923401767e-01, -1.8561874788646704e-01, 2.9993528142734771e-01, 6.7625303314120611e-01, -1.6815361388865593e-01, -3.3930776213055852e-01, -1.2114679754832709e-01, -1.6815361388865593e-01, 5.6353888423218390e-01, -7.5784855072358825e-02, -3.3221823724983218e-01, -3.3930776213055852e-01, -7.5784855072358825e-02, 5.1470686148396338e-01, -4.1819974401314991e-02, -1.2114679754832709e-01, -3.3221823724983218e-01, -4.1819974401314991e-02, 4.2819483757235705e-01, 1.0507172480981881e+00, -1.4246809991945683e-01, 4.1456358442478705e-02, -4.5365891641821365e-01, -1.4246809991945683e-01, 7.1494810971572931e-01, -2.4883057770682826e-01, -2.4395987950943798e-01, 4.1456358442478705e-02, -2.4883057770682826e-01, 5.4552055268521205e-01, 1.2834013149857285e-01, -4.5365891641821365e-01, -2.4395987950943798e-01, 1.2834013149857285e-01, 3.3340397458978077e-01, 6.9069450025674894e-01, -3.8338543582916840e-01, -1.6642138961100816e-01, -3.5535624148913159e-01, -3.8338543582916840e-01, 5.2909436738615123e-01, 9.6172848604107755e-02, 5.5205364851978050e-02, -1.6642138961100816e-01, 9.6172848604107755e-02, 4.2819483757235710e-01, 5.9926260952505861e-03, -3.5535624148913159e-01, 5.5205364851978050e-02, 5.9926260952505861e-03, 2.6226647193037689e-01, 8.6130335545327363e-01, -6.2395423965096508e-02, -3.8567287714901394e-01, -1.5408554583685347e-01, -6.2395423965096508e-02, 7.4300380743635475e-01, -4.7285239739268992e-01, -3.2035899709108612e-02, -3.8567287714901394e-01, -4.7285239739268992e-01, 6.0171576901660107e-01, -1.0536966428907621e-01, -1.5408554583685347e-01, -3.2035899709108612e-02, -1.0536966428907621e-01, 4.9396599356834686e-01, 7.5302376148517924e-01, -6.0232285163413626e-02, -2.7132649171672180e-01, -1.4054053948995909e-01, -6.0232285163413626e-02, 6.0650459602198470e-01, -2.4794771537629287e-01, -1.4485909587327089e-01, -2.7132649171672180e-01, -2.4794771537629287e-01, 4.5518325168409662e-01, -1.6092902586109215e-01, -1.4054053948995909e-01, -1.4485909587327089e-01, -1.6092902586109215e-01, 4.0174654365823070e-01, 1.0547679530528609e+00, -6.4439808979974744e-01, 6.9192182950400305e-02, -3.3250796613430167e-01, -6.4439808979974744e-01, 8.2256346358859145e-01, -4.8266166410158140e-01, -7.9980773185718390e-02, 6.9192182950400305e-02, -4.8266166410158140e-01, 8.0017606793538776e-01, -2.7854894491724819e-02, -3.3250796613430167e-01, -7.9980773185718390e-02, -2.7854894491724819e-02, 5.4817010902737695e-01, 1.5502492798962887e+01, -9.4036183520005387e+00, -9.0871843532376817e-01, 1.5458603036460241e+00, -9.4036183520005387e+00, 5.8545618851096108e+00, 8.7584459712876173e-01, -1.0260595565373307e+00, -9.0871843532376817e-01, 8.7584459712876173e-01, 7.5773424105520826e-01, -3.2391012220482440e-01, 1.5458603036460241e+00, -1.0260595565373307e+00, -3.2391012220482440e-01, 6.6152719782725655e-01, 1.5881484684546885e+01, -8.5373015094081293e+00, 1.2910407629253464e+00, 1.2854398517437213e+00, -8.5373015094081293e+00, 5.0892253563939507e+00, -3.8569840009954620e-01, -9.9850386318081996e-01, 1.2910407629253464e+00, -3.8569840009954620e-01, 4.1577040236200724e-01, -9.1388286019611339e-02, 1.2854398517437213e+00, -9.9850386318081996e-01, -9.1388286019611339e-02, 2.9351817210899528e-01, 1.8355238388750472e+01, -9.9693288297551721e+00, 1.4065955534477488e+00, 1.5236758321532125e+00, -9.9693288297551721e+00, 5.4788018334752540e+00, -7.5992948757336931e-01, -6.6807507000148814e-01, 1.4065955534477488e+00, -7.5992948757336931e-01, 5.8340911874120316e-01, 9.9856752432820606e-03, 1.5236758321532125e+00, -6.6807507000148814e-01, 9.9856752432820606e-03, 5.5177690536312463e-01, 1.7078778656839585e+01, -6.5205205128653105e+00, 1.5817366283975822e+00, -9.3640888727240412e-01, -6.5205205128653105e+00, 2.9619037115622602e+00, -1.1542576186016471e+00, -8.8584406490304773e-02, 1.5817366283975822e+00, -1.1542576186016471e+00, 8.3454304950111569e-01, 3.1272631908606119e-01, -9.3640888727240412e-01, -8.8584406490304773e-02, 3.1272631908606119e-01, 7.8087115310507782e-01, 1.5669862194862620e+01, -6.2090727600143900e+00, 1.3817794924064011e+00, -1.1475004406408358e+00, -6.2090727600143900e+00, 2.5567265715258070e+00, -5.4440265806779431e-01, 3.8699899553155109e-01, 1.3817794924064011e+00, -5.4440265806779431e-01, 3.6446802134584444e-01, -2.8076757475429592e-01, -1.1475004406408358e+00, 3.8699899553155109e-01, -2.8076757475429592e-01, 2.6130199022294265e-01, 1.7118875747980759e+01, -5.9277226216953718e+00, 1.9346813580902582e+00, -1.6872205590095239e+00, -5.9277226216953718e+00, 2.1086146215067632e+00, -8.0747551757328861e-01, 4.7699095328329716e-01, 1.9346813580902582e+00, -8.0747551757328861e-01, 5.5797080293767543e-01, 8.9832319658025406e-02, -1.6872205590095239e+00, 4.7699095328329716e-01, 8.9832319658025406e-02, 5.5399995944851188e-01, 1.7058565959549352e+01, -5.9263293882909478e+00, 1.5342405590746959e+00, -1.1630302303586997e+00, -5.9263293882909478e+00, 2.6205293378999683e+00, -4.7688305722955537e-01, 3.1804603922241220e-01, 1.5342405590746959e+00, -4.7688305722955537e-01, 3.1296424858530869e-01, -2.8669977614844999e-01, -1.1630302303586997e+00, 3.1804603922241220e-01, -2.8669977614844999e-01, 2.7020324134820112e-01, 1.6874725411781530e+01, -6.9150979008264484e+00, 1.0065221329504688e+00, -1.8347878924481371e+00, -6.9150979008264484e+00, 3.5369590566282492e+00, -5.2228937791154206e-01, 2.3672173077240022e-01, 1.0065221329504688e+00, -5.2228937791154206e-01, 6.0329263881086503e-01, 4.6309711033365369e-02, -1.8347878924481371e+00, 2.3672173077240022e-01, 4.6309711033365369e-02, 5.8765944434164141e-01, 1.6856507449690348e+01, -6.8342292657673545e+00, 8.9036581290816785e-01, -4.5904138300777919e-01, -6.8342292657673545e+00, 3.9011240056387244e+00, -4.1077122687276202e-02, 5.6895999928936403e-01, 8.9036581290816785e-01, -4.1077122687276202e-02, 2.7435588945926814e-01, 1.9420750375548933e-02, -4.5904138300777919e-01, 5.6895999928936403e-01, 1.9420750375548933e-02, 1.7277500144155888e-01, 1.7645529606875833e+01, -3.2538877192050002e+00, -5.5012517023538066e-01, 1.0677788759412139e+00, -3.2538877192050002e+00, 1.0546932562360065e+00, -2.3658187113371332e-01, 2.1841816790460902e-01, -5.5012517023538066e-01, -2.3658187113371332e-01, 7.1656120537425561e-01, -1.7040630728823944e-01, 1.0677788759412139e+00, 2.1841816790460902e-01, -1.7040630728823944e-01, 5.0974972717888634e-01, 1.7839842450333954e+01, -5.7379299944719877e+00, -1.9569351707197109e+00, 1.7713682302855058e+00, -5.7379299944719877e+00, 2.4328041444884976e+00, -1.7544854846175517e-02, -1.6864721058949111e-01, -1.9569351707197109e+00, -1.7544854846175517e-02, 1.0677513531326404e+00, -5.9215023869599626e-01, 1.7713682302855058e+00, -1.6864721058949111e-01, -5.9215023869599626e-01, 4.6360784428232726e-01, 1.5732517152453081e+01, -7.4014739000296572e+00, -1.6207000095637598e+00, 1.2383190370614494e+00, -7.4014739000296572e+00, 3.9409851277204022e+00, 7.5740083786654211e-01, -5.5800827056355640e-01, -1.6207000095637598e+00, 7.5740083786654211e-01, 7.7642511193896813e-01, -4.9466228929792039e-01, 1.2383190370614494e+00, -5.5800827056355640e-01, -4.9466228929792039e-01, 3.1958775176868037e-01, 1.6183696670937064e+01, -8.4210107899133941e+00, -1.4603023231162975e+00, -4.8487004987150362e-01, -8.4210107899133941e+00, 5.1398804721325435e+00, 2.1595570912014417e-01, 8.3208280045450567e-01, -1.4603023231162975e+00, 2.1595570912014417e-01, 5.4394923212798296e-01, -3.4805126389386676e-01, -4.8487004987150362e-01, 8.3208280045450567e-01, -3.4805126389386676e-01, 4.8452486035887499e-01, 1.6451611837149500e+01, -4.2969418428867261e+00, -7.3888024461400414e-01, -2.0815144409407478e+00, -4.2969418428867261e+00, 1.5430243106399666e+00, 1.8729935762291541e-01, 1.4033663037202498e-01, -7.3888024461400414e-01, 1.8729935762291541e-01, 7.4205169442909757e-01, 5.7628851673904691e-02, -2.0815144409407478e+00, 1.4033663037202498e-01, 5.7628851673904691e-02, 6.5242091014819859e-01, 1.5883062757360207e+01, -7.5907511804771470e+00, -9.7007798581707783e-01, 7.0138283553798109e-01, -7.5907511804771470e+00, 4.0217572548274134e+00, -2.6014708482761550e-02, -5.3204932822260864e-01, -9.7007798581707783e-01, -2.6014708482761550e-02, 1.0259721921051177e+00, -1.8715321271601759e-01, 7.0138283553798109e-01, -5.3204932822260864e-01, -1.8715321271601759e-01, 5.5149306670030152e-01, 1.5978116669665239e+01, -7.4006259214596790e+00, -6.7969646246634663e-01, 6.5546600275605982e-01, -7.4006259214596790e+00, 3.6071706440359010e+00, 5.3491110661316954e-01, -5.2666565248665098e-01, -6.7969646246634663e-01, 5.3491110661316954e-01, 4.7936057181766917e-01, -1.5025322700354665e-01, 6.5546600275605982e-01, -5.2666565248665098e-01, -1.5025322700354665e-01, 4.3112434274449579e-01, 1.6757855414725086e+01, -3.1391994419655687e+00, -3.6827363627511667e+00, -2.7493582565171533e+00, -3.1391994419655687e+00, 1.2878229875735527e+00, 6.2385886839847293e-01, 3.4411042773468631e-01, -3.6827363627511667e+00, 6.2385886839847293e-01, 1.2200979174145221e+00, 9.3261333604507307e-02, -2.7493582565171533e+00, 3.4411042773468631e-01, 9.3261333604507307e-02, 1.1795148354251150e+00, 1.5763176470987085e+01, -7.2993099681066589e+00, 2.1748999120100793e-01, -4.7546386776476129e-01, -7.2993099681066589e+00, 3.4569862729331460e+00, -1.6096289888525245e-01, 3.5019733955885657e-01, 2.1748999120100793e-01, -1.6096289888525245e-01, 4.2354082998887993e-01, -3.1304152419179737e-01, -4.7546386776476129e-01, 3.5019733955885657e-01, -3.1304152419179737e-01, 3.4623783030108429e-01, 1.7657045077804337e+01, -5.4634153749758578e+00, -2.8502393669993009e+00, 1.2603451180663379e+00, -5.4634153749758578e+00, 2.3705777605989660e+00, 4.7298264603542695e-01, -1.8824806410410400e-01, -2.8502393669993009e+00, 4.7298264603542695e-01, 8.1312479693863837e-01, -4.9719372983025689e-01, 1.2603451180663379e+00, -1.8824806410410400e-01, -4.9719372983025689e-01, 4.2735579792528156e-01, 1.7234879466141379e+01, -6.9447619542898051e+00, -1.3909105342656662e+00, -1.4629483521415327e+00, -6.9447619542898051e+00, 3.2257037587765272e+00, 3.4613714263225803e-01, 7.1038882433094597e-01, -1.3909105342656662e+00, 3.4613714263225803e-01, 5.4019461216426035e-01, -2.4590599471464314e-01, -1.4629483521415327e+00, 7.1038882433094597e-01, -2.4590599471464314e-01, 4.4551884917879320e-01, 1.7438713147183115e+01, -8.4187054220745114e+00, 9.9555878171084999e-01, 1.3301908774116342e+00, -8.4187054220745114e+00, 4.1903876236321533e+00, -7.3825814341549312e-01, -6.7163472873814567e-01, 9.9555878171084999e-01, -7.3825814341549312e-01, 5.8548847257088532e-01, 1.6930014844653318e-01, 1.3301908774116342e+00, -6.7163472873814567e-01, 1.6930014844653318e-01, 5.3684670796748524e-01, 1.6505843421877792e+01, -8.9672859383958023e+00, -5.9586737243852639e-01, -4.9508712315185199e-01, -8.9672859383958023e+00, 4.8781866610061115e+00, 2.8538236205314221e-01, 3.0156093579837179e-01, -5.9586737243852639e-01, 2.8538236205314221e-01, 4.8868089712416274e-01, -4.4039031061756390e-01, -4.9508712315185199e-01, 3.0156093579837179e-01, -4.4039031061756390e-01, 4.7201467710741113e-01, 1.8009048716596261e+01, -9.5048416816852885e+00, -1.4889359349719808e+00, 5.5230572557832114e-01, -9.5048416816852885e+00, 5.0429843614824552e+00, 8.0889627597129587e-01, -2.6367499950333312e-01, -1.4889359349719808e+00, 8.0889627597129587e-01, 1.1121395372372715e+00, -9.0442258964978972e-01, 5.5230572557832114e-01, -2.6367499950333312e-01, -9.0442258964978972e-01, 8.5074190371660285e-01, 1.8969812503837847e+01, -7.7867026450257386e+00, 1.6621469974591756e+00, -1.3535726585282810e+00, -7.7867026450257386e+00, 3.2242358228553925e+00, -5.5729571150631485e-01, 5.2143834192344174e-01, 1.6621469974591756e+00, -5.5729571150631485e-01, 7.4287944271406869e-01, -4.0168187357492424e-01, -1.3535726585282810e+00, 5.2143834192344174e-01, -4.0168187357492424e-01, 5.7837665640757685e-01, 1.6268715288654960e+01, -8.9640581019331016e+00, 1.9472562726742195e+00, 1.1634394630256961e+00, -8.9640581019331016e+00, 5.2999985470828141e+00, -9.0781775797605180e-01, -9.9127146007630118e-01, 1.9472562726742195e+00, -9.0781775797605180e-01, 5.5880703055816960e-01, -2.0482720926808279e-02, 1.1634394630256961e+00, -9.9127146007630118e-01, -2.0482720926808279e-02, 4.2314243080374431e-01, 1.7309134332310190e+01, -6.7958317179822716e+00, 1.7430014752690762e-01, 7.4781078592582828e-01, -6.7958317179822716e+00, 2.7832702389289379e+00, -2.9174947002806606e-01, -2.5266256491062944e-01, 1.7430014752690762e-01, -2.9174947002806606e-01, 4.3576818045690519e-01, -8.8896184051415236e-02, 7.4781078592582828e-01, -2.5266256491062944e-01, -8.8896184051415236e-02, 3.9997224840640028e-01, 1.7153574941598098e+01, -6.8846934950832344e+00, 1.8254724366772068e+00, 4.8039342168786198e-01, -6.8846934950832344e+00, 2.8276344038991410e+00, -5.6790635321077287e-01, -1.9270510354144452e-01, 1.8254724366772068e+00, -5.6790635321077287e-01, 6.9227219483725666e-01, -1.2709334375461934e-01, 4.8039342168786198e-01, -1.9270510354144452e-01, -1.2709334375461934e-01, 4.2917978157262676e-01, 1.8132184964363688e+01, -3.1733007722489184e+00, 1.7825953260422662e+00, -1.3140033285181132e+00, -3.1733007722489184e+00, 6.9684206950381822e-01, -5.4962857370590601e-01, -1.0351982721358500e-02, 1.7825953260422662e+00, -5.4962857370590601e-01, 6.5568195496490611e-01, 1.7607191243391238e-01, -1.3140033285181132e+00, -1.0351982721358500e-02, 1.7607191243391238e-01, 6.2263345089164690e-01, 1.6127141186360564e+01, -9.2175933167952362e+00, 4.5482959624410402e-01, 1.5607705465624031e+00, -9.2175933167952362e+00, 5.8646726994165235e+00, -7.6823914094072088e-01, -5.4262462349906693e-01, 4.5482959624410402e-01, -7.6823914094072088e-01, 5.9151161800332619e-01, -2.4286672705208728e-01, 1.5607705465624031e+00, -5.4262462349906693e-01, -2.4286672705208728e-01, 3.5666849406587792e-01, 1.6165753976243874e+01, -7.3811188161212806e+00, -2.9732014153906883e-01, 1.0894338527618317e+00, -7.3811188161212806e+00, 3.4986894854838320e+00, 1.2640223060154221e-02, -7.0520798584256972e-01, -2.9732014153906883e-01, 1.2640223060154221e-02, 5.6149770153482581e-01, 3.6870659678219064e-02, 1.0894338527618317e+00, -7.0520798584256972e-01, 3.6870659678219064e-02, 4.5536900447269846e-01, 1.5398988006027349e+01, -8.1208517349885732e+00, 8.6228252695234930e-01, 1.1537806103765487e+00, -8.1208517349885732e+00, 4.3222427499704104e+00, -5.2765755042823237e-01, -6.0836165343511328e-01, 8.6228252695234930e-01, -5.2765755042823237e-01, 4.7558839586188817e-01, -2.2039226845147369e-01, 1.1537806103765487e+00, -6.0836165343511328e-01, -2.2039226845147369e-01, 3.6326310248611182e-01, 1.6653862335772988e+01, -6.3212833166201801e+00, 1.7035548874689670e+00, 1.7818121037836856e+00, -6.3212833166201801e+00, 3.2372475346428593e+00, -1.6771360201147706e-01, -7.3260377579450964e-01, 1.7035548874689670e+00, -1.6771360201147706e-01, 4.5584861179226355e-01, 1.7938188763718371e-01, 1.7818121037836856e+00, -7.3260377579450964e-01, 1.7938188763718371e-01, 3.0343699262163576e-01, 1.6005636284380316e+01, -8.9463211901340483e+00, -1.4995353233230992e+00, 1.6144976837089247e+00, -8.9463211901340483e+00, 5.4553198002689216e+00, 8.2430401820014310e-01, -7.7450210219583759e-01, -1.4995353233230992e+00, 8.2430401820014310e-01, 3.0203181275741886e-01, -5.0431498184811262e-02, 1.6144976837089247e+00, -7.7450210219583759e-01, -5.0431498184811262e-02, 2.6690426032037673e-01, 1.5957019862552055e+01, -8.4122236810732502e+00, 1.4776406260905088e+00, 1.1752014846148688e+00, -8.4122236810732502e+00, 5.1499519890947365e+00, -1.2438745362258066e+00, -2.4793216417570077e-01, 1.4776406260905088e+00, -1.2438745362258066e+00, 4.3907159105778493e-01, -1.3201484086761139e-01, 1.1752014846148688e+00, -2.4793216417570077e-01, -1.3201484086761139e-01, 2.8971532780081477e-01, 1.7264626784129828e+01, -7.1717293181514563e+00, 1.8173778481524891e-01, 1.6638776173614904e+00, -7.1717293181514563e+00, 3.3498514787396210e+00, 5.3070406002362888e-02, -8.3211897250151612e-01, 1.8173778481524891e-01, 5.3070406002362888e-02, 3.5750188990225340e-01, 1.7905618157975495e-01, 1.6638776173614904e+00, -8.3211897250151612e-01, 1.7905618157975495e-01, 3.5631089226304219e-01, 1.6951331209440209e+01, -6.9265044010797432e+00, 1.6649505088436367e+00, 1.2476223930507584e+00, -6.9265044010797432e+00, 3.0908353140153588e+00, -7.9349084304257178e-01, -5.5641718253825512e-01, 1.6649505088436367e+00, -7.9349084304257178e-01, 5.1957810047939335e-01, -1.8256034059952314e-01, 1.2476223930507584e+00, -5.5641718253825512e-01, -1.8256034059952314e-01, 4.4508123489757256e-01, 1.9012236387930873e+01, -8.2612906987401367e+00, 1.7822397061112394e+00, 1.3130655015793740e+00, -8.2612906987401367e+00, 3.9785776745364090e+00, -4.5911569699280996e-01, -7.8540804412996457e-01, 1.7822397061112394e+00, -4.5911569699280996e-01, 7.1651557376422037e-01, -2.1545675296134048e-01, 1.3130655015793740e+00, -7.8540804412996457e-01, -2.1545675296134048e-01, 3.0131845380712763e-01, 1.6597075714728486e+01, -5.7800006677690803e+00, 1.5381006811275582e+00, -1.5197413389828438e+00, -5.7800006677690803e+00, 3.0963818435550561e+00, -7.6010709310479962e-01, 4.4547615970032189e-01, 1.5381006811275582e+00, -7.6010709310479962e-01, 3.1280696744315561e-01, -1.7575813761777823e-01, -1.5197413389828438e+00, 4.4547615970032189e-01, -1.7575813761777823e-01, 1.6771576241498387e-01, 1.7019126909133337e+01, -6.6527113537535385e+00, 1.3704144651040868e+00, 1.5022343603610107e+00, -6.6527113537535385e+00, 2.9978544936485725e+00, -1.8662862581940526e-01, -6.5208288251651947e-01, 1.3704144651040868e+00, -1.8662862581940526e-01, 5.2409376673618568e-01, -7.5645601483690317e-02, 1.5022343603610107e+00, -6.5208288251651947e-01, -7.5645601483690317e-02, 3.2522439346024634e-01, 1.7662116032947637e+01, -7.3173562685033531e+00, -4.8473999550733515e-01, 1.2177126365029898e+00, -7.3173562685033531e+00, 3.0582743607486380e+00, 9.5372312198528986e-02, -4.4792222499260004e-01, -4.8473999550733515e-01, 9.5372312198528986e-02, 4.2961213757689048e-01, -2.5302983486156821e-01, 1.2177126365029898e+00, -4.4792222499260004e-01, -2.5302983486156821e-01, 3.5756224652772239e-01, 1.6523014322908107e+01, -8.0087006367676903e+00, 1.8183754790473388e+00, 1.3553052743949423e+00, -8.0087006367676903e+00, 4.0733577269970231e+00, -1.1117655839274267e+00, -7.2719651384385020e-01, 1.8183754790473388e+00, -1.1117655839274267e+00, 5.2643538086294572e-01, 1.0149565570079074e-01, 1.3553052743949423e+00, -7.2719651384385020e-01, 1.0149565570079074e-01, 4.9227647014805931e-01, 1.7397143785954697e+01, 9.1482994934314243e-01, -1.2055972918955953e+00, 1.5195127886243485e+00, 9.1482994934314243e-01, 6.0050109036606192e-01, 2.3235888502821123e-01, -2.5896893411041405e-01, -1.2055972918955953e+00, 2.3235888502821123e-01, 4.5114243240770924e-01, -3.9549546725682455e-01, 1.5195127886243485e+00, -2.5896893411041405e-01, -3.9549546725682455e-01, 3.9713449207042850e-01, 1.6754962568939149e+01, -6.9909519455758877e+00, 1.3238214949533700e-02, 1.7461462053388506e+00, -6.9909519455758877e+00, 3.5271059676844763e+00, -4.8608852379905848e-01, -3.9767641403791087e-01, 1.3238214949533700e-02, -4.8608852379905848e-01, 4.6008364836193916e-01, -2.3283300864365591e-01, 1.7461462053388506e+00, -3.9767641403791087e-01, -2.3283300864365591e-01, 3.6997630813085547e-01, 1.6914438459335880e+01, -8.2018573101250176e+00, 1.8607992969625340e+00, 3.6214197147114746e-01, -8.2018573101250176e+00, 4.1440760753178036e+00, -7.4448077344820940e-01, 2.1802370922440709e-02, 1.8607992969625340e+00, -7.4448077344820940e-01, 4.2429585090834404e-01, 1.5770605248055045e-01, 3.6214197147114746e-01, 2.1802370922440709e-02, 1.5770605248055045e-01, 3.0822560548642208e-01, 1.7076011430674352e+01, -8.8780259960025418e+00, 4.1114807990525343e-01, 1.7160868438637338e+00, -8.8780259960025418e+00, 4.6868693538444646e+00, -3.9792997857664614e-01, -7.8413912548256937e-01, 4.1114807990525343e-01, -3.9792997857664614e-01, 4.9907853940129265e-01, -2.2844650397698621e-01, 1.7160868438637338e+00, -7.8413912548256937e-01, -2.2844650397698621e-01, 3.4562144955490959e-01, 1.6875577654372840e+01, -6.7321113543278024e+00, 1.5467259695484039e+00, 1.5835725354604495e+00, -6.7321113543278024e+00, 3.2243427952557981e+00, -3.9371520741376925e-01, -3.6997207365849999e-01, 1.5467259695484039e+00, -3.9371520741376925e-01, 7.3691512999550857e-01, -6.5365934650236268e-02, 1.5835725354604495e+00, -3.6997207365849999e-01, -6.5365934650236268e-02, 4.7827347437249718e-01, 1.7164841669190295e+01, -3.9184842337808603e+00, -1.9330224573943846e+00, -3.5384656982588716e+00, -3.9184842337808603e+00, 1.8459124004525904e+00, -2.7968361445259859e-01, 6.3051523823343891e-01, -1.9330224573943846e+00, -2.7968361445259859e-01, 9.1319007138394170e-01, 4.4823426503524189e-01, -3.5384656982588716e+00, 6.3051523823343891e-01, 4.4823426503524189e-01, 8.1043864783118424e-01, 1.7776298615595469e+01, -8.3125493630052887e+00, -7.7441931055878699e-01, 1.5226098031185762e+00, -8.3125493630052887e+00, 5.1190411322780580e+00, 2.2952835214172629e-02, -1.4773103203139837e-01, -7.7441931055878699e-01, 2.2952835214172629e-02, 8.5889441357603480e-01, -6.9917219907711145e-02, 1.5226098031185762e+00, -1.4773103203139837e-01, -6.9917219907711145e-02, 4.2035440559421033e-01, 1.5862691008342372e+01, -6.1176683909011116e+00, 1.2625855228210956e+00, -1.3435168854108823e+00, -6.1176683909011116e+00, 2.3824446972003832e+00, -5.4326864192360957e-01, 4.7940703668476647e-01, 1.2625855228210956e+00, -5.4326864192360957e-01, 4.3406572657200515e-01, 9.5842602881569275e-02, -1.3435168854108823e+00, 4.7940703668476647e-01, 9.5842602881569275e-02, 2.3854989743717084e-01, 1.8256070397132763e+01, -6.9737979519085576e+00, -2.4828924208535295e+00, -6.8067548292463942e-01, -6.9737979519085576e+00, 2.7984958050555582e+00, 6.3576956667988715e-01, 4.5488849481004173e-01, -2.4828924208535295e+00, 6.3576956667988715e-01, 1.1009943270852411e+00, -2.5112157021262832e-01, -6.8067548292463942e-01, 4.5488849481004173e-01, -2.5112157021262832e-01, 6.3576733446761735e-01, 1.7056582256752865e+01, -7.1009743841647808e+00, 1.1160779926499460e+00, 1.6103269168653844e+00, -7.1009743841647808e+00, 3.1733314893589308e+00, -7.0718250574770325e-01, -4.3660325341089939e-01, 1.1160779926499460e+00, -7.0718250574770325e-01, 5.0309713543792534e-01, -6.0959434222991160e-02, 1.6103269168653844e+00, -4.3660325341089939e-01, -6.0959434222991160e-02, 4.6051187668197546e-01, 1.8357242514234677e+01, -8.7847041747386729e+00, 2.9797124907094874e-01, -2.1027146029189812e+00, -8.7847041747386729e+00, 4.3983224978124813e+00, 1.6771532571466041e-01, 8.6501485551462776e-01, 2.9797124907094874e-01, 1.6771532571466041e-01, 6.6655368883518706e-01, -1.0627500830106132e-01, -2.1027146029189812e+00, 8.6501485551462776e-01, -1.0627500830106132e-01, 4.8427082375374647e-01, 1.6772311022508610e+01, -5.5233743204468615e+00, -1.4827239072848402e+00, -1.6683315295413981e-01, -5.5233743204468615e+00, 2.8632372577610519e+00, -1.9908041485308506e-01, 6.7147007749442378e-02, -1.4827239072848402e+00, -1.9908041485308506e-01, 5.8384443306666967e-01, 1.8696608263835957e-02, -1.6683315295413981e-01, 6.7147007749442378e-02, 1.8696608263835957e-02, 4.1948303453199803e-01, 1.6628501414098437e+01, -8.4601815957694324e+00, -1.2499311118743477e+00, 1.3390492539579859e+00, -8.4601815957694324e+00, 4.3761271922541898e+00, 6.0876619378843877e-01, -5.7252755667562849e-01, -1.2499311118743477e+00, 6.0876619378843877e-01, 5.4159859530740562e-01, 1.6462302179435517e-01, 1.3390492539579859e+00, -5.7252755667562849e-01, 1.6462302179435517e-01, 4.8726092250544450e-01, 1.7067254080129800e+01, -8.6191292214447621e+00, 8.4191297536122567e-01, 1.6991769398963010e+00, -8.6191292214447621e+00, 4.7912823899616992e+00, -9.5325899518160639e-01, -9.9723259667135367e-01, 8.4191297536122567e-01, -9.5325899518160639e-01, 8.3710075559284569e-01, 9.7283473262340187e-02, 1.6991769398963010e+00, -9.9723259667135367e-01, 9.7283473262340187e-02, 3.6200206548711727e-01, 1.7702046820994806e+01, 1.1560960095286070e+00, -3.3325166201283918e+00, -1.4775683676695714e+00, 1.1560960095286070e+00, 1.0791246214081565e+00, 1.8182679840027050e-01, -5.0410612828568047e-01, -3.3325166201283918e+00, 1.8182679840027050e-01, 9.5912816564144698e-01, 3.5209385289513767e-01, -1.4775683676695714e+00, -5.0410612828568047e-01, 3.5209385289513767e-01, 6.1173061322520472e-01, 1.7855425834543190e+01, -3.2697217693515706e+00, 1.4807091014457323e+00, -2.6764346453544805e+00, -3.2697217693515706e+00, 8.3027813273245776e-01, 4.0426879175947228e-02, 3.3848123209104880e-01, 1.4807091014457323e+00, 4.0426879175947228e-02, 6.9975094084688649e-01, -4.2689334899290365e-01, -2.6764346453544805e+00, 3.3848123209104880e-01, -4.2689334899290365e-01, 5.0049960147362493e-01, 1.6401315819584983e+01, -7.9740616572801812e+00, 1.0005058774036371e+00, -1.4203138481446598e+00, -7.9740616572801812e+00, 4.4023705076543553e+00, -9.1941184955615007e-01, 5.7637509877548454e-01, 1.0005058774036371e+00, -9.1941184955615007e-01, 4.5841164275306279e-01, 5.3383138750947870e-02, -1.4203138481446598e+00, 5.7637509877548454e-01, 5.3383138750947870e-02, 1.9979339037632937e-01, 1.7332501002967668e+01, -7.0781330453987845e+00, 9.2037176711584545e-01, -1.7498405404426292e+00, -7.0781330453987845e+00, 3.1470072102227165e+00, -6.9097983597892554e-01, 6.2185046840444402e-01, 9.2037176711584545e-01, -6.9097983597892554e-01, 5.5932116260968723e-01, 2.1858582089281359e-01, -1.7498405404426292e+00, 6.2185046840444402e-01, 2.1858582089281359e-01, 5.2680120312008438e-01, 1.7734484227892562e+01, -5.3654898514610219e+00, -1.6146396348259038e+00, 9.9033821011608625e-01, -5.3654898514610219e+00, 2.0384509120220251e+00, 2.5981041293593549e-01, 1.2547778095454426e-01, -1.6146396348259038e+00, 2.5981041293593549e-01, 7.0973144206899874e-01, -2.6410086403128091e-01, 9.9033821011608625e-01, 1.2547778095454426e-01, -2.6410086403128091e-01, 4.9890531981897601e-01, 1.7373803386373812e+01, -7.3655877697476715e+00, 7.8270125086657205e-02, -2.0999061171249407e+00, -7.3655877697476715e+00, 3.8164841996986887e+00, 3.9641030912017372e-02, 6.5278955794156168e-01, 7.8270125086657205e-02, 3.9641030912017372e-02, 4.1742585469994020e-01, 1.6686881152650240e-02, -2.0999061171249407e+00, 6.5278955794156168e-01, 1.6686881152650240e-02, 3.4144491013516665e-01, 1.7266719068868216e+01, -9.0704352506311370e+00, 1.0694216573455857e+00, -1.5795224196213768e+00, -9.0704352506311370e+00, 5.0037919681802911e+00, -9.3097451917909035e-01, 6.6974458417928873e-01, 1.0694216573455857e+00, -9.3097451917909035e-01, 7.5988767453192230e-01, 7.6311108649566889e-02, -1.5795224196213768e+00, 6.6974458417928873e-01, 7.6311108649566889e-02, 2.9489768008036216e-01, 1.6376515340100283e+01, -6.9793256398089296e+00, 1.5243144645041427e+00, 2.7079268382937571e-01, -6.9793256398089296e+00, 3.0541734354131531e+00, -7.7135074324503605e-01, -6.8397349674078733e-02, 1.5243144645041427e+00, -7.7135074324503605e-01, 3.6395566937932222e-01, 6.2728909404942329e-02, 2.7079268382937571e-01, -6.8397349674078733e-02, 6.2728909404942329e-02, 3.6164470489278866e-01, 1.7516133518582485e+01, -8.3514443126068123e+00, -1.7180303760588957e+00, 1.5748319209869406e+00, -8.3514443126068123e+00, 5.3528291603548226e+00, -3.9980896941006638e-01, -7.0540361469493940e-02, -1.7180303760588957e+00, -3.9980896941006638e-01, 1.2547848615007291e+00, -7.5175461576019531e-01, 1.5748319209869406e+00, -7.0540361469493940e-02, -7.5175461576019531e-01, 5.0207052491314630e-01, 1.6471332655238577e+01, -1.9840240794333170e+00, -2.7675952686613430e+00, 1.7245254662592493e+00, -1.9840240794333170e+00, 1.3786012470006792e+00, -1.7634724450792111e-01, -6.8648055131182217e-01, -2.7675952686613430e+00, -1.7634724450792111e-01, 7.1321367156083215e-01, -1.2327176278311069e-01, 1.7245254662592493e+00, -6.8648055131182217e-01, -1.2327176278311069e-01, 4.9397169669978858e-01, 1.7091226474203204e+01, -4.5675405166074583e+00, 1.4400642541699227e+00, 1.2141833386470169e+00, -4.5675405166074583e+00, 1.3161027292266854e+00, -3.1345863399964818e-01, -5.0066767853585703e-01, 1.4400642541699227e+00, -3.1345863399964818e-01, 6.7234081428616688e-01, -1.3503981152357039e-01, 1.2141833386470169e+00, -5.0066767853585703e-01, -1.3503981152357039e-01, 4.3385283110391992e-01, 1.8246506732367031e+01, -7.6558698630583821e+00, 1.9213354454183826e+00, 1.6373332810021559e+00, -7.6558698630583821e+00, 3.9467944680536289e+00, -4.0297301651530965e-01, -5.2963503443511684e-01, 1.9213354454183826e+00, -4.0297301651530965e-01, 5.3176392085153512e-01, 8.9574705000869517e-02, 1.6373332810021559e+00, -5.2963503443511684e-01, 8.9574705000869517e-02, 4.4536642738675492e-01, 1.7911671783219571e+01, -5.9985252112864700e+00, -4.4745656306102388e-01, 1.1721399057841080e+00, -5.9985252112864700e+00, 2.4139535616836252e+00, -1.7219293329619995e-01, -5.0172832204407780e-01, -4.4745656306102388e-01, -1.7219293329619995e-01, 9.7476836720338544e-01, -3.2171662686669505e-01, 1.1721399057841080e+00, -5.0172832204407780e-01, -3.2171662686669505e-01, 3.0939894061872220e-01, 1.6155032382388242e+01, -7.2064352418320210e+00, 2.5981969792469695e-01, 2.8727950159025917e-01, -7.2064352418320210e+00, 3.9980129943264391e+00, -5.1910321191687969e-01, 3.2409611261106097e-01, 2.5981969792469695e-01, -5.1910321191687969e-01, 6.1299094127694165e-01, -4.6966198549264621e-01, 2.8727950159025917e-01, 3.2409611261106097e-01, -4.6966198549264621e-01, 4.1154555711070495e-01, 1.5942674207027702e+01, -8.2948902984678892e+00, -1.9203061850624374e+00, 3.4964168574330856e-01, -8.2948902984678892e+00, 4.7444890690473800e+00, 6.1177999082136658e-01, -5.6828655972420206e-01, -1.9203061850624374e+00, 6.1177999082136658e-01, 6.4406756760102324e-01, 3.0795787565507104e-01, 3.4964168574330856e-01, -5.6828655972420206e-01, 3.0795787565507104e-01, 3.5590181457520187e-01, 1.6944946717618595e+01, -6.0254959800609988e+00, -2.8357767854573899e+00, 4.1473435870618813e-01, -6.0254959800609988e+00, 3.0665746424677778e+00, 2.3975145045391522e-01, 3.0627924467723289e-01, -2.8357767854573899e+00, 2.3975145045391522e-01, 1.2345787399737920e+00, -3.3814683111213717e-01, 4.1473435870618813e-01, 3.0627924467723289e-01, -3.3814683111213717e-01, 3.3103947150526031e-01, 1.7117314554066201e+01, -8.8236140201371818e+00, 1.0581536138514975e+00, 1.3980599420035920e+00, -8.8236140201371818e+00, 4.8129582535771425e+00, -8.8712259222580214e-01, -7.9225911531011384e-01, 1.0581536138514975e+00, -8.8712259222580214e-01, 5.7517102508127571e-01, 7.2363511911666983e-02, 1.3980599420035920e+00, -7.9225911531011384e-01, 7.2363511911666983e-02, 2.9909589630683520e-01, 1.6972810488291550e+01, -5.9892458751059818e+00, 1.2149630718729185e+00, -2.0798444379053964e+00, -5.9892458751059818e+00, 2.4380332297536320e+00, -2.0344300671588472e-01, 5.3145291090067048e-01, 1.2149630718729185e+00, -2.0344300671588472e-01, 6.6369223311103109e-01, -6.0094731681376024e-04, -2.0798444379053964e+00, 5.3145291090067048e-01, -6.0094731681376024e-04, 5.7957663759360090e-01, 1.7343766938479227e+01, -6.6063740219858600e+00, -2.4017757423619175e+00, 1.6051740578902221e+00, -6.6063740219858600e+00, 3.4993875746971375e+00, -3.2234547803818028e-02, -3.2663881218999763e-01, -2.4017757423619175e+00, -3.2234547803818028e-02, 1.2504135194559383e+00, -5.1587400430594810e-01, 1.6051740578902221e+00, -3.2663881218999763e-01, -5.1587400430594810e-01, 3.0068541586224429e-01, 1.6848023901869816e+01, -7.7245284650863724e+00, -6.3375161950989023e-01, 1.7598733104365907e+00, -7.7245284650863724e+00, 3.6368411257897200e+00, 5.4898635756645486e-01, -9.3708181741907781e-01, -6.3375161950989023e-01, 5.4898635756645486e-01, 7.2509667201839190e-01, -4.1251648474508129e-01, 1.7598733104365907e+00, -9.3708181741907781e-01, -4.1251648474508129e-01, 4.9975565734465982e-01, 1.7511854581625478e+01, -8.5404613942496930e+00, 4.7313924091567117e-01, -5.1532816823540972e-01, -8.5404613942496930e+00, 4.4031820387689145e+00, -3.6267289012126913e-01, -1.3528742104501390e-02, 4.7313924091567117e-01, -3.6267289012126913e-01, 6.9456125684342040e-01, -1.6317464306172286e-01, -5.1532816823540972e-01, -1.3528742104501390e-02, -1.6317464306172286e-01, 4.5384531453753552e-01, 1.6749377427726831e+01, -5.7772682564884885e+00, -1.9377388468336154e+00, 1.2795905554738882e+00, -5.7772682564884885e+00, 2.3751734474029860e+00, 3.8187973000458070e-01, -1.1511718783472463e-01, -1.9377388468336154e+00, 3.8187973000458070e-01, 1.1071634761400777e+00, -9.4204177495531310e-02, 1.2795905554738882e+00, -1.1511718783472463e-01, -9.4204177495531310e-02, 5.0911537749846014e-01, 1.6804926100729805e+01, -6.7694804790437084e+00, 1.3290892723432168e+00, -2.0329912302039057e+00, -6.7694804790437084e+00, 3.0050053244019006e+00, -6.0641807348948229e-01, 6.5667795941713925e-01, 1.3290892723432168e+00, -6.0641807348948229e-01, 4.4527286361472279e-01, -2.5453902810523399e-01, -2.0329912302039057e+00, 6.5667795941713925e-01, -2.5453902810523399e-01, 3.9739245233707388e-01, 1.5868413007832768e+01, -9.6058749345061720e+00, -1.2704002060724218e+00, 8.6185583106525021e-01, -9.6058749345061720e+00, 5.8473081312071127e+00, 7.6752664926696923e-01, -4.0695460424360097e-01, -1.2704002060724218e+00, 7.6752664926696923e-01, 1.1283540336783828e+00, -1.6618593567686379e-01, 8.6185583106525021e-01, -4.0695460424360097e-01, -1.6618593567686379e-01, 4.6113112128941769e-01, 1.8481541594398891e+01, -6.6615632043770274e+00, -7.2359822450546107e-01, -1.4644271889003471e+00, -6.6615632043770274e+00, 2.4918957745186043e+00, 9.0679417982270888e-02, 5.3245874472517518e-01, -7.2359822450546107e-01, 9.0679417982270888e-02, 4.0656985508236138e-01, 1.5911683154833606e-01, -1.4644271889003471e+00, 5.3245874472517518e-01, 1.5911683154833606e-01, 3.2173334280500521e-01, 1.6228236401107939e+01, -8.1101411423722158e+00, 1.3634571880312281e+00, -2.6078480124847037e-03, -8.1101411423722158e+00, 5.1879397503268816e+00, -2.9695259856663125e-01, -6.7344064243872026e-01, 1.3634571880312281e+00, -2.9695259856663125e-01, 5.2461909416950570e-01, -1.3795586005253810e-01, -2.6078480124847037e-03, -6.7344064243872026e-01, -1.3795586005253810e-01, 4.3066586374098781e-01, 1.5589961900178967e+01, -4.5372333345909119e+00, -3.3934718591944404e+00, 1.3540033039710933e+00, -4.5372333345909119e+00, 1.8585499822791560e+00, 6.1587937597953046e-01, -5.8318661350036138e-01, -3.3934718591944404e+00, 6.1587937597953046e-01, 1.4074389868822910e+00, -4.7675943751143041e-01, 1.3540033039710933e+00, -5.8318661350036138e-01, -4.7675943751143041e-01, 4.2143576596484850e-01, 1.5611938835367109e+01, -7.8362555612747959e+00, 1.1634362937361629e+00, 8.1530526315465568e-01, -7.8362555612747959e+00, 4.0174329797324688e+00, -7.4513440796506791e-01, -3.6945515457627198e-01, 1.1634362937361629e+00, -7.4513440796506791e-01, 3.9648861064960028e-01, -3.2953317777742928e-02, 8.1530526315465568e-01, -3.6945515457627198e-01, -3.2953317777742928e-02, 3.7578484154027375e-01, 1.8315149213395173e+01, -7.7012188993654940e+00, 1.1508124885520843e+00, 1.3123590383630339e+00, -7.7012188993654940e+00, 3.4780615549256009e+00, -1.9166820372788307e-01, -7.6063211015438847e-01, 1.1508124885520843e+00, -1.9166820372788307e-01, 5.4468638241889589e-01, -1.2072611619645017e-01, 1.3123590383630339e+00, -7.6063211015438847e-01, -1.2072611619645017e-01, 2.9841808326885588e-01, 1.6362440054767124e+01, -5.9499672523232112e+00, 1.0052721428804259e+00, -1.3668397556002811e+00, -5.9499672523232112e+00, 3.2387435072295325e+00, -7.2396011033843383e-02, -1.5809150279620302e-01, 1.0052721428804259e+00, -7.2396011033843383e-02, 7.2454982301604076e-01, -2.0381095229621829e-01, -1.3668397556002811e+00, -1.5809150279620302e-01, -2.0381095229621829e-01, 5.1930847189189466e-01, 1.5436369548255527e+01, -9.1710802008604020e+00, 1.4209357173119814e+00, -5.2333006294648410e-01, -9.1710802008604020e+00, 5.4880135746084751e+00, -9.2088987123056165e-01, 1.8726405116782840e-01, 1.4209357173119814e+00, -9.2088987123056165e-01, 8.3812049939319733e-01, 2.7683676776817306e-01, -5.2333006294648410e-01, 1.8726405116782840e-01, 2.7683676776817306e-01, 4.1959049781180191e-01, 1.5666964412535105e+01, -9.7207170538441261e+00, 1.4333836194418987e+00, 1.5960317913987163e+00, -9.7207170538441261e+00, 6.6896120732782265e+00, -5.5802284343341291e-01, -1.3529115567912897e+00, 1.4333836194418987e+00, -5.5802284343341291e-01, 4.9413228052236202e-01, -1.9425632733667825e-01, 1.5960317913987163e+00, -1.3529115567912897e+00, -1.9425632733667825e-01, 4.8918814427528240e-01, 1.5290527713026155e+01, -7.6833137332910173e+00, -1.0751883919467500e+00, 1.6910116895309504e+00, -7.6833137332910173e+00, 3.9082192239312277e+00, 4.6621331985589443e-01, -9.1883958537926080e-01, -1.0751883919467500e+00, 4.6621331985589443e-01, 5.9851497650838281e-01, -2.2322179187601654e-01, 1.6910116895309504e+00, -9.1883958537926080e-01, -2.2322179187601654e-01, 3.9830059929712897e-01, 1.7658936182671727e+01, -7.9196848078408060e+00, 2.0235234121177790e+00, 1.4901521845277215e+00, -7.9196848078408060e+00, 3.6001776162691934e+00, -1.0140973115763243e+00, -6.7507570700253083e-01, 2.0235234121177790e+00, -1.0140973115763243e+00, 6.3032871812139324e-01, -7.6750309951210835e-02, 1.4901521845277215e+00, -6.7507570700253083e-01, -7.6750309951210835e-02, 5.4789767104015730e-01, 1.5457997297160160e+01, -6.1526949506673070e+00, 6.7273961399725346e-01, 9.5770758001695433e-01, -6.1526949506673070e+00, 2.7731742075607597e+00, -4.3012052691323910e-01, -1.2255714588398386e-01, 6.7273961399725346e-01, -4.3012052691323910e-01, 3.3958384751714510e-01, -1.2798871872114692e-01, 9.5770758001695433e-01, -1.2255714588398386e-01, -1.2798871872114692e-01, 2.7268656714191419e-01, 1.7503328855994745e+01, -9.9687455312455189e+00, -1.0297062203185405e+00, -1.7031047478256800e+00, -9.9687455312455189e+00, 5.9614589455051270e+00, 6.5247739444482156e-01, 7.6794594281655504e-01, -1.0297062203185405e+00, 6.5247739444482156e-01, 3.5943482704316476e-01, -1.7606821206230184e-02, -1.7031047478256800e+00, 7.6794594281655504e-01, -1.7606821206230184e-02, 3.2716633335958190e-01, 1.6373795057571847e+01, -7.7660482226949981e+00, 1.5882849012515776e+00, -1.8494279583300000e-01, -7.7660482226949981e+00, 3.9632086562391491e+00, -1.0503970491370456e+00, -2.0638321023081674e-01, 1.5882849012515776e+00, -1.0503970491370456e+00, 7.3133644598289149e-01, 1.7037283334568715e-01, -1.8494279583300000e-01, -2.0638321023081674e-01, 1.7037283334568715e-01, 3.6991380078193070e-01, 1.7380218869596824e+01, -4.6733393169385451e+00, -1.0112374919091498e+00, 6.9440806713566350e-01, -4.6733393169385451e+00, 1.7664189364611840e+00, 5.2267413518611217e-01, -4.7073978286014190e-01, -1.0112374919091498e+00, 5.2267413518611217e-01, 7.8792508851916998e-01, -6.0593541507832505e-01, 6.9440806713566350e-01, -4.7073978286014190e-01, -6.0593541507832505e-01, 4.8532777010071737e-01, 1.6688480100290171e+01, -2.4452257429467745e+00, -4.3502468301668573e+00, 2.8686321885832311e-01, -2.4452257429467745e+00, 1.9558655769328408e+00, -2.2650646337660044e-01, 2.7255380760267456e-01, -4.3502468301668573e+00, -2.2650646337660044e-01, 1.8601358238474783e+00, -7.0528037041038183e-01, 2.8686321885832311e-01, 2.7255380760267456e-01, -7.0528037041038183e-01, 8.8533224650523568e-01, 1.7558888970958407e+01, -3.5525153278861916e+00, 1.3269063954226350e+00, 1.2711647576232004e+00, -3.5525153278861916e+00, 1.9328537647668227e+00, -7.6166921543233657e-01, 2.0334544186972267e-01, 1.3269063954226350e+00, -7.6166921543233657e-01, 4.4584049689038086e-01, 2.2203925188891041e-02, 1.2711647576232004e+00, 2.0334544186972267e-01, 2.2203925188891041e-02, 3.5499333012698542e-01, 1.7503104597019327e+01, -8.2902573074777788e+00, 1.4809359053080735e+00, 5.4433271851216269e-01, -8.2902573074777788e+00, 4.7655682747958519e+00, 4.0148581275228912e-02, -8.3819205806563979e-01, 1.4809359053080735e+00, 4.0148581275228912e-02, 7.9084822528710697e-01, -4.0761145344905420e-01, 5.4433271851216269e-01, -8.3819205806563979e-01, -4.0761145344905420e-01, 7.7052589374216640e-01, 1.6708990617842698e+01, -4.8454163020535930e+00, -2.2673879150930514e+00, -7.3833835476206833e-01, -4.8454163020535930e+00, 1.8474149183735951e+00, 5.7960906898683118e-01, -2.9034175784539162e-01, -2.2673879150930514e+00, 5.7960906898683118e-01, 9.1094402341096992e-01, 2.7337020492260133e-01, -7.3833835476206833e-01, -2.9034175784539162e-01, 2.7337020492260133e-01, 6.2002369775813038e-01, 1.6722997168847904e+01, -8.6990946412050523e+00, 3.1803438787591409e-01, 1.7340206087992600e+00, -8.6990946412050523e+00, 4.6729144559920757e+00, -4.4803121361281151e-01, -7.6591403512264922e-01, 3.1803438787591409e-01, -4.4803121361281151e-01, 6.2109380665325764e-01, -2.1702562038719872e-01, 1.7340206087992600e+00, -7.6591403512264922e-01, -2.1702562038719872e-01, 3.0659507102349531e-01, 1.6670375714696366e+01, -6.3873997157981712e+00, 1.1422940619218669e+00, 1.6114990153830422e+00, -6.3873997157981712e+00, 2.7467584657824697e+00, -1.4220435608326074e-01, -7.6707780933203606e-01, 1.1422940619218669e+00, -1.4220435608326074e-01, 4.0764553257721842e-01, -2.8201231903517046e-02, 1.6114990153830422e+00, -7.6707780933203606e-01, -2.8201231903517046e-02, 2.3272465802632000e-01, 1.7177850258051929e+01, -7.7485985175283538e+00, 1.7642463130395969e+00, 1.1947573624901038e+00, -7.7485985175283538e+00, 3.7541650257159613e+00, -9.4830419732951277e-01, -3.4701571693749028e-01, 1.7642463130395969e+00, -9.4830419732951277e-01, 2.7843609384632584e-01, 2.2138492879799063e-02, 1.1947573624901038e+00, -3.4701571693749028e-01, 2.2138492879799063e-02, 2.4621808163691816e-01, 1.7591518642987552e+01, -6.1334674582773179e+00, -1.0188517048841167e-01, -1.0141463265801072e+00, -6.1334674582773179e+00, 2.8881371999494077e+00, -6.5349503463848291e-01, 6.1786208815218846e-01, -1.0188517048841167e-01, -6.5349503463848291e-01, 6.8766586365008342e-01, -1.3177271773416527e-01, -1.0141463265801072e+00, 6.1786208815218846e-01, -1.3177271773416527e-01, 3.5763038289252136e-01, 1.6460555062664469e+01, -1.0801338119838852e-01, -2.1676479110304978e+00, 2.3797610762749716e-01, -1.0801338119838852e-01, 1.0801941303644456e+00, -5.3813243483207052e-01, -5.6237375422626656e-01, -2.1676479110304978e+00, -5.3813243483207052e-01, 5.7967459423528112e-01, 2.4903805958998279e-01, 2.3797610762749716e-01, -5.6237375422626656e-01, 2.4903805958998279e-01, 2.9853118936744938e-01, 1.6363067821237387e+01, -7.5801725217658182e+00, -2.3154589119114033e+00, 8.6491950425521069e-01, -7.5801725217658182e+00, 4.1093160719549147e+00, 7.5936478003922270e-01, -7.9305772741362146e-01, -2.3154589119114033e+00, 7.5936478003922270e-01, 5.5950251851751887e-01, 1.9960092492144615e-02, 8.6491950425521069e-01, -7.9305772741362146e-01, 1.9960092492144615e-02, 3.6240473105355619e-01, 1.7604721419101381e+01, -5.5982944484012851e+00, -5.1115292685000280e-01, 1.1238691719448353e+00, -5.5982944484012851e+00, 1.9974013218860862e+00, 4.2330085337569273e-01, -3.1553160809344194e-01, -5.1115292685000280e-01, 4.2330085337569273e-01, 3.9483438550305838e-01, 1.4146215690809766e-01, 1.1238691719448353e+00, -3.1553160809344194e-01, 1.4146215690809766e-01, 3.0911274591187871e-01, 1.7481000191587800e+01, -8.4105488031411451e+00, 1.3039429588996374e+00, -1.8943355562813982e+00, -8.4105488031411451e+00, 4.2117242584921115e+00, -9.9620212290555921e-01, 7.2096851111157401e-01, 1.3039429588996374e+00, -9.9620212290555921e-01, 1.0262916082555362e+00, 2.6308977000995892e-01, -1.8943355562813982e+00, 7.2096851111157401e-01, 2.6308977000995892e-01, 4.2893693974357433e-01, 1.6142451134319948e+01, -8.5829976232168583e+00, -1.1914111242942487e+00, -1.7828308739068168e+00, -8.5829976232168583e+00, 4.5916185650689387e+00, 6.9925837704708993e-01, 8.5594103603863902e-01, -1.1914111242942487e+00, 6.9925837704708993e-01, 6.4694343394202003e-01, -2.2381142282272101e-01, -1.7828308739068168e+00, 8.5594103603863902e-01, -2.2381142282272101e-01, 5.4706240054847100e-01, 1.6219952760326322e+01, -5.2583246351358710e+00, -1.1318857704341121e+00, 1.5464549680915729e+00, -5.2583246351358710e+00, 1.9538218170664110e+00, 4.7907376803457552e-01, -5.5765002134851860e-01, -1.1318857704341121e+00, 4.7907376803457552e-01, 8.0266791476788724e-01, 4.1910587271559552e-01, 1.5464549680915729e+00, -5.5765002134851860e-01, 4.1910587271559552e-01, 6.1337978657368797e-01, 1.6349812064295705e+01, -9.1250338518494676e+00, 1.4372115668954004e+00, 1.3574151001729744e+00, -9.1250338518494676e+00, 5.6689651253021376e+00, -7.7650769531650343e-01, -1.0781752355166871e+00, 1.4372115668954004e+00, -7.7650769531650343e-01, 5.1241508875946074e-01, -1.1886028240939153e-01, 1.3574151001729744e+00, -1.0781752355166871e+00, -1.1886028240939153e-01, 4.2133699414022918e-01, 1.7730633966916006e+01, -4.2079378425024334e+00, -2.4082318029242074e+00, -2.9326567435152695e+00, -4.2079378425024334e+00, 1.8729697103084302e+00, -1.7025332772484633e-01, 6.4769249581465438e-01, -2.4082318029242074e+00, -1.7025332772484633e-01, 9.6422971517920864e-01, 4.9637876618550530e-01, -2.9326567435152695e+00, 6.4769249581465438e-01, 4.9637876618550530e-01, 9.0607906741843292e-01, 1.6520034768467930e+01, -9.3863666536330559e+00, 1.6290624727505552e+00, 1.0051389816863621e+00, -9.3863666536330559e+00, 5.5944801287984092e+00, -9.4935489921666538e-01, -4.5064099036376648e-01, 1.6290624727505552e+00, -9.4935489921666538e-01, 5.8585060608512141e-01, -2.9952535059011953e-01, 1.0051389816863621e+00, -4.5064099036376648e-01, -2.9952535059011953e-01, 4.7197837495700723e-01, 1.7111516934883419e+01, -6.5918535028673038e+00, 1.6710814028293859e+00, -9.1262323378824761e-01, -6.5918535028673038e+00, 3.0835319597298456e+00, -2.6912281791743353e-01, 4.2893119166797788e-01, 1.6710814028293859e+00, -2.6912281791743353e-01, 4.2342213175980664e-01, -6.1060011100531768e-02, -9.1262323378824761e-01, 4.2893119166797788e-01, -6.1060011100531768e-02, 3.3382324238618233e-01, 1.7310554752654891e+01, -6.3254698558225808e+00, -1.5889046370618964e+00, 4.7998865450534484e-01, -6.3254698558225808e+00, 3.5951424725613625e+00, -4.3752933180985376e-01, -2.0052810357878864e-01, -1.5889046370618964e+00, -4.3752933180985376e-01, 1.1169674552585609e+00, -4.3289156862570732e-01, 4.7998865450534484e-01, -2.0052810357878864e-01, -4.3289156862570732e-01, 1.0348453338540857e+00, 1.5725152411710674e+01, -8.1251695223259723e+00, -1.6221105906193571e+00, -6.5726645228657188e-01, -8.1251695223259723e+00, 4.2655830965537183e+00, 6.8154083535105903e-01, 4.4651245957010732e-01, -1.6221105906193571e+00, 6.8154083535105903e-01, 6.2732001380343250e-01, -2.8865920201796080e-01, -6.5726645228657188e-01, 4.4651245957010732e-01, -2.8865920201796080e-01, 3.1861318811044576e-01, 1.5902288815268022e+01, -8.2452928765255784e+00, -2.0278743230239088e-01, 6.3486533567920422e-01, -8.2452928765255784e+00, 4.6020174712364978e+00, -3.3658080874833285e-01, -4.3977303097587184e-01, -2.0278743230239088e-01, -3.3658080874833285e-01, 6.0187702290916412e-01, 1.1014613257737753e-01, 6.3486533567920422e-01, -4.3977303097587184e-01, 1.1014613257737753e-01, 4.8184779224444352e-01, 1.6317485422902017e+01, -9.0944710620236826e+00, 1.3236077186681450e+00, 1.0543599207408361e+00, -9.0944710620236826e+00, 5.0770952150987654e+00, -6.8283997895415571e-01, -6.1415172571988774e-01, 1.3236077186681450e+00, -6.8283997895415571e-01, 4.6986429225388077e-01, -7.1779176794846591e-02, 1.0543599207408361e+00, -6.1415172571988774e-01, -7.1779176794846591e-02, 3.6646138105239617e-01, 1.6629466429731355e+01, -5.1277919122112783e+00, 1.4340055938268250e+00, 7.5497557373088631e-01, -5.1277919122112783e+00, 2.5533892790953709e+00, -6.1907458698641737e-01, 2.8548716783701866e-01, 1.4340055938268250e+00, -6.1907458698641737e-01, 5.5021114982679775e-01, 2.1988595200875122e-01, 7.5497557373088631e-01, 2.8548716783701866e-01, 2.1988595200875122e-01, 4.6790021907528190e-01, 1.8051015423857478e+01, -6.3374886699541655e+00, 6.7052196738207570e-01, 1.7901863112928220e+00, -6.3374886699541655e+00, 3.6107306567453308e+00, -1.1554776709999790e+00, -1.6138128679965386e-01, 6.7052196738207570e-01, -1.1554776709999790e+00, 6.6215475367233645e-01, -2.0835424063584101e-01, 1.7901863112928220e+00, -1.6138128679965386e-01, -2.0835424063584101e-01, 3.8230347739520337e-01, 1.4983783383912956e+01, -6.5303033327345901e+00, 1.5693503671455411e+00, 1.3115128213232672e+00, -6.5303033327345901e+00, 3.3609306656827300e+00, -3.8347465608612052e-01, -8.5246645597040638e-01, 1.5693503671455411e+00, -3.8347465608612052e-01, 4.6444575454540948e-01, 1.1133056795538820e-01, 1.3115128213232672e+00, -8.5246645597040638e-01, 1.1133056795538820e-01, 4.2050090792459205e-01, 1.6516272469929035e+01, -8.1086408858622381e+00, 1.2444910428820757e+00, 1.4111414188071807e+00, -8.1086408858622381e+00, 4.7397792897538693e+00, -3.4785795277638909e-01, -3.0333528301140100e-01, 1.2444910428820757e+00, -3.4785795277638909e-01, 6.7086370063026790e-01, 2.4773393103872440e-03, 1.4111414188071807e+00, -3.0333528301140100e-01, 2.4773393103872440e-03, 4.3791110589613524e-01, 1.6929227088899484e+01, -7.8477753916727986e+00, 5.9122351013238139e-01, 1.1995974186685161e+00, -7.8477753916727986e+00, 5.0130450761309371e+00, -4.8119378860454465e-01, -7.7299224219143381e-01, 5.9122351013238139e-01, -4.8119378860454465e-01, 6.6837844940101898e-01, -2.8293163674859706e-01, 1.1995974186685161e+00, -7.7299224219143381e-01, -2.8293163674859706e-01, 3.2651017287964618e-01, 1.5491954999314911e+01, -6.9941923612056076e+00, 1.6077749159874175e+00, 7.8834911790693296e-01, -6.9941923612056076e+00, 3.9990238979833492e+00, -3.1324180602386442e-01, -2.9940611862675603e-01, 1.6077749159874175e+00, -3.1324180602386442e-01, 3.8426170077342980e-01, 6.1325558045078110e-02, 7.8834911790693296e-01, -2.9940611862675603e-01, 6.1325558045078110e-02, 1.9844293738738106e-01, 1.8256490990353864e+01, -7.2596656308294474e+00, 7.2457573583544210e-02, 1.6393036849568940e+00, -7.2596656308294474e+00, 3.1514450092236164e+00, 1.1658304601420899e-01, -4.7408825564360935e-01, 7.2457573583544210e-02, 1.1658304601420899e-01, 4.8823333158455351e-01, -9.9774500139366201e-02, 1.6393036849568940e+00, -4.7408825564360935e-01, -9.9774500139366201e-02, 3.6855349855371966e-01, 1.6820120859691094e+01, -6.8557939495607343e+00, 9.3076613697930166e-01, 1.3614159506132830e+00, -6.8557939495607343e+00, 2.8579888611432107e+00, -4.9935124766213668e-01, -5.7041886268769781e-01, 9.3076613697930166e-01, -4.9935124766213668e-01, 4.6829838997314921e-01, -1.4197642929627971e-01, 1.3614159506132830e+00, -5.7041886268769781e-01, -1.4197642929627971e-01, 4.3316577284207314e-01, 1.6419872338608442e+01, -8.5832050734274041e+00, 1.7192566692161901e+00, -5.1218989222962830e-01, -8.5832050734274041e+00, 4.4976512426775175e+00, -9.1124533514664441e-01, 2.8795414896579641e-01, 1.7192566692161901e+00, -9.1124533514664441e-01, 7.4971043306955509e-01, -5.5608503289291955e-01, -5.1218989222962830e-01, 2.8795414896579641e-01, -5.5608503289291955e-01, 4.6700869661319677e-01, 1.8135538967327214e+01, -3.2166336691478290e+00, -2.4622113217501940e+00, -3.6996532735385559e+00, -3.2166336691478290e+00, 1.2549932403658035e+00, -2.2292652269956342e-01, 5.6100325796761008e-01, -2.4622113217501940e+00, -2.2292652269956342e-01, 1.0930566691740100e+00, 4.2547956233729428e-01, -3.6996532735385559e+00, 5.6100325796761008e-01, 4.2547956233729428e-01, 9.9882325270528738e-01, 1.7439351281629058e+01, -5.8296895831009508e+00, 1.5263763648410595e+00, -1.0127116989967861e+00, -5.8296895831009508e+00, 3.0345134422808195e+00, -1.2453100751923698e+00, 2.5921890911740408e-01, 1.5263763648410595e+00, -1.2453100751923698e+00, 6.4872185802115923e-01, -1.0471192819150560e-01, -1.0127116989967861e+00, 2.5921890911740408e-01, -1.0471192819150560e-01, 3.4321528712268262e-01, 1.8225452775936304e+01, -8.3661120474364186e+00, 1.6298037497783302e-01, 1.3744275939125781e+00, -8.3661120474364186e+00, 4.1934382694433019e+00, -4.2888473389741155e-01, -3.3111383016288920e-01, 1.6298037497783302e-01, -4.2888473389741155e-01, 3.9223109407533963e-01, -2.8438634962977571e-01, 1.3744275939125781e+00, -3.3111383016288920e-01, -2.8438634962977571e-01, 3.5861917482977251e-01, 1.7848001583696501e+01, -9.0977146732770837e+00, 1.3037096678166800e+00, -1.3549605490328065e-01, -9.0977146732770837e+00, 4.6663261401614928e+00, -6.7036638792592496e-01, 1.9232787391811296e-01, 1.3037096678166800e+00, -6.7036638792592496e-01, 1.1260447105843117e+00, -9.1370205009458638e-03, -1.3549605490328065e-01, 1.9232787391811296e-01, -9.1370205009458638e-03, 5.2699958100439070e-01 + }; + std::vector em = em_x; + std::vector table = { + -1.0600000163027882e+02, 7.7059358807135015e+02, -5.6954714749735385e+03, 1.2167808756610991e+03, -7.6199102434332218e+01, 1.0706136029373441e+00, -1.0600000164528124e+02, 7.7059358630452323e+02, -5.6954715659539552e+03, 1.2167808757436076e+03, -7.6199099707724926e+01, 1.0706134206080884e+00, -1.0600000163027882e+02, 7.7059358807135015e+02, -5.6954714749735385e+03, 1.2167808756610991e+03, -7.6199102434332218e+01, 1.0706136029373441e+00, -1.0600000164528124e+02, 7.7059358630452323e+02, -5.6954715659539552e+03, 1.2167808757436076e+03, -7.6199099707724926e+01, 1.0706134206080884e+00, -9.6000006759336443e+01, 6.2969719646863621e+02, -4.2053706363664551e+03, 9.0372155784831205e+02, -5.7600014239472898e+01, 8.6528676197113796e-01, -9.6000006828502180e+01, 6.2969718981238339e+02, -4.2053709121998018e+03, 9.0372156236848912e+02, -5.7600006817493266e+01, 8.6528625106787871e-01, -9.6000006759336443e+01, 6.2969719646863621e+02, -4.2053706363664551e+03, 9.0372155784831205e+02, -5.7600014239472898e+01, 8.6528676197113796e-01, -9.6000006828502180e+01, 6.2969718981238339e+02, -4.2053709121998018e+03, 9.0372156236848912e+02, -5.7600006817493266e+01, 8.6528625106787871e-01, -8.6000028021606425e+01, 5.0303296429845562e+02, -3.0008648248894533e+03, 6.4939597734382562e+02, -4.2250984019314707e+01, 6.8180015607155764e-01, -8.6000028340480625e+01, 5.0303293978396903e+02, -3.0008656209622986e+03, 6.4939600529391078e+02, -4.2250965541906716e+01, 6.8179882734268982e-01, -8.6000028021606425e+01, 5.0303296429845562e+02, -3.0008648248894533e+03, 6.4939597734382562e+02, -4.2250984019314707e+01, 6.8180015607155764e-01, -8.6000028340480625e+01, 5.0303293978396903e+02, -3.0008656209622986e+03, 6.4939600529353049e+02, -4.2250965541830588e+01, 6.8179882733888086e-01, -7.6000116148038558e+01, 3.9060139597613619e+02, -2.0515743554479322e+03, 4.4772754091167945e+02, -2.9848087537832814e+01, 5.2014755686537917e-01, -7.6000117618125429e+01, 3.9060130821883052e+02, -2.0515765138621105e+03, 4.4772766653712006e+02, -2.9848047259266409e+01, 5.2014443989116910e-01, -7.6000116148038558e+01, 3.9060139597613619e+02, -2.0515743554479322e+03, 4.4772754091167945e+02, -2.9848087537832814e+01, 5.2014755686537917e-01, -7.6000117618125742e+01, 3.9060130821877993e+02, -2.0515765138659344e+03, 4.4772766652483722e+02, -2.9848047256692499e+01, 5.2014443976043645e-01, -6.6000481290731443e+01, 2.9240425245900917e+02, -1.3271250821434478e+03, 2.9263955624337893e+02, -2.0087224005740719e+01, 3.8031147992206349e-01, -6.6000488067863742e+01, 2.9240394960550276e+02, -1.3271304743966571e+03, 2.9264002765325057e+02, -2.0087154325946980e+01, 3.8030522013794582e-01, -6.6000481290731443e+01, 2.9240425245900917e+02, -1.3271250821434478e+03, 2.9263955624337893e+02, -2.0087224005740719e+01, 3.8031147992206349e-01, -6.6000488067883694e+01, 2.9240394960308691e+02, -1.3271304745319526e+03, 2.9264002727267626e+02, -2.0087154245656002e+01, 3.8030521605011575e-01, -5.6001992867343972e+01, 2.0844745574402617e+02, -7.9715799906587699e+02, 1.7805563184427194e+02, -1.2663929104029080e+01, 2.6224978307822894e-01, -5.6002024103130161e+01, 2.0844646075692629e+02, -7.9717003898786652e+02, 1.7805715054974732e+02, -1.2663864677938077e+01, 2.6224029170957303e-01, -5.6001992867343972e+01, 2.0844745574402617e+02, -7.9715799906587699e+02, 1.7805563184427194e+02, -1.2663929104029080e+01, 2.6224978307822894e-01, -5.6002024104383771e+01, 2.0844646064871867e+02, -7.9717004324410516e+02, 1.7805714044473001e+02, -1.2663862524337585e+01, 2.6224018166598279e-01, -4.6008230210744550e+01, 1.3874976550319553e+02, -4.3134867537287749e+02, 9.7902623595157010e+01, -7.2734403121911884e+00, 1.6589123996688057e-01, -4.6008373996710617e+01, 1.3874671965012058e+02, -4.3137141216256458e+02, 9.7906861443792735e+01, -7.2735856084076280e+00, 1.6588642735924275e-01, -4.6008230210744550e+01, 1.3874976550319553e+02, -4.3134867537287749e+02, 9.7902623595157010e+01, -7.2734403121911884e+00, 1.6589123996688057e-01, -4.6008374075307870e+01, 1.3874671513440606e+02, -4.3137152784492957e+02, 9.7906652364871050e+01, -7.2735401377994249e+00, 1.6588408717348646e-01, -3.6033642533368131e+01, 8.3364086172019398e+01, -1.9942175516407502e+02, 4.6124022747838069e+01, -3.6130563858549958e+00, 9.1249773312287188e-02, -3.6034298111245583e+01, 8.3355843868269616e+01, -1.9945266030093268e+02, 4.6135000705962462e+01, -3.6142786797647353e+00, 9.1293932043118198e-02, -3.6033642533368131e+01, 8.3364086172019398e+01, -1.9942175516407502e+02, 4.6124022747838069e+01, -3.6130563858549958e+00, 9.1249773312287188e-02, -3.6034302998781108e+01, 8.3355675173745269e+01, -1.9945516784358935e+02, 4.6132303200740992e+01, -3.6136582565667807e+00, 9.1261386291659793e-02, -2.6132076703837274e+01, 4.2398929436319683e+01, -7.1037171119057973e+01, 1.3425662262407457e+01, -7.5172495708992593e-01, 7.7522572203268742e-03, -2.6134776894873077e+01, 4.2384732735328775e+01, -7.1030526549717337e+01, 1.3431455085299461e+01, -7.5302028721199155e-01, 7.8186246126207160e-03, -2.6132076703837274e+01, 4.2398929436319683e+01, -7.1037171119057973e+01, 1.3425662262405055e+01, -7.5172495708944420e-01, 7.7522572203027138e-03, -2.6135071381093578e+01, 4.2379566840123424e+01, -7.1067162844830236e+01, 1.3434603316099608e+01, -7.5251233833488806e-01, 7.7734884077347950e-03, -2.2221480705551805e+01, 3.0067218434037404e+01, -4.1779705297521097e+01, -1.9077757705724110e+02, 3.6413466026808294e+02, -1.6067397401486718e+02, -2.2225430071703467e+01, 3.0060809113889512e+01, -4.1712800191721314e+01, -1.9084786311022177e+02, 3.6410062714257685e+02, -1.6063028238785057e+02, -2.2221480705551830e+01, 3.0067218434036263e+01, -4.1779705297545611e+01, -1.9077757705723738e+02, 3.6413466026815809e+02, -1.6067397401492047e+02, -2.2226913938674084e+01, 3.0042371820589185e+01, -4.1801582285426832e+01, -1.9048619249019526e+02, 3.6373874557858261e+02, -1.6052358406417352e+02, -2.1250858373060836e+01, 2.7343847665267702e+01, -3.6044215009418814e+01, -1.7618484800469861e+02, 3.3120085405644409e+02, -1.4534825256321494e+02, -2.1254939505030809e+01, 2.7342716030835884e+01, -3.5955450545431681e+01, -1.7635550119316844e+02, 3.3127447930769307e+02, -1.4533876561022046e+02, -2.1250858373060954e+01, 2.7343847665262818e+01, -3.6044215009514119e+01, -1.7618484800464822e+02, 3.3120085405666612e+02, -1.4534825256338749e+02, -2.1257155379297881e+01, 2.7317691772612619e+01, -3.6063526926252166e+01, -1.7588696592837897e+02, 3.3079005662384850e+02, -1.4519086534447842e+02, -2.0283472228681301e+01, 2.4763027042036295e+01, -3.0876160316998963e+01, -1.6184864900381874e+02, 2.9976970905591691e+02, -1.3084395423768876e+02, -2.0287461515322455e+01, 2.4769400540137131e+01, -3.0762734380983186e+01, -1.6214886052089241e+02, 2.9998995088792128e+02, -1.3088331758129965e+02, -2.0283472228681809e+01, 2.4763027042017129e+01, -3.0876160317336627e+01, -1.6184864900359682e+02, 2.9976970905662938e+02, -1.3084395423826805e+02, -2.0290765181946348e+01, 2.4735639907973120e+01, -3.0892738413082597e+01, -1.6154574482310053e+02, 2.9934595420013272e+02, -1.3068028494926122e+02, -1.9319499689234629e+01, 2.2323824431805683e+01, -2.6243395369841849e+01, -1.4782286378121026e+02, 2.6985759662396487e+02, -1.1715474197881395e+02, -1.9323022570439292e+01, 2.2340565860680357e+01, -2.6102786429129356e+01, -1.4828764857305418e+02, 2.7027298759214750e+02, -1.1726163007473576e+02, -1.9319499689236839e+01, 2.2323824431730525e+01, -2.6243395371031539e+01, -1.4782286378021576e+02, 2.6985759662609979e+02, -1.1715474198068593e+02, -1.9327939259284843e+01, 2.2295320666731183e+01, -2.6257097174199931e+01, -1.4751677383623073e+02, 2.6942341041084092e+02, -1.1698575776762208e+02, -1.8359079763330211e+01, 2.0025118950280675e+01, -2.2113826757823226e+01, -1.3415932552431914e+02, 2.4147795894487624e+02, -1.0427314537549884e+02, -1.8361534194530734e+01, 2.0055847278170305e+01, -2.1944107342764479e+01, -1.3482982214648752e+02, 2.4214772485703989e+02, -1.0447085300268679e+02, -1.8359079763339750e+01, 2.0025118949989704e+01, -2.2113826761939308e+01, -1.3415932552009582e+02, 2.4147795895089951e+02, -1.0427314538136979e+02, -1.8368836959765495e+01, 1.9995657614892380e+01, -2.2124533894067383e+01, -1.3385233293246981e+02, 2.4103659293914149e+02, -1.0410011400771683e+02, -1.7402299525814517e+01, 1.7865597763687486e+01, -1.8455503416511757e+01, -1.2090765118569301e+02, 2.1464125749038132e+02, -9.2190581022134992e+01, -1.7402744551259310e+01, 1.7914800567904472e+01, -1.8255754666855470e+01, -1.2183089355280822e+02, 2.1563582256173194e+02, -9.2507405324257306e+01, -1.7402299525855486e+01, 1.7865597762572605e+01, -1.8455503430527756e+01, -1.2090765116826699e+02, 2.1464125750558804e+02, -9.2190581039770791e+01, -1.7413567239985614e+01, 1.7835392747330133e+01, -1.8463115133795956e+01, -1.2060260469703572e+02, 2.1419685510959093e+02, -9.2015134441585104e+01, -1.6449179896085464e+01, 1.5843762224435309e+01, -1.5236722252652665e+01, -1.0811515163854509e+02, 1.8935506712501905e+02, -8.0897437157402223e+01, -1.6446174965543889e+01, 1.5916874201410112e+01, -1.5007553197461570e+01, -1.0934291295595986e+02, 1.9075532567542470e+02, -8.1366596347119696e+01, -1.6449179896260411e+01, 1.5843762220214204e+01, -1.5236722299508587e+01, -1.0811515156878269e+02, 1.8935506715588940e+02, -8.0897437207525684e+01, -1.6462173655481337e+01, 1.5813096619069219e+01, -1.5241142983208677e+01, -1.0781563484017332e+02, 1.8891289499393798e+02, -8.0721658713418606e+01, -1.5499661595231082e+01, 1.3957945516559789e+01, -1.2426145992195885e+01, -9.5826844741964834e+01, 1.6562434781973772e+02, -7.0383233416004117e+01, -1.5491037589250178e+01, 1.4061349904707843e+01, -1.2170301483989650e+01, -9.7412966929875139e+01, 1.6751874597575440e+02, -7.1041920384880939e+01, -1.5499661595973759e+01, 1.3957945500778198e+01, -1.2426146145776961e+01, -9.5826844470313858e+01, 1.6562434784656404e+02, -7.0383233547510557e+01, -1.5514618579274794e+01, 1.3927192540790591e+01, -1.2427264674287118e+01, -9.5537423121432880e+01, 1.6519113036542510e+02, -7.0209783384625098e+01, -1.4553592409098401e+01, 1.2206343505203831e+01, -9.9929274597052196e+00, -8.4085595900823435e+01, 1.4345191724964303e+02, -6.0636862050381758e+01, -1.4536130507533649e+01, 1.2347228125716077e+01, -9.7159302678980044e+00, -8.6081002959763751e+01, 1.4592996741513730e+02, -6.1523840242331410e+01, -1.4553592412232879e+01, 1.2206343446986155e+01, -9.9929279524397305e+00, -8.4085594870780753e+01, 1.4345191706222485e+02, -6.0636862352071532e+01, -1.4570766853404239e+01, 1.2175998366492486e+01, -9.9905856922863112e+00, -8.3812185051328299e+01, 1.4303633648493073e+02, -6.0469165577726159e+01, -1.3610717065161962e+01, 1.0587059629986399e+01, -7.9068321681349163e+00, -7.2932404423885004e+01, 1.2283913327111270e+02, -5.1646910322317169e+01, -1.3579708436673444e+01, 1.0773027159520954e+01, -7.6175370796795425e+00, -7.5376833196183071e+01, 1.2597958225245242e+02, -5.2797863799745748e+01, -1.3610717078313911e+01, 1.0587059418306087e+01, -7.9068337121483454e+00, -7.2932400620636059e+01, 1.2283913169238102e+02, -5.1646910832841897e+01, -1.3630368323321786e+01, 1.0557789879027116e+01, -7.9007777139483810e+00, -7.2682825476758552e+01, 1.2245259140017740e+02, -5.1489446559796768e+01, -1.2670671078399982e+01, 9.0981634949263963e+00, -6.1383490362855788e+00, -6.2406844162279825e+01, 1.0378677653422224e+02, -4.3402055519687693e+01, -1.2619333100308433e+01, 9.3364634226935799e+00, -5.8491811509717584e+00, -6.5316414528433455e+01, 1.0763857666200300e+02, -4.4841832720191050e+01, -1.2670671133253135e+01, 9.0981627374157021e+00, -6.1383537481895356e+00, -6.2406830503476570e+01, 1.0378676818216074e+02, -4.3402055529436716e+01, -1.2693036794620980e+01, 9.0708908225804148e+00, -6.1281713411274001e+00, -6.2191660620037396e+01, 1.0344456594081470e+02, -4.3260806640248063e+01, -1.1732979767504439e+01, 7.7377614739662697e+00, -4.6587775146685351e+00, -5.2547655563671029e+01, 8.6296103981829802e+01, -3.5891515805495345e+01, -1.1651721415208119e+01, 8.0340005825064456e+00, -4.3852919661646119e+00, -5.5898160750405737e+01, 9.0851291378134590e+01, -3.7622755083739385e+01, -1.1732979994779518e+01, 7.7377588120662892e+00, -4.6587914600219875e+00, -5.2547607987974565e+01, 8.6296066930227624e+01, -3.5891510429190419e+01, -1.1758218632638741e+01, 7.7137968422318544e+00, -4.6438239588320966e+00, -5.2381405657406454e+01, 8.6019170302439520e+01, -3.5774653697918737e+01, -1.0797063195543267e+01, 6.5040766534586290e+00, -3.4402783696562169e+00, -4.3393478931462226e+01, 7.0370032342568010e+01, -2.9105535302381853e+01, -1.0672637254876815e+01, 6.8603244928014488e+00, -3.1995767859681346e+00, -4.7101348454718874e+01, 7.5530774605740319e+01, -3.1094453979913311e+01, -1.0797064129672576e+01, 6.5040675030570139e+00, -3.4403181344841500e+00, -4.3393319126804485e+01, 7.0369884883020177e+01, -2.9105501594155889e+01, -1.0825134802124644e+01, 6.4853446725127366e+00, -3.4195560956016346e+00, -4.3296381389022351e+01, 7.0187483762520671e+01, -2.9024415860031247e+01, -9.8622468030169337e+00, 5.3955359781222549e+00, -2.4558741324534137e+00, -3.4983728078555984e+01, 5.6014425934291204e+01, -2.3035887876475471e+01, -9.6769173769353625e+00, 5.8079540801032961e+00, -2.2635143148159220e+00, -3.8890523502249145e+01, 6.1563046720547966e+01, -2.5198820521877391e+01, -9.8622505990399034e+00, 5.3955054149765509e+00, -2.4559821583353774e+00, -3.4983216045684472e+01, 5.6013889382190079e+01, -2.3035736114340502e+01, -9.8926597117464805e+00, 5.3849440641688187e+00, -2.4279562878572039e+00, -3.4983707025980287e+01, 5.5966629574570753e+01, -2.3006306589550750e+01, -8.9277749780883457e+00, 4.4108678323349286e+00, -1.6793815271288624e+00, -2.7359655656676122e+01, 4.3239544183593061e+01, -1.7676416286664047e+01, -8.6587749152265552e+00, 4.8674392165289442e+00, -1.5450097170494306e+00, -3.1230915545542118e+01, 4.8829474992442343e+01, -1.9874755288141955e+01, -8.9277901202336185e+00, 4.4107699183102085e+00, -1.6796551456533098e+00, -2.7358123514289456e+01, 4.3237769027728554e+01, -1.7675844947587926e+01, -8.9590559763951383e+00, 4.4128957610428623e+00, -1.6423658138809611e+00, -2.7493743583145054e+01, 4.3380518846300511e+01, -1.7719639183506050e+01, -7.9928164326293913e+00, 3.5492331091008302e+00, -1.0852462622393610e+00, -2.0565792757352423e+01, 3.2061909496398073e+01, -1.3023704651715642e+01, -7.6125412569887647e+00, 4.0287966748633526e+00, -1.0084592804412351e+00, -2.4116992333062022e+01, 3.7252797603904497e+01, -1.5077495076198684e+01, -7.9928747817255603e+00, 3.5489404571097585e+00, -1.0858609980296849e+00, -2.0561701094768868e+01, 3.2056747083970720e+01, -1.3021877019728107e+01, -8.0213899495838241e+00, 3.5708128515175943e+00, -1.0368753205735253e+00, -2.0877831538201836e+01, 3.2456559535389509e+01, -1.3165540198118645e+01, -7.0564174984379102e+00, 2.8104770395789380e+00, -6.4821407306458223e-01, -1.4652118176169953e+01, 2.2507145963021038e+01, -9.0780963613608154e+00, -6.5338936679228468e+00, 3.2846161494194233e+00, -6.1760141818709846e-01, -1.7606122820367215e+01, 2.6855555289500277e+01, -1.0803821410528570e+01, -7.0566263531717324e+00, 2.8097184139861691e+00, -6.4925197579297411e-01, -1.4643483271177150e+01, 2.2495243692983838e+01, -9.0734373052814821e+00, -7.0742646195707266e+00, 2.8621047467298468e+00, -5.8641470402843421e-01, -1.5178915176777426e+01, 2.3211717123277591e+01, -9.3414295847965061e+00, -6.1172231064332783e+00, 2.1957964102200167e+00, -3.4265643705632465e-01, -9.6769153352706798e+00, 1.4613873405033004e+01, -5.8450824172251430e+00, -5.4212678780860326e+00, 2.6341589573018260e+00, -3.4085224757280796e-01, -1.1835854891340576e+01, 1.7794701474942944e+01, -7.1075278532253687e+00, -6.1178367984533244e+00, 2.1945528943967396e+00, -3.4261268423617658e-01, -9.6695829134679272e+00, 1.4600877298870854e+01, -5.8381668136523013e+00, -6.1072022151656586e+00, 2.2922503774685161e+00, -2.6715334266026142e-01, -1.0408120531614587e+01, 1.5617405440391840e+01, -6.2270636615178061e+00, -5.1722074807324017e+00, 1.7098190643016411e+00, -1.4098618492175408e-01, -5.7061337346696464e+00, 8.4331806866534098e+00, -3.3349192888568142e+00, -4.2766424379800121e+00, 2.0860564217794284e+00, -1.5548660419053545e-01, -7.0034949575065015e+00, 1.0332245608764421e+01, -4.0873492185766374e+00, -5.1727690165421372e+00, 1.7132539127425084e+00, -1.2776576793785877e-01, -5.7565343018918274e+00, 8.4941254548170697e+00, -3.3479852132230872e+00, -5.0998839330979591e+00, 1.8678855512825561e+00, -5.7718910331047868e-02, -6.5095346397755423e+00, 9.5462002113817768e+00, -3.7632628689263172e+00, -4.2112469382255613e+00, 1.3675717927787789e+00, -9.4961575783498800e-03, -2.7877417589321136e+00, 3.9953503912711956e+00, -1.5499906707437840e+00, -3.1046711877098376e+00, 1.6568346830533449e+00, -4.5990009889900242e-02, -3.3140676307068091e+00, 4.7472200808709299e+00, -1.8492173878772247e+00, -4.1976749320353317e+00, 1.4246952243441517e+00, 8.7531923058200650e-02, -3.0996975434049761e+00, 4.4668738099197531e+00, -1.7103055321708385e+00, -4.0163145894665320e+00, 1.5923303121893606e+00, 5.8249749369824022e-02, -3.3748048713195491e+00, 4.7925769874900315e+00, -1.8598420111853879e+00, -3.1955533414298376e+00, 1.2168024121915868e+00, 9.9474205814620603e-02, -8.6811124876189694e-01, 1.1994338853723501e+00, -4.4837238870567747e-01, -1.9098914522594992e+00, 1.3654451552507061e+00, 2.9537044429980407e-03, -9.3701125207094127e-01, 1.2575365835116745e+00, -4.7248060681970733e-01, -3.0285770502890443e+00, 1.6166340190704305e+00, 4.8662683065338386e-01, -1.2308607057515726e+00, 1.6114560066217587e+00, -6.5896729332189652e-01, -2.8078044229222514e+00, 1.4555130910035559e+00, 9.0876948497501955e-02, -1.0566809618626720e+00, 1.3938154223720176e+00, -5.2279617091852160e-01, -1.9963264755188566e+00, 1.3672906754961440e+00, 2.0801988470625002e-01, 2.0083818728351077e-02, -1.5135587406137185e-02, -1.4175240342178652e-02, -6.9344786794476854e-01, 1.2280621078720415e+00, 1.2333381103148277e-02, -1.0895386066093759e-02, 2.1764282171790141e-02, -1.0106900291744604e-02, -1.2036881930169383e+00, 2.0482931230000392e+00, -1.2689218008973949e-01, -5.0580690719339239e-01, 3.4047786101030464e-01, -7.0959386937004015e-02, -1.4470760938303664e+00, 1.4285049373060201e+00, 5.5764887956399375e-02, -2.9461990750009881e-02, 2.3005167601875431e-02, -1.0760396189439407e-02, -4.3024292433642597e-01, 1.7121633497582587e+00, 3.5705413032693957e-02, -9.9216800479772127e-01, 1.5115432403429119e+00, -6.3985596276149748e-01, 5.4770961684437192e-01, 1.2565653391084903e+00, 9.1639130181564755e-03, -6.8547618650262643e-01, 1.2037212931265591e+00, -5.1526772142324506e-01, 4.8142431677326969e-01, 1.2842025505965851e+00, -3.1103960497811806e-01, -3.8667287940463613e-01, 9.2663039525338942e-01, -4.1330437951972537e-01, 1.9976512094478704e-02, 1.4898674304290889e+00, -2.1940405767858565e-03, -8.0791207141984167e-01, 1.3979310081478775e+00, -5.9845265079421794e-01, 1.1971451112382212e+00, 1.6539633089946477e+00, -2.7009878691796618e-01, -2.8868139196850624e+00, 4.7294193613612734e+00, -1.9578020397520424e+00, 1.8164162541717044e+00, 1.4570111710269262e+00, 2.2385898037164991e-02, -3.1195681762439769e+00, 4.9723722392038878e+00, -2.0423972644796100e+00, 1.5812403987207633e+00, 1.1421043858413655e+00, -4.4319666868952730e-02, -2.3144705949527720e+00, 3.7448930479898297e+00, -1.5426803544433196e+00, 1.4992161878806018e+00, 1.6612039136364238e+00, -2.2870713891204597e-02, -3.4442115437939465e+00, 5.5057190995408973e+00, -2.2657208348376137e+00, 2.4658130352390710e+00, 1.5819912227884063e+00, -1.3204477532594588e-01, -5.7752803465671017e+00, 9.0677018990478242e+00, -3.6843468204828174e+00, 3.1062201217160963e+00, 1.8205810727868250e+00, 7.3942159732456811e-02, -7.3418038323250947e+00, 1.1309154676354810e+01, -4.5733470083866452e+00, 2.5667672162869133e+00, 1.3762236869878626e+00, 5.4823291778512563e-02, -5.5558964069977943e+00, 8.5620133672289516e+00, -3.4575259608624478e+00, 2.9333361085351610e+00, 1.9771000784477066e+00, 2.1600903596218385e-02, -7.7786452012965430e+00, 1.2026327126407146e+01, -4.8722408979121159e+00, 3.5238342146994350e+00, 1.8411341262124141e+00, 1.0485737443151430e-01, -1.0316470080846322e+01, 1.5628354265192609e+01, -6.2547428286449396e+00, 4.3947471898784478e+00, 2.3129375587624681e+00, 1.6998863701958250e-01, -1.3069120913924280e+01, 1.9764673064124775e+01, -7.9234176878170990e+00, 3.5464051944219954e+00, 1.7786047141550632e+00, 1.8395466553434961e-01, -1.0256713338978345e+01, 1.5450540198835597e+01, -6.1709943751208902e+00, 4.3074781177775723e+00, 2.4284702978185178e+00, 1.2121907902830774e-01, -1.3510697720561426e+01, 2.0490823414440431e+01, -8.2265504110307699e+00, 4.5269670710447079e+00, 2.3411415500822019e+00, 3.7814443659878427e-01, -1.6533454371385766e+01, 2.4532574055181296e+01, -9.7222898630871342e+00, 5.6498078480438974e+00, 2.8871559084424092e+00, 3.1648740182441881e-01, -1.9832336139347099e+01, 2.9630584562783888e+01, -1.1804975183138390e+01, 4.5317970588477650e+00, 2.3235629480266455e+00, 4.0711209040396701e-01, -1.6523611973754900e+01, 2.4482080409856291e+01, -9.6968326211377835e+00, 5.6107427774726322e+00, 2.9693568967987254e+00, 2.6856229367890733e-01, -2.0186235796983127e+01, 3.0228033555488111e+01, -1.2057362656117963e+01, 5.5230828784340904e+00, 3.0159142144119913e+00, 7.5032702265793638e-01, -2.4452361306480910e+01, 3.5745746299744695e+01, -1.4059387633540990e+01, 6.8467243986091164e+00, 3.5205846294935204e+00, 5.5323452910250115e-01, -2.7424447720726722e+01, 4.0542113968978946e+01, -1.6058340606199877e+01, 5.5241079122419858e+00, 3.0111097413061287e+00, 7.6043241689918206e-01, -2.4453330947201032e+01, 3.5733842835424838e+01, -1.4052622761934279e+01, 6.8330970703372866e+00, 3.5730950345697865e+00, 5.0442967447855436e-01, -2.7630302835415993e+01, 4.0921397061842079e+01, -1.6223699529825666e+01, 6.5233214752268127e+00, 3.8455313715589599e+00, 1.2738445662734672e+00, -3.4142511056048967e+01, 4.9288751118195229e+01, -1.9258816488331760e+01, 7.9798691992574877e+00, 4.2304633704347614e+00, 9.4916911879724064e-01, -3.6082800915305256e+01, 5.2740474636382487e+01, -2.0757970588732530e+01, 6.5235391967368317e+00, 3.8442392655293900e+00, 1.2772689685023881e+00, -3.4144245582802192e+01, 4.9286600694030149e+01, -1.9257235266278844e+01, 7.9780164759860508e+00, 4.2581364755189171e+00, 9.0490824102641643e-01, -3.6146890048111374e+01, 5.2902251888236343e+01, -2.0834714063750525e+01, 7.5301209868737518e+00, 4.8266093670811516e+00, 1.9906532239804082e+00, -4.5696171225139402e+01, 6.5222794336738914e+01, -2.5330008845677121e+01, 9.0592048208341964e+00, 5.0524444639807982e+00, 1.5639083038511417e+00, -4.6227354827270197e+01, 6.6742768625790532e+01, -2.6090733281390481e+01, 7.5301672757177256e+00, 4.8262668988539703e+00, 1.9917837214882572e+00, -4.5697152262800707e+01, 6.5222641787790508e+01, -2.5329699752317662e+01, 9.0617089689058279e+00, 5.0627200474303731e+00, 1.5306087886050987e+00, -4.6201245261995687e+01, 6.6753711704174307e+01, -2.6103836713323240e+01, 8.5439978438576958e+00, 5.9605352581937785e+00, 2.9388171122244109e+00, -5.9213652478598007e+01, 8.3623964589400401e+01, -3.2288651007290504e+01, 1.0100238105795977e+01, 6.0156046860821641e+00, 2.4311227628788585e+00, -5.8189717323516248e+01, 8.2972590004142106e+01, -3.2212869674305303e+01, 8.5440076687321067e+00, 5.9604459430021439e+00, 2.9391801366526531e+00, -5.9214078468041464e+01, 8.3624068891376510e+01, -3.2288610777657510e+01, 1.0103667533796683e+01, 6.0158650887345448e+00, 2.4107760944314816e+00, -5.8125625048064265e+01, 8.2906979417176174e+01, -3.2191629006406409e+01, 9.5650113177877785e+00, 7.2498153679976820e+00, 4.1551371399277919e+00, -7.4795843598083408e+01, 1.0457037732454131e+02, -4.0151433068943419e+01, 1.1116968561077568e+01, 7.1347098863330896e+00, 3.5688140741297674e+00, -7.2151486218593305e+01, 1.0165680693075836e+02, -3.9206269356622016e+01, 9.5650133940644455e+00, 7.2497924894015711e+00, 4.1552503042122613e+00, -7.4796005009548836e+01, 1.0457044971811401e+02, -4.0151435976986221e+01, 1.1120034079668221e+01, 7.1303147700774092e+00, 3.5594873892317103e+00, -7.2082067018068685e+01, 1.0156598726189708e+02, -3.9171834664292227e+01, 1.0593064483227742e+01, 8.6969028070512202e+00, 5.6755396034912966e+00, -9.2539537763180832e+01, 1.2813560149579646e+02, -4.8933613418447223e+01, 1.2119543877083460e+01, 8.4137603187360543e+00, 4.9925034366798311e+00, -8.8194505075704640e+01, 1.2287993196505218e+02, -4.7096724506223822e+01, 1.0593064919257221e+01, 8.6968970567044934e+00, 5.6755738143875760e+00, -9.2539593640863643e+01, 1.2813563331215474e+02, -4.8933618162805772e+01, 1.2121921818513506e+01, 8.4078642204619420e+00, 4.9908632634858190e+00, -8.8134432374832016e+01, 1.2279086550380391e+02, -4.7060844505587738e+01, 1.1627957207938659e+01, 1.0303707615441018e+01, 7.5344011042552923e+00, -1.1253294830348190e+02, 1.5438372244089408e+02, -5.8647453529357783e+01, 1.3114510015623049e+01, 9.8513572940713416e+00, 6.7213349376406626e+00, -1.0635738219113546e+02, 1.4665751311861146e+02, -5.5881528760137869e+01, 1.1627957298834614e+01, 1.0303706197478814e+01, 7.5344111366673712e+00, -1.1253296638384563e+02, 1.5438373415898508e+02, -5.8647455853629580e+01, 1.3116237925845430e+01, 9.8455331102145145e+00, 6.7243141059359051e+00, -1.0631074264006560e+02, 1.4658112805680690e+02, -5.5849452095162235e+01, 1.2669386535689361e+01, 1.2071287030293307e+01, 9.7633555455962835e+00, -1.3485075345900265e+02, 1.8336444946299886e+02, -6.9300787627414508e+01, 1.4105804414673191e+01, 1.1444289269702800e+01, 8.7789794745243590e+00, -1.2666835962860844e+02, 1.7298274034188972e+02, -6.5547771558832267e+01, 1.2669386554490638e+01, 1.2071286687068984e+01, 9.7633584027450482e+00, -1.3485075900242089e+02, 1.8336445335820781e+02, -6.9300788508071975e+01, 1.4107018463574896e+01, 1.1439185153305873e+01, 8.7843335749580440e+00, -1.2663444344319166e+02, 1.7292158897636148e+02, -6.5521162694327174e+01, 1.3716937488160630e+01, 1.3999597459400730e+01, 1.2389915672436279e+01, -1.5954894249539399e+02, 2.1510813446746886e+02, -8.0895567204040049e+01, 1.5095682313349364e+01, 1.3189272906323732e+01, 1.1192627051714643e+01, -1.4915916817312757e+02, 2.0184825850919157e+02, -7.6081293415969839e+01, 1.3716937492019641e+01, 1.3999597377767842e+01, 1.2389916464009524e+01, -1.5954894412085929e+02, 2.1510813567394996e+02, -8.0895567498068928e+01, 1.5096520030681436e+01, 1.3185064407456906e+01, 1.1198910160279951e+01, -1.4913565617175487e+02, 2.0180124290250004e+02, -7.6060129778156622e+01, 1.4770075388032444e+01, 1.6087303167766446e+01, 1.5436222950666867e+01, -1.8666021493779203e+02, 2.4962122089688103e+02, -9.3426463524457304e+01, 1.6085379191481852e+01, 1.5083589447287226e+01, 1.3991739427782750e+01, -1.7386892459375579e+02, 2.3325385095807121e+02, -8.7470099643500802e+01, 1.4770075388818769e+01, 1.6087303148664304e+01, 1.5436223164442264e+01, -1.8666021539675981e+02, 2.4962122125116741e+02, -9.3426463615076329e+01, 1.6085951551006787e+01, 1.5080238931969067e+01, 1.3998101278449143e+01, -1.7385331837944693e+02, 2.3321864790104019e+02, -8.7453697552144448e+01, 1.5828143941097450e+01, 1.8331670220961666e+01, 1.8918268274003861e+01, -2.1619095210442941e+02, 2.8688297635978756e+02, -1.0687973526499771e+02, 1.7075534787366465e+01, 1.7125200136366264e+01, 1.7207074959934751e+01, -2.0084388544719391e+02, 2.6720765911058965e+02, -9.9705133726570395e+01, 1.5828143941256627e+01, 1.8331670216557445e+01, 1.8918268330404022e+01, -2.1619095222989833e+02, 2.8688297645950814e+02, -1.0687973529137253e+02, 1.7075923730873765e+01, 1.7122590193964911e+01, 1.7213058024904747e+01, -2.0083402645820061e+02, 2.6718180837697332e+02, -9.9692640534772679e+01, 1.6890371426423382e+01, 2.0728579569842751e+01, 2.2845917469463828e+01, -2.4812083435502871e+02, 3.2684448823688496e+02, -1.2123263616047282e+02, 1.8066449820492846e+01, 1.9312661524160735e+01, 2.0870036016187061e+01, -2.3013589616073858e+02, 3.0372498377642154e+02, -1.1277999824352135e+02, 1.6890371426455424e+01, 2.0728579568840633e+01, 2.2845917484032956e+01, -2.4812083438838550e+02, 3.2684448826399682e+02, -1.2123263616782057e+02, 1.8066713333743454e+01, 1.9310657703202459e+01, 2.0875423564416035e+01, -2.3013008228413184e+02, 3.0370630494679148e+02, -1.1277060230387309e+02, 1.7955886187113396e+01, 2.3272683588860026e+01, 2.7223982220959247e+01, -2.8240595076334000e+02, 3.6943078590316281e+02, -1.3645364576977221e+02, 1.9058236733002300e+01, 2.1644988962398710e+01, 2.5012267757287322e+01, -2.6180071928343307e+02, 3.4282650121799617e+02, -1.2669036882336400e+02, 1.7955886187119816e+01, 2.3272683588634656e+01, 2.7223982224651898e+01, -2.8240595077199526e+02, 3.6943078591032139e+02, -1.3645364577174797e+02, 1.9058414960148450e+01, 2.1643466247439289e+01, 2.5016983354038196e+01, -2.6179767020610126e+02, 3.4281320617581565e+02, -1.2668337355331974e+02, 1.9023741366983238e+01, 2.5957710504548576e+01, 3.2054387652193789e+01, -3.1898571318422574e+02, 4.1454655650462962e+02, -1.5250373535684176e+02, 2.0050906563887416e+01, 2.4121527381838824e+01, 2.9665428981325245e+01, -2.9589665055055406e+02, 3.8453661583827250e+02, -1.4143340987287985e+02, 1.9023741366984520e+01, 2.5957710504498362e+01, 3.2054387653114766e+01, -3.1898571318642672e+02, 4.1454655650647550e+02, -1.5250373535735841e+02, 2.0051026978020587e+01, 2.4120379273875816e+01, 2.9669474257430963e+01, -2.9589543070583102e+02, 3.8452729731205977e+02, -1.4142824748467820e+02, 2.0092947487287756e+01, 2.8776895490568755e+01, 3.7339233558876920e+01, -9.8781982607414882e+00, 7.0916635282296292e-01, -1.2340880155534291e-02, 2.1044418341890132e+01, 2.6741847681518077e+01, 3.4861073630499796e+01, -9.1700568642165461e+00, 6.5220324713443967e-01, -1.1045071585279443e-02, 2.0092947487288011e+01, 2.8776895490557653e+01, 3.7339233559103448e+01, -9.8781982608033179e+00, 7.0916635282857932e-01, -1.2340880155703077e-02, 2.1044499630877905e+01, 2.6740987496092696e+01, 3.4864491165514394e+01, -9.1707199731434574e+00, 6.5223741134844682e-01, -1.1045188698410773e-02, 2.1162510215379026e+01, 3.1723491960797684e+01, 4.3084295875067085e+01, -4.1033675985379521e+00, -6.6095139594000130e-01, 6.0977735530407223e-02, 2.2038706806958309e+01, 2.9505670300337073e+01, 4.0630600131872811e+01, -2.7905442844326718e+00, -8.3885972791335117e-01, 6.8309956404426039e-02, 2.1162510215379076e+01, 3.1723491960795304e+01, 4.3084295875120795e+01, -4.1033675985539224e+00, -6.6095139593840913e-01, 6.0977735530354210e-02, 2.2038761643178379e+01, 2.9505029336592230e+01, 4.0633451796171073e+01, -2.7913314472201640e+00, -8.3878528163749511e-01, 6.8307595298566767e-02, 3.1719012432820758e+01, 6.7480322661109355e+01, 1.3318978565899991e+02, -1.6791944323404795e+01, -1.0181217992701848e+00, 1.2989592638281225e-01, 3.2009499874031789e+01, 6.5013296175889408e+01, 1.3669799889514238e+02, -1.7009031615065428e+01, -1.0689880784706638e+00, 1.3388972346122466e-01, 3.1719012432820758e+01, 6.7480322661109355e+01, 1.3318978565899991e+02, -1.6791944323404795e+01, -1.0181217992701848e+00, 1.2989592638281225e-01, 3.2009500887769519e+01, 6.5013269472322307e+01, 1.3669829238273672e+02, -1.7009116366540379e+01, -1.0689798256828462e+00, 1.3388945486998777e-01, 4.1931127118492086e+01, 1.1600186087954401e+02, 3.1751764022286790e+02, -4.6438894455748802e+01, -8.7599401950869438e-01, 2.2297105562740663e-01, 4.2002297497564768e+01, 1.1479764873768737e+02, 3.2393143797302810e+02, -4.7847299173836262e+01, -7.8150712905299369e-01, 2.2131248436241077e-01, 4.1931127118492086e+01, 1.1600186087954401e+02, 3.1751764022286790e+02, -4.6438894455748802e+01, -8.7599401950869438e-01, 2.2297105562740663e-01, 4.2002297514594851e+01, 1.1479764793294436e+02, 3.2393145467669495e+02, -4.7847304068128608e+01, -7.8150664807362491e-01, 2.2131246858403722e-01, 5.1984670105634827e+01, 1.7926303194781252e+02, 6.2846495111925287e+02, -1.0034649475039414e+02, 2.4606292097951082e-01, 3.3256752105517051e-01, 5.2000554052128159e+01, 1.7883235795593501e+02, 6.3273302895025176e+02, -1.0138733878813618e+02, 3.2804187851642969e-01, 3.3055293107858102e-01, 5.1984670105634827e+01, 1.7926303194781252e+02, 6.2846495111925287e+02, -1.0034649475039414e+02, 2.4606292097951082e-01, 3.3256752105517051e-01, 5.2000554052402805e+01, 1.7883235793562420e+02, 6.3273302962903426e+02, -1.0138733898825184e+02, 3.2804189825766372e-01, 3.3055293042886030e-01, 6.1996666427075382e+01, 2.5724136589119979e+02, 1.0913830717468406e+03, -1.8317243758181812e+02, 2.5193786568880601e+00, 4.6277932792022042e-01, 6.2000133522892554e+01, 2.5710536851489377e+02, 1.0934673032018356e+03, -1.8370056934287794e+02, 2.5630609198690104e+00, 4.6162176037505448e-01, 6.1996666427075382e+01, 2.5724136589119979e+02, 1.0913830717468406e+03, -1.8317243758181812e+02, 2.5193786568880601e+00, 4.6277932792022042e-01, 6.2000133522896938e+01, 2.5710536851442714e+02, 1.0934673032246803e+03, -1.8370056934963364e+02, 2.5630609205366826e+00, 4.6162176035304603e-01, 7.1999279107664492e+01, 3.4965254984584158e+02, 1.7356304176273381e+03, -3.0063395678020430e+02, 6.2079056750108883e+00, 6.1505333334154833e-01, 7.2000032172982571e+01, 3.4961232791697932e+02, 1.7365043785874466e+03, -3.0086002522613632e+02, 6.2270725229979789e+00, 6.1452738833821030e-01, 7.1999279107664492e+01, 3.4965254984584158e+02, 1.7356304176273381e+03, -3.0063395678020430e+02, 6.2079056750108883e+00, 6.1505333334154833e-01, 7.2000032172982642e+01, 3.4961232791696904e+02, 1.7365043785881401e+03, -3.0086002522634379e+02, 6.2270725230187063e+00, 6.1452738833751985e-01, 8.1999844359310714e+01, 4.5636323545227941e+02, 2.5918884526432239e+03, -4.5885344883307727e+02, 1.1616256691917803e+01, 7.8948404417119522e-01, 8.2000007751936337e+01, 4.5635184072744744e+02, 2.5922210189842476e+03, -4.5894061525528980e+02, 1.1623761628208563e+01, 7.8927378661620728e-01, 8.1999844359310714e+01, 4.5636323545227941e+02, 2.5918884526432239e+03, -4.5885344883307727e+02, 1.1616256691917803e+01, 7.8948404417119522e-01, 8.2000007751936337e+01, 4.5635184072744744e+02, 2.5922210189842476e+03, -4.5894061525528980e+02, 1.1623761628208563e+01, 7.8927378661620728e-01 + }; + std::vector expected_xyz_scatter = { + 1.4271973325754339e+00, 2.5214997685364109e+00, 3.1394341134078902e+00, 2.2727894815158436e+00, 1.9127738317829568e+00, 2.5288382955492263e+00, 3.1401587802428659e+00, 2.5252400661016079e+00, 9.4806287131835343e-01, 2.3778589851963829e+00, 2.8273548699126683e+00, 1.9358633427396228e+00, 2.1586806210305824e+00, 2.6256636737020518e+00, 3.3955783231847523e+00, 2.7091329174140033e+00, -1.9231004620365049e+00, -4.6499941633630704e-01, -1.1594526098009617e+00, -1.2686640472208488e+00, 2.0867847214069872e+00, 3.0003750888529219e+00, 3.6325449823191440e+00, 2.8788902557067368e+00, 1.2684738158575621e+00, 1.8537695728403008e+00, 2.1955525109720693e+00, 1.7836450721166277e+00, 1.8550735634159015e+00, 2.4434013845454778e+00, 3.0971074319021614e+00, 2.4481507963338514e+00, 1.6439641588553517e+00, 1.9173245315063490e+00, 2.4213050183154365e+00, 2.0154649449162125e+00, 8.6044027444396542e-01, 1.6761956340909820e+00, 1.9714372427825169e+00, 1.4694269993819085e+00, 1.1578881590922248e+00, 2.4304644465537262e+00, 2.8997419900334167e+00, 2.0775716876050363e+00, 2.3918652577373138e+00, 2.7767532459788180e+00, 3.5565699066582859e+00, 2.9253650111396308e+00, 1.6429790566102422e+00, 2.3353986933747315e+00, 2.8497701445565649e+00, 2.2665599345093730e+00, 6.2452940515269861e-01, 1.3483891434563131e+00, 1.5336055353368097e+00, 1.1476467351376733e+00, 2.3375903116778036e+00, 3.0294370345439616e+00, 3.8114115382246951e+00, 3.0486965696352639e+00, 6.1627879872497271e-01, 1.0222504107870520e+00, 1.1967221175625382e+00, 9.4398210879701261e-01, -1.7068032019607302e+00, 3.3613403560802918e-01, -1.0411939552994098e-01, -6.6667768860645871e-01, 1.8511171935709925e+00, 2.2616949107465572e+00, 2.8901786544735999e+00, 2.3344195582834213e+00, 1.8684390110773692e+00, 2.3089422940069237e+00, 2.9226805832398313e+00, 2.3717710946817374e+00, 1.9375672494736595e+00, 2.2557776291035463e+00, 2.8639227616937220e+00, 2.3752065452942275e+00, -3.2972712763415735e-02, 1.0799332278489837e+00, 1.0885237991034180e+00, 6.4736232064759269e-01, 2.8245006381754121e+00, 3.5259487523490192e+00, 4.6113702776738981e+00, 3.5998420702676723e+00, 2.5905343058532044e+00, 3.9030377854459730e+00, 4.9001863961421570e+00, 3.6937521865974929e+00, -2.0466716707172710e+00, -1.0685624603518851e-01, -7.3049231485296484e-01, -1.1126777403630335e+00, 4.5968126827466538e+00, 4.8629895439961155e+00, 6.7099151660558576e+00, 5.3272372780027073e+00, 1.3052826537711313e+00, 1.5171629367774915e+00, 1.9011692135645539e+00, 1.5983578000913097e+00, 2.7500355791930211e+00, 3.1012540902842334e+00, 4.0886454556166472e+00, 3.2988732261870899e+00, -3.7661410117701113e+00, -1.3602653035667422e+00, -2.5196607983439852e+00, -2.7610055328203522e+00, 2.4189623440903629e+00, 2.8963366391936933e+00, 3.8107924830413253e+00, 3.0083029750449866e+00, -3.4747440084737047e+00, -1.1388326294486402e+00, -2.4182412000178957e+00, -2.4782338467864626e+00, 1.8127024518519697e+00, 2.2230503769241436e+00, 2.7468475012971849e+00, 2.2887727041772736e+00, -1.2285895780562228e-01, 1.5434771625279660e+00, 1.6000755001429154e+00, 8.7985271502585627e-01, 1.9011366955569318e+00, 2.4167616547852120e+00, 3.1462829168951041e+00, 2.4517928223455625e+00, 3.4327869085046898e+00, 3.8030109751616310e+00, 4.8712356823465610e+00, 4.0764499721493568e+00, -1.2035506504910221e-01, 1.0522704557335492e+00, 9.2716931026249949e-01, 5.7901898977964616e-01, 2.3714074680568968e+00, 2.7069461333245264e+00, 3.4788009563530058e+00, 2.8668785353548181e+00, 1.3173599955901605e+00, 2.3373622162330081e+00, 2.7950932510153166e+00, 2.0958887568436859e+00, 8.4352826372327494e-01, 1.3787710702843035e+00, 1.6194525500748886e+00, 1.2753294206512922e+00, 2.1844818576218366e+00, 2.4600046540695972e+00, 3.2061976014984541e+00, 2.6275850202185489e+00, 7.7180988879817070e-01, 1.0272946579967681e+00, 1.2621894302014174e+00, 1.0265959882640883e+00, 1.6049231964243249e+00, 2.4546141304955089e+00, 3.1543902252316531e+00, 2.3261815654970941e+00, -1.5851508845166586e+00, 1.7616609630246921e-01, -4.6927862795076358e-01, -7.0847602690642730e-01, 9.7206101520523258e-01, 2.1984829496765985e+00, 2.5547429254737746e+00, 1.8332359752494667e+00, 1.7754746253185822e+00, 2.6825317821817345e+00, 3.3991991376107316e+00, 2.5509087537769037e+00, 1.0721995919270044e-01, 1.2979498217369176e+00, 1.3387413397315138e+00, 8.4672254891200061e-01, 2.9293634097685093e+00, 3.7870623674013393e+00, 4.9049858395715571e+00, 3.8089529879570825e+00, 8.4019940154161687e-01, 1.4394975389766356e+00, 1.7212747966327231e+00, 1.3114842544997232e+00, -4.4681831340750566e-01, 1.6558375814978303e+00, 1.5645433370489232e+00, 7.7463977013538887e-01, 5.8820535859917089e-01, 9.1338322532915583e-01, 1.1202010966286036e+00, 8.6364930010308649e-01, 7.0926056868283660e-01, 1.4218405965219119e+00, 1.6894042270047498e+00, 1.2378761768042328e+00, 2.3099219963216546e+00, 2.6932789796161916e+00, 3.5453161672341622e+00, 2.8363942767964803e+00, 1.8678281855424870e+00, 2.6920312666879047e+00, 3.2316323462531087e+00, 2.6004324324975743e+00, 1.4005208650900944e+00, 2.1811523726875692e+00, 2.7048686478151001e+00, 2.0527720081111767e+00, 2.3136081060507738e+00, 2.9431216274000898e+00, 3.7927332883433289e+00, 2.9867484155017419e+00, 2.4183082750679299e+00, 3.4684542472901878e+00, 4.3044551492126208e+00, 3.3503390337668466e+00, 1.3369220495937211e+00, 2.0576414218953958e+00, 2.5357049204003479e+00, 1.9409755276270539e+00, -2.1107398251243468e+00, -4.8988978509617087e-01, -1.0925226074379997e+00, -1.3770224787571617e+00, 1.6149648338580387e+00, 1.9730143918958940e+00, 2.5005508027915648e+00, 2.0369912328773259e+00, 1.9358903207989977e+00, 2.1474863817546317e+00, 2.7816238043705535e+00, 2.3089858393655152e+00, 5.9610253563576776e-01, 1.8174801336559421e+00, 2.1475049000471036e+00, 1.4151097394224248e+00, 5.1090833065932995e-01, 1.2160531841070317e+00, 1.3152029243838474e+00, 1.0054053301687891e+00, 2.1775101573737672e+00, 2.7592291083038578e+00, 3.5514577227476543e+00, 2.8051749838391071e+00, 1.4574449572780601e+00, 1.8257794999887023e+00, 2.3529474069241134e+00, 1.8687169225939499e+00, 3.9934672259732729e+00, 4.6069625003686925e+00, 6.0365399138833418e+00, 4.8498952298984239e+00, 5.0206849491088514e+02, 5.2990619575924950e+02, 5.0718360719485423e+02, 5.3078609113850609e+02, 4.2099706807708640e+02, 4.4995011865286330e+02, 4.2613481931665478e+02, 4.5054742567627943e+02, 6.4624126958401507e+02, 6.7232697462462943e+02, 6.5259938738906271e+02, 6.7148563064230416e+02, 4.2978137542372599e+02, 4.5001246708893814e+02, 4.3535643412910235e+02, 4.5267999547985386e+02, 3.3452377892226485e+02, 3.5665923744531250e+02, 3.3921143957791395e+02, 3.5904334578072132e+02, 4.2377024314145552e+02, 4.4269249140996482e+02, 4.3013979474766063e+02, 4.4602164788241845e+02, 4.0106095277790195e+02, 4.2025993436574691e+02, 4.0597499630524908e+02, 4.2366611812473144e+02, 3.8696471878412717e+02, 4.0913035507396773e+02, 3.8997037364405418e+02, 4.1288273173906788e+02, 3.6331657327614209e+02, 3.8874925104444759e+02, 3.6568536230961831e+02, 3.8997929930096944e+02, 3.0766058208873443e+02, 3.3007628916707438e+02, 3.1095679032603221e+02, 3.3214170847015549e+02, 3.7417389932974652e+02, 3.9610585314922997e+02, 3.8035396619631911e+02, 4.0089442860025434e+02, 3.8077716282948728e+02, 4.0439557659241609e+02, 3.8495076836644944e+02, 4.0710965835899611e+02, 5.0832346005741056e+02, 5.2918139375030751e+02, 5.0999148566291376e+02, 5.3238824760640534e+02, 3.5895613327835940e+02, 3.7388263235784137e+02, 3.6145315529806305e+02, 3.7979834444073651e+02, 3.7956459278145832e+02, 4.0172961800115581e+02, 3.8172408748658489e+02, 4.0500568949748191e+02, 3.9684913832843944e+02, 4.1936779798804349e+02, 3.9896001344387633e+02, 4.2078747802693186e+02, 3.3493113256513072e+02, 3.5265874034487007e+02, 3.3927703565539474e+02, 3.6107266137720109e+02, 3.6604604197249961e+02, 3.8893228913059670e+02, 3.6730129201251361e+02, 3.9043728041862283e+02, 3.8812581530185560e+02, 4.0924615019224609e+02, 3.9322998085289782e+02, 4.1390337702757438e+02, 4.8856029801345204e+02, 5.0271192916654570e+02, 4.9091594198952845e+02, 5.0809377638444926e+02, 5.2321208035994221e+02, 5.4770952441490192e+02, 5.2817006845838080e+02, 5.4714484189009147e+02, 4.9081225091120268e+02, 5.1240397279603928e+02, 4.9158073027031935e+02, 5.1502480371472871e+02, 5.7223828602721358e+02, 5.9667394727556575e+02, 5.7537894734461975e+02, 5.9925470530439986e+02, 4.9646123158168882e+02, 5.1903534403318656e+02, 5.0181634086118572e+02, 5.2172511442944483e+02, 5.2233159969818155e+02, 5.4780412588510796e+02, 5.2846475284626229e+02, 5.4849754426441416e+02, 4.5617530091144931e+02, 4.7396892884953650e+02, 4.5877481670469268e+02, 4.7577802295466512e+02, 4.4587673834159580e+02, 4.6732395897795834e+02, 4.5162986675993960e+02, 4.6775280428052747e+02, 4.0283354373844770e+02, 4.1961075089899697e+02, 4.0745259059538853e+02, 4.2303359882010614e+02, 5.3834353723774768e+02, 5.6525863139407920e+02, 5.4218387057666916e+02, 5.6547607603652864e+02, 4.4261815891116561e+02, 4.6322139706306598e+02, 4.4557510448028398e+02, 4.6409152553377004e+02, 4.4247192104148161e+02, 4.6619614492253584e+02, 4.4614612324987121e+02, 4.6625206457720230e+02, 3.6128738532891242e+02, 3.8988552843469040e+02, 3.6940156471526564e+02, 3.8850389331677923e+02, 4.7389962473318047e+02, 5.0013101019299427e+02, 4.7841978642421418e+02, 5.0240916591976708e+02, 4.4507921732155654e+02, 4.7390246086556681e+02, 4.5040565227666025e+02, 4.7420235641495236e+02, 4.8993159334334075e+02, 5.1119545519430841e+02, 4.9450301753071705e+02, 5.1079476503475638e+02, 3.9149835455877076e+02, 4.1695134005502371e+02, 3.9850449284689967e+02, 4.1768853701434006e+02, 5.5536615451459932e+02, 5.8218713084686863e+02, 5.6219650600540592e+02, 5.8110371415189206e+02, 3.5857465223194458e+02, 3.7805893452052851e+02, 3.6313044059114281e+02, 3.8278842764472688e+02, 3.9613973998079075e+02, 4.2121250028684204e+02, 4.0322872883880973e+02, 4.2129275763397044e+02, 4.2936205593131211e+02, 4.5353270360878503e+02, 4.3287345252040535e+02, 4.5448469042238418e+02, 4.4950161157908241e+02, 4.7532105082965637e+02, 4.5647550173342893e+02, 4.7547228050864646e+02, 3.2679348365595416e+02, 3.4926301408293159e+02, 3.3184472007084833e+02, 3.4766058741420920e+02, 3.7386411648728529e+02, 3.9861550748020761e+02, 3.7822194089162900e+02, 4.0013870143390176e+02, 4.4829502159394286e+02, 4.7694032786407286e+02, 4.5274923302353994e+02, 4.7569909856079317e+02, 5.0605732715124822e+02, 5.2982960997204441e+02, 5.1018585735851212e+02, 5.3097883224725592e+02, 3.5863665220726045e+02, 3.8682448095468220e+02, 3.6639443664339842e+02, 3.8645991462669110e+02, 4.2980724222044194e+02, 4.4200509624071060e+02, 4.3377896483976394e+02, 4.5049650817962009e+02, 4.7353077646683136e+02, 5.0370872675117209e+02, 4.7734477563720822e+02, 5.0383525959610876e+02, 3.3018963371387275e+02, 3.5335741347599691e+02, 3.3490687757435569e+02, 3.5549069288195227e+02, 5.4332101159480499e+02, 5.5773440615406525e+02, 5.4693692877181081e+02, 5.6229241672395904e+02, 4.3911838198513118e+02, 4.6389559606570378e+02, 4.4555972724814870e+02, 4.6330360136131640e+02, 5.9012746767991473e+02, 6.0881103345863914e+02, 5.9238046236563105e+02, 6.1153339968383796e+02, 3.3567654669942158e+02, 3.5471649519755044e+02, 3.3738613420476901e+02, 3.6005001180565057e+02, 4.5183734883466576e+02, 4.7792551648388240e+02, 4.5576605194928987e+02, 4.7882989629493864e+02, 4.9963595110319130e+02, 5.2447596823087997e+02, 5.0502830948039860e+02, 5.2575477228206853e+02, 3.0830398627916276e+02, 3.3105832470397951e+02, 3.1259710901928895e+02, 3.3410602914065612e+02, 3.2743062231073293e+02, 3.4891769728491801e+02, 3.3177463909525744e+02, 3.5362578896828563e+02, 4.7519868494408144e+02, 4.9515900441603736e+02, 4.7779717459656456e+02, 4.9826625741531501e+02, 4.9284871034497542e+02, 5.1106984057352037e+02, 4.9641742709973187e+02, 5.1336922593850761e+02, 3.5619924997896692e+02, 3.7973909500494273e+02, 3.6036976487086139e+02, 3.8244786836827933e+02, 5.1023777988632258e+02, 5.2792203133332578e+02, 5.1181598214733094e+02, 5.3093252840119374e+02, 5.8515851970733684e+02, 6.0662095917829583e+02, 5.8836080558705828e+02, 6.0856740950778476e+02, 4.2320528132965376e+02, 4.4433409233260238e+02, 4.2774087770870437e+02, 4.4493227381084881e+02, 5.3649403282476419e+02, 5.6002649142365817e+02, 5.4100544048421602e+02, 5.6344537433495304e+02, 3.2108481178606974e+02, 3.3751402850679085e+02, 3.2639602686352305e+02, 3.4280497127600313e+02, 3.6088723193495770e+02, 3.8330727327246012e+02, 3.6766216892694246e+02, 3.8408740198273415e+02, 5.3789217565204069e+02, 5.6077082817082771e+02, 5.4561433429711803e+02, 5.6002190651264800e+02, 3.8542525382667549e+02, 4.0818706679585972e+02, 3.8989237977139476e+02, 4.1071672112601937e+02, 4.4017080999360439e+02, 4.6096607829162730e+02, 4.4148597908498385e+02, 4.6196829584408204e+02, 4.4910149091628733e+02, 4.7307394363534456e+02, 4.5076663700944351e+02, 4.7551430237289520e+02, 3.9227814682587132e+02, 4.1257798087214985e+02, 3.9521371950921736e+02, 4.1711897460767489e+02, 5.1452231508718626e+02, 5.3859526400720893e+02, 5.1952695545599056e+02, 5.3961923578685878e+02, 3.8109275356108719e+02, 4.0346504014461300e+02, 3.8610825630499738e+02, 4.0617030886712132e+02, 4.8452457505595487e+02, 5.0041974589465406e+02, 4.8964722068875346e+02, 5.0550298995865631e+02, 3.9804275290318742e+02, 4.2264782863487699e+02, 4.0321007872679951e+02, 4.2492264399123434e+02, 5.0509147743746217e+02, 5.2641842260951853e+02, 5.0615604907658002e+02, 5.2851782681184159e+02, 3.3459128881879548e+02, 3.5785989581765483e+02, 3.4014053555863410e+02, 3.6113224451566020e+02, 3.7243330242446217e+02, 3.9454364197091792e+02, 3.7709573464905105e+02, 3.9881470989101706e+02, 4.6373112347911342e+02, 4.9281947700430516e+02, 4.6700883320630629e+02, 4.9432223003118827e+02, 4.8356479533205874e+02, 4.9924019843893802e+02, 4.8602244108327392e+02, 5.0403944602754200e+02, 4.9405865887431497e+02, 5.1764099704293153e+02, 4.9706952654679344e+02, 5.1857657553800686e+02, 3.2353192335630808e+02, 3.4294458004548534e+02, 3.3005085921688982e+02, 3.4832237869662674e+02, 3.7709990325592833e+02, 4.0170421910177203e+02, 3.8082050238146007e+02, 4.0277160778705706e+02, 5.3575655043259815e+02, 5.5597457504626493e+02, 5.4128699868812475e+02, 5.5631870299488321e+02, 4.0289822061838851e+02, 4.2011136295440394e+02, 4.0596438967835684e+02, 4.2376691548223067e+02, 4.9819597054194503e+02, 5.2512706626398779e+02, 5.0197315929161960e+02, 5.2498152747596998e+02, 4.6817667276634273e+02, 5.0014722377303497e+02, 4.7458649647723576e+02, 5.0150495171951650e+02, 4.1941666791857625e+02, 4.3883151509608655e+02, 4.2401675298355462e+02, 4.4187117909268017e+02, 4.4249214476881394e+02, 4.6979092814746605e+02, 4.5029339653448335e+02, 4.7008378840721508e+02, 3.5083981057495157e+02, 3.7313087632713695e+02, 3.5453905916645419e+02, 3.7328522070000724e+02, 5.9181740721854158e+02, 6.1605214530968453e+02, 5.9371833379543057e+02, 6.1944402449941288e+02, 4.5118997327463666e+02, 4.7157660592958536e+02, 4.5537464602637550e+02, 4.7369769489472503e+02, 4.0138177193179956e+02, 4.1688091486099427e+02, 4.0497942623326634e+02, 4.2129355581523492e+02, 3.2454476068904751e+02, 3.4375410350243283e+02, 3.2903937444908257e+02, 3.4952628701737717e+02, 3.2747645819233173e+02, 3.5223863743599901e+02, 3.3462584368670485e+02, 3.5238457621937755e+02, 5.1406293910821455e+02, 5.4054861864779025e+02, 5.1862961318339524e+02, 5.3983332800144319e+02, 3.2626786506568948e+02, 3.4484875304946956e+02, 3.2968597510617190e+02, 3.5115983614977830e+02, 4.3161832975765424e+02, 4.5816388988101232e+02, 4.3575161757387821e+02, 4.5976959274894392e+02, 3.5654924037338890e+02, 3.8348265216764844e+02, 3.6337728104134629e+02, 3.8304023786626510e+02, 4.8355605334720593e+02, 5.0545440221729081e+02, 4.8988058276802548e+02, 5.0618475071249503e+02, 4.0865412723946616e+02, 4.2800407219894146e+02, 4.1095472883899140e+02, 4.3167357399059108e+02, 2.9708316761901597e+02, 3.1383146748172811e+02, 2.9852303627317781e+02, 3.1710940115047504e+02, 4.7142270881905046e+02, 4.8987477487142007e+02, 4.7426138183490190e+02, 4.9403105234490903e+02, 3.6559540566801468e+02, 3.8870805730365379e+02, 3.6991533799271076e+02, 3.9018512214896242e+02, 5.3051276561692873e+02, 5.5329760522784727e+02, 5.3526924112310326e+02, 5.5512839001953125e+02, 4.9480860427837490e+02, 5.1417711766271100e+02, 4.9754426266298464e+02, 5.1841294991322388e+02, 3.7755705785212410e+02, 3.9871403752122859e+02, 3.8399149816388353e+02, 3.9960423516196516e+02, 5.4606883830754020e+02, 5.7269177786419971e+02, 5.5192452991915388e+02, 5.7279122308323679e+02, 3.7257595764014530e+02, 3.9118140540161784e+02, 3.7687331126515454e+02, 3.9896571592453080e+02, 5.2752105969967772e+02, 5.5611293296971098e+02, 5.3281649703346557e+02, 5.5594920150426572e+02, 4.3231846898537026e+02, 4.5288085667282348e+02, 4.3724671179152733e+02, 4.5453580950036348e+02, 4.7848078271205947e+02, 4.9368761153563361e+02, 4.8122739366968841e+02, 4.9813960591790141e+02, 4.1534041583572440e+02, 4.3799832356033329e+02, 4.1706050141647705e+02, 4.4120345083811236e+02, 4.2450222526363888e+02, 4.4987978590265880e+02, 4.2579221415895904e+02, 4.5062389839125194e+02, 5.3367829890285134e+02, 5.5925299920437146e+02, 5.3827600387794030e+02, 5.5875107897809198e+02, 3.1886322192082901e+02, 3.4604588931737567e+02, 3.2494503946587514e+02, 3.4498806567101104e+02, 4.5828995348069998e+02, 4.8134664875820539e+02, 4.6362842114441025e+02, 4.8218008418611402e+02, 3.2815013315367082e+02, 3.5330311766072475e+02, 3.3495746008397487e+02, 3.5350425839038832e+02, 4.5574559510937661e+02, 4.8461323076426964e+02, 4.6148981459068813e+02, 4.8263615785873958e+02, 4.2501531271042046e+02, 4.5132404339430190e+02, 4.2823468757230540e+02, 4.5342670023963024e+02, 3.7302979260694212e+02, 3.9811608227111651e+02, 3.7780483413354148e+02, 3.9775933708215859e+02, 5.3852258883589616e+02, 5.5889183953662018e+02, 5.4315382086627653e+02, 5.5817153685794256e+02, 3.5722233146453829e+02, 3.8308198559090272e+02, 3.6280918007156259e+02, 3.8401915651539724e+02, 4.8783906491191323e+02, 5.0943975381762459e+02, 4.9217676882174038e+02, 5.1161007146016738e+02, 4.6331654743950884e+02, 4.7499075201329407e+02, 4.6705071715769213e+02, 4.8374951558367218e+02, 4.2992453268172954e+02, 4.4705802004563787e+02, 4.3466409215168113e+02, 4.5143517678668394e+02, 5.8618728290886418e+02, 6.0641021558623345e+02, 5.8936993438739410e+02, 6.0675386578249834e+02, 4.9964299424902913e+02, 5.2969306085468259e+02, 5.0356864383992928e+02, 5.2869887670804815e+02 + }; + std::vector expected_dy_dem_x = { + -8.5282566239703315e-04, 9.7434896238470878e-05, 2.8198670103749745e-05, 1.4683718725367045e-04, 9.7434896238470878e-05, -8.2177981795691663e-04, 1.4730179246350320e-04, 2.2697700888374512e-04, 2.8198670103749745e-05, 1.4730179246350320e-04, -3.0546099757163860e-04, -1.0691955275624962e-04, 1.4683718725367045e-04, 2.2697700888374512e-04, -1.0691955275624962e-04, -2.0472393176281583e-04, -5.8986194101090419e-04, 2.0301485015849167e-05, 1.4477690521761920e-04, 1.5589944596906697e-04, 2.0301485015849167e-05, -3.4790826051777767e-04, 1.6188954118095066e-04, -6.4754798873391454e-05, 1.4477690521761920e-04, 1.6188954118095066e-04, -2.9910593935885945e-04, -5.0936876433791144e-05, 1.5589944596906697e-04, -6.4754798873391454e-05, -5.0936876433791144e-05, -1.0355517826698125e-04, -6.9459162938867685e-04, 2.8327565248668782e-05, 2.8430463755414195e-05, 4.6197796678653893e-05, 2.8327565248668782e-05, -4.3779001120184660e-04, 3.9125697200477542e-05, -3.1649070137933027e-05, 2.8430463755414195e-05, 3.9125697200477542e-05, -4.0417658999342403e-04, 7.6519185026183547e-05, 4.6197796678653893e-05, -3.1649070137933027e-05, 7.6519185026183547e-05, -2.8017988936616456e-04, -8.6012936276845748e-04, 8.9176446815027605e-05, 1.6318850169617433e-05, 1.4131109278314074e-04, 8.9176446815027605e-05, -2.3539344428552647e-04, 1.0554839943982053e-04, 6.1754240894486574e-05, 1.6318850169617433e-05, 1.0554839943982053e-04, -2.2356938850427748e-04, -5.5818570386833549e-05, 1.4131109278314074e-04, 6.1754240894486574e-05, -5.5818570386833549e-05, -1.8579307724742348e-04, -1.4579471606530545e-05, 1.8373628547591411e-06, 6.3404455687761808e-09, 1.6520017494432617e-05, 1.8373628547591411e-06, 4.0055076619841326e-05, 5.1870915506862245e-07, 6.5455154995692458e-06, 6.3404455687761808e-09, 5.1870915506862245e-07, 3.5777013395772088e-05, 1.2331072770409084e-06, 1.6520017494432617e-05, 6.5455154995692458e-06, 1.2331072770409084e-06, 2.9330374271367200e-05, -1.0591838639597137e-03, 3.7324386487840001e-04, 1.1201603730535799e-04, -1.3094998715550417e-04, 3.7324386487840001e-04, -5.3825225173688483e-04, 1.5433463803274103e-05, 6.7929980996522018e-06, 1.1201603730535799e-04, 1.5433463803274103e-05, -1.5347431592130682e-04, 7.3816404184980853e-05, -1.3094998715550417e-04, 6.7929980996522018e-06, 7.3816404184980853e-05, -1.4291206449640258e-04, -3.3653765271671784e-04, 9.6409327441248667e-05, 6.4078783813068993e-05, -5.7869265471838981e-05, 9.6409327441248667e-05, -2.2563161687222687e-04, 7.2686803292055054e-05, -6.2940087651933607e-05, 6.4078783813068993e-05, 7.2686803292055054e-05, -1.6173581616879356e-04, 8.8234028851700132e-05, -5.7869265471838981e-05, -6.2940087651933607e-05, 8.8234028851700132e-05, -9.8164250335740287e-05, -4.8591645753528860e-04, 9.7740935502860828e-05, 4.4478158377719349e-05, 1.0419035578929442e-04, 9.7740935502860828e-05, -2.8008833575539116e-04, 1.0234342742699657e-05, -1.1066375197239399e-05, 4.4478158377719349e-05, 1.0234342742699657e-05, -2.7922668025205693e-04, 1.1806531746953321e-04, 1.0419035578929442e-04, -1.1066375197239399e-05, 1.1806531746953321e-04, -1.9974381044785472e-04, -4.5059018192044660e-04, 1.5855953261830535e-04, 1.0703891128048314e-04, -2.9320496830173838e-05, 1.5855953261830535e-04, -2.0101672795536590e-04, 5.3750835060043594e-06, 6.3549045572453374e-05, 1.0703891128048314e-04, 5.3750835060043594e-06, -2.0012584084945853e-04, 8.1458143700506442e-05, -2.9320496830173838e-05, 6.3549045572453374e-05, 8.1458143700506442e-05, -1.7784532544621600e-04, -4.7553664863859020e-04, 9.0606794366112880e-05, 1.1547757782783547e-04, 1.7942202135921617e-04, 9.0606794366112880e-05, -3.7929902055660708e-04, 1.1141005377050407e-04, -1.0369999356845449e-04, 1.1547757782783547e-04, 1.1141005377050407e-04, -3.0010715447777566e-04, 5.9933061644597410e-05, 1.7942202135921617e-04, -1.0369999356845449e-04, 5.9933061644597410e-05, -1.5711713725349039e-04, -8.4512694843494427e-04, -1.5998766504508188e-04, 1.3459366005811189e-04, 8.9050023157920225e-05, -1.5998766504508188e-04, -7.4497721059562944e-04, 2.1490465386259777e-04, 8.6993211035125107e-06, 1.3459366005811189e-04, 2.1490465386259777e-04, -3.5600902729500730e-04, 1.2490893288676265e-04, 8.9050023157920225e-05, 8.6993211035125107e-06, 1.2490893288676265e-04, -2.4912698171829185e-04, -5.4677729864457437e-04, 1.4544760640130386e-04, 2.1553577462634675e-04, -5.7697338533785043e-05, 1.4544760640130386e-04, -3.9389932680879196e-04, 9.3103780652668911e-05, 1.5781465334615180e-04, 2.1553577462634675e-04, 9.3103780652668911e-05, -3.5209769647293266e-04, 3.0908080405118161e-05, -5.7697338533785043e-05, 1.5781465334615180e-04, 3.0908080405118161e-05, -2.9381991178282840e-04, -8.4897434901643008e-04, 2.4958296800820011e-04, 1.6042793880630297e-04, 2.0191539006263895e-04, 2.4958296800820011e-04, -4.9074044762242083e-04, -3.8390868153238754e-05, 6.9794232794373798e-06, 1.6042793880630297e-04, -3.8390868153238754e-05, -2.3180481912080813e-04, -1.2344065882196416e-04, 2.0191539006263895e-04, 6.9794232794373798e-06, -1.2344065882196416e-04, -1.9034910644016834e-04, -3.9234583079674319e-04, 1.0779962022557863e-04, 7.7172205961845708e-05, -1.0845300489032052e-04, 1.0779962022557863e-04, -3.6501653375163899e-04, 1.6932449587587277e-04, 1.1497268397185604e-04, 7.7172205961845708e-05, 1.6932449587587277e-04, -2.4040175903203856e-04, 6.3315499528010007e-05, -1.0845300489032052e-04, 1.1497268397185604e-04, 6.3315499528010007e-05, -1.4974518011387353e-04, -7.5415253127048964e-04, 1.4628668950921841e-04, 1.6870584651280783e-04, 1.4071636815485435e-04, 1.4628668950921841e-04, -3.5212613519392216e-04, 8.9576029143352727e-05, -4.6556408920235956e-05, 1.6870584651280783e-04, 8.9576029143352727e-05, -2.7822614419127146e-04, -7.8397846864094857e-05, 1.4071636815485435e-04, -4.6556408920235956e-05, -7.8397846864094857e-05, -2.7346041679651505e-04, -2.6033241393473680e-04, 6.1297134521054865e-05, 1.1370943254758424e-05, 4.1345709213983918e-05, 6.1297134521054865e-05, -2.0636544320363793e-04, 3.6633072227909512e-05, 9.6487308065657876e-05, 1.1370943254758424e-05, 3.6633072227909512e-05, -1.7929253284262379e-04, 1.0075401050272592e-04, 4.1345709213983918e-05, 9.6487308065657876e-05, 1.0075401050272592e-04, -1.7514728993513846e-04, -9.1976596775803973e-04, -1.1250093174975719e-05, 1.0574848982308227e-04, 5.6892733088968351e-05, -1.1250093174975719e-05, -5.3677767474141075e-04, 7.5975050471457976e-05, 2.1341143425148652e-04, 1.0574848982308227e-04, 7.5975050471457976e-05, -4.6910404481979227e-04, -1.1961907952442812e-04, 5.6892733088968351e-05, 2.1341143425148652e-04, -1.1961907952442812e-04, -1.6713401053931241e-04, -5.0318411138792780e-04, -7.8642752393614670e-06, 1.4734980833474556e-04, 1.5694775605953879e-04, -7.8642752393614670e-06, -4.3428679183052686e-04, 1.3240571026484175e-04, 9.4284982663807307e-05, 1.4734980833474556e-04, 1.3240571026484175e-04, -2.6476905195442599e-04, -4.4663985184858534e-05, 1.5694775605953879e-04, 9.4284982663807307e-05, -4.4663985184858534e-05, -1.4901238055304647e-04, -5.2022971006193295e-04, 1.8673817993987294e-04, 3.4824093151832877e-06, 1.9316715603711834e-04, 1.8673817993987294e-04, -3.5661136332554733e-04, 9.2514978038235173e-05, -8.6662703285468833e-07, 3.4824093151832877e-06, 9.2514978038235173e-05, -3.4175347533838051e-04, 8.7641158627568827e-05, 1.9316715603711834e-04, -8.6662703285468833e-07, 8.7641158627568827e-05, -2.3228701071836513e-04, -2.3172744537639032e-04, -2.0145061787998218e-05, 8.0361470649018278e-05, 7.8686158095771946e-05, -2.0145061787998218e-05, -2.1788437548850563e-04, 6.8244915676574325e-05, 4.7753842109443553e-05, 8.0361470649018278e-05, 6.8244915676574325e-05, -1.8538465680992033e-04, 6.3885883987663494e-06, 7.8686158095771946e-05, 4.7753842109443553e-05, 6.3885883987663494e-06, -1.5576199151219941e-04, -2.6053492859149329e-04, 3.7985008257765683e-05, -4.1618594585238569e-05, 1.2029024299682188e-04, 3.7985008257765683e-05, -2.5883101840537494e-04, 2.5194049224745282e-05, 3.9122106536011156e-05, -4.1618594585238569e-05, 2.5194049224745282e-05, -1.8215627475922963e-04, 9.5955293419038848e-05, 1.2029024299682188e-04, 3.9122106536011156e-05, 9.5955293419038848e-05, -8.8495058887861342e-05, -8.5077111031875798e-04, 2.7142179798126236e-05, 2.3506811184181804e-04, -4.0221741191713819e-05, 2.7142179798126236e-05, -5.8825160593922092e-04, 1.2928972567669391e-04, 1.8492434110243213e-04, 2.3506811184181804e-04, 1.2928972567669391e-04, -4.8258381050380911e-04, 3.8157653583435203e-05, -4.0221741191713819e-05, 1.8492434110243213e-04, 3.8157653583435203e-05, -1.3868330525066390e-04, -1.0046100956135153e-03, 1.0071981850677016e-04, 2.8872287092350934e-05, 2.7319207214565589e-05, 1.0071981850677016e-04, -5.8995054305638166e-04, 1.9420289297011076e-04, -6.4394518219290390e-05, 2.8872287092350934e-05, 1.9420289297011076e-04, -4.6301081539337568e-04, 6.2018876700662711e-05, 2.7319207214565589e-05, -6.4394518219290390e-05, 6.2018876700662711e-05, -3.1496980814001716e-04, -3.5505598207892898e-04, 4.2612480787834219e-05, 4.6241360306879771e-06, 2.1108926449578879e-05, 4.2612480787834219e-05, -7.2955350707598794e-06, 1.8755820202733821e-05, -2.6907330166430605e-06, 4.6241360306879771e-06, 1.8755820202733821e-05, -1.6364300965012292e-06, -2.4890773965917357e-06, 2.1108926449578879e-05, -2.6907330166430605e-06, -2.4890773965917357e-06, -3.1119034082904818e-06, -1.3157832950043162e-03, 1.5109719057770362e-04, 2.2136411827069661e-04, 1.5620989616059463e-04, 1.5109719057770362e-04, -6.9909797727330165e-04, 1.3446109058413912e-04, 1.1910768000198964e-04, 2.2136411827069661e-04, 1.3446109058413912e-04, -5.1538523518062426e-04, 7.5113122085345235e-05, 1.5620989616059463e-04, 1.1910768000198964e-04, 7.5113122085345235e-05, -3.0505421799441914e-04, -3.0462471772100332e-04, 3.9065280474790656e-05, 1.4614293236463156e-04, 9.5882906751313209e-05, 3.9065280474790656e-05, -1.9543435847266125e-04, -1.1587160757702439e-05, 1.1931573498340947e-04, 1.4614293236463156e-04, -1.1587160757702439e-05, -1.8470088623319614e-04, 2.0760996552120055e-05, 9.5882906751313209e-05, 1.1931573498340947e-04, 2.0760996552120055e-05, -1.6350571548014739e-04, -1.0266618133570869e-03, 2.2637567583948763e-04, 4.5648808840412684e-05, 1.0877858120403682e-04, 2.2637567583948763e-04, -3.5855168729171305e-04, 1.0575495913196480e-04, 4.7811314368940493e-05, 4.5648808840412684e-05, 1.0575495913196480e-04, -2.0795540905090660e-04, -2.8126314053445194e-05, 1.0877858120403682e-04, 4.7811314368940493e-05, -2.8126314053445194e-05, -1.7015380275751095e-04, -1.0008635815443789e-03, 1.0735782597685222e-04, -8.2342347671546411e-05, -1.0621035075752513e-04, 1.0735782597685222e-04, -3.9974471825416052e-04, -4.2847890972143996e-05, 1.1148431692879854e-04, -8.2342347671546411e-05, -4.2847890972143996e-05, -1.0364328030944446e-04, 1.8027890592285884e-05, -1.0621035075752513e-04, 1.1148431692879854e-04, 1.8027890592285884e-05, -5.3335283910055137e-05, -1.1022946107544056e-03, 1.2081709463727365e-04, -8.2220753605772352e-06, 1.9181878385071303e-04, 1.2081709463727365e-04, -6.5229668590924652e-04, 2.4088842641070782e-04, 3.0891558204400955e-05, -8.2220753605772352e-06, 2.4088842641070782e-04, -2.8026542830655177e-04, 4.0661846631706496e-05, 1.9181878385071303e-04, 3.0891558204400955e-05, 4.0661846631706496e-05, -1.8847765937598992e-04, -2.4152123416571131e-04, -8.6922232482105472e-05, 1.6955733405143119e-04, -6.4111931600053363e-05, -8.6922232482105472e-05, -2.3487488104965331e-04, 5.1809081045441156e-05, 2.1850790757452379e-05, 1.6955733405143119e-04, 5.1809081045441156e-05, -2.0805036913184400e-04, -3.4798889866819799e-05, -6.4111931600053363e-05, 2.1850790757452379e-05, -3.4798889866819799e-05, -1.2312192870086894e-04, -8.8445499497716014e-05, 2.4421506868587198e-05, 2.2044662993515158e-06, 6.5601836003054636e-05, 2.4421506868587198e-05, -5.6415856340379447e-05, 9.2118704714902022e-05, -1.9706075570445630e-05, 2.2044662993515158e-06, 9.2118704714902022e-05, -4.0161585013125762e-05, 5.5260071625401637e-05, 6.5601836003054636e-05, -1.9706075570445630e-05, 5.5260071625401637e-05, -3.3100174236203148e-05, -8.2209958500230578e-04, -1.0375911757141743e-04, 2.0710347102188484e-04, -2.4978971692820028e-05, -1.0375911757141743e-04, -7.3910742021759002e-04, 5.5770606372779485e-06, 1.0998087187508114e-04, 2.0710347102188484e-04, 5.5770606372779485e-06, -3.0781601772972918e-04, 1.7042830142744166e-04, -2.4978971692820028e-05, 1.0998087187508114e-04, 1.7042830142744166e-04, -1.9931789083998724e-04, -8.2864124175878646e-04, 2.1156993777400108e-04, 1.3222387946538444e-04, 7.3125614419211014e-05, 2.1156993777400108e-04, -6.7998120575166903e-04, -4.5284032495574803e-05, 1.5448833703626970e-04, 1.3222387946538444e-04, -4.5284032495574803e-05, -1.6486495415349870e-04, -2.7910903682007980e-05, 7.3125614419211014e-05, 1.5448833703626970e-04, -2.7910903682007980e-05, -1.3616141316573496e-04, -6.9168427037508595e-04, 1.6848337105401589e-04, 2.0347536376145238e-05, 2.1730204740541038e-04, 1.6848337105401589e-04, -5.1567181939462376e-04, 2.2442346917809767e-04, -4.1679702475652895e-05, 2.0347536376145238e-05, 2.2442346917809767e-04, -2.7886738080818829e-04, -7.2100020041017474e-05, 2.1730204740541038e-04, -4.1679702475652895e-05, -7.2100020041017474e-05, -2.1591694500250683e-04, -6.3118866694050128e-04, 1.7407698534034082e-04, 2.2506708310326124e-04, -1.1855521616198067e-04, 1.7407698534034082e-04, -3.4276284162076975e-04, -2.4678327316965964e-05, -7.0132865868384085e-05, 2.2506708310326124e-04, -2.4678327316965964e-05, -2.4829751991729555e-04, 8.0665853806108728e-05, -1.1855521616198067e-04, -7.0132865868384085e-05, 8.0665853806108728e-05, -1.4560588738992748e-04, -5.4516146248809595e-04, 1.5429566078092839e-04, 7.3002508020663260e-05, -5.4127939476372286e-05, 1.5429566078092839e-04, -2.7006749001243577e-04, 3.8245018464081647e-05, 6.4001466507229872e-05, 7.3002508020663260e-05, 3.8245018464081647e-05, -1.9064634129416447e-04, 4.8315978083461554e-05, -5.4127939476372286e-05, 6.4001466507229872e-05, 4.8315978083461554e-05, -9.6287270269265838e-05, -1.0867871706203747e-03, 2.2665889399974761e-05, 3.8527790611792381e-04, -5.7606042046986583e-05, 2.2665889399974761e-05, -5.9377950867244152e-04, 1.6458617522517848e-04, 9.8647825618332862e-05, 3.8527790611792381e-04, 1.6458617522517848e-04, -3.5517610380986437e-04, 1.5047714871992010e-06, -5.7606042046986583e-05, 9.8647825618332862e-05, 1.5047714871992010e-06, -2.4847179034300285e-04, 1.7287370288645611e-05, 2.4751905195362399e-05, 5.7409278650844171e-06, 1.5778384555210318e-06, 2.4751905195362399e-05, 6.9233671176431198e-05, 1.2056763828265809e-05, 5.8193528185755228e-06, 5.7409278650844171e-06, 1.2056763828265809e-05, 2.5803013934721890e-05, 9.7800078032772889e-06, 1.5778384555210318e-06, 5.8193528185755228e-06, 9.7800078032772889e-06, 1.2290894822139189e-05, -4.7276129504627868e-04, 1.7996071145545987e-04, -2.3559613078385275e-05, 9.1817364759875372e-05, 1.7996071145545987e-04, -4.4031640931680089e-04, 1.3129665680176059e-04, 8.5753307504504326e-05, -2.3559613078385275e-05, 1.3129665680176059e-04, -2.6082369278761529e-04, 1.1008038444960020e-04, 9.1817364759875372e-05, 8.5753307504504326e-05, 1.1008038444960020e-04, -2.2448972892423485e-04, 7.3552911328923082e-05, -4.6386393976681180e-07, -3.6302661188033895e-07, 8.1292949427203692e-06, -4.6386393976681180e-07, 7.1855824442516002e-05, 2.0977058635296311e-06, -8.1660148046199487e-07, -3.6302661188033895e-07, 2.0977058635296311e-06, 4.9178179355036989e-05, 5.5111033912396161e-07, 8.1292949427203692e-06, -8.1660148046199487e-07, 5.5111033912396161e-07, 4.3126931159686622e-05, -1.0356645478699321e-03, 3.5655021996677344e-05, 1.7863455145561712e-04, 9.9345386863453332e-05, 3.5655021996677344e-05, -4.6868369051367967e-04, 8.4528986648654719e-05, 2.6178454134616406e-05, 1.7863455145561712e-04, 8.4528986648654719e-05, -4.0189105052683167e-04, 1.7827808102152204e-04, 9.9345386863453332e-05, 2.6178454134616406e-05, 1.7827808102152204e-04, -2.4096643692971789e-04, -5.2947177689736285e-04, 2.9155995696660334e-04, 1.0881309013420403e-04, 6.5216605248019719e-05, 2.9155995696660334e-04, -3.7174127902678917e-04, -1.0713195660439511e-05, -1.4652487345689542e-04, 1.0881309013420403e-04, -1.0713195660439511e-05, -3.0848787251277967e-04, -8.1788880974243585e-05, 6.5216605248019719e-05, -1.4652487345689542e-04, -8.1788880974243585e-05, -1.8187514475847530e-04, -8.1675147016875278e-04, -9.1132812142916361e-05, 3.7581090557227133e-04, -1.6391843424681004e-05, -9.1132812142916361e-05, -8.0928426677666680e-04, 1.0186682914852628e-04, 6.6974048500429245e-05, 3.7581090557227133e-04, 1.0186682914852628e-04, -3.9501800512992450e-04, 1.1097843258641806e-04, -1.6391843424681004e-05, 6.6974048500429245e-05, 1.1097843258641806e-04, -1.6922741791056576e-04, -9.5550488243767526e-04, 8.9834092429790310e-05, 1.4676450162607620e-04, -8.9486200173685058e-05, 8.9834092429790310e-05, -5.7124935343482407e-04, 1.9674652749940278e-04, 2.7820147231366192e-05, 1.4676450162607620e-04, 1.9674652749940278e-04, -4.5311825527210236e-04, 1.5751505797459724e-04, -8.9486200173685058e-05, 2.7820147231366192e-05, 1.5751505797459724e-04, -2.1504103761769665e-04, -7.4655646881417747e-04, 1.7137670375811731e-04, 2.3305985967898233e-04, 2.0974576016239595e-05, 1.7137670375811731e-04, -6.3490393076444808e-04, -3.7460934394642461e-05, 3.8915692525215832e-05, 2.3305985967898233e-04, -3.7460934394642461e-05, -2.3619871093608590e-04, -9.5966181361813001e-05, 2.0974576016239595e-05, 3.8915692525215832e-05, -9.5966181361813001e-05, -1.5753671328738253e-04, -8.4952522473083828e-04, 1.0905671249046153e-04, 1.6050124252773700e-04, -3.5054030436609191e-06, 1.0905671249046153e-04, -7.8916382592320218e-04, 5.4410597072967622e-05, 3.1697067660733621e-04, 1.6050124252773700e-04, 5.4410597072967622e-05, -6.2431502557835482e-04, 4.0685779353217203e-05, -3.5054030436609191e-06, 3.1697067660733621e-04, 4.0685779353217203e-05, -3.1869838579109982e-04, -4.9197474900203851e-04, 2.3175272353917089e-05, 4.8349993627407357e-05, 8.3431439033521311e-05, 2.3175272353917089e-05, -2.1237686325728994e-04, 1.1579272422842471e-04, 4.4948063413445143e-06, 4.8349993627407357e-05, 1.1579272422842471e-04, -1.4387542985386714e-04, -2.8807020627775499e-05, 8.3431439033521311e-05, 4.4948063413445143e-06, -2.8807020627775499e-05, -8.8314386801545690e-05, -1.0377932964218571e-03, 3.1548342438866215e-04, -7.7539032460135759e-06, 1.5862268899707329e-04, 3.1548342438866215e-04, -5.2279750482530675e-04, -9.7156212604978696e-05, 7.8070746092368879e-05, -7.7539032460135759e-06, -9.7156212604978696e-05, -5.1909921397954076e-04, -7.0888358650239406e-05, 1.5862268899707329e-04, 7.8070746092368879e-05, -7.0888358650239406e-05, -4.3559478522129486e-04, -3.4047542887403288e-04, 9.9485414683848702e-05, 5.3467360125445010e-05, 5.5687160259303899e-05, 9.9485414683848702e-05, -2.1688590318227243e-04, 5.8207991561983353e-05, 3.8807798300090633e-05, 5.3467360125445010e-05, 5.8207991561983353e-05, -1.4444518993710908e-04, 6.9712029871340034e-05, 5.5687160259303899e-05, 3.8807798300090633e-05, 6.9712029871340034e-05, -8.8308431324746653e-05, -3.9748580631234745e-04, 1.6061520126347354e-05, 7.4535335825201364e-05, -1.2455905762848885e-05, 1.6061520126347354e-05, -3.0428877235287100e-04, 6.7638842446531838e-05, 9.0216218435291209e-05, 7.4535335825201364e-05, 6.7638842446531838e-05, -1.8476291197258023e-04, 8.6490256848873625e-05, -1.2455905762848885e-05, 9.0216218435291209e-05, 8.6490256848873625e-05, -1.7290120999552497e-04, -6.3017890323441988e-04, 6.8925477551151667e-05, 1.4591855761896425e-04, 5.8696848733904197e-05, 6.8925477551151667e-05, -4.4840596419734699e-04, 1.7185871633737483e-04, 1.3636588971003942e-04, 1.4591855761896425e-04, 1.7185871633737483e-04, -3.5633566053381324e-04, 6.9798068644545436e-05, 5.8696848733904197e-05, 1.3636588971003942e-04, 6.9798068644545436e-05, -2.8194929183414266e-04, -5.2084239851836716e-04, 1.3919308449155372e-04, 7.9297302299835321e-05, -1.0666209674729959e-04, 1.3919308449155372e-04, -4.8272864193460750e-04, 1.4580575117746091e-04, 1.8231015042159636e-04, 7.9297302299835321e-05, 1.4580575117746091e-04, -2.4109836209905614e-04, -9.6833449169729229e-05, -1.0666209674729959e-04, 1.8231015042159636e-04, -9.6833449169729229e-05, -2.2652176409735576e-04, -5.9265052462615796e-04, 3.2921496507552024e-05, 1.6741839899836377e-04, -1.8287094414409389e-05, 3.2921496507552024e-05, -4.7506545424716396e-04, 8.4264843353050377e-05, 3.2049421513026473e-05, 1.6741839899836377e-04, 8.4264843353050377e-05, -2.6830906645465910e-04, 1.0562771031346049e-04, -1.8287094414409389e-05, 3.2049421513026473e-05, 1.0562771031346049e-04, -1.8350092261002626e-04, -7.5685265706840210e-04, 2.5873322291747580e-04, 6.4147355541750696e-06, 4.8379384836955772e-05, 2.5873322291747580e-04, -4.5814972293314378e-04, 5.2257125816072260e-05, 1.7555007655928345e-04, 6.4147355541750696e-06, 5.2257125816072260e-05, -4.5065665859371718e-04, 1.3365888276934993e-04, 4.8379384836955772e-05, 1.7555007655928345e-04, 1.3365888276934993e-04, -2.8055080587645673e-04, -6.8617367385189203e-04, 2.0111488204472137e-04, -3.5677735636164015e-05, 1.5038190317367591e-05, 2.0111488204472137e-04, -6.8307312914361548e-04, 3.7317688096927270e-04, 2.2841592720268073e-05, -3.5677735636164015e-05, 3.7317688096927270e-04, -5.7456997444991467e-04, 9.8364592445165106e-05, 1.5038190317367591e-05, 2.2841592720268073e-05, 9.8364592445165106e-05, -4.9874689148873266e-04, -9.7937117522682625e-04, 1.3365493445469090e-04, -3.5389680247569871e-05, 1.7409836485831839e-04, 1.3365493445469090e-04, -3.0622931287801688e-04, 2.0447820578798172e-04, -2.0241678808795576e-05, -3.5389680247569871e-05, 2.0447820578798172e-04, -2.9965533718679050e-04, -5.0337561347213358e-05, 1.7409836485831839e-04, -2.0241678808795576e-05, -5.0337561347213358e-05, -9.7218271603616025e-05, -7.2361999623641096e-04, 6.0238770967297993e-05, 9.5583099624811875e-06, -4.8817196772293789e-05, 6.0238770967297993e-05, -4.2873796707020983e-04, 3.5290062490134286e-05, 1.0186668437085330e-04, 9.5583099624811875e-06, 3.5290062490134286e-05, -1.7327171153624178e-04, 7.5826006592362129e-05, -4.8817196772293789e-05, 1.0186668437085330e-04, 7.5826006592362129e-05, -6.6625240506338742e-05, -5.1082007985057531e-04, 9.0145069011309307e-05, 1.0545714803937606e-04, 9.9838943414637320e-05, 9.0145069011309307e-05, -2.9666343813120901e-04, 1.6607259863244859e-04, -8.0518077824595215e-05, 1.0545714803937606e-04, 1.6607259863244859e-04, -2.7888486151681505e-04, 9.9492300205694249e-05, 9.9838943414637320e-05, -8.0518077824595215e-05, 9.9492300205694249e-05, -1.6153484908064224e-04, -3.8893832026680984e-04, 8.4480598688399479e-05, 1.3917939203462009e-04, 6.4028139288043303e-05, 8.4480598688399479e-05, -2.9031480540068355e-04, 4.1581354137412694e-05, 1.3719607068135917e-04, 1.3917939203462009e-04, 4.1581354137412694e-05, -2.5727897912316704e-04, 2.3355557894492145e-05, 6.4028139288043303e-05, 1.3719607068135917e-04, 2.3355557894492145e-05, -2.0869295477029555e-04, -1.2940743236606971e-03, 9.9242112186229852e-05, -3.0883079008754396e-05, 2.4110922845507835e-04, 9.9242112186229852e-05, -5.4844927137480013e-04, 1.5284676340009107e-04, 1.5074954460357989e-04, -3.0883079008754396e-05, 1.5284676340009107e-04, -3.5890880634564429e-04, -9.2111965277796469e-05, 2.4110922845507835e-04, 1.5074954460357989e-04, -9.2111965277796469e-05, -2.1606644503010969e-04, -3.6859095195743705e-04, 1.2811340827727349e-04, 7.1617940606111583e-05, 1.2147695942115610e-04, 1.2811340827727349e-04, -2.4093211232803426e-04, -4.5817408623953358e-05, -2.6602561355328134e-05, 7.1617940606111583e-05, -4.5817408623953358e-05, -1.8715896488104966e-04, -2.9073404906152265e-06, 1.2147695942115610e-04, -2.6602561355328134e-05, -2.9073404906152265e-06, -1.1706145694897137e-04, -6.6786161879298469e-04, 3.5686415150814899e-05, 1.5672913017009308e-04, 8.1161443650996505e-05, 3.5686415150814899e-05, -4.8659658995020956e-04, 1.8844759048223573e-04, 1.8572079841468395e-05, 1.5672913017009308e-04, 1.8844759048223573e-04, -3.3591563943804917e-04, 5.8356892899359846e-05, 8.1161443650996505e-05, 1.8572079841468395e-05, 5.8356892899359846e-05, -2.5729244353890580e-04, -4.9057710957934610e-04, 3.5189384292357178e-05, 1.2768527966492655e-04, 7.7111559864786111e-05, 3.5189384292357178e-05, -3.3022996564975609e-04, 1.1998806440725087e-04, 7.9127474986119613e-05, 1.2768527966492655e-04, 1.1998806440725087e-04, -2.2746335211673159e-04, 8.6400013414019474e-05, 7.7111559864786111e-05, 7.9127474986119613e-05, 8.6400013414019474e-05, -2.0017500168890246e-04, -1.1930564853647864e-03, 3.6059432907568098e-04, -4.5802509267350180e-05, 1.6549786604919792e-04, 3.6059432907568098e-04, -6.6459120881215568e-04, 2.2815747528720748e-04, 5.2558786107152938e-05, -4.5802509267350180e-05, 2.2815747528720748e-04, -6.2636194192342237e-04, 1.8747959874315315e-05, 1.6549786604919792e-04, 5.2558786107152938e-05, 1.8747959874315315e-05, -3.2850961953909732e-04, 7.8706872876127071e-02, -2.2653584740143385e-02, 9.7740231630116456e-04, -8.0082023113196138e-04, -2.2653584740143385e-02, -8.9447422099334685e-03, -8.8792954595688558e-04, 1.1368137224524006e-03, 9.7740231630116456e-04, -8.8792954595688558e-04, -7.7069586145163686e-04, 2.3926064253705276e-04, -8.0082023113196138e-04, 1.1368137224524006e-03, 2.3926064253705276e-04, -6.6984821695051074e-04, -1.0791500301598586e-01, -3.2421420547026750e-02, -1.6310176240427882e-03, -1.6324544291089519e-03, -3.2421420547026750e-02, -1.0068088253218213e-02, 3.7700151002736698e-04, 1.5579093564988948e-03, -1.6310176240427882e-03, 3.7700151002736698e-04, -6.4009621154418304e-04, 1.3364317447552753e-04, -1.6324544291089519e-03, 1.5579093564988948e-03, 1.3364317447552753e-04, -4.5908668219426775e-04, 1.9931647893026413e-02, 1.8868322719129860e-02, -2.6059845702808022e-04, -1.7677688147390257e-04, 1.8868322719129860e-02, -2.0969852051011485e-04, 3.6441845096447370e-04, 2.7994468344729460e-04, -2.6059845702808022e-04, 3.6441845096447370e-04, -2.8402192286764761e-04, -5.7796869103119775e-06, -1.7677688147390257e-04, 2.7994468344729460e-04, -5.7796869103119775e-06, -2.7098438358809471e-04, -2.5171876544209310e-01, -1.6215224556807420e-02, -1.7135769001415840e-03, 1.5448964301369569e-03, -1.6215224556807420e-02, -2.3023489339995557e-03, 1.5454079463723322e-03, 1.4540171518868922e-04, -1.7135769001415840e-03, 1.5454079463723322e-03, -1.5783435634697221e-03, -5.6267811205058696e-04, 1.5448964301369569e-03, 1.4540171518868922e-04, -5.6267811205058696e-04, -1.4668478620555464e-03, 9.0064272324944780e-02, 1.0159854544206332e-02, -1.8417068624857591e-03, 1.5581049875507725e-03, 1.0159854544206332e-02, -1.2626008140885292e-03, 5.5672594951240815e-04, -6.6992565886242451e-04, -1.8417068624857591e-03, 5.5672594951240815e-04, -6.3183326791236682e-04, 3.4048390925801674e-04, 1.5581049875507725e-03, -6.6992565886242451e-04, 3.4048390925801674e-04, -4.5748292658650842e-04, -2.2443359343759173e-01, 2.3550848933061329e-02, -1.3605814923240537e-03, 1.6581748189729430e-03, 2.3550848933061329e-02, -1.2119208694257528e-03, 1.1877044380419087e-03, -8.7237437059008887e-04, -1.3605814923240537e-03, 1.1877044380419087e-03, -1.0281058010702063e-03, -1.6587408018026506e-04, 1.6581748189729430e-03, -8.7237437059008887e-04, -1.6587408018026506e-04, -1.0202663289786004e-03, -2.6189782764010544e-01, 2.2986538547258420e-02, -1.7632069264149162e-03, 1.5107406905049286e-03, 2.2986538547258420e-02, -1.2746144428941505e-03, 4.8343451999544908e-04, -5.7379894194384067e-04, -1.7632069264149162e-03, 4.8343451999544908e-04, -5.6480453719830008e-04, 3.4954233236744335e-04, 1.5107406905049286e-03, -5.7379894194384067e-04, 3.4954233236744335e-04, -4.8900362963805524e-04, -1.2749879550430313e-01, 2.8523823603441460e-02, -1.9125154882043663e-03, 2.7084423469016369e-03, 2.8523823603441460e-02, -2.3862377659643530e-03, 5.1372875563453473e-04, -4.3154165895067698e-04, -1.9125154882043663e-03, 5.1372875563453473e-04, -1.1276693699735670e-03, -8.2543254355266842e-05, 2.7084423469016369e-03, -4.3154165895067698e-04, -8.2543254355266842e-05, -1.0951105630241573e-03, -1.0158866176089715e-01, 1.6602982315561777e-02, -1.7772582548894245e-03, 4.3920384137976376e-04, 1.6602982315561777e-02, -4.7547131566108777e-03, 6.8783893805187952e-05, -1.0748720663370313e-03, -1.7772582548894245e-03, 6.8783893805187952e-05, -5.0205120710799611e-04, -3.3919151133434616e-05, 4.3920384137976376e-04, -1.0748720663370313e-03, -3.3919151133434616e-05, -3.1422927937441901e-04, 1.5108981059488835e-01, 2.1974044174935103e-03, 4.9973793800626900e-04, -2.0324655241621987e-03, 2.1974044174935103e-03, -2.0249834345665027e-03, 2.8912368069502044e-04, -3.9641649235281848e-04, 4.9973793800626900e-04, 2.8912368069502044e-04, -1.4279057483715719e-03, 2.3314451382963299e-04, -2.0324655241621987e-03, -3.9641649235281848e-04, 2.3314451382963299e-04, -9.6409814362172263e-04, -9.7276844843051602e-02, 3.5504450866312058e-03, 3.8203830614491319e-03, -1.4507036855037911e-03, 3.5504450866312058e-03, -1.1050016265867703e-03, 3.0086962405142534e-05, 2.4446145351075344e-04, 3.8203830614491319e-03, 3.0086962405142534e-05, -1.8455518785688229e-03, 6.3062318909916208e-04, -1.4507036855037911e-03, 2.4446145351075344e-04, 6.3062318909916208e-04, -8.0531364204684895e-04, 2.9804711899684446e-02, -1.5978165071995631e-02, 1.0778430397325950e-03, -1.4446815201809952e-03, -1.5978165071995631e-02, -5.6345296642827322e-03, -1.0626058855508918e-03, 5.0267944657573355e-04, 1.0778430397325950e-03, -1.0626058855508918e-03, -1.0914650387547676e-03, 4.3259937074976290e-04, -1.4446815201809952e-03, 5.0267944657573355e-04, 4.3259937074976290e-04, -4.5142572576815595e-04, -6.9494494593566603e-02, -2.0051133800610695e-02, 5.8710738033530515e-04, 3.5010545121283096e-04, -2.0051133800610695e-02, -7.1098727986929232e-03, -2.5423159406186779e-04, -9.3348474227718226e-04, 5.8710738033530515e-04, -2.5423159406186779e-04, -6.0375150441947952e-04, 2.7259811627839867e-04, 3.5010545121283096e-04, -9.3348474227718226e-04, 2.7259811627839867e-04, -5.4004855753860101e-04, 1.5401777363000305e-01, 8.1160613829283474e-04, 9.2628051082108370e-04, 3.6211771998279100e-03, 8.1160613829283474e-04, -1.7141151995087387e-03, -3.3990186206657631e-04, -2.5417245475395459e-04, 9.2628051082108370e-04, -3.3990186206657631e-04, -1.4122852387503289e-03, -1.0293746792916486e-04, 3.6211771998279100e-03, -2.5417245475395459e-04, -1.0293746792916486e-04, -1.2188212673550846e-03, -1.2066137822324684e-01, -2.1235012929347232e-02, 1.6254696003078779e-03, -1.2375969117924457e-03, -2.1235012929347232e-02, -6.7234109883531987e-03, 4.4368086424732868e-05, 5.4519256781239238e-04, 1.6254696003078779e-03, 4.4368086424732868e-05, -1.7851002974240047e-03, 2.6435810786128557e-04, -1.2375969117924457e-03, 5.4519256781239238e-04, 2.6435810786128557e-04, -9.5219784353280741e-04, -2.0428062162336544e-01, -1.8822402400988779e-02, 8.0995888860823685e-04, -1.1746114214952719e-03, -1.8822402400988779e-02, -3.1320256402858366e-03, -9.4173163158250614e-04, 5.3739665278471852e-04, 8.0995888860823685e-04, -9.4173163158250614e-04, -8.4068628230452394e-04, 2.2479483070440945e-04, -1.1746114214952719e-03, 5.3739665278471852e-04, 2.2479483070440945e-04, -7.5550494619625963e-04, 2.0478820977633771e-02, 5.6432816347106261e-03, 1.5567905281500807e-03, 3.1015131190170152e-03, 5.6432816347106261e-03, -1.8593392130277192e-03, -1.0887013744527499e-03, -5.9319521599679871e-04, 1.5567905281500807e-03, -1.0887013744527499e-03, -1.8709510047864347e-03, -1.6277602269798984e-04, 3.1015131190170152e-03, -5.9319521599679871e-04, -1.6277602269798984e-04, -1.8685922653237117e-03, 6.5107726886614783e-03, -3.6209927731580757e-03, -3.8090764358667738e-04, 4.8090075186154230e-04, -3.6209927731580757e-03, -2.5033792471679753e-03, 2.3549698237273272e-04, -6.0379270164610918e-04, -3.8090764358667738e-04, 2.3549698237273272e-04, -7.2631802550718566e-04, 3.6336152250252679e-04, 4.8090075186154230e-04, -6.0379270164610918e-04, 3.6336152250252679e-04, -5.9720775259365871e-04, 1.5597924802072299e-01, -9.1287267636976852e-03, 5.1940698209609363e-03, -1.9270196737078854e-03, -9.1287267636976852e-03, -1.0171355227070189e-03, -8.3707115487426011e-04, 2.6648470329260969e-04, 5.1940698209609363e-03, -8.3707115487426011e-04, -1.5133538294868042e-03, 5.0193401691492020e-04, -1.9270196737078854e-03, 2.6648470329260969e-04, 5.0193401691492020e-04, -7.5563292076018306e-04, -5.1184574889172579e-02, 2.7987397416586360e-02, 7.5080665467791617e-04, 7.0012226943657642e-04, 2.7987397416586360e-02, -2.1716159037027593e-03, -4.8086372671787576e-04, -9.8018175618315873e-04, 7.5080665467791617e-04, -4.8086372671787576e-04, -7.3577207641989818e-04, 2.6562080093313106e-04, 7.0012226943657642e-04, -9.8018175618315873e-04, 2.6562080093313106e-04, -6.0973448697062660e-04, 1.3382668541587697e-01, -2.2722610733945561e-02, -1.2434543017359170e-03, -1.2728876503270323e-03, -2.2722610733945561e-02, -4.3688188071787232e-03, 7.7645284186351441e-04, 6.3845101211561532e-04, -1.2434543017359170e-03, 7.7645284186351441e-04, -7.3990375201463685e-04, -2.2683288029159060e-04, -1.2728876503270323e-03, 6.3845101211561532e-04, -2.2683288029159060e-04, -6.7815800933636403e-04, 1.3120488245988418e-01, 4.8547089951903696e-02, 5.2274693388694210e-04, 4.0755937244519420e-04, 4.8547089951903696e-02, -8.3907942102404050e-03, -3.8252224092104130e-04, -4.0281273644884673e-04, 5.2274693388694210e-04, -3.8252224092104130e-04, -6.3441175364586163e-04, 3.6516056969289123e-04, 4.0755937244519420e-04, -4.0281273644884673e-04, 3.6516056969289123e-04, -6.1351796194961811e-04, -1.5706881122068012e-01, -2.8064609799324989e-02, 5.2866261510743358e-04, -5.0158079779070628e-04, -2.8064609799324989e-02, -7.9255987347370254e-03, -7.2907309715153476e-04, 2.0023722312570202e-04, 5.2866261510743358e-04, -7.2907309715153476e-04, -8.7726820594463044e-04, 9.3347733493378919e-04, -5.0158079779070628e-04, 2.0023722312570202e-04, 9.3347733493378919e-04, -7.6444145077611959e-04, -2.4185449825431532e-01, 9.3190796925950465e-03, -1.0360517460863226e-03, 7.5166654771481383e-04, 9.3190796925950465e-03, -1.8089927328024354e-03, 4.5529238180270712e-04, -6.7548648300266129e-04, -1.0360517460863226e-03, 4.5529238180270712e-04, -9.8195456656893299e-04, 3.2953602624600028e-04, 7.5166654771481383e-04, -6.7548648300266129e-04, 3.2953602624600028e-04, -7.5088680434060126e-04, -6.2266646786067938e-03, 4.3482306684820587e-02, -6.7931544150714681e-04, -1.1318057792932172e-03, 4.3482306684820587e-02, -3.0163016880633831e-03, 1.1154407743767898e-03, 1.2680847876108238e-03, -6.7931544150714681e-04, 1.1154407743767898e-03, -6.2686682105641005e-04, 2.4799994581475311e-05, -1.1318057792932172e-03, 1.2680847876108238e-03, 2.4799994581475311e-05, -4.8261912829217966e-04, 4.4034487767024420e-02, 1.2027693141635914e-02, -3.1617252369308421e-04, -1.3810034906793043e-03, 1.2027693141635914e-02, -1.9189382672973364e-03, 3.5771288333242082e-04, 3.2903469104204127e-04, -3.1617252369308421e-04, 3.5771288333242082e-04, -7.7434480573853985e-04, 1.4662364658262446e-04, -1.3810034906793043e-03, 3.2903469104204127e-04, 1.4662364658262446e-04, -7.1146947717666163e-04, -1.7439979214498852e-01, 2.5559515911645361e-02, -1.3609574326154626e-03, -8.1347979141622453e-04, 2.5559515911645361e-02, -1.9101252004779083e-03, 5.8401786740861872e-04, 2.6463889212369153e-04, -1.3609574326154626e-03, 5.8401786740861872e-04, -1.2022614404840581e-03, 1.9183593028871340e-04, -8.1347979141622453e-04, 2.6463889212369153e-04, 1.9183593028871340e-04, -7.2688162163724512e-04, -2.6243218345287456e-01, 4.9398209870091560e-03, -1.5696233107405566e-03, 1.0567220238967725e-03, 4.9398209870091560e-03, -1.4022787059415794e-03, 5.8256688059597295e-04, 1.9025682403384436e-05, -1.5696233107405566e-03, 5.8256688059597295e-04, -1.3070453333682249e-03, -3.3851763928833433e-04, 1.0567220238967725e-03, 1.9025682403384436e-05, -3.3851763928833433e-04, -1.2320723061259361e-03, -8.2714390070501104e-02, 1.0975412304762084e-02, -4.2098382115161030e-04, -6.7702215349947506e-04, 1.0975412304762084e-02, -8.7719105355609575e-03, 6.4626568971351604e-04, 3.4082672855708366e-04, -4.2098382115161030e-04, 6.4626568971351604e-04, -5.3896694179129197e-04, 1.8641783252149489e-04, -6.7702215349947506e-04, 3.4082672855708366e-04, 1.8641783252149489e-04, -3.3770767382408195e-04, -1.2193197837136689e-01, -1.5380109380286541e-02, 3.3246097773883404e-04, -1.6628731429037037e-03, -1.5380109380286541e-02, -2.5373182102857170e-03, -2.0443003453976912e-05, 8.2993773737306147e-04, 3.3246097773883404e-04, -2.0443003453976912e-05, -8.8731220769894020e-04, -6.0009982488096482e-05, -1.6628731429037037e-03, 8.2993773737306147e-04, -6.0009982488096482e-05, -7.1835196502551967e-04, 7.8012293187519954e-02, 3.1132058063563710e-02, -1.1696899428820292e-03, -1.3753629772876416e-03, 3.1132058063563710e-02, -2.9320533071647394e-03, 4.4209006788966515e-04, 5.4507080398151985e-04, -1.1696899428820292e-03, 4.4209006788966515e-04, -6.3061860103715917e-04, 2.3835122850300404e-04, -1.3753629772876416e-03, 5.4507080398151985e-04, 2.3835122850300404e-04, -4.8807112103491492e-04, 1.2489186610021678e-01, -4.2121994803512846e-03, -1.4615798111060961e-03, -1.3284544933660910e-03, -4.2121994803512846e-03, -1.9469301448134307e-03, 2.4383944981524552e-04, 9.0191595070716760e-04, -1.4615798111060961e-03, 2.4383944981524552e-04, -8.4489411054921443e-04, -3.2983470816998103e-04, -1.3284544933660910e-03, 9.0191595070716760e-04, -3.2983470816998103e-04, -5.5892089821921545e-04, -1.3647684084529246e-01, 3.9776937406723417e-02, 6.1334383189688029e-04, -8.9816660782599911e-04, 3.9776937406723417e-02, -4.3020139627246353e-04, -9.3557080941627938e-04, 7.7958935546057592e-04, 6.1334383189688029e-04, -9.3557080941627938e-04, -3.5211495153066340e-04, 5.8831013523111768e-05, -8.9816660782599911e-04, 7.7958935546057592e-04, 5.8831013523111768e-05, -3.1382659001747163e-04, -1.6918244557222134e-01, -2.6159967833569130e-02, -1.5521334165285558e-03, -1.6552342040953793e-03, -2.6159967833569130e-02, -8.5733501045166700e-03, 1.1884549627826878e-03, 2.9341595161121387e-04, -1.5521334165285558e-03, 1.1884549627826878e-03, -6.8065594315602643e-04, 1.8544180606965729e-04, -1.6552342040953793e-03, 2.9341595161121387e-04, 1.8544180606965729e-04, -4.5742794945487230e-04, -2.0437904412209100e-02, 2.0278428462028566e-02, -3.1833318892537861e-04, -1.5672358504625003e-03, 2.0278428462028566e-02, -2.3308820806625680e-03, -9.2413164888836781e-05, 1.2261910114897223e-03, -3.1833318892537861e-04, -9.2413164888836781e-05, -6.1509973454375463e-04, -3.1369065713858962e-04, -1.5672358504625003e-03, 1.2261910114897223e-03, -3.1369065713858962e-04, -6.1311372710779475e-04, -2.1068663677001948e-01, 3.0586587729756580e-02, -1.4638083866968235e-03, -1.7663639913775599e-03, 3.0586587729756580e-02, -2.4896000109830450e-03, 1.0878021744662348e-03, 5.5784495882122158e-04, -1.4638083866968235e-03, 1.0878021744662348e-03, -8.5226768150814753e-04, 2.4896475900177203e-04, -1.7663639913775599e-03, 5.5784495882122158e-04, 2.4896475900177203e-04, -7.2954677906140835e-04, -2.0670514661104566e-01, 2.3101872560098982e-03, -6.3615804468624706e-04, -1.0191121784747393e-03, 2.3101872560098982e-03, -4.5012725771569565e-03, 3.0966024212130121e-04, 7.3480770548280236e-04, -6.3615804468624706e-04, 3.0966024212130121e-04, -7.6577049132799655e-04, 1.9069869693581136e-04, -1.0191121784747393e-03, 7.3480770548280236e-04, 1.9069869693581136e-04, -3.3060687835284803e-04, 1.4979203013904469e-01, 7.1888739604317664e-03, -1.6498112176118718e-03, 7.6090317854697837e-04, 7.1888739604317664e-03, -1.6847252134716941e-03, 9.1765413550792092e-04, -8.1660576268565398e-04, -1.6498112176118718e-03, 9.1765413550792092e-04, -5.6736119553899445e-04, 2.4293054607895927e-04, 7.6090317854697837e-04, -8.1660576268565398e-04, 2.4293054607895927e-04, -3.0141462646083878e-04, -2.5893180917593467e-01, -8.1773154879442519e-03, -1.8051666448928912e-03, -1.7078409149329419e-03, -8.1773154879442519e-03, -2.4391627814064182e-03, 2.5949401569512947e-04, 7.3814751053530889e-04, -1.8051666448928912e-03, 2.5949401569512947e-04, -8.9181287706463540e-04, 1.2172298924298193e-04, -1.7078409149329419e-03, 7.3814751053530889e-04, 1.2172298924298193e-04, -5.5666018893034495e-04, 1.3114296578438139e-01, -6.0075695408917090e-03, 4.3242635386152908e-04, -1.6174936298550607e-03, -6.0075695408917090e-03, -2.1679629901973250e-03, -1.4790030682634993e-04, 4.0251669474305628e-04, 4.3242635386152908e-04, -1.4790030682634993e-04, -6.4946095998946827e-04, 2.8330245317322158e-04, -1.6174936298550607e-03, 4.0251669474305628e-04, 2.8330245317322158e-04, -5.4324532966633539e-04, 1.5527234406654167e-01, 4.5641696399707443e-02, -1.1740442464421269e-03, -1.5447474252538395e-03, 4.5641696399707443e-02, -6.1793537674581797e-03, 1.5275860858276267e-03, 8.6030738024539066e-04, -1.1740442464421269e-03, 1.5275860858276267e-03, -7.8182161800352231e-04, -1.5885947204160602e-04, -1.5447474252538395e-03, 8.6030738024539066e-04, -1.5885947204160602e-04, -7.3143195173575981e-04, 1.1697380540622421e-01, -1.8452554290786850e-03, 1.0016961110488366e-03, -1.4829998143344009e-03, -1.8452554290786850e-03, -1.1489869626221500e-03, -4.0492587305436355e-04, 2.7582642932447637e-04, 1.0016961110488366e-03, -4.0492587305436355e-04, -8.2641017439131223e-04, 3.3828661526212394e-04, -1.4829998143344009e-03, 2.7582642932447637e-04, 3.3828661526212394e-04, -7.1775652801919342e-04, 2.4087451790493968e-02, 3.6657022667584582e-02, -2.3160169352650246e-05, -1.4754112553395189e-03, 3.6657022667584582e-02, -2.7192998188347670e-03, 4.9290573568910629e-04, 4.2019978689329402e-04, -2.3160169352650246e-05, 4.9290573568910629e-04, -8.0300459280031010e-04, 3.0740767103767887e-04, -1.4754112553395189e-03, 4.2019978689329402e-04, 3.0740767103767887e-04, -6.4767348263235984e-04, -1.6342252605621727e-01, 1.8127756599951576e-02, -1.1824339446502448e-03, -5.6869655256144131e-04, 1.8127756599951576e-02, -5.4663896424227282e-03, 9.1072733783564201e-04, -3.4788638053916024e-05, -1.1824339446502448e-03, 9.1072733783564201e-04, -6.6244397559521830e-04, -2.5432935828759249e-04, -5.6869655256144131e-04, -3.4788638053916024e-05, -2.5432935828759249e-04, -4.8751736339739109e-04, -1.5701412918240615e-01, 2.7992421613277401e-02, -4.6515704530449996e-04, -7.9964297888608410e-04, 2.7992421613277401e-02, -3.9221662977877072e-03, 2.9742173175084299e-04, 7.9338187798423123e-04, -4.6515704530449996e-04, 2.9742173175084299e-04, -5.5822864771270446e-04, 2.1181161515901515e-04, -7.9964297888608410e-04, 7.9338187798423123e-04, 2.1181161515901515e-04, -3.9618825805997281e-04, -1.2003184408108214e-01, 1.8018611826677796e-03, -1.4587721561363381e-03, -1.4251735569210393e-03, 1.8018611826677796e-03, -2.3078955332620707e-03, 3.8402353601999531e-04, 3.6911457383390317e-04, -1.4587721561363381e-03, 3.8402353601999531e-04, -1.1519702353104452e-03, 9.8372557539751129e-05, -1.4251735569210393e-03, 3.6911457383390317e-04, 9.8372557539751129e-05, -7.3110630617569865e-04, -1.6462935008848217e-01, 1.0600946531245254e-02, 3.6977083048745467e-03, -1.4181644891152485e-03, 1.0600946531245254e-02, -1.3992372112707173e-03, 3.4296475948198595e-04, -1.1160182310149349e-03, 3.6977083048745467e-03, 3.4296475948198595e-04, -1.6724888886693919e-03, -7.8051702685589782e-04, -1.4181644891152485e-03, -1.1160182310149349e-03, -7.8051702685589782e-04, -1.4795383843152874e-03, 2.5193698717292578e-03, -7.2228540313489780e-03, 8.7131868658691573e-04, -1.1871506186637458e-03, -7.2228540313489780e-03, -8.3981655790035283e-03, -3.1337460147976907e-05, 1.7507484534873217e-04, 8.7131868658691573e-04, -3.1337460147976907e-05, -1.1381726073134719e-03, 9.0691674803716218e-05, -1.1871506186637458e-03, 1.7507484534873217e-04, 9.0691674803716218e-05, -5.4923843003268163e-04, -9.8541123283475321e-02, 2.0328745160678043e-02, -2.0458389881609796e-03, 9.0630002244413007e-04, 2.0328745160678043e-02, -7.4221841135019870e-04, 5.3925274862334381e-04, -9.0169207887240497e-04, -2.0458389881609796e-03, 5.3925274862334381e-04, -8.1237048011580354e-04, -1.7572671277406646e-04, 9.0630002244413007e-04, -9.0169207887240497e-04, -1.7572671277406646e-04, -4.4334028399372748e-04, -2.7234646577534043e-02, 2.5369573930940532e-02, 1.5705630781754590e-04, 6.1198876277706281e-04, 2.5369573930940532e-02, -1.3071102928106637e-03, -7.2368957471257269e-04, -5.2265333441466977e-04, 1.5705630781754590e-04, -7.2368957471257269e-04, -1.1462134164282704e-03, 2.3337377507278166e-04, 6.1198876277706281e-04, -5.2265333441466977e-04, 2.3337377507278166e-04, -7.2368697376535771e-04, -2.4800412677279193e-01, 3.0558589210589435e-02, -1.7679744500246292e-03, -1.5482210113520617e-03, 3.0558589210589435e-02, -2.4786384007542978e-03, 8.5356892230528767e-04, 4.3403640403337902e-04, -1.7679744500246292e-03, 8.5356892230528767e-04, -8.3267405947969693e-04, 9.7644740296861087e-05, -1.5482210113520617e-03, 4.3403640403337902e-04, 9.7644740296861087e-05, -7.6184659130409797e-04, 6.4088879122978254e-02, 7.4033191067212354e-03, -3.0166036111540026e-04, 2.2866639862654022e-03, 7.4033191067212354e-03, -1.9075281126971072e-03, -1.7513934189862141e-04, -8.3685859198624453e-04, -3.0166036111540026e-04, -1.7513934189862141e-04, -6.4451282241398183e-04, 1.0242732795762495e-04, 2.2866639862654022e-03, -8.3685859198624453e-04, 1.0242732795762495e-04, -4.7151425790170642e-04, 2.6280963047272420e-03, -8.2523616999923399e-03, 6.9274155947225767e-04, 2.2806878867334188e-04, -8.2523616999923399e-03, -9.9628281026221477e-04, 2.5812365885309900e-04, -1.1568156376056965e-04, 6.9274155947225767e-04, 2.5812365885309900e-04, -1.0820703009744815e-03, -3.1530431722761752e-05, 2.2806878867334188e-04, -1.1568156376056965e-04, -3.1530431722761752e-05, -7.5527985458567228e-04, 9.1101115309797379e-02, -2.2884644065069411e-02, 9.1368322276223378e-04, -1.0850217992609608e-03, -2.2884644065069411e-02, -2.2375718156060894e-03, -6.8171614704544544e-04, 4.3298786621578863e-04, 9.1368322276223378e-04, -6.8171614704544544e-04, -6.0655582575581761e-04, -1.9700957313198807e-04, -1.0850217992609608e-03, 4.3298786621578863e-04, -1.9700957313198807e-04, -5.4760699412119591e-04, -2.2640416371139069e-01, -2.5191853524724098e-02, -1.3198312724450061e-03, -1.3359296338435942e-03, -2.5191853524724098e-02, -7.4114781706104268e-03, 1.4884771292347921e-03, 1.5714670158787022e-03, -1.3198312724450061e-03, 1.4884771292347921e-03, -1.3119866716855199e-03, -1.5536016251956771e-04, -1.3359296338435942e-03, 1.5714670158787022e-03, -1.5536016251956771e-04, -5.5696348361036394e-04, 9.2726790578004134e-02, -2.0242369791069288e-03, 3.8405423708959545e-04, 6.3967354157029978e-04, -2.0242369791069288e-03, -2.0157885093086683e-03, -3.2058306533741196e-04, 4.2971640692212861e-04, 3.8405423708959545e-04, -3.2058306533741196e-04, -1.9162959308769002e-03, -6.3827678873427419e-04, 6.3967354157029978e-04, 4.2971640692212861e-04, -6.3827678873427419e-04, -1.1765363907219604e-03, -1.1010994988318533e-01, 1.8364595516921723e-03, -1.7052805868601824e-03, 1.5722132456195729e-03, 1.8364595516921723e-03, -1.6428731888699357e-03, -6.9125587365518731e-05, -6.1003796613763421e-04, -1.7052805868601824e-03, -6.9125587365518731e-05, -1.3517785548352037e-03, 3.9700219843950759e-04, 1.5722132456195729e-03, -6.1003796613763421e-04, 3.9700219843950759e-04, -9.2229729865463410e-04, 1.0482002463291755e-01, 4.1269081557235562e-02, -1.4243305558994911e-03, 7.2560546133989428e-04, 4.1269081557235562e-02, -2.4533418282568198e-03, 1.3112208013762515e-03, -8.1980100268941986e-04, -1.4243305558994911e-03, 1.3112208013762515e-03, -6.5149545825962077e-04, -7.9051325639480671e-05, 7.2560546133989428e-04, -8.1980100268941986e-04, -7.9051325639480671e-05, -2.9459876874997148e-04, 5.2687763814020426e-02, 2.5753172346694865e-02, -1.1364673445506180e-03, 1.6847984517648468e-03, 2.5753172346694865e-02, -1.9744024088759471e-03, 6.5730101175142199e-04, -7.6106378367768782e-04, -1.1364673445506180e-03, 6.5730101175142199e-04, -6.8322027298045856e-04, -2.8125846979498703e-04, 1.6847984517648468e-03, -7.6106378367768782e-04, -2.8125846979498703e-04, -6.4384013872712724e-04, 5.9218429229155281e-02, -4.4827537798794370e-03, 1.0270811096682296e-03, -1.9168586516354947e-03, -4.4827537798794370e-03, -6.8593562640291818e-04, -4.6835704849159830e-04, -2.2268361484443280e-04, 1.0270811096682296e-03, -4.6835704849159830e-04, -1.3752987145318814e-03, 3.1079072153330253e-04, -1.9168586516354947e-03, -2.2268361484443280e-04, 3.1079072153330253e-04, -9.2178851145784926e-04, 6.4476070763014870e-02, -8.2336865840800244e-03, -8.2613444706260115e-05, 2.2870252609144106e-03, -8.2336865840800244e-03, -3.2744036284686373e-03, -4.1704987639591122e-05, -6.4166807270754048e-04, -8.2613444706260115e-05, -4.1704987639591122e-05, -4.1548386737107250e-04, -1.7479369462839750e-05, 2.2870252609144106e-03, -6.4166807270754048e-04, -1.7479369462839750e-05, -3.4493527017004036e-04, -6.1828450559953466e-03, 2.8428624253357817e-02, -7.5163309079767377e-04, 5.4940663791714793e-04, 2.8428624253357817e-02, -6.7829893110121844e-03, 8.1954861327358602e-04, -5.3114117045863587e-04, -7.5163309079767377e-04, 8.1954861327358602e-04, -6.0173813996780567e-04, -6.7160875496170326e-05, 5.4940663791714793e-04, -5.3114117045863587e-04, -6.7160875496170326e-05, -2.4845337051236009e-04, 1.0533880384113113e-01, 3.6210071469509400e-02, -1.7107999791441781e-03, -4.7083865881270926e-04, 3.6210071469509400e-02, -2.5591369610882472e-03, 1.0474090798181057e-03, 1.1172328488792965e-04, -1.7107999791441781e-03, 1.0474090798181057e-03, -6.2681311095400482e-04, -1.0956014522883990e-04, -4.7083865881270926e-04, 1.1172328488792965e-04, -1.0956014522883990e-04, -6.2294976074032634e-04, 1.1902524122755653e-01, -1.0909771422571959e-02, 1.2448713677229743e-03, -7.8880695949750681e-04, -1.0909771422571959e-02, -1.5951056840545162e-03, 2.7429495897176952e-04, 7.3506920307663841e-05, 1.2448713677229743e-03, 2.7429495897176952e-04, -9.7786177177501001e-04, 6.6672113049240779e-04, -7.8880695949750681e-04, 7.3506920307663841e-05, 6.6672113049240779e-04, -5.0679343969428484e-04, 1.6159868780566031e-01, 3.7481421868546007e-03, 3.3383110783462289e-03, -1.4389234118571464e-03, 3.7481421868546007e-03, -1.8935398127113731e-03, 2.5240285683527449e-04, 7.9691796069654658e-04, 3.3383110783462289e-03, 2.5240285683527449e-04, -1.3490096726389840e-03, 1.9074939429963151e-04, -1.4389234118571464e-03, 7.9691796069654658e-04, 1.9074939429963151e-04, -8.9877461888945853e-04, -2.2502909073369459e-01, -3.7321287736186322e-03, -1.7987858311244466e-03, -1.9708047702503991e-03, -3.7321287736186322e-03, -1.9214934405923476e-03, 3.5283302489755764e-04, 4.8110254301636892e-04, -1.7987858311244466e-03, 3.5283302489755764e-04, -1.2680475239585940e-03, 2.0205708932592430e-04, -1.9708047702503991e-03, 4.8110254301636892e-04, 2.0205708932592430e-04, -7.8681400841451241e-04, -2.3952675967064883e-02, -6.5409371210018051e-03, -2.6851469943078203e-04, -4.9805803828584922e-04, -6.5409371210018051e-03, -3.7561313498678302e-03, 2.3206665741903022e-04, 3.0048228666978370e-04, -2.6851469943078203e-04, 2.3206665741903022e-04, -4.4121780270757531e-04, -8.2215427654899162e-05, -4.9805803828584922e-04, 3.0048228666978370e-04, -8.2215427654899162e-05, -3.7500716387283193e-04, -2.1228367131798687e-01, 2.7113291916486364e-02, 4.6016317977870395e-04, -1.9594764054362895e-03, 2.7113291916486364e-02, -1.0853572773374739e-03, 2.5219233819137219e-04, 5.1172231054065541e-04, 4.6016317977870395e-04, 2.5219233819137219e-04, -1.8213699224644336e-03, 3.7393213810628812e-04, -1.9594764054362895e-03, 5.1172231054065541e-04, 3.7393213810628812e-04, -5.5588635692437049e-04, -1.2594061059151607e-01, 1.2592656724822970e-02, -4.0285977545487228e-04, -4.4341698620668172e-04, 1.2592656724822970e-02, -6.1572534159074592e-03, 4.8152144273438187e-04, -4.9727853505236708e-04, -4.0285977545487228e-04, 4.8152144273438187e-04, -9.3221660800086681e-04, 4.3322214079744763e-04, -4.4341698620668172e-04, -4.9727853505236708e-04, 4.3322214079744763e-04, -6.2440230731627326e-04, -1.3585509657998510e-01, -3.8909226522543441e-03, 3.0193138572521729e-03, -4.6754349870445496e-04, -3.8909226522543441e-03, -5.6220236698656771e-03, -8.0283448531211330e-04, 4.9313364840345667e-04, 3.0193138572521729e-03, -8.0283448531211330e-04, -8.4724654438547091e-04, -4.1535841093396958e-04, -4.6754349870445496e-04, 4.9313364840345667e-04, -4.1535841093396958e-04, -4.7533675347965273e-04, -2.1524465492323114e-01, 2.6830885704129219e-02, 4.8987007530070020e-03, -7.4769890428344238e-04, 2.6830885704129219e-02, -2.4008792532451296e-03, -4.3520127006076755e-04, -5.5384351394842390e-04, 4.8987007530070020e-03, -4.3520127006076755e-04, -1.9799533182669562e-03, 3.8152653140380451e-04, -7.4769890428344238e-04, -5.5384351394842390e-04, 3.8152653140380451e-04, -5.9785566430098553e-04, -1.6784213165410733e-01, 2.0244058370466202e-02, -1.3420658225188609e-03, -1.3189834234737445e-03, 2.0244058370466202e-02, -7.2263618147349398e-03, 1.1939034574010880e-03, 9.4667802145852230e-04, -1.3420658225188609e-03, 1.1939034574010880e-03, -7.6018644585713153e-04, -1.0210604952061623e-04, -1.3189834234737445e-03, 9.4667802145852230e-04, -1.0210604952061623e-04, -4.0821100223754577e-04, -2.3751039223678280e-01, 2.6165234489045515e-02, -1.9846169871215727e-03, 3.7170136665050013e-03, 2.6165234489045515e-02, -9.2783860491590594e-04, 2.8032134504712048e-04, -9.6957018524106912e-04, -1.9846169871215727e-03, 2.8032134504712048e-04, -1.2399063877565513e-03, 1.0528263449886494e-06, 3.7170136665050013e-03, -9.6957018524106912e-04, 1.0528263449886494e-06, -1.0651880667174132e-03, 6.8982016656450096e-02, -1.0194592948666651e-02, 4.0224436318745071e-04, -1.1696801694026104e-03, -1.0194592948666651e-02, -2.2058854995895081e-03, 4.3964931839049595e-05, 3.0469030637087147e-04, 4.0224436318745071e-04, 4.3964931839049595e-05, -1.3777303555632778e-03, 4.3404728696010988e-04, -1.1696801694026104e-03, 3.0469030637087147e-04, 4.3404728696010988e-04, -4.0950956530560686e-04, -9.2461109228483468e-02, -1.3650469255974099e-03, 6.9549155108044203e-04, -1.3791803108254750e-03, -1.3650469255974099e-03, -3.4426360382142198e-03, -8.9312514713293023e-04, 1.5185160693596093e-03, 6.9549155108044203e-04, -8.9312514713293023e-04, -1.2055712134559134e-03, 4.1803484747986825e-04, -1.3791803108254750e-03, 1.5185160693596093e-03, 4.1803484747986825e-04, -8.1179190435472248e-04, 1.7492329868952405e-01, -2.9646943331972139e-02, -6.6563429686947173e-04, 4.4682634999685810e-04, -2.9646943331972139e-02, -2.4173338183744193e-03, 3.3542502232973938e-04, 1.9443059924745616e-05, -6.6563429686947173e-04, 3.3542502232973938e-04, -9.9183926207136765e-04, 2.0084203476038989e-04, 4.4682634999685810e-04, 1.9443059924745616e-05, 2.0084203476038989e-04, -6.3908876172596529e-04, 3.1539924140147259e-02, 7.6934952984409844e-03, 3.6809732723858749e-03, -1.9589949144150562e-03, 7.6934952984409844e-03, -1.0048732991394538e-03, -6.8525949419794615e-04, 1.8196951541743413e-04, 3.6809732723858749e-03, -6.8525949419794615e-04, -1.9447331645722941e-03, 1.5283056340779168e-04, -1.9589949144150562e-03, 1.8196951541743413e-04, 1.5283056340779168e-04, -9.1738980564369424e-04, -3.9105676791886118e-02, 7.5467303744346081e-03, -1.8311155447274202e-03, 3.9722587176520933e-03, 7.5467303744346081e-03, -2.4625304120398338e-03, 6.5110354661161197e-04, -1.1424140297801545e-03, -1.8311155447274202e-03, 6.5110354661161197e-04, -7.6013689055211709e-04, 3.1982239567331837e-04, 3.9722587176520933e-03, -1.1424140297801545e-03, 3.1982239567331837e-04, -6.7960924512481386e-04, -6.1502540343009511e-02, -2.5918936122811376e-02, 8.2260585699679700e-04, -8.5737616123583824e-04, -2.5918936122811376e-02, -9.1045995335339511e-03, -7.6582517466873253e-04, 2.8347777788018374e-04, 8.2260585699679700e-04, -7.6582517466873253e-04, -9.8142522999483802e-04, 1.5709724166556660e-04, -8.5737616123583824e-04, 2.8347777788018374e-04, 1.5709724166556660e-04, -4.6604755267205529e-04, 2.1148032486205992e-01, -6.3731337543181353e-03, 8.0925966188404811e-04, 7.1463137701211561e-04, -6.3731337543181353e-03, -9.3133813463367691e-04, -1.3767800389944088e-04, -7.8134699474057532e-04, 8.0925966188404811e-04, -1.3767800389944088e-04, -5.9804522674363695e-04, -2.4139815546091799e-04, 7.1463137701211561e-04, -7.8134699474057532e-04, -2.4139815546091799e-04, -4.7788602351705350e-04, -3.7426472661389397e-02, 2.8157517432252185e-02, -1.0934144857086604e-03, 3.1458337980653973e-06, 2.8157517432252185e-02, -5.9309295616162740e-03, 2.5241788599436376e-04, 5.8278212231620947e-04, -1.0934144857086604e-03, 2.5241788599436376e-04, -5.9659440322057725e-04, 1.4773715153137406e-04, 3.1458337980653973e-06, 5.8278212231620947e-04, 1.4773715153137406e-04, -4.9439441008183914e-04, 1.2948178533847016e-01, -4.4858655966967973e-03, -7.3394293433360475e-04, -1.8318789975846868e-03, -4.4858655966967973e-03, -1.3111242346283915e-03, -1.0753742916959626e-03, 6.1098836231564713e-04, -7.3394293433360475e-04, -1.0753742916959626e-03, -1.7980863990064372e-03, 4.7774929182747947e-04, -1.8318789975846868e-03, 6.1098836231564713e-04, 4.7774929182747947e-04, -7.2636447350640115e-04, 1.1246840827726504e-01, 2.1631039403096977e-02, -1.6528967084977182e-03, -1.3069704047438378e-03, 2.1631039403096977e-02, -6.2735534467153794e-03, 9.1784915417337241e-04, 3.7040218222533875e-04, -1.6528967084977182e-03, 9.1784915417337241e-04, -6.1695925898898460e-04, 5.1243512009102464e-05, -1.3069704047438378e-03, 3.7040218222533875e-04, 5.1243512009102464e-05, -5.8607009651544847e-04, 2.3191443887515172e-02, -2.6201262501037315e-03, -7.6581304428420961e-04, -7.0793868722308494e-04, -2.6201262501037315e-03, -1.2427377919089994e-03, 1.3774011176280213e-04, 5.3774177980385463e-04, -7.6581304428420961e-04, 1.3774011176280213e-04, -4.4342025234310304e-04, 9.5568301616175372e-05, -7.0793868722308494e-04, 5.3774177980385463e-04, 9.5568301616175372e-05, -2.5406888063130668e-04, 9.3549762929661148e-02, 2.4789338496675476e-02, -1.7999250937495298e-03, 9.1491381013646730e-04, 2.4789338496675476e-02, -2.4686873006394385e-03, 1.1959407625742012e-04, 2.3453642308929890e-04, -1.7999250937495298e-03, 1.1959407625742012e-04, -1.3058948205367103e-03, 2.8220390404668665e-04, 9.1491381013646730e-04, 2.3453642308929890e-04, 2.8220390404668665e-04, -9.0785502558073951e-04, 8.0716780382424247e-02, 2.5365518135899486e-02, -1.0966084834676559e-03, 3.9092402053228742e-04, 2.5365518135899486e-02, -3.5742604372390177e-04, 1.1161995819547624e-03, -2.3097494896500610e-04, -1.0966084834676559e-03, 1.1161995819547624e-03, -9.9846801442279387e-04, -3.3563094825744084e-04, 3.9092402053228742e-04, -2.3097494896500610e-04, -3.3563094825744084e-04, -4.9557683336977874e-04, 4.0111243527158481e-02, -5.1743679607531489e-03, -6.4668882267963457e-04, -5.3256658217573259e-04, -5.1743679607531489e-03, -2.1959890278307748e-03, 3.3406917357637071e-04, 5.5860960683749177e-04, -6.4668882267963457e-04, 3.3406917357637071e-04, -4.0449951144626246e-04, 1.4870745971877001e-04, -5.3256658217573259e-04, 5.5860960683749177e-04, 1.4870745971877001e-04, -4.0088619373289668e-04, 1.3936559957096936e-02, -9.2345720437320422e-03, 1.7112603251330989e-03, -1.5812360931300338e-03, -9.2345720437320422e-03, -6.0853404733679922e-03, -8.1587691393434074e-04, 1.5127202442355710e-03, 1.7112603251330989e-03, -8.1587691393434074e-04, -1.0582512267918579e-03, 3.0169179197568852e-04, -1.5812360931300338e-03, 1.5127202442355710e-03, 3.0169179197568852e-04, -6.9818940668481635e-04, 1.2271035315361421e-01, 3.3834534832647936e-02, -8.4425345932574085e-04, -1.2845411056578771e-03, 3.3834534832647936e-02, -2.7021681799793487e-03, 1.4589329290161041e-03, 6.8134662217169816e-04, -8.4425345932574085e-04, 1.4589329290161041e-03, -8.5494381562856267e-04, 1.0288019018269524e-04, -1.2845411056578771e-03, 6.8134662217169816e-04, 1.0288019018269524e-04, -7.3984903838043902e-04, 1.3426877609097060e-01, 1.7349015756059596e-02, -1.3246192802241865e-03, -1.9305711238934450e-03, 1.7349015756059596e-02, -1.5270102850098125e-03, 4.4956077650381329e-04, 1.9714221201937447e-04, -1.3246192802241865e-03, 4.4956077650381329e-04, -6.4182469515364075e-04, 2.0433900996145839e-04, -1.9305711238934450e-03, 1.9714221201937447e-04, 2.0433900996145839e-04, -5.1586663656365934e-04, 8.6391392047467014e-02, 3.6980521889860229e-02, 9.3321794676103585e-04, 9.3948476321605902e-04, 3.6980521889860229e-02, -9.8928180871077319e-03, -5.3070991717869105e-04, -6.2377425133496771e-04, 9.3321794676103585e-04, -5.3070991717869105e-04, -3.0488330333303663e-04, 1.5659386456603057e-05, 9.3948476321605902e-04, -6.2377425133496771e-04, 1.5659386456603057e-05, -2.7999703918634182e-04, 1.0146744017771264e-01, 7.2816252131513558e-03, -1.6155238061515398e-03, 2.5644205137853007e-04, 7.2816252131513558e-03, -6.3112672678755539e-03, 1.6890811365672615e-03, 2.7693395172731348e-04, -1.6155238061515398e-03, 1.6890811365672615e-03, -1.2647032121969489e-03, -2.9370354320717849e-04, 2.5644205137853007e-04, 2.7693395172731348e-04, -2.9370354320717849e-04, -6.2342159510005504e-04, 1.2959056902281882e-01, -6.1301776131238232e-05, 1.6534216868320365e-03, -1.3058105872678105e-03, -6.1301776131238232e-05, -1.4249381206094562e-03, -9.5380678395206903e-04, 4.7419065106550553e-04, 1.6534216868320365e-03, -9.5380678395206903e-04, -1.5076699002388233e-03, 6.4805275159887318e-04, -1.3058105872678105e-03, 4.7419065106550553e-04, 6.4805275159887318e-04, -8.8238756291255597e-04, 8.9247536658271412e-02, 2.3842441423826456e-04, -1.4020150585281280e-03, -5.1152963328585229e-04, 2.3842441423826456e-04, -9.0191620517334706e-04, 2.8625020652046294e-04, -4.8584743596851983e-04, -1.4020150585281280e-03, 2.8625020652046294e-04, -1.0559260959327309e-03, 7.8831939884397011e-04, -5.1152963328585229e-04, -4.8584743596851983e-04, 7.8831939884397011e-04, -1.7109718079099097e-03, 1.8544185491158613e-01, -1.0718215088244560e-03, -1.8813810349262525e-03, -1.9339063688273953e-03, -1.0718215088244560e-03, -7.2274547605494753e-04, 8.6435447211605384e-04, -3.6026940405211872e-04, -1.8813810349262525e-03, 8.6435447211605384e-04, -8.1277716432238999e-04, -3.7277556893117323e-05, -1.9339063688273953e-03, -3.6026940405211872e-04, -3.7277556893117323e-05, -6.3915541802464406e-04, 1.4434415789530322e-01, -2.6872190143700809e-03, -1.0846599699061007e-03, -6.4477346277669280e-04, -2.6872190143700809e-03, -5.6018401280834278e-03, -5.0911052653693770e-05, 9.6450041626136844e-04, -1.0846599699061007e-03, -5.0911052653693770e-05, -9.4975456411969575e-04, 3.1940035065807030e-04, -6.4477346277669280e-04, 9.6450041626136844e-04, 3.1940035065807030e-04, -9.2420815563382878e-04, 7.8171789374052239e-02, 1.1131802654471883e-02, 1.7637127962972779e-03, 9.4754776914784435e-04, 1.1131802654471883e-02, -1.3560867793273460e-03, -1.0739127680493021e-03, 3.5149852956582349e-04, 1.7637127962972779e-03, -1.0739127680493021e-03, -1.7712750948741785e-03, -4.9962373537378436e-04, 9.4754776914784435e-04, 3.5149852956582349e-04, -4.9962373537378436e-04, -1.1574313866688994e-03, 5.2957801369084900e-02, -1.0005678772364536e-02, -4.7804628490494196e-04, -1.2361403815386134e-03, -1.0005678772364536e-02, -4.5116744307715714e-03, 4.1017691673670646e-04, 9.4250866230064672e-04, -4.7804628490494196e-04, 4.1017691673670646e-04, -9.2170564193822815e-04, 2.5987259125205417e-04, -1.2361403815386134e-03, 9.4250866230064672e-04, 2.5987259125205417e-04, -4.6176740062320114e-04, 1.0365332441174736e-01, -9.8221692578944128e-03, -1.9915804870139055e-03, -1.4829964959047716e-03, -9.8221692578944128e-03, -8.0048197952266309e-04, 2.0484157742938923e-04, 9.2083194131700275e-04, -1.9915804870139055e-03, 2.0484157742938923e-04, -7.4074324466875886e-04, 4.6683122189394826e-05, -1.4829964959047716e-03, 9.2083194131700275e-04, 4.6683122189394826e-05, -4.1774348179361514e-04, -1.3421959250355708e-01, 3.3713260029558795e-03, -1.3243455638564110e-03, -1.6977772976177158e-03, 3.3713260029558795e-03, -4.3427989568832846e-03, 1.5008659836834226e-03, 3.6385328660509515e-04, -1.3243455638564110e-03, 1.5008659836834226e-03, -4.4959639797028332e-04, -3.5993966797194079e-05, -1.6977772976177158e-03, 3.6385328660509515e-04, -3.5993966797194079e-05, -3.9959494583247839e-04, 1.5319795291380678e-01, 1.5606938575385582e-02, 1.2896044217671962e-04, 1.4042986843700814e-03, 1.5606938575385582e-02, -1.6968432451613968e-03, 6.1781606247467071e-04, -8.1022537873411268e-04, 1.2896044217671962e-04, 6.1781606247467071e-04, -9.0781561089793507e-04, 1.6085869694517344e-04, 1.4042986843700814e-03, -8.1022537873411268e-04, 1.6085869694517344e-04, -4.7576363868138846e-04, 1.2254882374915720e-01, 1.4693545363766116e-04, 2.1938591971393732e-03, -3.9982695744349866e-04, 1.4693545363766116e-04, -1.8792585045041851e-03, 4.2828698130133657e-04, 4.5172919991448188e-04, 2.1938591971393732e-03, 4.2828698130133657e-04, -1.0432885144689712e-03, -4.1920428823997481e-04, -3.9982695744349866e-04, 4.5172919991448188e-04, -4.1920428823997481e-04, -5.0654766396187863e-04, 6.2567741767689450e-02, -1.5942792665184773e-02, 9.4219357229582805e-04, -1.0047556566068546e-03, -1.5942792665184773e-02, -4.9222058747801188e-03, -8.8081902987161898e-04, 8.5096048171422992e-04, 9.4219357229582805e-04, -8.8081902987161898e-04, -6.4309961958789396e-04, -2.4752901697258346e-05, -1.0047556566068546e-03, 8.5096048171422992e-04, -2.4752901697258346e-05, -4.2749730100807973e-04, 2.0912880324801708e-01, -7.6064433064355411e-03, 5.3323058431332623e-04, -2.0532588685479958e-03, -7.6064433064355411e-03, -1.3135732341439657e-03, -7.8988659553189968e-04, 3.7928836724313318e-04, 5.3323058431332623e-04, -7.8988659553189968e-04, -7.3649612756357917e-04, -2.6573320308412903e-04, -2.0532588685479958e-03, 3.7928836724313318e-04, -2.6573320308412903e-04, -5.7797801692182097e-04, 1.5055274915795547e-01, -2.1924936777925716e-02, -1.2612611024713787e-03, 2.7821375886116877e-03, -2.1924936777925716e-02, -4.2068916459438062e-03, 1.3684687816957373e-03, -9.0195379182343130e-04, -1.2612611024713787e-03, 1.3684687816957373e-03, -1.2345358774530297e-03, -3.4235325803685078e-04, 2.7821375886116877e-03, -9.0195379182343130e-04, -3.4235325803685078e-04, -5.3816147544009192e-04, -9.4235991321761955e-02, -2.1367388228175833e-02, 1.0448569989099396e-03, 1.7485944032731482e-03, -2.1367388228175833e-02, -2.6716566352546589e-03, -7.3073829452656989e-04, -8.9610200660087183e-04, 1.0448569989099396e-03, -7.3073829452656989e-04, -6.7501385163585615e-04, 2.0355441125821877e-04, 1.7485944032731482e-03, -8.9610200660087183e-04, 2.0355441125821877e-04, -5.7236545019238698e-04, -6.9503808891479835e-02, 3.1383773305167471e-03, 1.5986828965354864e-03, -1.7402981803352200e-03, 3.1383773305167471e-03, -1.2489391870377849e-03, -8.5751835076954214e-04, 5.8268795422606202e-04, 1.5986828965354864e-03, -8.5751835076954214e-04, -1.5076108825684306e-03, -7.4944126836778806e-04, -1.7402981803352200e-03, 5.8268795422606202e-04, -7.4944126836778806e-04, -1.1142665347040458e-03, 4.7151474002859579e-02, 3.1740728795928382e-02, -9.4301630376494167e-04, -9.8957451562421933e-04, 3.1740728795928382e-02, -3.0449847294980667e-03, 7.3961197038087075e-04, 1.1737609646021582e-03, -9.4301630376494167e-04, 7.3961197038087075e-04, -5.3898208391981741e-04, 1.2215824265585573e-04, -9.8957451562421933e-04, 1.1737609646021582e-03, 1.2215824265585573e-04, -4.4878575781268185e-04, 6.9890878585001953e-02, 5.5058801359721072e-03, 4.3303599481317440e-04, 6.7321007698798990e-03, 5.5058801359721072e-03, -1.3315438647224626e-03, 2.4082287446273412e-04, -1.1070907494787486e-03, 4.3303599481317440e-04, 2.4082287446273412e-04, -1.6833930700655335e-03, -8.3440886703805834e-04, 6.7321007698798990e-03, -1.1070907494787486e-03, -8.3440886703805834e-04, -1.5967950989918100e-03, 8.5697346753994430e-02, -1.8323684437467489e-02, -5.9473967389843570e-04, -8.4314615829175212e-04, -1.8323684437467489e-02, -1.1875494620811390e-03, 9.7079341037182275e-04, 2.7501065040690445e-04, -5.9473967389843570e-04, 9.7079341037182275e-04, -5.2204544453808501e-04, 2.0919905664672838e-04, -8.4314615829175212e-04, 2.7501065040690445e-04, 2.0919905664672838e-04, -4.2639051308978156e-04, -2.2631222057764766e-01, -1.3601514363752679e-02, -1.6137759080852433e-03, 1.4950759744726269e-03, -1.3601514363752679e-02, -2.6453255589728329e-03, 3.3968249078500379e-04, -7.5978605583900977e-04, -1.6137759080852433e-03, 3.3968249078500379e-04, -7.5010511151759902e-04, 1.0313781788359500e-04, 1.4950759744726269e-03, -7.5978605583900977e-04, 1.0313781788359500e-04, -5.9448373489452483e-04, 4.1116271730053472e-02, -4.5650281290770516e-03, 1.0174302480600848e-03, -7.4144675491881571e-04, -4.5650281290770516e-03, -3.0111597874487158e-03, 4.1892149452060594e-04, 2.5662370553909404e-04, 1.0174302480600848e-03, 4.1892149452060594e-04, -1.6351264041670321e-03, 4.1540798986628444e-04, -7.4144675491881571e-04, 2.5662370553909404e-04, 4.1540798986628444e-04, -1.5888041899304089e-03, 3.8822005325296664e-02, 3.4682085173453489e-02, 1.1383554058731083e-03, 6.9763412662967174e-04, 3.4682085173453489e-02, -3.8778621426546131e-03, -1.0502459984301810e-03, -6.7815726314415445e-04, 1.1383554058731083e-03, -1.0502459984301810e-03, -9.5953811566263974e-04, 3.1547011250005413e-04, 6.9763412662967174e-04, -6.7815726314415445e-04, 3.1547011250005413e-04, -4.9094447850356326e-04, -1.3275117419010990e-01, 7.7532610202420025e-03, 2.6481743952388775e-04, -1.0233025705150341e-03, 7.7532610202420025e-03, -3.5307144422284118e-03, 3.6081301612281790e-04, 4.2984812221947383e-04, 2.6481743952388775e-04, 3.6081301612281790e-04, -9.6620391750736523e-04, -1.8268752035532475e-04, -1.0233025705150341e-03, 4.2984812221947383e-04, -1.8268752035532475e-04, -7.6865748942518946e-04, 2.0902111595826358e-02, 2.8778407466407915e-02, -7.3589637063263200e-04, -7.9232400295570942e-04, 2.8778407466407915e-02, -6.8244148733939813e-03, 4.6837342411344191e-04, 3.8337154291441424e-04, -7.3589637063263200e-04, 4.6837342411344191e-04, -3.9870959441253668e-04, 6.3661217652106041e-05, -7.9232400295570942e-04, 3.8337154291441424e-04, 6.3661217652106041e-05, -3.1873633147878725e-04, 1.2306903655844117e-01, 1.1790694654017940e-02, -1.6930694484504614e-03, -1.4478486853861244e-03, 1.1790694654017940e-02, -2.6362432912232283e-04, 5.7801647588433605e-04, -5.0260654622532848e-04, -1.6930694484504614e-03, 5.7801647588433605e-04, -1.0057986783918533e-03, -3.8473412149296924e-04, -1.4478486853861244e-03, -5.0260654622532848e-04, -3.8473412149296924e-04, -8.4155363764574717e-04, -2.6072264441980286e-01, -5.3839312494948985e-03, -9.8286481836498294e-04, -1.1077071063072626e-03, -5.3839312494948985e-03, -2.8144397517049936e-03, 1.3570909178396836e-03, 2.0352636344653444e-04, -9.8286481836498294e-04, 1.3570909178396836e-03, -9.6948887126791080e-04, 2.4502863167189274e-04, -1.1077071063072626e-03, 2.0352636344653444e-04, 2.4502863167189274e-04, -5.5674073681467461e-04, -1.4461247437719649e-01, -1.3593073092315819e-02, -1.4591789215271309e-03, -1.8444309608458772e-03, -1.3593073092315819e-02, -1.3790376045237883e-03, 3.6507611650380594e-04, 1.0989155862134194e-03, -1.4591789215271309e-03, 3.6507611650380594e-04, -8.2908061421262179e-04, -1.9093220436421467e-04, -1.8444309608458772e-03, 1.0989155862134194e-03, -1.9093220436421467e-04, -7.4603886066833670e-04, 1.5718948391852267e-01, 3.7509266885863875e-02, -1.6535289369067915e-03, -1.5835485386314300e-03, 3.7509266885863875e-02, -5.9100449278194633e-03, 3.5324348250094338e-04, 3.2687210216937634e-04, -1.6535289369067915e-03, 3.5324348250094338e-04, -1.0567635462974483e-03, -3.8959429122272150e-06, -1.5835485386314300e-03, 3.2687210216937634e-04, -3.8959429122272150e-06, -6.7923415962746719e-04, -2.0460083814262353e-01, 2.6461245236457143e-02, -1.0964643716453355e-03, -2.0155849422843325e-03, 2.6461245236457143e-02, -1.1607499563668883e-02, 4.9712684089718660e-04, 1.0744196203423314e-03, -1.0964643716453355e-03, 4.9712684089718660e-04, -1.2575560415559299e-03, 3.5355569910289946e-04, -2.0155849422843325e-03, 1.0744196203423314e-03, 3.5355569910289946e-04, -5.9829675272084996e-04, 1.4227457831415008e-01, 3.7409193782549421e-02, -1.6999608012668766e-03, -1.4775810720134223e-03, 3.7409193782549421e-02, -6.6980266639126880e-03, 3.7313860360236049e-04, 3.6390843416024218e-04, -1.6999608012668766e-03, 3.7313860360236049e-04, -6.8923641265649434e-04, -1.1118512619169452e-04, -1.4775810720134223e-03, 3.6390843416024218e-04, -1.1118512619169452e-04, -3.6167006383143143e-04, -3.0408057058263434e-02, 2.8102062565578297e-03, -9.9186210750588349e-05, -1.0848067328911566e-03, 2.8102062565578297e-03, -1.9168669695793325e-03, -1.5989044944160041e-04, 3.7928404911171667e-04, -9.9186210750588349e-05, -1.5989044944160041e-04, -6.4212418303488965e-04, 1.2414097957366861e-04, -1.0848067328911566e-03, 3.7928404911171667e-04, 1.2414097957366861e-04, -4.8937310642877495e-04, -5.8151762326818325e-02, 2.0612537512420814e-02, -1.8069350755089380e-03, -1.9159225235797013e-03, 2.0612537512420814e-02, -1.5273838578175230e-03, 4.8641178441277146e-04, 5.7168029371084457e-04, -1.8069350755089380e-03, 4.8641178441277146e-04, -8.5373804438111515e-04, 2.1202613463988789e-04, -1.9159225235797013e-03, 5.7168029371084457e-04, 2.1202613463988789e-04, -7.8732691228072034e-04, 1.1798334731925102e-01, -2.8140161709447171e-02, -1.2130478454804230e-03, 4.6360451013415833e-04, -2.8140161709447171e-02, -2.5008525901788615e-03, 1.3314100722865963e-03, -4.2462405872217889e-04, -1.2130478454804230e-03, 1.3314100722865963e-03, -1.0941534938684865e-03, 5.1529382431827911e-04, 4.6360451013415833e-04, -4.2462405872217889e-04, 5.1529382431827911e-04, -6.7010849513206446e-04, -1.9635177370699608e-01, 3.1453073526812375e-03, 1.9943467720728159e-04, 1.8490379843829265e-03, 3.1453073526812375e-03, -1.4755660222860181e-03, 2.5216877122513355e-04, -7.8528688355206447e-04, 1.9943467720728159e-04, 2.5216877122513355e-04, -1.4555949485632702e-03, -5.9788067272640536e-04, 1.8490379843829265e-03, -7.8528688355206447e-04, -5.9788067272640536e-04, -1.4009824713669468e-03, 1.8079617326597403e-01, 1.3311705456384233e-02, -1.6813583174003447e-03, 1.6754281220701891e-03, 1.3311705456384233e-02, -2.5836933131966949e-03, 1.2524623632335693e-03, -4.4318906832989434e-04, -1.6813583174003447e-03, 1.2524623632335693e-03, -1.1071301248058326e-03, 1.6237170251639789e-04, 1.6754281220701891e-03, -4.4318906832989434e-04, 1.6237170251639789e-04, -5.8066403722343185e-04, -4.7658243260452392e-02, -1.1547225654362255e-02, -1.6724724586724625e-04, -8.6129819877703328e-04, -1.1547225654362255e-02, -3.2589278968339326e-03, 2.6655390367207022e-04, 2.2553348630838165e-04, -1.6724724586724625e-04, 2.6655390367207022e-04, -3.8468694210936846e-04, 2.0656031333140019e-04, -8.6129819877703328e-04, 2.2553348630838165e-04, 2.0656031333140019e-04, -3.5394037175600837e-04, -6.6559656789261540e-02, 3.6780140422847912e-02, -1.0833735822547434e-03, 1.3914255331022606e-04, 3.6780140422847912e-02, -3.3904219310184804e-03, 5.4509538508334726e-04, -2.2439596920688072e-04, -1.0833735822547434e-03, 5.4509538508334726e-04, -1.1195941512654017e-03, 1.0549891235828525e-05, 1.3914255331022606e-04, -2.2439596920688072e-04, 1.0549891235828525e-05, -5.8598966626073352e-04 + }; + std::vector expected_dy_dem = { + 4.8687245272451135e-03, 5.5397159651024933e-03, 5.4642599386694624e-03, 5.6057159373549721e-03, 5.5397159651024933e-03, 4.8806501836285432e-03, 5.6063767619383984e-03, 5.6970860664459413e-03, 5.4642599386694624e-03, 5.6063767619383984e-03, 5.1211835750794290e-03, 5.3223602254697877e-03, 5.6057159373549721e-03, 5.6970860664459413e-03, 5.3223602254697877e-03, 5.2081125072059725e-03, 4.5292484636524086e-03, 4.9816448819259198e-03, 5.1363544712933759e-03, 5.1501696199608019e-03, 4.9816448819259198e-03, 4.6419992655419269e-03, 5.1569135569294903e-03, 4.8945949693767234e-03, 5.1363544712933759e-03, 5.1569135569294903e-03, 4.6706613224722264e-03, 4.9092906666249794e-03, 5.1501696199608019e-03, 4.8945949693767234e-03, 4.9092906666249794e-03, 4.8515691645339065e-03, 3.0190652370495003e-03, 3.4898418636317798e-03, 3.4899656132580579e-03, 3.5145415393083900e-03, 3.4898418636317798e-03, 3.1452916478866737e-03, 3.5037937416394702e-03, 3.4286490652383700e-03, 3.4899656132580579e-03, 3.5037937416394702e-03, 3.1627359787623427e-03, 3.5804187550924349e-03, 3.5145415393083900e-03, 3.4286490652383700e-03, 3.5804187550924349e-03, 3.2322057910012985e-03, 3.6840833901333001e-03, 4.3387482039692832e-03, 4.2515867787302417e-03, 4.4155206099863904e-03, 4.3387482039692832e-03, 4.0028491849676334e-03, 4.3652298563497116e-03, 4.3014839026378881e-03, 4.2515867787302417e-03, 4.3652298563497116e-03, 4.0124345907042516e-03, 4.1790409970907686e-03, 4.4155206099863904e-03, 4.3014839026378881e-03, 4.1790409970907686e-03, 4.0456958994549621e-03, 4.7788898285209408e-03, 4.7316589641173863e-03, 4.7277506758881538e-03, 4.7420787786160946e-03, 4.7316589641173863e-03, 4.7632472674770847e-03, 4.7277799549445667e-03, 4.7294160442947122e-03, 4.7277506758881538e-03, 4.7277799549445667e-03, 4.7576353824550499e-03, 4.7311669494084236e-03, 4.7420787786160946e-03, 4.7294160442947122e-03, 4.7311669494084236e-03, 4.7516945819656171e-03, 3.9248662197341371e-03, 4.9180169646964866e-03, 4.7190463613870446e-03, 4.4520204316217879e-03, 4.9180169646964866e-03, 4.1556621508365569e-03, 4.6033674111750095e-03, 4.5946737014459435e-03, 4.7190463613870446e-03, 4.6033674111750095e-03, 4.4277151435242147e-03, 4.6668537390335040e-03, 4.4520204316217879e-03, 4.5946737014459435e-03, 4.6668537390335040e-03, 4.4390865038161998e-03, 3.7758343215872881e-03, 4.1709220560399215e-03, 4.1202109662083035e-03, 3.9893563131548088e-03, 4.1709220560399215e-03, 3.8379626600639266e-03, 4.1328523107198936e-03, 3.9840470230024889e-03, 4.1202109662083035e-03, 4.1328523107198936e-03, 3.8852759103779593e-03, 4.1580920004019896e-03, 3.9893563131548088e-03, 3.9840470230024889e-03, 4.1580920004019896e-03, 3.9467666481718391e-03, 3.9761642677066033e-03, 4.4504099654305816e-03, 4.3794412854383678e-03, 4.4604317957851781e-03, 4.4504099654305816e-03, 4.0768567666647814e-03, 4.3435449806077473e-03, 4.3222112460340181e-03, 4.3794412854383678e-03, 4.3435449806077473e-03, 4.0773633573082564e-03, 4.4803490047300660e-03, 4.4604317957851781e-03, 4.3222112460340181e-03, 4.4803490047300660e-03, 4.1310317786866901e-03, 4.3424352854361066e-03, 4.9111142266166450e-03, 4.8395299678665781e-03, 4.6865337839814593e-03, 4.9111142266166450e-03, 4.5038688193735138e-03, 4.7214010478928668e-03, 4.7827433155358395e-03, 4.8395299678665781e-03, 4.7214010478928668e-03, 4.5047095985936833e-03, 4.8043858107812464e-03, 4.6865337839814593e-03, 4.7827433155358395e-03, 4.8043858107812464e-03, 4.5266636394073641e-03, 5.0023501655291321e-03, 5.4960776974766461e-03, 5.5277863655162403e-03, 5.6131610328358644e-03, 5.4960776974766461e-03, 5.0497823718039583e-03, 5.5223616852066977e-03, 5.2893854856376011e-03, 5.5277863655162403e-03, 5.5223616852066977e-03, 5.0964808619146599e-03, 5.4611470394996591e-03, 5.6131610328358644e-03, 5.2893854856376011e-03, 5.4611470394996591e-03, 5.2261512984334412e-03, 3.9613105413924812e-03, 4.4015218128590642e-03, 4.7307418574636991e-03, 4.6640477363723105e-03, 4.4015218128590642e-03, 4.0082410785944010e-03, 4.8261944934070503e-03, 4.5757895595947961e-03, 4.7307418574636991e-03, 4.8261944934070503e-03, 4.2233067127250678e-03, 4.7151130208320496e-03, 4.6640477363723105e-03, 4.5757895595947961e-03, 4.7151130208320496e-03, 4.3106975949268253e-03, 4.7334182996649809e-03, 5.3634328062759831e-03, 5.4504527709104602e-03, 5.1343603133321243e-03, 5.3634328062759831e-03, 4.8165188916604821e-03, 5.2922209819789611e-03, 5.3818126119621716e-03, 5.4504527709104602e-03, 5.2922209819789611e-03, 4.8435704822708701e-03, 5.2240647899442503e-03, 5.1343603133321243e-03, 5.3818126119621716e-03, 5.2240647899442503e-03, 4.8868508221224377e-03, 4.7996614634904892e-03, 5.7179083120858371e-03, 5.6117201232891321e-03, 5.6700701716033263e-03, 5.7179083120858371e-03, 4.9727735792746818e-03, 5.3848863488571300e-03, 5.4305318435656936e-03, 5.6117201232891321e-03, 5.3848863488571300e-03, 5.1731090323620544e-03, 5.2948377822407506e-03, 5.6700701716033263e-03, 5.4305318435656936e-03, 5.2948377822407506e-03, 5.2191935738847518e-03, 5.0888074222028307e-03, 5.5682241956695952e-03, 5.5311688481500410e-03, 5.3354587343858242e-03, 5.5682241956695952e-03, 5.1039796573406886e-03, 5.6527375470408862e-03, 5.5776264780928323e-03, 5.5311688481500410e-03, 5.6527375470408862e-03, 5.1908856775166106e-03, 5.5157564883515529e-03, 5.3354587343858242e-03, 5.5776264780928323e-03, 5.5157564883515529e-03, 5.2868882706481306e-03, 4.1148293948602473e-03, 4.8099243464647095e-03, 4.8354127941843744e-03, 4.8024021654871083e-03, 4.8099243464647095e-03, 4.3036710353764468e-03, 4.7276148863981300e-03, 4.5819645174844182e-03, 4.8354127941843744e-03, 4.7276148863981300e-03, 4.3517326519376268e-03, 4.5484962243548931e-03, 4.8024021654871083e-03, 4.5819645174844182e-03, 4.5484962243548931e-03, 4.3552076928133352e-03, 4.4103831260207836e-03, 4.7323280089558032e-03, 4.6792976546604689e-03, 4.7101816275088885e-03, 4.7323280089558032e-03, 4.4526464210458746e-03, 4.7051812634566420e-03, 4.7775181030841720e-03, 4.6792976546604689e-03, 4.7051812634566420e-03, 4.4777362867795979e-03, 4.7837047168366176e-03, 4.7101816275088885e-03, 4.7775181030841720e-03, 4.7837047168366176e-03, 4.4818283635942383e-03, 4.8171219099102398e-03, 5.4329072017648130e-03, 5.5577769098679687e-03, 5.5021033559999686e-03, 5.4329072017648130e-03, 4.9840681446863353e-03, 5.5228366571162838e-03, 5.6998588607070494e-03, 5.5577769098679687e-03, 5.5228366571162838e-03, 5.0189254656526020e-03, 5.3181989951699288e-03, 5.5021033559999686e-03, 5.6998588607070494e-03, 5.3181989951699288e-03, 5.2634498662377308e-03, 4.6530516200842852e-03, 5.0650394647562338e-03, 5.2488756975870273e-03, 5.2624908207797184e-03, 5.0650394647562338e-03, 4.6874447846501164e-03, 5.2268492983034561e-03, 5.1745585095054458e-03, 5.2488756975870273e-03, 5.2268492983034561e-03, 4.7952117024116173e-03, 5.0276292129730416e-03, 5.2624908207797184e-03, 5.1745585095054458e-03, 5.0276292129730416e-03, 4.9122789880896404e-03, 4.9251289238065234e-03, 5.5933241606014724e-03, 5.3718594158626860e-03, 5.6008531360396017e-03, 5.5933241606014724e-03, 5.0150407582346635e-03, 5.4662584346395404e-03, 5.3675120118810739e-03, 5.3718594158626860e-03, 5.4662584346395404e-03, 5.0248322936143662e-03, 5.4605145072560580e-03, 5.6008531360396017e-03, 5.3675120118810739e-03, 5.4605145072560580e-03, 5.1143948516248604e-03, 3.7657249048296967e-03, 3.9486926147620297e-03, 4.0691576682980483e-03, 4.0664780157542643e-03, 3.9486926147620297e-03, 3.7736205874903724e-03, 4.0495831622273807e-03, 4.0206411044168824e-03, 4.0691576682980483e-03, 4.0495831622273807e-03, 3.7937874360149263e-03, 3.9752869288898511e-03, 4.0664780157542643e-03, 4.0206411044168824e-03, 3.9752869288898511e-03, 3.8149050745084684e-03, 5.0114061217704613e-03, 5.2617991497744074e-03, 5.1790540139945674e-03, 5.3591951134087162e-03, 5.2617991497744074e-03, 5.0120596768805908e-03, 5.2487154791349359e-03, 5.2629872576859908e-03, 5.1790540139945674e-03, 5.2487154791349359e-03, 5.0448755388764347e-03, 5.3303535417822852e-03, 5.3591951134087162e-03, 5.2629872576859908e-03, 5.3303535417822852e-03, 5.1174676770792023e-03, 4.8377902633377264e-03, 5.3788546108106241e-03, 5.6040768285432335e-03, 5.3105068959469794e-03, 5.3788546108106241e-03, 4.9326524445286032e-03, 5.4991607490083253e-03, 5.5655132712644469e-03, 5.6040768285432335e-03, 5.4991607490083253e-03, 4.9741473776001794e-03, 5.3900550171932238e-03, 5.3105068959469794e-03, 5.5655132712644469e-03, 5.3900550171932238e-03, 5.1950052918756442e-03, 3.2492587714875146e-03, 3.9827109719638674e-03, 3.8774234602011355e-03, 3.8757465280329336e-03, 3.9827109719638674e-03, 3.4344256880305358e-03, 4.0545162671736575e-03, 3.7835976611712702e-03, 3.8774234602011355e-03, 4.0545162671736575e-03, 3.4953556313526311e-03, 3.9175382918411519e-03, 3.8757465280329336e-03, 3.7835976611712702e-03, 3.9175382918411519e-03, 3.5756760339217433e-03, 4.7617721014200634e-03, 4.8614225555244890e-03, 4.8279075259455451e-03, 4.8427156866381622e-03, 4.8614225555244890e-03, 4.8195643885916269e-03, 4.8405369639889255e-03, 4.8156734615472999e-03, 4.8279075259455451e-03, 4.8405369639889255e-03, 4.8197639580753243e-03, 4.8155984576041654e-03, 4.8427156866381622e-03, 4.8156734615472999e-03, 4.8155984576041654e-03, 4.8158790994777771e-03, 4.5833139216121957e-03, 5.4837863258358531e-03, 5.5638146695959553e-03, 5.4909136024647049e-03, 5.4837863258358531e-03, 4.8077007992908590e-03, 5.4604862042020273e-03, 5.4396976154026198e-03, 5.5638146695959553e-03, 5.4604862042020273e-03, 4.8833715685529635e-03, 5.3864868915666220e-03, 5.4909136024647049e-03, 5.4396976154026198e-03, 5.3864868915666220e-03, 4.9933154810546838e-03, 5.1071558319641056e-03, 5.4433074957792454e-03, 5.5765474271101826e-03, 5.5076635863146820e-03, 5.4433074957792454e-03, 5.1894387573805066e-03, 5.3922841131452456e-03, 5.5390126993166908e-03, 5.5765474271101826e-03, 5.3922841131452456e-03, 5.2001066482717309e-03, 5.4246759715010128e-03, 5.5076635863146820e-03, 5.5390126993166908e-03, 5.4246759715010128e-03, 5.2227895716483279e-03, 4.0497679429561092e-03, 4.9282556022888424e-03, 4.7237087909624649e-03, 4.8020213273712292e-03, 4.9282556022888424e-03, 4.3464895120150539e-03, 4.7976125238518097e-03, 4.7260356905951930e-03, 4.7237087909624649e-03, 4.7976125238518097e-03, 4.4574002582117803e-03, 4.6487121118467404e-03, 4.8020213273712292e-03, 4.7260356905951930e-03, 4.6487121118467404e-03, 4.4956041562837267e-03, 4.9714633531457494e-03, 5.7206656536672494e-03, 5.5180734069265810e-03, 5.4919793509825418e-03, 5.7206656536672494e-03, 5.2356045663686296e-03, 5.5596394964592278e-03, 5.7261136434031473e-03, 5.5180734069265810e-03, 5.5596394964592278e-03, 5.4948204143983092e-03, 5.6210477153196018e-03, 5.4919793509825418e-03, 5.7261136434031473e-03, 5.6210477153196018e-03, 5.5487852497226155e-03, 4.5489572462475468e-03, 5.4201128096244833e-03, 5.2786942174057458e-03, 5.5227997797472469e-03, 5.4201128096244833e-03, 4.7506506604289601e-03, 5.5748809286163452e-03, 5.3179890118108808e-03, 5.2786942174057458e-03, 5.5748809286163452e-03, 4.9901034258736697e-03, 5.3280021534388728e-03, 5.5227997797472469e-03, 5.3179890118108808e-03, 5.3280021534388728e-03, 5.0865046620443850e-03, 5.1827253238118367e-03, 5.3230838505120767e-03, 5.6092186758799996e-03, 5.3498800832996584e-03, 5.3230838505120767e-03, 5.1866043895766913e-03, 5.4707858854025323e-03, 5.4396236153839645e-03, 5.6092186758799996e-03, 5.4707858854025323e-03, 5.2034777387438419e-03, 5.3820989828010021e-03, 5.3498800832996584e-03, 5.4396236153839645e-03, 5.3820989828010021e-03, 5.2795140455242154e-03, 4.9373256245385227e-03, 5.0306921107680144e-03, 5.0091821137527148e-03, 5.0745697481229361e-03, 5.0306921107680144e-03, 4.9460721328636499e-03, 5.0969673520163961e-03, 4.9827250786815078e-03, 5.0091821137527148e-03, 5.0969673520163961e-03, 4.9517391298398959e-03, 5.0636541925258224e-03, 5.0745697481229361e-03, 4.9827250786815078e-03, 5.0636541925258224e-03, 4.9552504666375910e-03, 4.9253932922107505e-03, 5.3550337394986059e-03, 5.7052084741338500e-03, 5.4398733606965252e-03, 5.3550337394986059e-03, 4.9574861537077779e-03, 5.4706377679931946e-03, 5.5861137412303828e-03, 5.7052084741338500e-03, 5.4706377679931946e-03, 5.1587536865196583e-03, 5.6684419038570740e-03, 5.4398733606965252e-03, 5.5861137412303828e-03, 5.6684419038570740e-03, 5.2466699874090649e-03, 4.8003051442717763e-03, 5.6347570986480860e-03, 5.5318100167355980e-03, 5.4577257727782651e-03, 5.6347570986480860e-03, 4.8631401810418006e-03, 5.3361212366442329e-03, 5.5641617598327581e-03, 5.5318100167355980e-03, 5.3361212366442329e-03, 5.2040986395562412e-03, 5.3538907108157440e-03, 5.4577257727782651e-03, 5.5641617598327581e-03, 5.3538907108157440e-03, 5.2371703144156933e-03, 4.4297692564271461e-03, 5.1376827019044427e-03, 4.9522665099534903e-03, 5.1833712956327353e-03, 5.1376827019044427e-03, 4.5080542081015985e-03, 5.1885572418010912e-03, 4.8897090121346205e-03, 4.9522665099534903e-03, 5.1885572418010912e-03, 4.6450870564566172e-03, 4.8578374835205329e-03, 5.1833712956327353e-03, 4.8897090121346205e-03, 4.8578374835205329e-03, 4.6996959359605593e-03, 4.7160296966838235e-03, 5.4354504254331372e-03, 5.4914673382449068e-03, 5.0994910096800114e-03, 5.4354504254331372e-03, 4.8780256297317678e-03, 5.1983414836785551e-03, 5.1516527659604631e-03, 5.4914673382449068e-03, 5.1983414836785551e-03, 4.9576745968284569e-03, 5.3076257372673850e-03, 5.0994910096800114e-03, 5.1516527659604631e-03, 5.3076257372673850e-03, 5.0692404281867864e-03, 3.7521616561831035e-03, 4.3041195036684684e-03, 4.2083507066731441e-03, 4.0701551841232234e-03, 4.3041195036684684e-03, 3.8824364771630269e-03, 4.1647702647614748e-03, 4.1958378911293734e-03, 4.2083507066731441e-03, 4.1647702647614748e-03, 3.9355724928669756e-03, 4.1762562220603594e-03, 4.0701551841232234e-03, 4.1958378911293734e-03, 4.1762562220603594e-03, 4.0252316104492768e-03, 4.6691024276046839e-03, 5.4358497609484843e-03, 5.7984562413035670e-03, 5.3548860709007389e-03, 5.4358497609484843e-03, 4.8958005316626745e-03, 5.6061623938377849e-03, 5.5174561676182539e-03, 5.7984562413035670e-03, 5.6061623938377849e-03, 5.0465532622400548e-03, 5.4146406099752648e-03, 5.3548860709007389e-03, 5.5174561676182539e-03, 5.4146406099752648e-03, 5.1458567514192288e-03, 4.7873760079603245e-03, 4.7132530407841666e-03, 4.7097574777507523e-03, 4.7067597555051121e-03, 4.7132530407841666e-03, 4.7759539827926852e-03, 4.7079931840557261e-03, 4.7054012333601347e-03, 4.7097574777507523e-03, 4.7079931840557261e-03, 4.7228911829172273e-03, 4.7124589687313331e-03, 4.7067597555051121e-03, 4.7054012333601347e-03, 4.7124589687313331e-03, 4.7141024992431325e-03, 4.7653857276584981e-03, 5.3935337863564433e-03, 5.1518363112552411e-03, 5.2735623271459057e-03, 5.3935337863564433e-03, 4.7820910815419837e-03, 5.3263045464322246e-03, 5.2662666132495747e-03, 5.1518363112552411e-03, 5.3263045464322246e-03, 4.8989837691361037e-03, 5.2967809909708055e-03, 5.2735623271459057e-03, 5.2662666132495747e-03, 5.2967809909708055e-03, 4.9322596897032446e-03, 4.7680183858758744e-03, 4.6997587251910548e-03, 4.6997371537680086e-03, 4.7022616189482079e-03, 4.6997587251910548e-03, 4.7586592022394198e-03, 4.7000771623067050e-03, 4.6999788734553346e-03, 4.6997371537680086e-03, 4.7000771623067050e-03, 4.7355142545157962e-03, 4.6997484975064945e-03, 4.7022616189482079e-03, 4.6999788734553346e-03, 4.6997484975064945e-03, 4.7310105077216554e-03, 4.8458569989126826e-03, 5.5268069114665867e-03, 5.7047145500931061e-03, 5.5972004900509010e-03, 5.5268069114665867e-03, 5.0812217431459358e-03, 5.5795445843535817e-03, 5.5171878865958018e-03, 5.7047145500931061e-03, 5.5795445843535817e-03, 5.1164073324255592e-03, 5.7042719550623056e-03, 5.5972004900509010e-03, 5.5171878865958018e-03, 5.7042719550623056e-03, 5.2296532393988453e-03, 5.0331052677300868e-03, 5.8126537574760377e-03, 5.6116616631243848e-03, 5.5610714403935496e-03, 5.8126537574760377e-03, 5.1251590071031677e-03, 5.4834432263391871e-03, 5.3387073396366554e-03, 5.6116616631243848e-03, 5.4834432263391871e-03, 5.1721761378333781e-03, 5.4102404378702880e-03, 5.5610714403935496e-03, 5.3387073396366554e-03, 5.4102404378702880e-03, 5.2983146020102812e-03, 4.5634809762413915e-03, 5.0708627795713048e-03, 5.5285155509920162e-03, 5.1479022947186481e-03, 5.0708627795713048e-03, 4.5667821151481602e-03, 5.2739955449695626e-03, 5.2334032096775420e-03, 5.5285155509920162e-03, 5.2739955449695626e-03, 4.7796850268603851e-03, 5.2855491938456917e-03, 5.1479022947186481e-03, 5.2334032096775420e-03, 5.2855491938456917e-03, 4.9846472734427676e-03, 4.3408943946436742e-03, 5.0828126111047292e-03, 5.1619429658240372e-03, 4.8950748576602138e-03, 5.0828126111047292e-03, 4.5132933116312137e-03, 5.2279735123596507e-03, 5.0148402712815654e-03, 5.1619429658240372e-03, 5.2279735123596507e-03, 4.5757840526439138e-03, 5.1782251466555721e-03, 4.8950748576602138e-03, 5.0148402712815654e-03, 5.1782251466555721e-03, 4.7573202851706603e-03, 4.5244066537111763e-03, 5.3139854411244740e-03, 5.3841932493787804e-03, 5.1243544377133250e-03, 5.3139854411244740e-03, 4.5788274266713343e-03, 5.0657051731653390e-03, 5.1426785687150312e-03, 5.3841932493787804e-03, 5.0657051731653390e-03, 4.8535063031089169e-03, 5.0053417498441321e-03, 5.1243544377133250e-03, 5.1426785687150312e-03, 5.0053417498441321e-03, 4.9387189843115254e-03, 4.1561105459001855e-03, 4.8760140670205537e-03, 4.9530604056372751e-03, 4.7505323982066205e-03, 4.8760140670205537e-03, 4.1827775726392021e-03, 4.8099710346094313e-03, 5.0738733877216434e-03, 4.9530604056372751e-03, 4.8099710346094313e-03, 4.2585652889670211e-03, 4.7953656364286730e-03, 4.7505323982066205e-03, 5.0738733877216434e-03, 4.7953656364286730e-03, 4.4343641159264166e-03, 3.8850145135094689e-03, 4.2621118153951384e-03, 4.2895589113090549e-03, 4.3362464279518660e-03, 4.2621118153951384e-03, 4.0308729657790154e-03, 4.3844806003525494e-03, 4.2431337390287519e-03, 4.2895589113090549e-03, 4.3844806003525494e-03, 4.0886915647727800e-03, 4.2096924634853674e-03, 4.3362464279518660e-03, 4.2431337390287519e-03, 4.2096924634853674e-03, 4.1471058237790934e-03, 4.0748278012573509e-03, 5.0975121874835549e-03, 4.7643646721661492e-03, 4.9684148451479976e-03, 5.0975121874835549e-03, 4.3184840883046935e-03, 4.6729488469095803e-03, 4.8545826387352059e-03, 4.7643646721661492e-03, 4.6729488469095803e-03, 4.3205131005967286e-03, 4.7004116957686162e-03, 4.9684148451479976e-03, 4.8545826387352059e-03, 4.7004116957686162e-03, 4.3690205971449432e-03, 4.9909978133227360e-03, 5.4143910989253663e-03, 5.3601062423874191e-03, 5.3625046092275102e-03, 5.4143910989253663e-03, 5.0724427507293137e-03, 5.3652489479395936e-03, 5.3446386733428074e-03, 5.3601062423874191e-03, 5.3652489479395936e-03, 5.1460907707869393e-03, 5.3780861391384124e-03, 5.3625046092275102e-03, 5.3446386733428074e-03, 5.3780861391384124e-03, 5.2120089804020249e-03, 3.9588108570032090e-03, 4.2938117793838125e-03, 4.3611063350752835e-03, 4.2651906389425988e-03, 4.2938117793838125e-03, 4.0073255645855172e-03, 4.3518483851969093e-03, 4.3842419740188166e-03, 4.3611063350752835e-03, 4.3518483851969093e-03, 4.0893058354619380e-03, 4.3784881764625605e-03, 4.2651906389425988e-03, 4.3842419740188166e-03, 4.3784881764625605e-03, 4.0997083424903474e-03, 4.7902809088108339e-03, 5.3533071692810326e-03, 5.4529697742326999e-03, 5.3421394953302170e-03, 5.3533071692810326e-03, 4.8783628762572584e-03, 5.4898943571368179e-03, 5.4389677679356945e-03, 5.4529697742326999e-03, 5.4898943571368179e-03, 4.9324715462178625e-03, 5.3542755941681317e-03, 5.3421394953302170e-03, 5.4389677679356945e-03, 5.3542755941681317e-03, 4.9864655774714602e-03, 4.2147742524955437e-03, 4.8044404091749406e-03, 4.7174654013479485e-03, 4.5212986560996676e-03, 4.8044404091749406e-03, 4.2334336465773776e-03, 4.8136572475988510e-03, 4.8526412903096890e-03, 4.7174654013479485e-03, 4.8136572475988510e-03, 4.3841364814263201e-03, 4.5319687487354911e-03, 4.5212986560996676e-03, 4.8526412903096890e-03, 4.5319687487354911e-03, 4.3968275756536071e-03, 4.0044555899961193e-03, 4.4761433683108247e-03, 4.6472307349424728e-03, 4.4243677432400045e-03, 4.4761433683108247e-03, 4.0591447141326868e-03, 4.5360985593542293e-03, 4.4752304699918616e-03, 4.6472307349424728e-03, 4.5360985593542293e-03, 4.1784992937999085e-03, 4.5671014827176528e-03, 4.4243677432400045e-03, 4.4752304699918616e-03, 4.5671014827176528e-03, 4.2505134990343130e-03, 4.9235489798322716e-03, 5.7605067104734096e-03, 5.4765479512553930e-03, 5.5189947100062961e-03, 5.7605067104734096e-03, 5.0563592914372041e-03, 5.5230364268243756e-03, 5.6783945214777731e-03, 5.4765479512553930e-03, 5.5230364268243756e-03, 5.0602340964368484e-03, 5.6195955397581694e-03, 5.5189947100062961e-03, 5.6783945214777731e-03, 5.6195955397581694e-03, 5.1685388328162494e-03, 4.5391542914077748e-03, 5.3162333178131448e-03, 5.0344382645984304e-03, 5.0853779913647874e-03, 5.3162333178131448e-03, 4.5405811858949636e-03, 5.4256287620576297e-03, 5.0932428800818767e-03, 5.0344382645984304e-03, 5.4256287620576297e-03, 4.5923554538614669e-03, 5.1764781936595632e-03, 5.0853779913647874e-03, 5.0932428800818767e-03, 5.1764781936595632e-03, 4.6315286436058034e-03, 4.8241517294518987e-03, 5.6107202492192646e-03, 5.4225910095940417e-03, 5.6682617800773672e-03, 5.6107202492192646e-03, 5.1440593741325126e-03, 5.7028655987132184e-03, 5.4379837043436550e-03, 5.4225910095940417e-03, 5.7028655987132184e-03, 5.1490285191096163e-03, 5.4071926623830719e-03, 5.6682617800773672e-03, 5.4379837043436550e-03, 5.4071926623830719e-03, 5.3570980160283933e-03, 5.0497164090830202e-03, 5.6354102305264170e-03, 5.5833976153072101e-03, 5.5241887515719923e-03, 5.6354102305264170e-03, 5.1844311703979455e-03, 5.6093229622724144e-03, 5.6832732406642099e-03, 5.5833976153072101e-03, 5.6093229622724144e-03, 5.3845110635961542e-03, 5.6525395728626391e-03, 5.5241887515719923e-03, 5.6832732406642099e-03, 5.6525395728626391e-03, 5.5054350484104982e-03, 4.7330318788156075e-03, 5.2558542628192498e-03, 5.2754180637923608e-03, 5.2680668623478954e-03, 5.2558542628192498e-03, 4.8583176679629775e-03, 5.3617411087909300e-03, 5.0763527226022447e-03, 5.2754180637923608e-03, 5.3617411087909300e-03, 4.8719763416853719e-03, 5.2676200731791344e-03, 5.2680668623478954e-03, 5.0763527226022447e-03, 5.2676200731791344e-03, 4.9861268237371891e-03, 4.4829218835174276e-03, 4.9248762537746826e-03, 5.0024520635936409e-03, 4.9006320187329356e-03, 4.9248762537746826e-03, 4.5440399582473258e-03, 4.8761752126013965e-03, 4.9995342540547251e-03, 5.0024520635936409e-03, 4.8761752126013965e-03, 4.5687972074181541e-03, 4.8573575677983922e-03, 4.9006320187329356e-03, 4.9995342540547251e-03, 4.8573575677983922e-03, 4.6113723269492028e-03, 4.7439766500629171e-03, 5.6665405738237223e-03, 5.5308219569568514e-03, 5.8507003829033413e-03, 5.6665405738237223e-03, 5.0720272017036588e-03, 5.7362709767074119e-03, 5.7332701844372063e-03, 5.5308219569568514e-03, 5.7362709767074119e-03, 5.1913803385325689e-03, 5.4671990661370592e-03, 5.8507003829033413e-03, 5.7332701844372063e-03, 5.4671990661370592e-03, 5.3276953731318695e-03, 4.1781480426967242e-03, 4.6569448684048234e-03, 4.5751840200780334e-03, 4.6474757680489750e-03, 4.6569448684048234e-03, 4.2572109329085161e-03, 4.4514347800524946e-03, 4.4710709510486640e-03, 4.5751840200780334e-03, 4.4514347800524946e-03, 4.3020526773420746e-03, 4.4948839390591307e-03, 4.6474757680489750e-03, 4.4710709510486640e-03, 4.4948839390591307e-03, 4.3748034929899484e-03, 4.3313384846384567e-03, 4.8609426130105955e-03, 5.0178932944894578e-03, 4.9114981624153410e-03, 4.8609426130105955e-03, 4.4156139708581314e-03, 5.0527692323661466e-03, 4.8434482548828555e-03, 5.0178932944894578e-03, 5.0527692323661466e-03, 4.5006787363920647e-03, 4.8851573490110390e-03, 4.9114981624153410e-03, 4.8434482548828555e-03, 4.8851573490110390e-03, 4.5585418241746826e-03, 4.6655021290269603e-03, 5.1099289511134065e-03, 5.2222030661634514e-03, 5.1555694826168827e-03, 5.1099289511134065e-03, 4.7523395276319845e-03, 5.2110469413705869e-03, 5.1579337232921206e-03, 5.2222030661634514e-03, 5.2110469413705869e-03, 4.8305384374289825e-03, 5.1666370817119011e-03, 5.1555694826168827e-03, 5.1579337232921206e-03, 5.1666370817119011e-03, 4.8571729590057358e-03, 4.4124832580118107e-03, 5.5124750974480611e-03, 5.1133380051136147e-03, 5.3588860042126296e-03, 5.5124750974480611e-03, 4.6356559976615270e-03, 5.4305254470557798e-03, 5.2131271012044759e-03, 5.1133380051136147e-03, 5.4305254470557798e-03, 4.6534597410828934e-03, 5.1783873042292455e-03, 5.3588860042126296e-03, 5.2131271012044759e-03, 5.1783873042292455e-03, 4.8236077833933117e-03, -9.6615927935495179e-03, 6.8675588255994405e-03, 1.0945473142527546e-03, -1.1194179153164361e-03, 6.8675588255994405e-03, -3.6346241719092134e-03, -5.8138983110610643e-04, 1.2243832363445957e-03, 1.0945473142527546e-03, -5.8138983110610643e-04, -4.6126147746582667e-04, 6.3180189590662426e-04, -1.1194179153164361e-03, 1.2243832363445957e-03, 6.3180189590662426e-04, -3.6362492012642004e-04, -1.2936789941773327e-02, 9.1335782726763882e-03, -9.2413647644373851e-04, -9.1704209021694640e-04, 9.1335782726763882e-03, -4.3195816573411861e-03, 1.5488956851291041e-03, 2.2741681746236771e-03, -9.2413647644373851e-04, 1.5488956851291041e-03, 4.0145236950450106e-04, 1.1955645031987922e-03, -9.1704209021694640e-04, 2.2741681746236771e-03, 1.1955645031987922e-03, 5.9101407236907759e-04, -5.3962989027005557e-03, 2.6072667157267145e-03, -8.2947112894151754e-04, -8.4707999007982405e-04, 2.6072667157267145e-03, -1.9454893539958128e-03, 1.3259854799795544e-04, 9.1413360753332847e-05, -8.2947112894151754e-04, 1.3259854799795544e-04, -5.1496376171848712e-04, -2.0587710943157896e-04, -8.4707999007982405e-04, 9.1413360753332847e-05, -2.0587710943157896e-04, -4.9949758378796459e-04, -1.7429393257872950e-02, 8.3520701836941741e-03, -1.3077437245125536e-03, 2.6219650960785422e-03, 8.3520701836941741e-03, -2.1730131032824999e-03, 2.9672791558086049e-03, 1.5427551148408693e-03, -1.3077437245125536e-03, 2.9672791558086049e-03, -1.2507874901661125e-04, 8.2649701074271687e-04, 2.6219650960785422e-03, 1.5427551148408693e-03, 8.2649701074271687e-04, -2.3883234105498484e-05, -1.3830306151965368e-02, 8.6140679080944672e-03, -1.0519351576461367e-03, 2.8626024695093547e-03, 8.6140679080944672e-03, -1.9075258568341821e-03, 2.0138742914406317e-03, 6.3743572321268256e-04, -1.0519351576461367e-03, 2.0138742914406317e-03, 6.7646549781383313e-04, 1.7337725787928556e-03, 2.8626024695093547e-03, 6.3743572321268256e-04, 1.7337725787928556e-03, 8.5614628175256790e-04, -1.8403458798923876e-02, 7.8573984911785699e-03, -1.5961302550980016e-03, 3.5149754327317441e-03, 7.8573984911785699e-03, -1.7067248914369689e-03, 2.5330322771104944e-03, 6.0551635352428294e-04, -1.5961302550980016e-03, 2.5330322771104944e-03, 4.5693878376751882e-04, 1.3178252989731669e-03, 3.5149754327317441e-03, 6.0551635352428294e-04, 1.3178252989731669e-03, 4.6425349761707751e-04, -1.7006699057036175e-02, 7.6077168403978004e-03, -1.2881631030799989e-03, 2.9480535843260710e-03, 7.6077168403978004e-03, -1.9702066590096216e-03, 2.0108629639478673e-03, 7.9516080819711594e-04, -1.2881631030799989e-03, 2.0108629639478673e-03, 8.0433046450099942e-04, 1.8038266298348227e-03, 2.9480535843260710e-03, 7.9516080819711594e-04, 1.8038266298348227e-03, 8.8160771996069693e-04, -1.3854826825508055e-02, 8.0479162307344476e-03, -5.4775445908707208e-04, 3.4108070940140215e-03, 8.0479162307344476e-03, -2.4855624480393168e-03, 2.0010342207153007e-03, 9.0050864299703079e-04, -5.4775445908707208e-04, 2.0010342207153007e-03, 2.2879961439639510e-04, 1.2455851470582276e-03, 3.4108070940140215e-03, 9.0050864299703079e-04, 1.2455851470582276e-03, 2.5797630491429058e-04, -1.3110891255545737e-02, 7.4933352500272960e-03, -3.9694223029317251e-04, 1.8696987208199419e-03, 7.4933352500272960e-03, -2.7269680038459173e-03, 1.3403236271558815e-03, 2.3125877913683179e-04, -3.9694223029317251e-04, 1.3403236271558815e-03, 7.7648354810912158e-04, 1.2366194463463297e-03, 1.8696987208199419e-03, 2.3125877913683179e-04, 1.2366194463463297e-03, 9.6189796048403537e-04, -1.3232672426819813e-02, 4.6823041218628670e-03, 1.8611702707055700e-03, -8.3085468064121430e-04, 4.6823041218628670e-03, -8.0583799511241597e-04, 1.5487811484838697e-03, 8.1349281204434981e-04, 1.8611702707055700e-03, 1.5487811484838697e-03, -1.2621786723316788e-04, 1.4631208905251433e-03, -8.3085468064121430e-04, 8.1349281204434981e-04, 1.4631208905251433e-03, 2.7511955514886215e-04, -1.5084537572866579e-02, 7.1293848562809948e-03, 3.7045468755946658e-03, -1.4711529451730022e-03, 7.1293848562809948e-03, -1.8494973697146082e-03, 1.3524358852716983e-03, 1.5936537318735912e-03, 3.7045468755946658e-03, 1.3524358852716983e-03, -5.7047546733440834e-04, 2.0738159150412733e-03, -1.4711529451730022e-03, 1.5936537318735912e-03, 2.0738159150412733e-03, 5.0829312908897773e-04, -1.1837724802376451e-02, 8.0065879433286139e-03, 2.5212064219669013e-03, -8.6580744742516692e-04, 8.0065879433286139e-03, -2.7205322761259480e-03, -2.2105454381026531e-04, 1.4586534642126341e-03, 2.5212064219669013e-03, -2.2105454381026531e-04, -2.4777193779670914e-04, 1.4026084563219037e-03, -8.6580744742516692e-04, 1.4586534642126341e-03, 1.4026084563219037e-03, 3.8679517983707091e-04, -1.2017287033769334e-02, 6.9442711205541319e-03, 1.8032964267264526e-03, 9.4303015390613293e-04, 6.9442711205541319e-03, -3.5149126553830593e-03, 2.3699469146257939e-04, -4.5705934851597888e-04, 1.8032964267264526e-03, 2.3699469146257939e-04, -1.3586411786986857e-04, 8.4154434886370209e-04, 9.4303015390613293e-04, -4.5705934851597888e-04, 8.4154434886370209e-04, -6.9789884734637771e-05, -1.6094817869361303e-02, 6.3002762847164506e-03, 2.2530843424977648e-03, 3.8816772736172305e-03, 6.3002762847164506e-03, -1.3384245408796698e-03, 1.0043242756394048e-03, 1.0894870315372434e-03, 2.2530843424977648e-03, 1.0043242756394048e-03, -1.2100553317588711e-05, 1.2384575776262850e-03, 3.8816772736172305e-03, 1.0894870315372434e-03, 1.2384575776262850e-03, 1.5692877333876084e-04, -1.4045801776472706e-02, 8.9584932766103818e-03, 2.6014796744964234e-03, 1.0166144512118521e-04, 8.9584932766103818e-03, -3.2080958107171351e-03, 1.3686944034468785e-03, 2.0148513302307739e-03, 2.6014796744964234e-03, 1.3686944034468785e-03, -4.7783480807389827e-04, 1.6221316661178631e-03, 1.0166144512118521e-04, 2.0148513302307739e-03, 1.6221316661178631e-03, 3.6300160393023845e-04, -1.5035351590565366e-02, 9.6530182335056031e-03, 2.1949862437937429e-03, 1.8246414683111131e-04, 9.6530182335056031e-03, -2.6652597726363591e-03, 3.9640886823489533e-04, 2.0283144683530636e-03, 2.1949862437937429e-03, 3.9640886823489533e-04, 4.9399384235801198e-04, 1.5872284744377334e-03, 1.8246414683111131e-04, 2.0283144683530636e-03, 1.5872284744377334e-03, 5.7853947086566285e-04, -1.3983142687551818e-02, 5.0756892732599047e-03, 5.1905234137764912e-03, 4.2875838849719752e-03, 5.0756892732599047e-03, -9.4954694217068230e-04, 1.8883020854449403e-04, 6.7109554143711891e-04, 5.1905234137764912e-03, 1.8883020854449403e-04, -8.4870028551829930e-04, 1.1077830644977793e-03, 4.2875838849719752e-03, 6.7109554143711891e-04, 1.1077830644977793e-03, -7.8542966877660964e-04, -1.3600736364920495e-02, 9.7195178003623480e-03, 9.3917635136515779e-04, 1.9513372114623364e-03, 9.7195178003623480e-03, -2.5366246539418099e-03, 1.5797585617940194e-03, 7.0858722530550288e-04, 9.3917635136515779e-04, 1.5797585617940194e-03, 5.8251868528788402e-04, 1.7783091417839559e-03, 1.9513372114623364e-03, 7.0858722530550288e-04, 1.7783091417839559e-03, 7.1541539011648952e-04, -1.5411936953103653e-02, 7.3903417706619335e-03, 4.5483022121753222e-03, -8.9677881534670116e-04, 7.3903417706619335e-03, -1.8311445673321121e-03, 5.0618507124800330e-04, 1.6497657392883489e-03, 4.5483022121753222e-03, 5.0618507124800330e-04, -1.0944142931277624e-04, 2.0053974090094044e-03, -8.9677881534670116e-04, 1.6497657392883489e-03, 2.0053974090094044e-03, 5.8688361598166873e-04, -1.5877997586428029e-02, 7.0102564979234233e-03, 2.3822117395892084e-03, 2.4183758414625888e-03, 7.0102564979234233e-03, -2.0572286784362988e-03, 3.5983393342809214e-04, -1.3917845780270373e-04, 2.3822117395892084e-03, 3.5983393342809214e-04, 9.3742516843216294e-05, 1.1681434312541931e-03, 2.4183758414625888e-03, -1.3917845780270373e-04, 1.1681434312541931e-03, 2.2289605656281757e-04, -1.4083572828047439e-02, 7.9925244381302291e-03, -6.0284983439355796e-04, -9.7568039067081366e-04, 7.9925244381302291e-03, -2.8403745612453242e-03, 1.4149913418989291e-03, 1.3483783335542488e-03, -6.0284983439355796e-04, 1.4149913418989291e-03, -7.9567702505795620e-05, 4.5761961508970946e-04, -9.7568039067081366e-04, 1.3483783335542488e-03, 4.5761961508970946e-04, -1.8122180927645343e-05, -1.3011750140965523e-02, 8.0448027672424538e-03, 1.3711087550206127e-03, 1.2861069552821227e-03, 8.0448027672424538e-03, -3.4752261499698942e-03, 3.7416175157491864e-04, 3.5251373821010203e-04, 1.3711087550206127e-03, 3.7416175157491864e-04, 1.0668685970538342e-04, 1.2410321451126983e-03, 1.2861069552821227e-03, 3.5251373821010203e-04, 1.2410321451126983e-03, 1.2833570311562067e-04, -1.0240113275587109e-02, 6.1802880789633766e-03, 1.3904451755038282e-03, -3.0192736293871718e-04, 6.1802880789633766e-03, -3.0059440450167932e-03, -5.3368175414203413e-04, 4.7195090306965839e-04, 1.3904451755038282e-03, -5.3368175414203413e-04, -7.9556056354311339e-04, 9.5483655393905145e-04, -3.0192736293871718e-04, 4.7195090306965839e-04, 9.5483655393905145e-04, -5.7134829653405345e-04, -1.3385066274392758e-02, 6.6124934381380064e-03, -1.2821932840326072e-03, 2.0730056600946553e-03, 6.6124934381380064e-03, -1.9317403170367104e-03, 1.2403275688648368e-03, -8.1880449480140711e-06, -1.2821932840326072e-03, 1.2403275688648368e-03, -2.9734471249323222e-04, 1.1154847964356678e-03, 2.0730056600946553e-03, -8.1880449480140711e-06, 1.1154847964356678e-03, -8.2008990837446980e-05, -1.2832033261169851e-02, 7.1731149113646135e-03, -1.3178043396281812e-03, -8.1927059876190744e-04, 7.1731149113646135e-03, -3.8547355138079868e-03, 1.3689397012382246e-03, 1.4741597574298045e-03, -1.3178043396281812e-03, 1.3689397012382246e-03, -1.5523748509968895e-04, 5.2913940932647796e-04, -8.1927059876190744e-04, 1.4741597574298045e-03, 5.2913940932647796e-04, -2.0477161752664730e-06, -1.9353179288859976e-02, 8.2472993213544895e-03, 1.1328241708247928e-03, 1.0173474843959841e-04, 8.2472993213544895e-03, -2.0348560585739840e-03, 1.8916142915004761e-03, 1.8422284519757142e-03, 1.1328241708247928e-03, 1.8916142915004761e-03, 6.6390870098819582e-04, 1.6001417768552631e-03, 1.0173474843959841e-04, 1.8422284519757142e-03, 1.6001417768552631e-03, 7.2754320201389323e-04, -1.8034057030268318e-02, 8.1439718393683594e-03, -1.4927369306321973e-03, 4.4653038520066791e-04, 8.1439718393683594e-03, -2.0375629958202432e-03, 1.9844371297367868e-03, 1.5706327924026185e-03, -1.4927369306321973e-03, 1.9844371297367868e-03, 8.4221508811039951e-05, 1.4760224149450302e-03, 4.4653038520066791e-04, 1.5706327924026185e-03, 1.4760224149450302e-03, 5.3324147164490450e-04, -1.9858435274308524e-02, 5.5927944148301684e-03, -1.5438870160433414e-03, 3.3657100726607308e-03, 5.5927944148301684e-03, 2.3561660401123010e-04, 2.3242973956863057e-03, 1.5999686201749661e-03, -1.5438870160433414e-03, 2.3242973956863057e-03, 3.1805397259977025e-04, 1.2466021708669767e-03, 3.3657100726607308e-03, 1.5999686201749661e-03, 1.2466021708669767e-03, 3.8368966980689126e-04, -9.7281699245688677e-03, 6.3301451421522454e-03, -2.1014055387616102e-04, -1.0747609780239144e-03, 6.3301451421522454e-03, -3.4334745573678408e-03, 8.1169421191581016e-04, 6.4971659824279585e-04, -2.1014055387616102e-04, 8.1169421191581016e-04, -3.3545824360928884e-04, 4.5259596268414320e-04, -1.0747609780239144e-03, 6.4971659824279585e-04, 4.5259596268414320e-04, -1.1830597768979520e-04, -1.6332140614730220e-02, 9.0103128113567417e-03, 1.5571710079426081e-03, -6.0630677294044237e-04, 9.0103128113567417e-03, -2.4498309848700814e-03, 1.1243148388670240e-03, 1.9746725100914823e-03, 1.5571710079426081e-03, 1.1243148388670240e-03, 2.4215985584758782e-04, 1.0849950462954012e-03, -6.0630677294044237e-04, 1.9746725100914823e-03, 1.0849950462954012e-03, 4.0955394785959507e-04, -1.3036079872743208e-02, 8.1986656104820928e-03, -4.1339688016818390e-04, -7.9169837147628590e-04, 8.1986656104820928e-03, -3.0128993657090463e-03, 1.3087483787852701e-03, 1.3783682086432115e-03, -4.1339688016818390e-04, 1.3087483787852701e-03, 1.0389730541704824e-04, 1.0307092574933089e-03, -7.9169837147628590e-04, 1.3783682086432115e-03, 1.0307092574933089e-03, 2.5367532767235548e-04, -1.3984645154594681e-02, 8.3445337042039359e-03, -1.5668162012331865e-03, -1.6294897225424144e-03, 8.3445337042039359e-03, -2.3222775494725553e-03, 1.5981596851179622e-03, 2.2297148976875006e-03, -1.5668162012331865e-03, 1.5981596851179622e-03, 4.9099274793647457e-04, 1.0006803325697039e-03, -1.6294897225424144e-03, 2.2297148976875006e-03, 1.0006803325697039e-03, 7.7225711991308439e-04, -1.0878208304283613e-02, 6.8495335213857884e-03, 1.8367434011084490e-03, -1.1684248224645396e-03, 6.8495335213857884e-03, -3.7485460530336514e-03, -4.5636048439838095e-04, 1.1909478868148576e-03, 1.8367434011084490e-03, -4.5636048439838095e-04, 1.3521556398979253e-04, 5.5654021720440735e-04, -1.1684248224645396e-03, 1.1909478868148576e-03, 5.5654021720440735e-04, 1.7634298653690025e-04, -1.3695683195390350e-02, 9.6440337102124008e-03, -1.1111079511340462e-03, -7.3806515965964433e-04, 9.6440337102124008e-03, -4.4655385326203868e-03, 2.6568699762222737e-03, 1.4535689858293593e-03, -1.1111079511340462e-03, 2.6568699762222737e-03, 4.0767188718331062e-04, 1.3035911326184557e-03, -7.3806515965964433e-04, 1.4535689858293593e-03, 1.3035911326184557e-03, 6.4104771006401936e-04, -1.8783865558349294e-02, 9.5046988369441830e-03, 1.0054895662265832e-03, -1.3398595612765494e-03, 9.5046988369441830e-03, -2.4387572360332795e-03, 1.2308518058462559e-03, 2.3676189965730725e-03, 1.0054895662265832e-03, 1.2308518058462559e-03, 7.0028547773139820e-04, 1.0101870932540413e-03, -1.3398595612765494e-03, 2.3676189965730725e-03, 1.0101870932540413e-03, 7.0233475365099978e-04, -1.4976622456425184e-02, 8.1162153169009460e-03, -1.3827823800513477e-03, -9.0475962868004594e-04, 8.1162153169009460e-03, -2.2249946889018480e-03, 2.1152052285084320e-03, 1.8399989448963172e-03, -1.3827823800513477e-03, 2.1152052285084320e-03, 2.8805366919883429e-04, 1.4321728932099045e-03, -9.0475962868004594e-04, 1.8399989448963172e-03, 1.4321728932099045e-03, 4.1014738409446026e-04, -1.1408226145512439e-02, 6.4954304965538492e-03, -1.2001987796160765e-03, -9.3863075040407956e-04, 6.4954304965538492e-03, -2.1295410823541430e-03, 8.3445635040981805e-04, 1.0828225854915745e-03, -1.2001987796160765e-03, 8.3445635040981805e-04, -3.5131917652677573e-04, 6.5050284812873853e-04, -9.3863075040407956e-04, 1.0828225854915745e-03, 6.5050284812873853e-04, 9.2967121716285277e-05, -1.3540012040553353e-02, 6.5044103809259023e-03, -1.4509842112690869e-03, 2.9456259587854984e-03, 6.5044103809259023e-03, -2.1362340178470861e-03, 2.1384291485579830e-03, 4.4150731301810292e-04, -1.4509842112690869e-03, 2.1384291485579830e-03, 6.8321237604588041e-04, 1.5155198702477583e-03, 2.9456259587854984e-03, 4.4150731301810292e-04, 1.5155198702477583e-03, 9.4537651413804154e-04, -1.6246993041419768e-02, 7.8928849361846465e-03, -1.0371149787786690e-03, -1.1988565506819290e-03, 7.8928849361846465e-03, -2.1711861031565201e-03, 1.5625386477991353e-03, 2.0766924041950999e-03, -1.0371149787786690e-03, 1.5625386477991353e-03, 3.6916286503139387e-04, 1.3956257087938428e-03, -1.1988565506819290e-03, 2.0766924041950999e-03, 1.3956257087938428e-03, 7.0753221752847730e-04, -1.3605718835333182e-02, 8.4677963790206132e-03, 1.5674417326158496e-03, -8.5156763496229099e-04, 8.4677963790206132e-03, -2.0207185798241187e-03, 8.5611012401802392e-04, 1.5345153178721719e-03, 1.5674417326158496e-03, 8.5611012401802392e-04, 3.4310085919516103e-04, 1.3423485494321341e-03, -8.5156763496229099e-04, 1.5345153178721719e-03, 1.3423485494321341e-03, 4.5225478537197169e-04, -1.4669000264706085e-02, 8.7488167172959999e-03, -1.4091880634556062e-03, -1.0017125355292859e-03, 8.7488167172959999e-03, -3.0719648092492838e-03, 2.3683327512114935e-03, 1.8164163784652701e-03, -1.4091880634556062e-03, 2.3683327512114935e-03, 1.8550057165905962e-04, 8.3199462205668607e-04, -1.0017125355292859e-03, 1.8164163784652701e-03, 8.3199462205668607e-04, 2.3623702228027553e-04, -1.3384842502986008e-02, -7.4284413610738024e-04, 2.2164427066970675e-03, -1.6984495314060527e-03, -7.4284413610738024e-04, -1.1789069761800031e-04, 5.5226050924804017e-04, 1.2865230734008021e-03, 2.2164427066970675e-03, 5.5226050924804017e-04, 1.6158157787987345e-04, 1.4162104619029536e-03, -1.6984495314060527e-03, 1.2865230734008021e-03, 1.4162104619029536e-03, 2.5984053718450023e-04, -1.4270238868634912e-02, 8.7435299844129590e-03, 1.3121371658921229e-03, -1.4459562540351310e-03, 8.7435299844129590e-03, -2.5869716021176296e-03, 1.9816371858388513e-03, 1.8906964142812917e-03, 1.3121371658921229e-03, 1.9816371858388513e-03, 5.2299567205402252e-04, 1.6974470674424424e-03, -1.4459562540351310e-03, 1.8906964142812917e-03, 1.6974470674424424e-03, 6.8040298431208874e-04, -1.3927808495450104e-02, 9.8114319729643538e-03, -1.4585605825814291e-03, 5.1081452718003395e-04, 9.8114319729643538e-03, -3.1780317017032489e-03, 1.9510988088846683e-03, 1.0552731530989561e-03, -1.4585605825814291e-03, 1.9510988088846683e-03, 4.1351930697924790e-04, 8.3639732480242888e-04, 5.1081452718003395e-04, 1.0552731530989561e-03, 8.3639732480242888e-04, 5.9577771535064973e-04, -1.2005957611549042e-02, 6.5269829696843855e-03, 5.4227086053001321e-06, -1.2166655959930230e-03, 6.5269829696843855e-03, -2.8587968071345869e-03, 8.7526046059167601e-04, 1.1870931547370881e-03, 5.4227086053001321e-06, 8.7526046059167601e-04, -9.3426039127427613e-05, 7.3532331884749720e-04, -1.2166655959930230e-03, 1.1870931547370881e-03, 7.3532331884749720e-04, 8.0025894282650164e-05, -1.3643474898358156e-02, 7.2377155928434068e-03, -1.2401442140379342e-03, -1.2740975088610677e-03, 7.2377155928434068e-03, -2.2422690205721159e-03, 1.5138822054081335e-03, 1.4904684300819143e-03, -1.2401442140379342e-03, 1.5138822054081335e-03, -1.3634361034485141e-04, 1.1127686221486200e-03, -1.2740975088610677e-03, 1.4904684300819143e-03, 1.1127686221486200e-03, 2.6190429702892281e-04, -1.8536129654681068e-02, 5.7362215425607876e-03, 3.7077483503927737e-03, 5.3703505121290920e-03, 5.7362215425607876e-03, -1.5089926703986004e-03, 1.7790988909616276e-03, 2.4855814646503223e-04, 3.7077483503927737e-03, 1.7790988909616276e-03, -2.6330472344847811e-04, 5.6773516831324072e-04, 5.3703505121290920e-03, 2.4855814646503223e-04, 5.6773516831324072e-04, -7.5082904071627644e-05, -1.2009357144285096e-02, 8.3436157388768785e-03, 1.4898887848690406e-03, -1.1644905255429930e-03, 8.3436157388768785e-03, -3.9039776700446403e-03, 6.7845541120046616e-04, 8.9957003910951026e-04, 1.4898887848690406e-03, 6.7845541120046616e-04, -4.3136022070644790e-04, 8.0279376655449392e-04, -1.1644905255429930e-03, 8.9957003910951026e-04, 8.0279376655449392e-04, 1.4214812713657745e-04, -1.3087027777011376e-02, 8.1091117611986480e-03, -9.9940829784200174e-04, 3.0841860053121063e-03, 8.1091117611986480e-03, -1.8957247580462895e-03, 2.0851167099702368e-03, 4.9914625071405998e-04, -9.9940829784200174e-04, 2.0851167099702368e-03, 5.8420276661913983e-04, 1.2124350953385600e-03, 3.0841860053121063e-03, 4.9914625071405998e-04, 1.2124350953385600e-03, 9.4839886112247610e-04, -1.4389875409702553e-02, 5.9650198667490139e-03, 2.7789793386952099e-03, 1.1951993133499255e-03, 5.9650198667490139e-03, -1.5525484868771592e-03, -1.8249951551237587e-04, 2.3756213628496397e-05, 2.7789793386952099e-03, -1.8249951551237587e-04, -7.0686530665496237e-04, 8.4787536338310149e-04, 1.1951993133499255e-03, 2.3756213628496397e-05, 8.4787536338310149e-04, -1.8249697460956133e-04, -1.6493749736099662e-02, 8.9845954108499354e-03, -6.5855511936865679e-04, -1.2916630860458668e-03, 8.9845954108499354e-03, -2.2720543779065185e-03, 2.0777202119131770e-03, 1.7945117300401194e-03, -6.5855511936865679e-04, 2.0777202119131770e-03, 3.7547177825330777e-04, 1.3207948522699173e-03, -1.2916630860458668e-03, 1.7945117300401194e-03, 1.3207948522699173e-03, 4.4592713214989318e-04, -1.1661413908588582e-02, 5.3356769263466705e-03, 2.3910148956967888e-05, 2.0144431709342532e-03, 5.3356769263466705e-03, -2.3763451303913793e-03, 1.5795406491315219e-04, -5.2947905482373585e-04, 2.3910148956967888e-05, 1.5795406491315219e-04, -3.3709897439123792e-04, 4.4156371402711333e-04, 2.0144431709342532e-03, -5.2947905482373585e-04, 4.4156371402711333e-04, -1.6067267371488200e-04, -1.2127195499525786e-02, 6.3575178498569175e-03, 2.7885477665962593e-03, 1.4103487851294598e-03, 6.3575178498569175e-03, -1.9866201831196012e-03, 1.4532946357492936e-03, 1.0405145770188389e-03, 2.7885477665962593e-03, 1.4532946357492936e-03, 1.1650211077524758e-04, 1.1231612548733749e-03, 1.4103487851294598e-03, 1.0405145770188389e-03, 1.1231612548733749e-03, 4.1629479593797221e-04, -1.0520573085213737e-02, 6.8711276675652456e-03, 1.6939148906612810e-03, -9.8113687728168184e-04, 6.8711276675652456e-03, -2.6887735100749720e-03, -2.1586496673177543e-04, 1.0064124321346908e-03, 1.6939148906612810e-03, -2.1586496673177543e-04, -1.4067199641936031e-04, 2.9361950909233638e-04, -9.8113687728168184e-04, 1.0064124321346908e-03, 2.9361950909233638e-04, -7.9727460669166473e-05, -1.5912209340202287e-02, 8.8708507277150267e-03, -2.2876273236139503e-04, -1.2962376027821204e-03, 8.8708507277150267e-03, -3.7886872287483438e-03, 2.2364194787288061e-03, 2.3055217337219964e-03, -2.2876273236139503e-04, 2.2364194787288061e-03, -2.2121966075386214e-04, 9.2457524181229541e-04, -1.2962376027821204e-03, 2.3055217337219964e-03, 9.2457524181229541e-04, 5.0819851534159976e-04, -1.1988487259813843e-02, -1.0817915696993614e-03, 4.4055451227390865e-03, 2.6194469136628352e-03, -1.0817915696993614e-03, -9.4238486903406541e-04, 7.7092876812179679e-04, 1.6765166378430332e-03, 4.4055451227390865e-03, 7.7092876812179679e-04, -7.0957594439696422e-04, 4.6632501327589830e-04, 2.6194469136628352e-03, 1.6765166378430332e-03, 4.6632501327589830e-04, -1.7141234209432641e-05, -1.2965555240827500e-02, 4.6872799977542403e-03, -1.4510821237139582e-03, 3.7946233749072901e-03, 4.6872799977542403e-03, -3.5962713002839149e-04, 1.0943003685336375e-03, 5.6646742816718408e-04, -1.4510821237139582e-03, 1.0943003685336375e-03, -1.0401090694716338e-04, 1.7088920364535401e-03, 3.7946233749072901e-03, 5.6646742816718408e-04, 1.7088920364535401e-03, 2.7162106952229655e-04, -1.4785199674470367e-02, 8.0633700283524075e-03, -5.6495833882158305e-04, 2.4514928345558051e-03, 8.0633700283524075e-03, -3.2143330398933306e-03, 1.9126378978111445e-03, 4.8878097615942593e-05, -5.6495833882158305e-04, 1.9126378978111445e-03, 2.1638725769767891e-04, 8.0709190039421428e-04, 2.4514928345558051e-03, 4.8878097615942593e-05, 8.0709190039421428e-04, 5.8999550418438625e-04, -1.4240209598961997e-02, 6.7516548024444747e-03, -5.1918416449977969e-04, 2.2353477983456612e-03, 6.7516548024444747e-03, -1.8474456163168917e-03, 1.2971818549383650e-03, -1.5014058192087238e-04, -5.1918416449977969e-04, 1.2971818549383650e-03, -7.3709152533265676e-05, 3.5066038413611596e-04, 2.2353477983456612e-03, -1.5014058192087238e-04, 3.5066038413611596e-04, -3.3978637630894203e-05, -1.3007652156842001e-02, 6.6856636493797493e-03, 2.9160109924347812e-03, -6.5543528412060508e-04, 6.6856636493797493e-03, -1.8216554256699276e-03, 7.3027879176171099e-04, 9.7092376946287130e-04, 2.9160109924347812e-03, 7.3027879176171099e-04, -1.0356785011530854e-04, 1.5729902578397168e-03, -6.5543528412060508e-04, 9.7092376946287130e-04, 1.5729902578397168e-03, 2.9497919465760276e-04, -1.0866405763036151e-02, 5.3513859835064457e-03, 2.4060930773512137e-04, 1.9862948106334644e-03, 5.3513859835064457e-03, -1.8512769013365881e-03, 2.8133169930445625e-04, -3.4053514307311997e-04, 2.4060930773512137e-04, 2.8133169930445625e-04, -1.0872990161973775e-04, 3.0543245383033921e-04, 1.9862948106334644e-03, -3.4053514307311997e-04, 3.0543245383033921e-04, -3.2564062326252566e-05, -9.0348481159891574e-03, 4.9368779887051665e-03, -7.7858023749495433e-04, 1.0920385408053164e-03, 4.9368779887051665e-03, -2.4651064596398460e-03, 7.2405402568656097e-04, -4.7031756933928247e-04, -7.7858023749495433e-04, 7.2405402568656097e-04, -5.4175927276914012e-04, 2.2338122120519862e-05, 1.0920385408053164e-03, -4.7031756933928247e-04, 2.2338122120519862e-05, -1.6681805314778045e-04, -1.7304180355604392e-02, 8.6652343814632091e-03, -1.2246474341539783e-03, 8.2415022799262484e-04, 8.6652343814632091e-03, -2.2303662865870645e-03, 2.2578893430555296e-03, 1.4121004619557782e-03, -1.2246474341539783e-03, 2.2578893430555296e-03, 6.6297648327747090e-04, 1.1885000811279875e-03, 8.2415022799262484e-04, 1.4121004619557782e-03, 1.1885000811279875e-03, 6.6695685275927760e-04, -1.0778073152356201e-02, 6.3694652581600224e-03, 1.6961032371909813e-03, -1.0948355265557148e-03, 6.3694652581600224e-03, -3.4260749027166322e-03, 7.2077630136983231e-04, 4.4084306708303315e-04, 1.6961032371909813e-03, 7.2077630136983231e-04, -8.9011520033262931e-04, 9.7699882816715137e-04, -1.0948355265557148e-03, 4.4084306708303315e-04, 9.7699882816715137e-04, -1.6721134424032506e-04, -1.5880217492160682e-02, 3.6937157026263458e-03, 4.3136575901598258e-03, -1.5179193912709036e-03, 3.6937157026263458e-03, -1.1384551327959617e-03, 1.6183111798056420e-03, 2.1843622767156786e-03, 4.3136575901598258e-03, 1.6183111798056420e-03, 3.6830220626843141e-05, 1.5392367332523269e-03, -1.5179193912709036e-03, 2.1843622767156786e-03, 1.5392367332523269e-03, 4.4276775065660106e-04, -1.6165406785950832e-02, 5.8699367964453637e-03, -1.2859790511595471e-03, -9.6075957427906315e-04, 5.8699367964453637e-03, -1.1179968213466582e-03, 1.7183301947139779e-03, 1.9085251825466231e-03, -1.2859790511595471e-03, 1.7183301947139779e-03, 4.8200250785465073e-05, 1.4857105304615032e-03, -9.6075957427906315e-04, 1.9085251825466231e-03, 1.4857105304615032e-03, 4.8795341166811253e-04, -1.0013561677409232e-02, 4.0961622741432311e-03, -1.1185134603428390e-03, -1.0574249764454893e-03, 4.0961622741432311e-03, -1.7667047214563205e-03, 4.4148465732737679e-04, 5.1292958257300033e-04, -1.1185134603428390e-03, 4.4148465732737679e-04, -3.2715402184443172e-04, 5.9087815524321886e-05, -1.0574249764454893e-03, 5.1292958257300033e-04, 5.9087815524321886e-05, -2.5498418232061302e-04, -1.5836685529134305e-02, 7.9890379919545819e-03, 2.0022017376080106e-03, -7.4049310281811551e-04, 7.9890379919545819e-03, -1.8638004635911647e-03, 1.6707614648490442e-03, 2.0576430038936316e-03, 2.0022017376080106e-03, 1.6707614648490442e-03, -3.8921469816428380e-04, 1.8660395072033363e-03, -7.4049310281811551e-04, 2.0576430038936316e-03, 1.8660395072033363e-03, 8.3166983679526074e-04, -1.5585823655705712e-02, 8.6547358140740775e-03, 6.4685805130901839e-04, 6.0437717832279090e-04, 8.6547358140740775e-03, -2.9179966185179798e-03, 1.6650090091244428e-03, 5.4772087545596447e-04, 6.4685805130901839e-04, 1.6650090091244428e-03, 1.0944173789827910e-04, 1.6193714005056402e-03, 6.0437717832279090e-04, 5.4772087545596447e-04, 1.6193714005056402e-03, 4.1435400171744153e-04, -1.2002918703686777e-02, 8.5494004897721486e-03, 2.6869376009802685e-03, 2.8041002192664066e-04, 8.5494004897721486e-03, -3.3375720513194084e-03, -6.4781613669761683e-05, 1.3524488057429884e-03, 2.6869376009802685e-03, -6.4781613669761683e-05, -1.0720086261486713e-04, 3.3638662164407361e-04, 2.8041002192664066e-04, 1.3524488057429884e-03, 3.3638662164407361e-04, 2.7204403023007185e-04, -1.5230508969721116e-02, 8.0033674082133788e-03, 4.5505572033502154e-03, 6.3142263681769913e-04, 8.0033674082133788e-03, -2.2473990191216016e-03, 9.4766049239732128e-04, 8.2712654749629762e-04, 4.5505572033502154e-03, 9.4766049239732128e-04, -8.7138227660763510e-04, 1.8741234298296900e-03, 6.3142263681769913e-04, 8.2712654749629762e-04, 1.8741234298296900e-03, 7.8238202994568230e-04, -1.4728673062431606e-02, 7.7272261248470383e-03, -6.5330828262795480e-04, -1.0320780914468404e-03, 7.7272261248470383e-03, -3.4770903165693631e-03, 1.7224962220012408e-03, 1.6016916176218841e-03, -6.5330828262795480e-04, 1.7224962220012408e-03, -1.0624168683721165e-05, 6.7411356195351047e-04, -1.0320780914468404e-03, 1.6016916176218841e-03, 6.7411356195351047e-04, 3.5793923973344256e-04, -1.5365215679984470e-02, 7.7326906220242997e-03, -8.7919884112348649e-04, 3.9232895421004452e-03, 7.7326906220242997e-03, -1.8951873402245514e-03, 1.6702212822668559e-03, 3.8698617614536183e-04, -8.7919884112348649e-04, 1.6702212822668559e-03, 1.4305624533357813e-04, 1.3483365102715016e-03, 3.9232895421004452e-03, 3.8698617614536183e-04, 1.3483365102715016e-03, 2.9887961813433576e-04, -1.5308665984215052e-02, 6.4035427214453088e-03, 3.2264588182182893e-03, -1.2172293673001523e-03, 6.4035427214453088e-03, -2.1803365653642855e-03, 8.1169763975199303e-04, 1.1516553650030236e-03, 3.2264588182182893e-03, 8.1169763975199303e-04, -8.9273426545469822e-04, 1.3149492890516085e-03, -1.2172293673001523e-03, 1.1516553650030236e-03, 1.3149492890516085e-03, 3.4916307447500070e-04, -1.4177990755768224e-02, 8.4436539885229638e-03, 1.9644514003780713e-03, -1.4051939351763857e-03, 8.4436539885229638e-03, -2.6365958058229824e-03, 2.7988734419438126e-04, 2.3791557288914468e-03, 1.9644514003780713e-03, 2.7988734419438126e-04, -9.3295814287705590e-06, 1.7397564635589064e-03, -1.4051939351763857e-03, 2.3791557288914468e-03, 1.7397564635589064e-03, 3.5990020010247216e-04, -1.4509970137912609e-02, 8.3321965664366754e-03, 1.9636760666178182e-04, 1.4472834003371236e-03, 8.3321965664366754e-03, -3.1644286858689462e-03, 1.3130696372032696e-03, 8.9901256003085657e-04, 1.9636760666178182e-04, 1.3130696372032696e-03, -1.1641434273373312e-04, 1.1005872558455100e-03, 1.4472834003371236e-03, 8.9901256003085657e-04, 1.1005872558455100e-03, 2.2352300907388465e-04, -1.4191547859266384e-02, 7.2008193102188825e-03, 3.7147795995076251e-03, -9.3769917155450059e-04, 7.2008193102188825e-03, -1.8544479396288801e-03, 6.8480624138753587e-04, 1.5662746452927681e-03, 3.7147795995076251e-03, 6.8480624138753587e-04, -6.5351803821489724e-04, 1.5327770952292775e-03, -9.3769917155450059e-04, 1.5662746452927681e-03, 1.5327770952292775e-03, 4.5630359182475774e-04, -1.4160940337820836e-02, 7.8821478615683557e-03, -9.8558148594463752e-04, 3.7918168562442430e-03, 7.8821478615683557e-03, -2.1790556607648682e-03, 2.0369416094564120e-03, 1.4304949644710783e-04, -9.8558148594463752e-04, 2.0369416094564120e-03, 5.0614978147559312e-04, 1.6610917581866963e-03, 3.7918168562442430e-03, 1.4304949644710783e-04, 1.6610917581866963e-03, 5.8794575109223548e-04, -9.2845894059316349e-03, 6.4939565529886596e-03, 1.4724840417364997e-03, -5.6610856876894574e-04, 6.4939565529886596e-03, -3.7833463506718019e-03, -4.7204290292012798e-04, 6.9241613661032045e-04, 1.4724840417364997e-03, -4.7204290292012798e-04, -8.1865701212015295e-04, 4.9894256490768405e-04, -5.6610856876894574e-04, 6.9241613661032045e-04, 4.9894256490768405e-04, -1.6611368267292618e-04, -1.6184598560687900e-02, 6.8439613963293747e-03, 1.7365578719469512e-03, 2.5532525388744873e-03, 6.8439613963293747e-03, -1.6997340703512266e-03, 8.1255506906787128e-04, 1.5422686239700562e-04, 1.7365578719469512e-03, 8.1255506906787128e-04, 3.3892711957371837e-04, 7.0860872528022347e-04, 2.5532525388744873e-03, 1.5422686239700562e-04, 7.0860872528022347e-04, 4.6427613812592781e-04, -1.2357824977244209e-02, 6.9688664588778747e-03, -1.0006387693880701e-03, 5.1736619278341814e-04, 6.9688664588778747e-03, -3.6183785683711568e-03, 8.2525839785551063e-04, 1.1173236493360916e-03, -1.0006387693880701e-03, 8.2525839785551063e-04, -1.0729556618775974e-04, 6.7298930398621047e-04, 5.1736619278341814e-04, 1.1173236493360916e-03, 6.7298930398621047e-04, -3.1875208496333698e-08, -1.4252206077143996e-02, 6.2144795121130665e-03, 5.2879460633076993e-03, -1.0233446873137595e-03, 6.2144795121130665e-03, -1.5331372596218611e-03, 2.2209966824697589e-04, 2.0306655712737896e-03, 5.2879460633076993e-03, 2.2209966824697589e-04, -1.0936295576075275e-03, 1.9226034908899328e-03, -1.0233446873137595e-03, 2.0306655712737896e-03, 1.9226034908899328e-03, 5.5839135772282368e-04, -1.3330238816035098e-02, 8.1905708096769846e-03, -7.6394721968447781e-04, -2.2394664524934586e-04, 8.1905708096769846e-03, -3.0167954985111760e-03, 1.9263001035794555e-03, 1.5435010884285658e-03, -7.6394721968447781e-04, 1.9263001035794555e-03, 4.3194129081048293e-04, 1.1144179655621770e-03, -2.2394664524934586e-04, 1.5435010884285658e-03, 1.1144179655621770e-03, 4.6419314960482224e-04, -9.3078896127232972e-03, 3.7808378683754900e-03, -8.2500659938291763e-04, -9.2253721497522736e-04, 3.7808378683754900e-03, -1.3735593478715945e-03, 2.6895103878370749e-04, 6.0702527043364398e-04, -8.2500659938291763e-04, 2.6895103878370749e-04, -3.5102799517677157e-04, 2.1534278727519608e-04, -9.2253721497522736e-04, 6.0702527043364398e-04, 2.1534278727519608e-04, -1.4676179836516589e-04, -1.7610358444604211e-02, 7.7354116237610682e-03, -4.3061286359086953e-04, 3.1429218095944938e-03, 7.7354116237610682e-03, -2.3939505233226636e-03, 1.4834692755721455e-03, 1.6181041921510780e-03, -4.3061286359086953e-04, 1.4834692755721455e-03, 8.0959866125124443e-05, 1.6836715825834035e-03, 3.1429218095944938e-03, 1.6181041921510780e-03, 1.6836715825834035e-03, 4.4455549963187051e-04, -1.1327541090185321e-02, 8.0271674256418221e-03, -1.0748118107689127e-03, 1.0220978517679886e-03, 8.0271674256418221e-03, -3.7625982726974937e-03, 1.4015004959102217e-03, 2.9941859123995497e-04, -1.0748118107689127e-03, 1.4015004959102217e-03, -4.7414240813170117e-04, 1.8984462650148025e-04, 1.0220978517679886e-03, 2.9941859123995497e-04, 1.8984462650148025e-04, 1.9168610525451880e-05, -7.7599972015153637e-03, 5.0487328889732026e-03, -1.0150335177867968e-03, -1.0787256637662084e-03, 5.0487328889732026e-03, -3.8283334708152520e-03, 4.8095191605469594e-04, 1.0961988375689778e-03, -1.0150335177867968e-03, 4.8095191605469594e-04, -3.5006941784132919e-04, 2.5142046401834721e-04, -1.0787256637662084e-03, 1.0961988375689778e-03, 2.5142046401834721e-04, -3.4601993607214204e-04, -1.6722037962919063e-02, 8.9023027147699402e-03, 2.8475764924646074e-03, -1.3668464681863240e-03, 8.9023027147699402e-03, -3.0464816895161466e-03, 5.5483638104875219e-04, 2.5866682413927889e-03, 2.8475764924646074e-03, 5.5483638104875219e-04, 3.2244817585743671e-04, 1.7335129486130759e-03, -1.3668464681863240e-03, 2.5866682413927889e-03, 1.7335129486130759e-03, 6.7373810044801786e-04, -1.3043290920668447e-02, 7.5390776005711682e-03, -1.4967658915720180e-03, -1.1709919402049843e-03, 7.5390776005711682e-03, -2.3042283980880748e-03, 1.8948249232033040e-03, 1.4633606749955709e-03, -1.4967658915720180e-03, 1.8948249232033040e-03, -1.1525903461801575e-04, 8.6522916210846480e-04, -1.1709919402049843e-03, 1.4633606749955709e-03, 8.6522916210846480e-04, -3.7584188113171969e-06, -1.5585713708593366e-02, 8.6119952233053958e-03, 2.0853199702149908e-04, -3.6538641086258830e-04, 8.6119952233053958e-03, -2.0708945444859184e-03, 2.0916162538495752e-03, 1.6963122585672433e-03, 2.0853199702149908e-04, 2.0916162538495752e-03, 8.4673232959246606e-04, 1.7050167169310555e-03, -3.6538641086258830e-04, 1.6963122585672433e-03, 1.7050167169310555e-03, 9.7321838069529471e-04, -8.5883608444249915e-03, 5.1230145745255022e-03, 8.9159902137280552e-04, 1.2313522195822745e-03, 5.1230145745255022e-03, -3.2070396434171729e-03, -4.0842243388821992e-04, -5.0228366774250242e-04, 8.9159902137280552e-04, -4.0842243388821992e-04, -1.6659900154853617e-04, 1.6703129248579686e-04, 1.2313522195822745e-03, -5.0228366774250242e-04, 1.6703129248579686e-04, -1.3910653088204271e-04, -1.7177487424496159e-02, 8.6439434323196465e-03, -1.2676542069329643e-03, 1.5530920065632684e-03, 8.6439434323196465e-03, -3.0711886933060752e-03, 2.6503690364040600e-03, 1.5823408628991603e-03, -1.2676542069329643e-03, 2.6503690364040600e-03, 2.0205318440554335e-05, 9.7190007034572039e-04, 1.5530920065632684e-03, 1.5823408628991603e-03, 9.7190007034572039e-04, 6.3167332201993298e-04, -1.8198829655079997e-02, 6.1783746997468043e-03, 2.7097557207645976e-03, 1.0674413677482549e-04, 6.1783746997468043e-03, -1.5255143488708853e-03, 4.2446808059708652e-04, 2.0056066309057513e-03, 2.7097557207645976e-03, 4.2446808059708652e-04, -7.0722767103247085e-05, 2.1439352866911227e-03, 1.0674413677482549e-04, 2.0056066309057513e-03, 2.1439352866911227e-03, 4.9248673587442768e-04, -1.2899230683789387e-02, 3.8809561952671224e-03, 5.8590703877280138e-03, 7.0773016587898281e-04, 3.8809561952671224e-03, -1.7333578324938207e-03, 1.5513687237511255e-03, 7.3324212478700988e-04, 5.8590703877280138e-03, 1.5513687237511255e-03, -1.6842888582058607e-03, 2.0427657447758315e-03, 7.0773016587898281e-04, 7.3324212478700988e-04, 2.0427657447758315e-03, -3.9900791227274096e-04, -1.3724818137332397e-02, 4.4851048250000233e-03, -1.3002218055153201e-03, -1.2182844898688022e-03, 4.4851048250000233e-03, -1.8202270259962889e-03, 1.9639130830478775e-03, 7.5413518528937972e-04, -1.3002218055153201e-03, 1.9639130830478775e-03, 3.1870016559458009e-04, 1.0685796268949520e-03, -1.2182844898688022e-03, 7.5413518528937972e-04, 1.0685796268949520e-03, 4.8322321243228277e-04, -1.2727886752614260e-02, 7.6627520867563600e-03, -1.0977617387408160e-03, -9.2741573884433924e-05, 7.6627520867563600e-03, -3.0923177506530018e-03, 5.2725763824365925e-04, 1.3750576763160460e-03, -1.0977617387408160e-03, 5.2725763824365925e-04, -3.8624246299677348e-04, 9.9356026784475603e-04, -9.2741573884433924e-05, 1.3750576763160460e-03, 9.9356026784475603e-04, -3.6185112057586651e-04, -1.4230368421296529e-02, 6.4613018087884382e-03, 4.2545431560881697e-03, 2.3312567462464729e-03, 6.4613018087884382e-03, -1.5885108206906821e-03, 3.4554820247452228e-04, 1.8419118470444126e-03, 4.2545431560881697e-03, 3.4554820247452228e-04, -2.8748861071839916e-04, 9.0566105830775748e-04, 2.3312567462464729e-03, 1.8419118470444126e-03, 9.0566105830775748e-04, 2.7039215950598432e-04, -1.2948564663328069e-02, 8.4157282283072385e-03, 5.0620127290202754e-04, -1.3314748626910642e-03, 8.4157282283072385e-03, -3.5176641019498255e-03, 1.5307289251449395e-03, 1.8525663157440806e-03, 5.0620127290202754e-04, 1.5307289251449395e-03, 5.6978788798275784e-05, 1.2935940913392739e-03, -1.3314748626910642e-03, 1.8525663157440806e-03, 1.2935940913392739e-03, 5.2341304653500667e-04, -1.2728855074017827e-02, 7.6246707479808553e-03, -9.3224478600061457e-04, -1.5620738423386569e-03, 7.6246707479808553e-03, -1.9782856856559788e-03, 1.4103550247563932e-03, 2.0801314899663139e-03, -9.3224478600061457e-04, 1.4103550247563932e-03, 4.6054846597137436e-04, 1.2330604509034212e-03, -1.5620738423386569e-03, 2.0801314899663139e-03, 1.2330604509034212e-03, 7.7627861087214880e-04, -1.7487269493133485e-02, 8.1945615648457912e-03, -1.3867371780327564e-03, -7.7203633752733088e-04, 8.1945615648457912e-03, -2.6839597442567947e-03, 2.3107751960829994e-03, 1.6061326831885306e-03, -1.3867371780327564e-03, 2.3107751960829994e-03, 6.8401777560147333e-04, 1.1030118862660431e-03, -7.7203633752733088e-04, 1.6061326831885306e-03, 1.1030118862660431e-03, 7.3617364729802910e-04, -1.3077682176402587e-02, 6.5573215542587115e-03, 8.9992300772369772e-04, 1.8631593971488946e-03, 6.5573215542587115e-03, -1.7553672105479552e-03, 1.4263355120099006e-03, -6.6131788423017066e-05, 8.9992300772369772e-04, 1.4263355120099006e-03, -1.5795558529122932e-04, 9.3709040689480281e-04, 1.8631593971488946e-03, -6.6131788423017066e-05, 9.3709040689480281e-04, 2.7558380426612552e-04, -1.2016216250619665e-02, 1.0198974276762779e-03, 2.9684095074686417e-03, 4.7574388367168719e-04, 1.0198974276762779e-03, -1.0457484751015638e-03, 1.4462132483450711e-03, 1.4655862021442318e-03, 2.9684095074686417e-03, 1.4462132483450711e-03, -1.1640147094591868e-04, 4.5714086841669362e-04, 4.7574388367168719e-04, 1.4655862021442318e-03, 4.5714086841669362e-04, 3.7349208876016085e-04, -1.2729011740008157e-02, 6.2751467801390199e-03, 2.7374611452219005e-03, -4.8769041908477083e-04, 6.2751467801390199e-03, -2.5912066920437101e-03, -3.6502488298821795e-04, 1.2747675934628886e-03, 2.7374611452219005e-03, -3.6502488298821795e-04, -1.3461493454670274e-04, 5.1297874213225157e-04, -4.8769041908477083e-04, 1.2747675934628886e-03, 5.1297874213225157e-04, 9.4116610547478451e-05, -1.6566854215408953e-02, 7.5656107280793628e-03, 2.2151664856272313e-03, -6.2355508020778901e-04, 7.5656107280793628e-03, -1.6767633757818533e-03, 7.2857964830252552e-04, 2.0018839786305805e-03, 2.2151664856272313e-03, 7.2857964830252552e-04, 7.8168565633001156e-04, 1.2563330405130704e-03, -6.2355508020778901e-04, 2.0018839786305805e-03, 1.2563330405130704e-03, 9.4171843217346913e-04, -1.3827220555148755e-02, 8.0412338777692237e-03, -9.2914695167431839e-04, 2.5139109709975873e-03, 8.0412338777692237e-03, -2.8902523593109979e-03, 1.7337506184933089e-03, -2.4479541495431615e-04, -9.2914695167431839e-04, 1.7337506184933089e-03, -6.2590822563885222e-04, 3.3033744058861008e-04, 2.5139109709975873e-03, -2.4479541495431615e-04, 3.3033744058861008e-04, 1.1867575245440103e-04, -1.1673022270160038e-02, 6.3818705599438547e-03, 1.5529035996520919e-03, 1.9023499244602438e-03, 6.3818705599438547e-03, -2.7838066962823493e-03, -3.4731692421037671e-04, -5.1143360268925109e-04, 1.5529035996520919e-03, -3.4731692421037671e-04, -2.9269524595909200e-04, 6.5189571167041226e-04, 1.9023499244602438e-03, -5.1143360268925109e-04, 6.5189571167041226e-04, -1.8842002149771150e-04, -1.7624872761522331e-02, 7.6102659079345675e-03, 2.9313602118366056e-03, -1.2670120134682693e-03, 7.6102659079345675e-03, -1.6176859045345274e-03, 5.2235785563708548e-04, 2.1125237303853172e-03, 2.9313602118366056e-03, 5.2235785563708548e-04, -6.9336521692627494e-05, 6.2961316498085299e-04, -1.2670120134682693e-03, 2.1125237303853172e-03, 6.2961316498085299e-04, 2.8049822298363992e-04, -1.1443728662686095e-02, 7.1090692233263322e-03, -1.0267468148229923e-03, -9.7148107774755140e-04, 7.1090692233263322e-03, -3.5986082376354236e-03, 1.0764295384459879e-03, 1.4077630540134337e-03, -1.0267468148229923e-03, 1.0764295384459879e-03, -1.4573111804904847e-04, 5.4872481291025343e-04, -9.7148107774755140e-04, 1.4077630540134337e-03, 5.4872481291025343e-04, -4.9397324463356642e-05, -1.4863131313394926e-02, 6.3276765757360525e-03, 4.1762520161465356e-03, 4.6274316270408979e-03, 6.3276765757360525e-03, -1.5123375588949818e-03, 1.5393235939542785e-03, 1.7038958618260523e-04, 4.1762520161465356e-03, 1.5393235939542785e-03, -3.8236797488742761e-04, 4.2645241632206950e-04, 4.6274316270408979e-03, 1.7038958618260523e-04, 4.2645241632206950e-04, -2.8027135821990324e-04, -9.2269149280535777e-03, 6.1800015820290153e-03, -1.1177505002325030e-03, -7.3655389049955076e-04, 6.1800015820290153e-03, -3.1949632173689504e-03, 9.2780949436812210e-04, 5.4061711672642406e-04, -1.1177505002325030e-03, 9.2780949436812210e-04, -3.6714991602973209e-04, 4.4326007298994439e-04, -7.3655389049955076e-04, 5.4061711672642406e-04, 4.4326007298994439e-04, -2.6509650841867525e-04, -1.8043017394515380e-02, 8.2372721119834628e-03, -1.3666634730605748e-03, 2.5937673526110440e-03, 8.2372721119834628e-03, -2.2765382593275749e-03, 1.8098836907605163e-03, 6.2774788742545431e-04, -1.3666634730605748e-03, 1.8098836907605163e-03, 6.3750682573144961e-04, 1.5016614599137833e-03, 2.5937673526110440e-03, 6.2774788742545431e-04, 1.5016614599137833e-03, 7.9656988901641562e-04, -1.7677863320201812e-02, 7.9918211430818238e-03, 2.8809808774680002e-03, 3.3219012282079618e-04, 7.9918211430818238e-03, -2.5093243675945506e-03, 1.6427685072652385e-03, 1.3849057522712679e-03, 2.8809808774680002e-03, 1.6427685072652385e-03, -6.6341536325757559e-04, 1.6383230989587684e-03, 3.3219012282079618e-04, 1.3849057522712679e-03, 1.6383230989587684e-03, -5.4016870687916257e-04, -1.2521451784673619e-02, 9.4223977497539906e-03, 2.8055309806167853e-03, 1.7872667456776705e-03, 9.4223977497539906e-03, -3.2829775106665285e-03, -1.4133846391320361e-05, 3.4392328922804288e-04, 2.8055309806167853e-03, -1.4133846391320361e-05, 6.9100703062136178e-05, 1.4271286897663935e-03, 1.7872667456776705e-03, 3.4392328922804288e-04, 1.4271286897663935e-03, 5.3937434576994730e-04, -1.3632536222978362e-02, 1.0257748639522706e-02, 1.4462286898582958e-03, 1.1312437976068149e-04, 1.0257748639522706e-02, -3.6576998284675250e-03, 1.6043422861050061e-03, 1.7092618248746980e-03, 1.4462286898582958e-03, 1.6043422861050061e-03, 1.6618519504767670e-04, 9.6235513287789881e-04, 1.1312437976068149e-04, 1.7092618248746980e-03, 9.6235513287789881e-04, 3.5802719238072031e-04, -9.2473425953727914e-03, 5.4646516574637042e-03, -9.5962421166676426e-04, -7.8247003179074371e-04, 5.4646516574637042e-03, -2.7506657954682067e-03, 5.9386441787538592e-04, 5.4897582411844530e-04, -9.5962421166676426e-04, 5.9386441787538592e-04, -3.0196113116772263e-04, 1.8573011529360198e-04, -7.8247003179074371e-04, 5.4897582411844530e-04, 1.8573011529360198e-04, -2.1319293163917321e-04, -1.2206117329009541e-02, 6.1092810650537708e-03, -1.4481345745921150e-03, -2.9722362809001516e-04, 6.1092810650537708e-03, -1.9126144266505905e-03, 1.7554634731526244e-03, 5.5972781767352870e-04, -1.4481345745921150e-03, 1.7554634731526244e-03, 8.6367120618161951e-05, 6.7487884435146381e-04, -2.9722362809001516e-04, 5.5972781767352870e-04, 6.7487884435146381e-04, 2.3556476013967504e-04, -1.5414965310465042e-02, 7.3079303890448630e-03, -6.8902344013111860e-05, -1.3856544124642479e-03, 7.3079303890448630e-03, -2.3520366982052667e-03, 2.2813099826687437e-03, 1.1422089660014121e-03, -6.8902344013111860e-05, 2.2813099826687437e-03, -5.6644557392149263e-05, 1.1994499087383925e-03, -1.3856544124642479e-03, 1.1422089660014121e-03, 1.1994499087383925e-03, 3.4944002873506328e-04, -1.1752221672219454e-02, 6.9456319878067867e-03, -1.5705985744796634e-03, -1.2694046046713740e-03, 6.9456319878067867e-03, -2.1753723772528176e-03, 1.5503236118177893e-03, 2.0219529729626610e-03, -1.5705985744796634e-03, 1.5503236118177893e-03, 2.4923829829079700e-04, 8.6809285519335883e-04, -1.2694046046713740e-03, 2.0219529729626610e-03, 8.6809285519335883e-04, 3.2743478195986473e-04, -1.4690777928137583e-02, 9.3382417975744429e-03, -8.7839174936228269e-04, -1.0828156619940759e-03, 9.3382417975744429e-03, -3.6232340991021171e-03, 1.5142500530220151e-03, 1.4677066708721873e-03, -8.7839174936228269e-04, 1.5142500530220151e-03, 4.0376228484767084e-06, 1.0556394847561596e-03, -1.0828156619940759e-03, 1.4677066708721873e-03, 1.0556394847561596e-03, 3.6668747259171032e-04, -1.5476441143750821e-02, 9.2269293204535301e-03, 3.6995872897747060e-04, -7.7034041709245649e-04, 9.2269293204535301e-03, -4.4645128477425761e-03, 2.1128383054205796e-03, 2.4504815100855485e-03, 3.6995872897747060e-04, 2.1128383054205796e-03, 2.2586802223079191e-04, 1.8926509467373552e-03, -7.7034041709245649e-04, 2.4504815100855485e-03, 1.8926509467373552e-03, 8.5536596431988856e-04, -1.5609938715099692e-02, 8.9557198628165934e-03, -1.3057139142808542e-03, -8.7481229959170993e-06, 8.9557198628165934e-03, -3.2036744864203165e-03, 1.8940821063869206e-03, 1.8774348378665215e-03, -1.3057139142808542e-03, 1.8940821063869206e-03, 7.2606869807052658e-04, 1.3119329296710162e-03, -8.7481229959170993e-06, 1.8774348378665215e-03, 1.3119329296710162e-03, 1.0619415404491685e-03, -1.5481617424794581e-02, 7.3302600110515892e-03, 6.5644710521001073e-04, -1.2283650081392702e-03, 7.3302600110515892e-03, -1.8696748976957526e-03, 5.9596303915765750e-04, 1.2499531031902422e-03, 6.5644710521001073e-04, 5.9596303915765750e-04, 9.7138240642967119e-05, 8.8503568194063412e-04, -1.2283650081392702e-03, 1.2499531031902422e-03, 8.8503568194063412e-04, 2.5511830692021193e-04, -1.3520103229756882e-02, 7.8484109792015359e-03, -4.1632387603416781e-04, -1.1558280730735919e-03, 7.8484109792015359e-03, -2.0728512914414475e-03, 1.9517286698148926e-03, 2.0216583965520000e-03, -4.1632387603416781e-04, 1.9517286698148926e-03, 4.6063232535377560e-04, 1.5362873491442208e-03, -1.1558280730735919e-03, 2.0216583965520000e-03, 1.5362873491442208e-03, 5.2457975844262875e-04, -1.5104116485397874e-02, 8.5473834321937604e-03, -1.3043437235430149e-03, 1.5276622030354890e-03, 8.5473834321937604e-03, -3.3780289659805359e-03, 1.9847858497536674e-03, 5.0757574599316559e-04, -1.3043437235430149e-03, 1.9847858497536674e-03, -1.5945405766001441e-04, 1.5678019969852818e-03, 1.5276622030354890e-03, 5.0757574599316559e-04, 1.5678019969852818e-03, 2.4763257841851471e-04, -1.6361908776597107e-02, 4.3568452185025310e-03, 3.4563556520869625e-03, 4.3658362914368865e-03, 4.3568452185025310e-03, -8.6002608849411477e-04, 1.1935067562380381e-03, 9.5763305550593627e-05, 3.4563556520869625e-03, 1.1935067562380381e-03, -6.5655114602498324e-04, 2.8551486975809026e-04, 4.3658362914368865e-03, 9.5763305550593627e-05, 2.8551486975809026e-04, -5.2757450862172910e-04, -1.7807729170147439e-02, 7.1610610051428998e-03, -1.1911025226577043e-03, 2.6056388311463353e-03, 7.1610610051428998e-03, -2.1885651750746023e-03, 2.9274154172681229e-03, 8.3377402546824613e-04, -1.1911025226577043e-03, 2.9274154172681229e-03, 1.7570402880203167e-04, 1.4504468369839752e-03, 2.6056388311463353e-03, 8.3377402546824613e-04, 1.4504468369839752e-03, 6.9093609359350367e-04, -1.1247931682705574e-02, 5.7455202196775934e-03, 1.1174148432837694e-04, -1.0001185298297672e-03, 5.7455202196775934e-03, -2.1567917357754388e-03, 6.2799688175952328e-04, 5.6474713215460648e-04, 1.1174148432837694e-04, 6.2799688175952328e-04, -1.1840763525279758e-04, 5.3189815096175985e-04, -1.0001185298297672e-03, 5.6474713215460648e-04, 5.3189815096175985e-04, -8.5340380560854686e-05, -1.0115751598013022e-02, 7.1450423354329218e-03, -9.6769024323713497e-04, 6.0983843256720333e-04, 7.1450423354329218e-03, -2.6969102530045707e-03, 1.0319270924417643e-03, 2.3496098746511552e-04, -9.6769024323713497e-04, 1.0319270924417643e-03, -8.0513400430597755e-04, 4.7088258363160609e-04, 6.0983843256720333e-04, 2.3496098746511552e-04, 4.7088258363160609e-04, -1.4457178691538466e-04 + }; + + std::vector dy = { + -3.7309172874861328e-03, 1.3333653131861634e-03, 8.8577244948839816e-04, 4.4771776498148510e-04, -3.2036744215949474e-03, 1.5721737770764861e-03, 7.5774810984830811e-04, -7.6103439612831651e-06, -1.5642091060224157e-03, 2.0953017056858877e-03, 1.8124937780487031e-04, -1.0725465141096370e-03, -2.2923883203997790e-03, 1.8300760226060355e-03, 4.8940452841059862e-04, -6.7210654905128198e-04, -4.3341964593999359e-03, 1.4253156363736956e-03, 9.8923131293439642e-04, 1.2115998085801848e-03, -2.6188268847725871e-03, 1.7119021466805964e-03, 5.6117501695963988e-04, -4.2324237308625010e-04, -2.2900131399922951e-03, 1.8386032061780638e-03, 5.2171200216442610e-04, -6.4883325259721075e-04, -2.5812803651831646e-03, 1.7293876231322676e-03, 6.0622938832230022e-04, -4.3579477573257375e-04, -2.8530249847228155e-03, 1.6808033517384546e-03, 6.6042307748444143e-04, -2.8824157968878450e-04, -3.7393733570642322e-03, 1.3716770835582519e-03, 8.7227862962462044e-04, 4.5872483076204578e-04, -2.4437272319513359e-03, 1.7400758488808985e-03, 5.1182653748711650e-04, -5.8105073770823191e-04, -3.2810369412823396e-03, 1.4918691558186718e-03, 7.4724281784686224e-04, 6.3875403362581882e-05, -3.4247726827394570e-03, 1.4560852013918019e-03, 7.6169488667705810e-04, 1.5409667480043344e-04, -3.7529573893496722e-03, 1.3819905220282573e-03, 8.6645778079868173e-04, 4.5529019815005340e-04, -2.7761885434245126e-03, 1.6221283633832189e-03, 6.4100239162366926e-04, -2.8385959725213443e-04, -2.8619525975001991e-03, 1.6742414664644566e-03, 6.5987011922647798e-04, -2.5588583656454151e-04, -3.5782213588599009e-03, 1.4067925694494811e-03, 8.1801204214687574e-04, 2.9417370593075526e-04, -3.2179311027623852e-03, 1.5351083071313909e-03, 7.6347536572857384e-04, -1.2101519294540594e-05, -3.5358134475080953e-03, 1.4165640362083939e-03, 8.2941835466963714e-04, 2.5395670515060458e-04, -2.3142024782931113e-03, 1.8181293172609410e-03, 5.4151803735599758e-04, -6.0053505167333827e-04, -4.0744831658946472e-03, 1.3071202590006017e-03, 9.9633651691117707e-04, 8.1295718049464744e-04, -3.8475776506513007e-03, 1.2962018740413476e-03, 9.3330073985875681e-04, 5.8452572124980187e-04, -1.9683320699194757e-03, 1.8795410802464392e-03, 3.5931418446205485e-04, -7.9622117146730320e-04, -4.2836432069418432e-03, 1.3057358949544792e-03, 1.0478903069398337e-03, 1.1388072821866995e-03, -3.6481289572086509e-03, 1.2680834323314254e-03, 9.1419213899072288e-04, 4.1250969611241915e-04, -3.7868589170986242e-03, 1.4248346144113545e-03, 8.6769642282752434e-04, 4.8009987220807757e-04, -2.8109243740096651e-03, 1.6305799665922716e-03, 6.4582051914641788e-04, -2.7233613703581204e-04, -3.8714234700849403e-03, 1.4365872229829926e-03, 7.9039059028440748e-04, 5.8082810834859922e-04, -3.2390351248452994e-03, 1.5083636177925740e-03, 7.0100191142906398e-04, 2.1276162385565475e-05, -4.0758464441795483e-03, 1.3467789743563711e-03, 9.0375839572505727e-04, 8.1824793954867955e-04, -4.2307140951998419e-03, 1.3156828039467543e-03, 1.0778378449497364e-03, 9.7419386839855905e-04, -3.8482460366827680e-03, 1.3425192918780234e-03, 8.4666567817293223e-04, 6.0686241880002418e-04, -3.5314219963828199e-03, 1.4381299141681125e-03, 7.9553487485571899e-04, 2.6599193472912579e-04, -3.0625963328189260e-03, 1.5598565683800933e-03, 7.2517085286940468e-04, -1.1417249492739269e-04, -3.2401979571803855e-03, 1.5423006052675326e-03, 7.2952676962876912e-04, -8.5276110538955337e-06, -2.3676304952203239e-03, 1.7913062056963405e-03, 5.4550664708489952e-04, -5.8142337266284694e-04, -3.3562413373964786e-03, 1.4439022806622475e-03, 7.3437787621236539e-04, 1.1756361872177236e-04, -4.3718193202676135e-03, 1.3310441122746876e-03, 1.1562039243607975e-03, 1.1472230349748561e-03, -3.3313992651614545e-03, 1.4915589370723611e-03, 7.8084944429722110e-04, 9.1372270882632316e-05, -4.3740557131606459e-03, 1.3268717399460552e-03, 1.1579324109626387e-03, 1.1521185202595687e-03, -3.7927167356510412e-03, 1.3231482293682613e-03, 8.4755885080500565e-04, 5.4665126459383327e-04, -3.5935568152285231e-03, 1.4290305096829330e-03, 7.8606733253349130e-04, 3.1315221994287850e-04, -3.1614896010873366e-03, 1.5105982619965496e-03, 7.0021276781268450e-04, -2.3983710609199078e-05, -3.0147657599133232e-03, 1.5495419519657451e-03, 6.6899185166963951e-04, -1.2057880405017928e-04, -2.9963762079088952e-03, 1.6034868108259063e-03, 6.3607675253841956e-04, -1.8417206074768401e-04, -2.7426643259774895e-03, 1.6157336038172625e-03, 6.1401235615143987e-04, -3.3306000572104970e-04, -2.4766813065329693e-03, 1.7832827610314490e-03, 5.7063483799111604e-04, -5.1939389006707012e-04, -2.7058508682563827e-03, 1.6218793335994073e-03, 5.7753543630265211e-04, -3.4623185385953561e-04, -3.6714826224726309e-03, 1.4597702029387118e-03, 8.5442239573998893e-04, 3.7440232615804407e-04, -2.5070783356414134e-03, 1.7472384470121067e-03, 5.6462790247820919e-04, -4.6769781044539899e-04, -3.4387661142797959e-03, 1.4535061065990102e-03, 7.7895608021457275e-04, 2.0354737904839946e-04, -2.7050922014808390e-03, 1.6959138589712618e-03, 6.1077224833082030e-04, -3.8335512759285180e-04, -2.5394784289842513e-03, 1.7326482568320832e-03, 5.8030789834628870e-04, -4.9658497358456876e-04, -3.7139080629854208e-03, 1.3083146125837427e-03, 8.8709895889324357e-04, 4.3181952475237497e-04, -3.0902912715277077e-03, 1.5458493373768866e-03, 6.8330337138244770e-04, -7.7274160748078970e-05, -3.6486457748529568e-03, 1.4380340692779169e-03, 8.0537862363315113e-04, 3.6166524605696793e-04, -3.8259014218855834e-03, 1.3935398904790382e-03, 8.5063806525072705e-04, 5.0369999227149601e-04, -3.3219004182506043e-03, 1.5169900953600937e-03, 7.2943565247969642e-04, 1.2454041603774481e-04, -2.9710886292812757e-03, 1.6122308537815642e-03, 6.9407008210209398e-04, -1.8550200210251709e-04, -3.5823989245151611e-03, 1.3803897342254585e-03, 7.9659655171532566e-04, 2.9806889205486018e-04, -2.6709594617153878e-03, 1.7132749589768494e-03, 6.1556197573809268e-04, -3.9142476838036435e-04, -2.9481681479607618e-03, 1.5766328367677427e-03, 6.6232144637444707e-04, -1.4989734301076280e-04, -3.2491817681866895e-03, 1.5341764317035110e-03, 7.6268657981340220e-04, 2.4406488167046862e-05, -3.2248794286049952e-03, 1.4615786669513607e-03, 7.3069596608385794e-04, 5.0300823976094416e-05, 1.2628166438797038e-03, -8.9274753600505690e-04, -4.9076860067750170e-04, -4.1869309125592256e-04, 1.3715102644465138e-03, -1.0141291898274978e-03, -7.8766533967201816e-04, -3.9772035849276288e-04, 1.1612302578037791e-03, -4.3053803181958750e-04, -2.0146672160117711e-04, -7.4016307381392259e-04, 1.4537946412679723e-03, -9.5065863784825301e-04, -1.0343570332467453e-03, -3.8768498653108665e-04, 1.3854917882825165e-03, -1.0295014786804450e-03, -9.6373001786993968e-04, -3.0535774540021835e-04, 1.4511572929488627e-03, -9.2318578761469057e-04, -1.0422547691855640e-03, -4.2723072416751189e-04, 1.4452773695581647e-03, -9.7728273043188804e-04, -1.0741338772551449e-03, -3.0937363161101300e-04, 1.5259064198728280e-03, -8.8493370962583592e-04, -1.2007486502659927e-03, -3.1227283339035905e-04, 1.5436700653155932e-03, -8.7589313303263426e-04, -1.3280253258668860e-03, -1.8105115666848938e-04, 1.5404932421320258e-03, -8.5758359271832579e-04, -1.4832458365412898e-03, 2.6407770476321191e-06, 1.4155612513337844e-03, -1.0046531590963029e-03, -9.6842134578424706e-04, -3.5401027400996205e-04, 1.3400184836343154e-03, -1.0256045874409653e-03, -6.5169805282558834e-04, -4.2701606068519234e-04, 1.2857877730087135e-03, -9.0508762673802764e-04, -5.1943388839087771e-04, -4.6352002092798518e-04, 1.5051360776270669e-03, -8.9594973727753411e-04, -1.1527368214360100e-03, -3.3755565066477403e-04, 1.4546640992893533e-03, -9.5988516608341162e-04, -9.1623755155505670e-04, -4.8588890598821486e-04, 1.4016196272903870e-03, -1.0178182975371106e-03, -9.9041538684325634e-04, -3.1234386991993196e-04, 1.4055482177189956e-03, -1.0213236463436626e-03, -9.7796586357687832e-04, -3.0205558859203868e-04, 1.4242664925470684e-03, -9.8289044930037375e-04, -9.1476699353148475e-04, -4.3593144707404561e-04, 1.4563487652851604e-03, -9.5674318018110129e-04, -1.0204295900713581e-03, -3.8614169684175954e-04, 1.3340538380055412e-03, -9.8166445704493547e-04, -6.4235456808529520e-04, -4.6185514700680652e-04, 1.3251633800902419e-03, -9.7699364526281410e-04, -6.0020814973974911e-04, -4.4027417584270871e-04, 1.3352824913690901e-03, -9.3227658428344747e-04, -6.1244190731189806e-04, -4.9720054122240971e-04, 1.2307343717364581e-03, -8.8718012579170583e-04, -3.7907952728977293e-04, -4.7149395834188992e-04, 1.2872312473441560e-03, -1.0074761274442257e-03, -6.9178075288798071e-04, -2.7705625448618486e-04, 1.2927150624045740e-03, -9.7803534445332430e-04, -4.8720068931597341e-04, -4.6004378064328215e-04, 1.4257607447382041e-03, -9.2923784202873067e-04, -9.6755887729674294e-04, -4.6242569285732927e-04, 1.3914989006502964e-03, -1.0200176224497951e-03, -9.2458206518511248e-04, -3.4506509486466449e-04, 1.4333760204447944e-03, -9.1097262948909249e-04, -1.2188972198513615e-03, -2.5996549081574896e-04, 1.2461083659099338e-03, -8.2861754923992321e-04, -4.1605933810697306e-04, -4.8905777062213857e-04, 1.3768615064283817e-03, -9.9713742062896974e-04, -7.9858980387651933e-04, -4.3504985424060790e-04, 1.3291889767625101e-03, -9.9040477132564600e-04, -6.5776027239921122e-04, -3.9846418355279120e-04, 1.5206075965447940e-03, -9.2006057004575127e-04, -1.2474234877704595e-03, -2.3140418373219520e-04, 1.2887820380652511e-03, -9.3080226682111587e-04, -5.2786640162325342e-04, -4.4118156984124777e-04, 1.3907544080002891e-03, -9.6351013629205012e-04, -7.8303340377082974e-04, -4.7622449399401109e-04, 1.4091048902409534e-03, -9.7169766598279418e-04, -9.3316898961782319e-04, -4.0724649531488546e-04, 1.3879165258355152e-03, -1.0811464187553267e-03, -8.8168502399533827e-04, -3.0373866499100119e-04, 1.2870954343195211e-03, -8.0890289393696527e-04, -5.3267861480527198e-04, -4.9356122412130170e-04, 1.5429774711287242e-03, -8.5866927657185196e-04, -1.3306313542314914e-03, -1.7873354099492435e-04, 1.4021085501997211e-03, -1.0221324371805936e-03, -9.2699019936940438e-04, -3.5288337262228114e-04, 1.3239521433104891e-03, -1.0047418588945290e-03, -8.3997109532851777e-04, -2.7449751294088233e-04, 1.3868837677795806e-03, -1.0276648847350152e-03, -7.1775681753454717e-04, -4.5782917392179828e-04, 1.5808700010297582e-03, -8.3038113202047045e-04, -1.6908149849389628e-03, 2.5410913367872757e-04, 1.4647947565545553e-03, -9.5678426470656116e-04, -9.6423665069701801e-04, -4.5175848785730601e-04, 1.3804322148250373e-03, -9.9700774526987799e-04, -8.1337331990322353e-04, -4.0374879312031649e-04, 1.2800075192210976e-03, -9.2649222387085459e-04, -5.2976884029422029e-04, -4.2936605007220911e-04, 1.3810570455247023e-03, -1.0722378487912155e-03, -7.7520984586550872e-04, -3.6667718738961482e-04, 1.4037862914692491e-03, -9.9147719341820565e-04, -9.5712521087853390e-04, -3.7439255803680066e-04, 1.3299618332732993e-03, -9.9744087373244928e-04, -6.3658280665772381e-04, -4.0128206925905106e-04, 1.5066576047840761e-03, -8.7658152159021132e-04, -1.2789382944763108e-03, -2.3242884748562141e-04, 1.3071504645501193e-03, -8.8996140075365670e-04, -4.9614457597987795e-04, -5.5075072441456200e-04, 1.4159595821463559e-03, -9.9120400447633844e-04, -8.8041573992293201e-04, -4.2244235314895873e-04, 1.2554376918387320e-03, -7.9168866122310321e-04, -4.5152569311177868e-04, -5.2227602995275849e-04, 1.5629805055743966e-03, -8.5058550567704435e-04, -1.3544920149855563e-03, -1.5043929062635852e-04, 1.2912095035131571e-03, -9.1748375464204240e-04, -5.3427771054738980e-04, -4.4418198090508067e-04, 1.4057467167300063e-03, -9.6192143297912003e-04, -7.2872273247311125e-04, -5.4389115598480571e-04, 1.5409804354399271e-03, -8.6418016393906435e-04, -1.5638051161781471e-03, 1.3537325148278473e-04, 1.5124150369572446e-03, -9.0296330713242421e-04, -1.4053433983987566e-03, -5.3260164448894459e-06, 1.3265199858472445e-03, -1.0277688029524160e-03, -7.2309275058722795e-04, -3.4610937063137806e-04, 1.3022445273983721e-03, -9.6922788833204770e-04, -5.7901862780536037e-04, -4.2446243249374797e-04, 1.5616377636392905e-03, -8.6248219043335368e-04, -1.3732629178650024e-03, -1.3294236495870761e-04, 1.2462257930009543e-03, -8.1096438665143804e-04, -4.9300621444041675e-04, -4.4992238895687783e-04, 1.2048138996092884e-03, -7.4155402937961089e-04, -3.8562341095634543e-04, -4.8265185178004110e-04, 1.3997918624215518e-03, -1.0216209066369918e-03, -9.4397461640859136e-04, -3.4199098846130318e-04, 1.2620735286065163e-03, -8.5604575570593841e-04, -4.6109387630229590e-04, -4.8688792774158394e-04, 1.5208163918870386e-03, -8.8722580384502858e-04, -1.1514411523661054e-03, -3.6068716415424309e-04, 1.5616431264257710e-03, -8.6373104473164315e-04, -1.2257596188148397e-03, -3.1867733110531793e-04, 1.2389429452407711e-03, -7.3425077652598814e-04, -3.7818319125320504e-04, -5.5296180344098229e-04, 1.4236745199894749e-03, -9.8144413894297945e-04, -1.0339917147206214e-03, -3.3224350123284209e-04, 1.3639301846054431e-03, -9.7698616283006996e-04, -7.6271561854903563e-04, -4.4038843862668760e-04, 1.3319077186367961e-03, -9.8418504392616744e-04, -6.1766568127290646e-04, -4.5146401018658297e-04, 1.4441953622589801e-03, -9.5685273246004965e-04, -1.0902814510323519e-03, -3.0850762291715034e-04, 1.3433425680632694e-03, -9.9356279190803516e-04, -6.1534605867470541e-04, -4.6558966036282527e-04, 1.4546708164129235e-03, -9.6300590913229187e-04, -1.1337359994421921e-03, -2.5746740945545017e-04, 1.3087856139503663e-03, -1.0168973495678301e-03, -6.4497486510166216e-04, -3.7542838371827940e-04, 1.4352742897946794e-03, -1.0009281183037142e-03, -8.1467745598339078e-04, -4.9780948738641084e-04, 1.3267280323664397e-03, -9.9210831897400889e-04, -7.2110752592905965e-04, -3.7053587233734035e-04, 1.4367724596286544e-03, -9.7287646205622543e-04, -1.0689733437145245e-03, -3.0929713667895097e-04, 1.4284826558297228e-03, -1.0032946508087481e-03, -9.3215819081625623e-04, -3.9266944529891222e-04, 1.2638165713808633e-03, -9.4203549844022077e-04, -4.3172960405687657e-04, -4.4825396345361454e-04, 1.3144188019784330e-03, -1.0229345575007232e-03, -7.8213089400696050e-04, -2.9459056286120584e-04, 1.2977967295550502e-03, -9.1823850004044442e-04, -5.4312108628579527e-04, -4.4973852658337552e-04, 1.4589399346793047e-03, -9.6337020175007505e-04, -9.5571443993837163e-04, -4.3500741544292166e-04, 1.3679082255077248e-03, -1.0456586778204123e-03, -7.9689290020671321e-04, -3.6447108180251604e-04, 1.2192301505625884e-03, -6.8914758742765522e-04, -4.1801535483624729e-04, -5.0855084936264909e-04, 1.4209422098361410e-03, -9.7982501545086425e-04, -9.5383290882026508e-04, -4.0628628933239729e-04, 1.2840876758166296e-03, -9.5726205004418880e-04, -5.9263894588719901e-04, -3.6034174047262833e-04, 1.2193989165566138e-03, -8.6362711921495882e-04, -3.3089474187258407e-04, -4.7367739380385751e-04, 1.4454340065456679e-03, -9.4903106726323190e-04, -9.4294645768342182e-04, -4.7443129921609459e-04, 1.3223313806910361e-03, -1.0793092901145601e-03, -6.6527927680775072e-04, -3.2359110026424040e-04, 1.4973920733738924e-03, -8.8491990550246182e-04, -1.2171591571912377e-03, -3.1820729740532089e-04, 1.2211117894376268e-03, -6.9737457441970436e-04, -3.8056940530299850e-04, -5.5878299644200971e-04, 1.3871354491036012e-03, -1.0196636806439811e-03, -8.9397096134337427e-04, -3.7123677529844246e-04, 1.5073496738918853e-03, -8.9914274586309299e-04, -1.1100923521957767e-03, -3.9534394591372900e-04, 1.5389834372201414e-03, -8.6865302431473463e-04, -1.2678597357811232e-03, -2.2479040064971316e-04, 1.5857715616750692e-03, -8.4802792400804413e-04, -1.4431505536957709e-03, -6.5066771441232851e-05, 1.3040976914584432e-03, -9.5378487842587192e-04, -5.6107062491615094e-04, -4.3607781750943416e-04, 1.4361202688561447e-03, -9.5224831111481809e-04, -1.1297794380246147e-03, -2.6778384205976237e-04, 1.3635676357551174e-03, -9.9412255599946506e-04, -7.3051951765478731e-04, -4.4154800562627911e-04, 1.5508477502083700e-03, -8.7939607075253085e-04, -1.3561935393377267e-03, -1.2635932200992647e-04, 1.3977902932929805e-03, -9.9082462373056976e-04, -8.0283463213426192e-04, -4.5650787679754265e-04, 1.3207741731525238e-03, -9.6358005340555517e-04, -6.3085951867438014e-04, -4.3968075348902217e-04, 1.6252559503457818e-03, -8.1207361624099851e-04, -1.5190942898645995e-03, 3.4005528914352132e-05, 1.2909097015961842e-03, -9.6864624807368213e-04, -5.2347902381152912e-04, -4.3715405458455165e-04, 1.4001851162801044e-03, -9.6767807815415017e-04, -1.1232365779831662e-03, -2.6481040763848013e-04, 1.3217217954799262e-03, -9.7991362264913690e-04, -5.5341730738921137e-04, -4.8295238369989513e-04, 1.2640698090863750e-03, -9.4752318346179938e-04, -4.4218067665729841e-04, -4.6578091847270366e-04, 1.4951267809199774e-03, -9.1642762891185319e-04, -1.0214860239958733e-03, -4.6970740845900466e-04, 1.2841169680887078e-03, -8.6417312011187616e-04, -4.8407383177561302e-04, -5.0088996321260085e-04, 1.3512353593900953e-03, -1.0439736418993391e-03, -9.0759977568137897e-04, -3.0173432033581658e-04, 1.2345305066023997e-03, -8.6838807328022921e-04, -4.0413743732280768e-04, -4.4479537319962951e-04, 1.4466525287875924e-03, -9.5170674859887969e-04, -9.7973885160627201e-04, -4.3948849284228546e-04, 1.3725941331708027e-03, -1.0166781230845619e-03, -7.6755671498962628e-04, -4.3191896703312273e-04, 1.3538563227843109e-03, -1.0199928751009691e-03, -7.7442160079980896e-04, -3.8161567314677245e-04, 1.3859472864117407e-03, -1.0345636883744270e-03, -8.1387538504034307e-04, -4.0318758271787206e-04, 1.2260084446081257e-03, -8.1329688437282478e-04, -3.8608177755934897e-04, -4.6857032807702829e-04, 1.6004862228002157e-03, -8.5869813229410576e-04, -1.4050654852032346e-03, -9.6057664582574606e-05, 1.3459385533108785e-03, -1.0014225817083209e-03, -7.5445110209810162e-04, -3.6394681092073613e-04, 1.5802112204707694e-03, -8.8418143184290860e-04, -1.3656321230276084e-03, -9.9859855886459247e-05, 1.3938777586773245e-03, -9.8734047951481258e-04, -8.2208943704233193e-04, -4.0539504453435344e-04, 1.4360158838886581e-03, -9.5466630949037588e-04, -1.0695021763836086e-03, -3.4966385012963883e-04, 1.4782335719419745e-03, -9.2096462932788139e-04, -9.9386049752195902e-04, -4.9044154671578190e-04, 1.3242075112925250e-03, -9.0801458127461564e-04, -6.9494238580068419e-04, -4.0874412726708961e-04, 1.4746718485486884e-03, -9.4454358380030991e-04, -1.2097928041591858e-03, -1.9567585923707621e-04, 1.3680805611761193e-03, -9.9714444979884850e-04, -6.7787533895967075e-04, -4.8246661902258490e-04, 1.3149700282666921e-03, -9.8943531187651343e-04, -6.8955875031408869e-04, -4.0063500875313666e-04, 1.3856527284201953e-03, -1.0075706013894159e-03, -8.9064557198323456e-04, -3.8669351114642469e-04, 1.2372161408350122e-03, -8.1355539160972760e-04, -5.0496057042071407e-04, -4.0950187262171203e-04, 1.2788105421023745e-03, -8.8057490788653024e-04, -5.8240597405691328e-04, -3.9217722061436343e-04 + }; + const int nloc = 192; + const int nnei_i = 4; + const int nnei_j = 4; + const int last_layer_size = 4; + + void SetUp() override { + } + void TearDown() override { + } +}; + +TEST_F(TestTabulateSeT, tabulate_fusion_se_t_cpu) +{ + std::vector xyz_scatter(nloc * last_layer_size, 0); + deepmd::tabulate_fusion_se_t_cpu(&xyz_scatter[0], &table[0], &info[0], &em_x[0], &em[0], nloc, nnei_i, nnei_j, last_layer_size); + EXPECT_EQ(xyz_scatter.size(), nloc * last_layer_size); + EXPECT_EQ(xyz_scatter.size(), expected_xyz_scatter.size()); + for (int jj = 0; jj < xyz_scatter.size(); ++jj) { + EXPECT_LT(fabs(xyz_scatter[jj] - expected_xyz_scatter[jj]) , 1e-5); + } +} + +TEST_F(TestTabulateSeT, tabulate_fusion_se_t_grad_cpu) +{ + std::vector dy_dem_x(em_x.size()); + std::vector dy_dem(em.size()); + deepmd::tabulate_fusion_se_t_grad_cpu(&dy_dem_x[0], &dy_dem[0], &table[0], &info[0], &em_x[0], &em[0], &dy[0], nloc, nnei_i, nnei_j, last_layer_size); + EXPECT_EQ(dy_dem_x.size(), nloc * nnei_i * nnei_j); + EXPECT_EQ(dy_dem.size(), nloc * nnei_i * nnei_j); + EXPECT_EQ(dy_dem_x.size(), expected_dy_dem_x.size()); + EXPECT_EQ(dy_dem.size(), expected_dy_dem.size()); + for (int jj = 0; jj < dy_dem_x.size(); ++jj) { + EXPECT_LT(fabs(dy_dem_x[jj] - expected_dy_dem_x[jj]) , 1e-5); + } + for (int jj = 0; jj < dy_dem.size(); ++jj) { + EXPECT_LT(fabs(dy_dem[jj] - expected_dy_dem[jj]) , 1e-5); + } +} + +#if GOOGLE_CUDA +TEST_F(TestTabulateSeT, tabulate_fusion_se_t_gpu_cuda) +{ + std::vector xyz_scatter(nloc * last_layer_size, 0.0); + double * xyz_scatter_dev = NULL, * table_dev = NULL, * em_x_dev = NULL, * em_dev = NULL; + deepmd::malloc_device_memory_sync(xyz_scatter_dev, xyz_scatter); + deepmd::malloc_device_memory_sync(table_dev, table); + deepmd::malloc_device_memory_sync(em_x_dev, em_x); + deepmd::malloc_device_memory_sync(em_dev, em); + deepmd::tabulate_fusion_se_t_gpu_cuda(xyz_scatter_dev, table_dev, &info[0], em_x_dev, em_dev, nloc, nnei_i, nnei_j, last_layer_size); + // deepmd::tabulate_fusion_se_t_cpu(&xyz_scatter[0], &table[0], &info[0], &em_x[0], &em[0], nloc, nnei_i, nnei_j, last_layer_size); + deepmd::memcpy_device_to_host(xyz_scatter_dev, xyz_scatter); + deepmd::delete_device_memory(xyz_scatter_dev); + deepmd::delete_device_memory(table_dev); + deepmd::delete_device_memory(em_x_dev); + deepmd::delete_device_memory(em_dev); + + EXPECT_EQ(xyz_scatter.size(), nloc * last_layer_size); + EXPECT_EQ(xyz_scatter.size(), expected_xyz_scatter.size()); + for (int jj = 0; jj < xyz_scatter.size() / 100; ++jj){ + EXPECT_LT(fabs(xyz_scatter[jj] - expected_xyz_scatter[jj]) , 1e-5); + } +} + +TEST_F(TestTabulateSeT, tabulate_fusion_se_a_grad_gpu_cuda) +{ + std::vector dy_dem_x(em_x.size(), 0.0); + std::vector dy_dem(em.size(), 0.0); + + double * dy_dem_x_dev = NULL, * dy_dem_dev = NULL, * table_dev = NULL, * em_x_dev = NULL, * em_dev = NULL, * dy_dev = NULL; + deepmd::malloc_device_memory_sync(dy_dem_x_dev, dy_dem_x); + deepmd::malloc_device_memory_sync(dy_dem_dev, dy_dem); + deepmd::malloc_device_memory_sync(table_dev, table); + deepmd::malloc_device_memory_sync(em_x_dev, em_x); + deepmd::malloc_device_memory_sync(em_dev, em); + deepmd::malloc_device_memory_sync(dy_dev, dy); + deepmd::tabulate_fusion_se_t_grad_gpu_cuda(dy_dem_x_dev, dy_dem_dev, table_dev, &info[0], em_x_dev, em_dev, dy_dev, nloc, nnei_i, nnei_j, last_layer_size); + deepmd::memcpy_device_to_host(dy_dem_x_dev, dy_dem_x); + deepmd::memcpy_device_to_host(dy_dem_dev, dy_dem); + deepmd::delete_device_memory(dy_dem_x_dev); + deepmd::delete_device_memory(dy_dem_dev); + deepmd::delete_device_memory(table_dev); + deepmd::delete_device_memory(em_x_dev); + deepmd::delete_device_memory(em_dev); + deepmd::delete_device_memory(dy_dev); + + EXPECT_EQ(dy_dem_x.size(), nloc * nnei_i * nnei_j); + EXPECT_EQ(dy_dem.size(), nloc * nnei_i * nnei_j); + EXPECT_EQ(dy_dem_x.size(), expected_dy_dem_x.size()); + EXPECT_EQ(dy_dem.size(), expected_dy_dem.size()); + for (int jj = 0; jj < dy_dem_x.size(); ++jj){ + EXPECT_LT(fabs(dy_dem_x[jj] - expected_dy_dem_x[jj]) , 1e-5); + } + for (int jj = 0; jj < dy_dem.size(); ++jj){ + EXPECT_LT(fabs(dy_dem[jj] - expected_dy_dem[jj]) , 1e-5); + } +} +#endif // GOOGLE_CUDA + +#if TENSORFLOW_USE_ROCM +TEST_F(TestTabulateSeT, tabulate_fusion_se_t_gpu_rocm) +{ + std::vector xyz_scatter(nloc * last_layer_size, 0.0); + double * xyz_scatter_dev = NULL, * table_dev = NULL, * em_x_dev = NULL, * em_dev = NULL; + deepmd::malloc_device_memory_sync(xyz_scatter_dev, xyz_scatter); + deepmd::malloc_device_memory_sync(table_dev, table); + deepmd::malloc_device_memory_sync(em_x_dev, em_x); + deepmd::malloc_device_memory_sync(em_dev, em); + deepmd::tabulate_fusion_se_t_gpu_rocm(xyz_scatter_dev, table_dev, &info[0], em_x_dev, em_dev, nloc, nnei_i, nnei_j, last_layer_size); + deepmd::memcpy_device_to_host(xyz_scatter_dev, xyz_scatter); + deepmd::delete_device_memory(xyz_scatter_dev); + deepmd::delete_device_memory(table_dev); + deepmd::delete_device_memory(em_x_dev); + deepmd::delete_device_memory(em_dev); + + EXPECT_EQ(xyz_scatter.size(), nloc * last_layer_size); + EXPECT_EQ(xyz_scatter.size(), expected_xyz_scatter.size()); + for (int jj = 0; jj < xyz_scatter.size() / 100; ++jj){ + EXPECT_LT(fabs(xyz_scatter[jj] - expected_xyz_scatter[jj]) , 1e-5); + } +} + +TEST_F(TestTabulateSeT, tabulate_fusion_se_t_grad_gpu_rocm) +{ + std::vector dy_dem_x(em_x.size(), 0.0); + std::vector dy_dem(em.size(), 0.0); + + double * dy_dem_x_dev = NULL, * dy_dem_dev = NULL, * table_dev = NULL, * em_x_dev = NULL, * em_dev = NULL, * dy_dev = NULL; + deepmd::malloc_device_memory_sync(dy_dem_x_dev, dy_dem_x); + deepmd::malloc_device_memory_sync(dy_dem_dev, dy_dem); + deepmd::malloc_device_memory_sync(table_dev, table); + deepmd::malloc_device_memory_sync(em_x_dev, em_x); + deepmd::malloc_device_memory_sync(em_dev, em); + deepmd::malloc_device_memory_sync(dy_dev, dy); + deepmd::tabulate_fusion_se_t_grad_gpu_rocm(dy_dem_x_dev, dy_dem_dev, table_dev, &info[0], em_x_dev, em_dev, dy_dev, nloc, nnei_i, nnei_j, last_layer_size); + deepmd::memcpy_device_to_host(dy_dem_x_dev, dy_dem_x); + deepmd::memcpy_device_to_host(dy_dem_dev, dy_dem); + deepmd::delete_device_memory(dy_dem_x_dev); + deepmd::delete_device_memory(dy_dem_dev); + deepmd::delete_device_memory(table_dev); + deepmd::delete_device_memory(em_x_dev); + deepmd::delete_device_memory(em_dev); + deepmd::delete_device_memory(dy_dev); + + EXPECT_EQ(dy_dem_x.size(), nloc * nnei_i * nnei_j); + EXPECT_EQ(dy_dem.size(), nloc * nnei_i * nnei_j); + EXPECT_EQ(dy_dem_x.size(), expected_dy_dem_x.size()); + EXPECT_EQ(dy_dem.size(), expected_dy_dem.size()); + for (int jj = 0; jj < dy_dem_x.size(); ++jj){ + EXPECT_LT(fabs(dy_dem_x[jj] - expected_dy_dem_x[jj]) , 1e-5); + } + for (int jj = 0; jj < dy_dem.size(); ++jj){ + EXPECT_LT(fabs(dy_dem[jj] - expected_dy_dem[jj]) , 1e-5); + } +} +#endif // TENSORFLOW_USE_ROCM From b9d20507c0c5632f5fb6e89d7cbfe46b515a9fea Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Tue, 11 Jan 2022 18:42:43 -0500 Subject: [PATCH 146/161] fix github git url (#1409) Since today, GitHub disable unencrypted `git://`. See https://github.blog/2021-09-01-improving-git-protocol-security-github/. We use `https://` instead. --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 5373ec05b3..7f3510b9d6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "source/lib/src/cuda/cub"] path = source/lib/src/cuda/cub - url = git://github.com/NVIDIA/cub.git + url = https://github.com/NVIDIA/cub.git From 05c785b185114208032710ce5761e104a6aab482 Mon Sep 17 00:00:00 2001 From: liangadam <45301969+liangadam@users.noreply.github.com> Date: Wed, 12 Jan 2022 07:45:16 +0800 Subject: [PATCH 147/161] Finish model compression for se_r descriptor! (#1361) * Finish model compression for se_r descriptor! * Improve error type. * Update compress.md * Improve error type. * Improve error type. * Improve exception handling and unittest. * Add gtest for tabulate_fusion se_r. * Fix variable mistake from #1391 Co-authored-by: huangliang --- deepmd/descriptor/se_a.py | 6 +- deepmd/descriptor/se_r.py | 65 ++- deepmd/descriptor/se_t.py | 2 +- deepmd/utils/tabulate.py | 102 ++++- doc/freeze/compress.md | 2 +- source/lib/include/tabulate.h | 99 +++++ source/lib/src/cuda/tabulate.cu | 216 +++++++++- source/lib/src/rocm/tabulate.hip.cu | 209 +++++++++- source/lib/src/tabulate.cc | 124 ++++++ source/lib/tests/test_tabulate_se_r.cc | 192 +++++++++ source/op/_tabulate_grad.py | 12 +- source/op/tabulate_multi_device.cc | 233 ++++++++++- source/tests/test_model_compression_se_a.py | 2 + source/tests/test_model_compression_se_r.py | 422 ++++++++++++++++++++ source/tests/test_model_compression_se_t.py | 2 + 15 files changed, 1668 insertions(+), 20 deletions(-) create mode 100644 source/lib/tests/test_tabulate_se_r.cc create mode 100644 source/tests/test_model_compression_se_r.py diff --git a/deepmd/descriptor/se_a.py b/deepmd/descriptor/se_a.py index db2980b882..9883909082 100644 --- a/deepmd/descriptor/se_a.py +++ b/deepmd/descriptor/se_a.py @@ -338,7 +338,7 @@ def enable_compression(self, for ii in range(len(self.filter_neuron) - 1): if self.filter_neuron[ii] * 2 != self.filter_neuron[ii + 1]: - raise RecursionError( + raise NotImplementedError( "Model Compression error: descriptor neuron [%s] is not supported by model compression! " "The size of the next layer of the neural network must be twice the size of the previous layer." % ','.join([str(item) for item in self.filter_neuron]) @@ -708,7 +708,7 @@ def _filter_lower( xyz_scatter, nframes, natoms, type_embedding) if self.compress: raise RuntimeError('compression of type embedded descriptor is not supported at the moment') - # with (natom x nei_type_i) x out_size + # natom x 4 x outputs_size if self.compress and (not is_exclude): info = [self.lower, self.upper, self.upper * self.table_config[0], self.table_config[1], self.table_config[2], self.table_config[3]] if self.type_one_side: @@ -718,6 +718,7 @@ def _filter_lower( return op_module.tabulate_fusion_se_a(tf.cast(self.table.data[net], self.filter_precision), info, xyz_scatter, tf.reshape(inputs_i, [natom, shape_i[1]//4, 4]), last_layer_size = outputs_size[-1]) else: if (not is_exclude): + # with (natom x nei_type_i) x out_size xyz_scatter = embedding_net( xyz_scatter, self.filter_neuron, @@ -743,6 +744,7 @@ def _filter_lower( # but if sel is zero # [588 0] -> [147 0 4] incorrect; the correct one is [588 0 4] # So we need to explicitly assign the shape to tf.shape(inputs_i)[0] instead of -1 + # natom x 4 x outputs_size return tf.matmul(tf.reshape(inputs_i, [natom, shape_i[1]//4, 4]), xyz_scatter, transpose_a = True) diff --git a/deepmd/descriptor/se_r.py b/deepmd/descriptor/se_r.py index 79ca850c83..c92c51225d 100644 --- a/deepmd/descriptor/se_r.py +++ b/deepmd/descriptor/se_r.py @@ -8,6 +8,8 @@ from deepmd.env import GLOBAL_NP_FLOAT_PRECISION from deepmd.env import op_module from deepmd.env import default_tf_session_config +from deepmd.utils.tabulate import DPTabulate +from deepmd.utils.graph import load_graph_def, get_tensor_by_name_from_graph from deepmd.utils.network import embedding_net, embedding_net_rand_seed_shift from deepmd.utils.sess import run_sess from .descriptor import Descriptor @@ -118,6 +120,7 @@ def __init__ (self, self.useBN = False self.davg = None self.dstd = None + self.compress=False self.embedding_net_variables = None self.place_holders = {} @@ -229,6 +232,60 @@ def compute_input_stats (self, self.davg = np.array(all_davg) self.dstd = np.array(all_dstd) + def enable_compression(self, + min_nbor_dist : float, + model_file : str = 'frozon_model.pb', + table_extrapolate : float = 5, + table_stride_1 : float = 0.01, + table_stride_2 : float = 0.1, + check_frequency : int = -1, + suffix : str = "", + ) -> None: + """ + Reveive the statisitcs (distance, max_nbor_size and env_mat_range) of the training data. + + Parameters + ---------- + min_nbor_dist + The nearest distance between atoms + model_file + The original frozen model, which will be compressed by the program + table_extrapolate + The scale of model extrapolation + table_stride_1 + The uniform stride of the first table + table_stride_2 + The uniform stride of the second table + check_frequency + The overflow check frequency + suffix : str, optional + The suffix of the scope + """ + assert ( + not self.filter_resnet_dt + ), "Model compression error: descriptor resnet_dt must be false!" + + for ii in range(len(self.filter_neuron) - 1): + if self.filter_neuron[ii] * 2 != self.filter_neuron[ii + 1]: + raise NotImplementedError( + "Model Compression error: descriptor neuron [%s] is not supported by model compression! " + "The size of the next layer of the neural network must be twice the size of the previous layer." + % ','.join([str(item) for item in self.filter_neuron]) + ) + + self.compress = True + self.table = DPTabulate( + self, self.filter_neuron, model_file, activation_fn = self.filter_activation_fn, suffix=suffix) + self.table_config = [table_extrapolate, table_stride_1, table_stride_2, check_frequency] + self.lower, self.upper \ + = self.table.build(min_nbor_dist, + table_extrapolate, + table_stride_1, + table_stride_2) + + graph, _ = load_graph_def(model_file) + self.davg = get_tensor_by_name_from_graph(graph, 'descrpt_attr%s/t_avg' % suffix) + self.dstd = get_tensor_by_name_from_graph(graph, 'descrpt_attr%s/t_std' % suffix) def build (self, coord_ : tf.Tensor, @@ -392,7 +449,7 @@ def _pass_filter(self, [ 0, start_index* self.ndescrpt], [-1, natoms[2+type_i]* self.ndescrpt] ) inputs_i = tf.reshape(inputs_i, [-1, self.ndescrpt]) - layer = self._filter_r(self.filter_precision, type_i, name='filter_type_'+str(type_i)+suffix, natoms=natoms, reuse=reuse, trainable = trainable, activation_fn = self.filter_activation_fn) + layer = self._filter_r(inputs_i, type_i, name='filter_type_'+str(type_i)+suffix, natoms=natoms, reuse=reuse, trainable = trainable, activation_fn = self.filter_activation_fn) layer = tf.reshape(layer, [tf.shape(inputs)[0], natoms[2+type_i] * self.get_dim_out()]) output.append(layer) start_index += natoms[2+type_i] @@ -476,7 +533,11 @@ def _filter_r(self, shape_i = inputs_i.get_shape().as_list() # with (natom x nei_type_i) x 1 xyz_scatter = tf.reshape(inputs_i, [-1, 1]) - if (type_input, type_i) not in self.exclude_types: + if self.compress and ((type_input, type_i) not in self.exclude_types): + info = [self.lower, self.upper, self.upper * self.table_config[0], self.table_config[1], self.table_config[2], self.table_config[3]] + net = 'filter_' + str(type_input) + '_net_' + str(type_i) + xyz_scatter = op_module.tabulate_fusion_se_r(tf.cast(self.table.data[net], self.filter_precision), info, inputs_i, last_layer_size = outputs_size[-1]) + elif (type_input, type_i) not in self.exclude_types: xyz_scatter = embedding_net(xyz_scatter, self.filter_neuron, self.filter_precision, diff --git a/deepmd/descriptor/se_t.py b/deepmd/descriptor/se_t.py index d7dd845cb6..1735757dcb 100644 --- a/deepmd/descriptor/se_t.py +++ b/deepmd/descriptor/se_t.py @@ -264,7 +264,7 @@ def enable_compression(self, for ii in range(len(self.filter_neuron) - 1): if self.filter_neuron[ii] * 2 != self.filter_neuron[ii + 1]: - raise RecursionError( + raise NotImplementedError( "Model Compression error: descriptor neuron [%s] is not supported by model compression! " "The size of the next layer of the neural network must be twice the size of the previous layer." % ','.join([str(item) for item in self.filter_neuron]) diff --git a/deepmd/utils/tabulate.py b/deepmd/utils/tabulate.py index cf0f17571e..e3107821a8 100644 --- a/deepmd/utils/tabulate.py +++ b/deepmd/utils/tabulate.py @@ -82,20 +82,40 @@ def __init__(self, self.sub_graph, self.sub_graph_def = self._load_sub_graph() self.sub_sess = tf.Session(graph = self.sub_graph) - try: - self.sel_a = self.graph.get_operation_by_name('ProdEnvMatA').get_attr('sel_a') - self.prod_env_mat_op = self.graph.get_operation_by_name ('ProdEnvMatA') - except Exception: - self.sel_a = self.graph.get_operation_by_name('DescrptSeA').get_attr('sel_a') - self.prod_env_mat_op = self.graph.get_operation_by_name ('DescrptSeA') + if isinstance(self.descrpt, deepmd.descriptor.DescrptSeR): + try: + self.sel_a = self.graph.get_operation_by_name('ProdEnvMatR').get_attr('sel') + self.prod_env_mat_op = self.graph.get_operation_by_name ('ProdEnvMatR') + except KeyError: + self.sel_a = self.graph.get_operation_by_name('DescrptSeR').get_attr('sel') + self.prod_env_mat_op = self.graph.get_operation_by_name ('DescrptSeR') + elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeA): + try: + self.sel_a = self.graph.get_operation_by_name('ProdEnvMatA').get_attr('sel_a') + self.prod_env_mat_op = self.graph.get_operation_by_name ('ProdEnvMatA') + except KeyError: + self.sel_a = self.graph.get_operation_by_name('DescrptSeA').get_attr('sel_a') + self.prod_env_mat_op = self.graph.get_operation_by_name ('DescrptSeA') + elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeT): + try: + self.sel_a = self.graph.get_operation_by_name('ProdEnvMatA').get_attr('sel_a') + self.prod_env_mat_op = self.graph.get_operation_by_name ('ProdEnvMatA') + except KeyError: + self.sel_a = self.graph.get_operation_by_name('DescrptSeA').get_attr('sel_a') + self.prod_env_mat_op = self.graph.get_operation_by_name ('DescrptSeA') + else: + raise RuntimeError("Unsupported descriptor") self.davg = get_tensor_by_name_from_graph(self.graph, f'descrpt_attr{self.suffix}/t_avg') self.dstd = get_tensor_by_name_from_graph(self.graph, f'descrpt_attr{self.suffix}/t_std') self.ntypes = get_tensor_by_name_from_graph(self.graph, 'descrpt_attr/ntypes') - - self.rcut = self.prod_env_mat_op.get_attr('rcut_r') - self.rcut_smth = self.prod_env_mat_op.get_attr('rcut_r_smth') + if isinstance(self.descrpt, deepmd.descriptor.DescrptSeR): + self.rcut = self.prod_env_mat_op.get_attr('rcut') + self.rcut_smth = self.prod_env_mat_op.get_attr('rcut_smth') + else: + self.rcut = self.prod_env_mat_op.get_attr('rcut_r') + self.rcut_smth = self.prod_env_mat_op.get_attr('rcut_r_smth') self.embedding_net_nodes = get_embedding_net_nodes_from_graph_def(self.graph_def, suffix=self.suffix) @@ -172,6 +192,21 @@ def build(self, net = "filter_" + str(ii) + "_net_" + str(jj) self._build_lower(net, xx, idx, upper, lower, stride0, stride1, extrapolate) idx += 1 + elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeR): + xx = np.arange(lower, upper, stride0, dtype = self.data_type) + xx = np.append(xx, np.arange(upper, extrapolate * upper, stride1, dtype = self.data_type)) + xx = np.append(xx, np.array([extrapolate * upper], dtype = self.data_type)) + self.nspline = int((upper - lower) / stride0 + (extrapolate * upper - upper) / stride1) + for ii in range(self.table_size): + if self.type_one_side or (ii // self.ntypes, ii % self.ntypes) not in self.exclude_types: + if self.type_one_side: + net = "filter_-1_net_" + str(ii) + else: + net = "filter_" + str(ii // self.ntypes) + "_net_" + str(ii % self.ntypes) + self._build_lower(net, xx, ii, upper, lower, stride0, stride1, extrapolate) + else: + raise RuntimeError("Unsupported descriptor") + return lower, upper def _build_lower(self, net, xx, idx, upper, lower, stride0, stride1, extrapolate): @@ -185,6 +220,9 @@ def _build_lower(self, net, xx, idx, upper, lower, stride0, stride1, extrapolate elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeT): tt = np.full((self.nspline, self.last_layer_size), stride1) tt[int((lower - extrapolate * lower) / stride1) + 1:(int((lower - extrapolate * lower) / stride1) + int((upper - lower) / stride0)), :] = stride0 + elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeR): + tt = np.full((self.nspline, self.last_layer_size), stride1) + tt[:int((upper - lower) / stride0), :] = stride0 else: raise RuntimeError("Unsupported descriptor") @@ -225,6 +263,20 @@ def _get_bias(self): for jj in range(ii, self.ntypes): node = self.embedding_net_nodes[f"filter_type_all{self.suffix}/bias_{layer}_{ii}_{jj}"] bias["layer_" + str(layer)].append(tf.make_ndarray(node)) + elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeR): + if self.type_one_side: + for ii in range(0, self.ntypes): + node = self.embedding_net_nodes[f"filter_type_all{self.suffix}/bias_{layer}_{ii}"] + bias["layer_" + str(layer)].append(tf.make_ndarray(node)) + else: + for ii in range(0, self.ntypes * self.ntypes): + if (ii // self.ntypes, ii % self.ntypes) not in self.exclude_types: + node = self.embedding_net_nodes[f"filter_type_{ii // self.ntypes}{self.suffix}/bias_{layer}_{ii % self.ntypes}"] + bias["layer_" + str(layer)].append(tf.make_ndarray(node)) + else: + bias["layer_" + str(layer)].append(np.array([])) + else: + raise RuntimeError("Unsupported descriptor") return bias def _get_matrix(self): @@ -248,6 +300,21 @@ def _get_matrix(self): for jj in range(ii, self.ntypes): node = self.embedding_net_nodes[f"filter_type_all{self.suffix}/matrix_{layer}_{ii}_{jj}"] matrix["layer_" + str(layer)].append(tf.make_ndarray(node)) + elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeR): + if self.type_one_side: + for ii in range(0, self.ntypes): + node = self.embedding_net_nodes[f"filter_type_all{self.suffix}/matrix_{layer}_{ii}"] + matrix["layer_" + str(layer)].append(tf.make_ndarray(node)) + else: + for ii in range(0, self.ntypes * self.ntypes): + if (ii // self.ntypes, ii % self.ntypes) not in self.exclude_types: + node = self.embedding_net_nodes[f"filter_type_{ii // self.ntypes}{self.suffix}/matrix_{layer}_{ii % self.ntypes}"] + matrix["layer_" + str(layer)].append(tf.make_ndarray(node)) + else: + matrix["layer_" + str(layer)].append(np.array([])) + else: + raise RuntimeError("Unsupported descriptor") + return matrix # one-by-one executions @@ -317,6 +384,11 @@ def _get_env_mat_range(self, var = np.square(sw / (min_nbor_dist * self.dstd[:, 1:4])) lower = np.min(-var) upper = np.max(var) + elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeR): + lower = np.min(-self.davg[:, 0] / self.dstd[:, 0]) + upper = np.max(((1 / min_nbor_dist) * sw - self.davg[:, 0]) / self.dstd[:, 0]) + else: + raise RuntimeError("Unsupported descriptor") log.info('training data with lower boundary: ' + str(lower)) log.info('training data with upper boundary: ' + str(upper)) return math.floor(lower), math.ceil(upper) @@ -342,6 +414,12 @@ def _get_layer_size(self): layer_size = len(self.embedding_net_nodes) // (self.ntypes * 2) elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeT): layer_size = len(self.embedding_net_nodes) // int(comb(self.ntypes + 1, 2) * 2) + elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeR): + layer_size = len(self.embedding_net_nodes) // ((self.ntypes * self.ntypes - len(self.exclude_types)) * 2) + if self.type_one_side : + layer_size = len(self.embedding_net_nodes) // (self.ntypes * 2) + else: + raise RuntimeError("Unsupported descriptor") return layer_size def _get_table_size(self): @@ -352,6 +430,12 @@ def _get_table_size(self): table_size = self.ntypes elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeT): table_size = int(comb(self.ntypes + 1, 2)) + elif isinstance(self.descrpt, deepmd.descriptor.DescrptSeR): + table_size = self.ntypes * self.ntypes + if self.type_one_side : + table_size = self.ntypes + else: + raise RuntimeError("Unsupported descriptor") return table_size def _get_data_type(self): diff --git a/doc/freeze/compress.md b/doc/freeze/compress.md index 362ae22840..a441c9571d 100644 --- a/doc/freeze/compress.md +++ b/doc/freeze/compress.md @@ -82,7 +82,7 @@ The model compression interface requires the version of deepmd-kit used in origi **Acceptable descriptor type** -Note only descriptors with `se_e2_a` or `se_e3` type are supported by the model compression feature. Hybrid mixed with above descriptors is also supported. +Descriptors with `se_e2_a`,`se_e3`,'se_e2_r' type are supported by the model compression feature. Hybrid mixed with above descriptors is also supported. **Available activation functions for descriptor:** diff --git a/source/lib/include/tabulate.h b/source/lib/include/tabulate.h index 841848380e..846f27263a 100644 --- a/source/lib/include/tabulate.h +++ b/source/lib/include/tabulate.h @@ -79,6 +79,40 @@ void tabulate_fusion_se_t_grad_grad_cpu( const int nnei_j, const int last_layer_size); +template +void tabulate_fusion_se_r_cpu( + FPTYPE * out, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em, + const int nloc, + const int nnei, + const int last_layer_size); + +template +void tabulate_fusion_se_r_grad_cpu( + FPTYPE * dy_dem, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em, + const FPTYPE * dy, + const int nloc, + const int nnei, + const int last_layer_size); + +template +void tabulate_fusion_se_r_grad_grad_cpu( + FPTYPE * dz_dy, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em, + const FPTYPE * dz_dy_dem, + const int nloc, + const int nnei, + const int last_layer_size); + + + #if GOOGLE_CUDA template void tabulate_fusion_se_a_gpu_cuda( @@ -156,6 +190,38 @@ void tabulate_fusion_se_t_grad_grad_gpu_cuda( const int nnei_i, const int nnei_j, const int last_layer_size); + +template +void tabulate_fusion_se_r_gpu_cuda( + FPTYPE * out, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em, + const int nloc, + const int nnei, + const int last_layer_size); + +template +void tabulate_fusion_se_r_grad_gpu_cuda( + FPTYPE * dy_dem, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em, + const FPTYPE * dy, + const int nloc, + const int nnei, + const int last_layer_size); + +template +void tabulate_fusion_se_r_grad_grad_gpu_cuda( + FPTYPE * dz_dy, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em, + const FPTYPE * dz_dy_dem, + const int nloc, + const int nnei, + const int last_layer_size); #endif // GOOGLE_CUDA #if TENSORFLOW_USE_ROCM @@ -235,6 +301,39 @@ void tabulate_fusion_se_t_grad_grad_gpu_rocm( const int nnei_i, const int nnei_j, const int last_layer_size); + +template +void tabulate_fusion_se_r_gpu_rocm( + FPTYPE * out, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em, + const int nloc, + const int nnei, + const int last_layer_size); + +template +void tabulate_fusion_se_r_grad_gpu_rocm( + FPTYPE * dy_dem, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em, + const FPTYPE * dy, + const int nloc, + const int nnei, + const int last_layer_size); + +template +void tabulate_fusion_se_r_grad_grad_gpu_rocm( + FPTYPE * dz_dy, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em, + const FPTYPE * dz_dy_dem, + const int nloc, + const int nnei, + const int last_layer_size); + #endif // TENSORFLOW_USE_ROCM } diff --git a/source/lib/src/cuda/tabulate.cu b/source/lib/src/cuda/tabulate.cu index 02032a45cf..feee7268b3 100644 --- a/source/lib/src/cuda/tabulate.cu +++ b/source/lib/src/cuda/tabulate.cu @@ -73,6 +73,36 @@ void locate_xx_se_t( } } +template +__forceinline__ __device__ +void locate_xx_se_r( + FPTYPE& xx, + int& table_idx, + const FPTYPE& lower, + const FPTYPE& upper, + const FPTYPE& max, + const FPTYPE& stride0, + const FPTYPE& stride1) +{ + if (xx < lower) { + table_idx = 0; + xx = 0; + } + else if (xx < upper) { + table_idx = (int)((xx - lower) / stride0); + xx -= (table_idx * stride0 + lower); + } + else if (xx < max) { + int first_stride = int((upper - lower) / stride0); + table_idx = first_stride + (int)((xx - upper) / stride1); + xx -= ((table_idx - first_stride) * stride1 + upper); + } + else { + table_idx = int((upper - lower) / stride0) + (int)((max - upper) / stride1) - 1; + xx = 0; + } +} + template __forceinline__ __device__ void load_polynomial_params( @@ -469,6 +499,119 @@ __global__ void tabulate_fusion_se_t_grad_grad_fifth_order_polynomial( dz_dy[block_idx * last_layer_size + thread_idx] = sum; } +template < + typename FPTYPE, + int MTILE, + int KTILE> +__global__ void tabulate_fusion_se_r_fifth_order_polynomial( + FPTYPE * out, + const FPTYPE * table, + const FPTYPE * em, + const FPTYPE lower, + const FPTYPE upper, + const FPTYPE max, + const FPTYPE stride0, + const FPTYPE stride1, + const int nnei, + const int last_layer_size) +{ + const int block_idx = blockIdx.x; // nloc + const int thread_idx = threadIdx.x; // last_layer_size + + int mark_table_idx = 0; + FPTYPE var[6]; + for (int ii = 0; ii < nnei; ii++) { + FPTYPE xx = em[block_idx * nnei + ii]; + int table_idx = 0; + locate_xx_se_r(xx, table_idx, lower, upper, max, stride0, stride1); + if (table_idx != mark_table_idx) { + load_polynomial_params(var, table, table_idx, thread_idx, last_layer_size); + } + out[block_idx * nnei * last_layer_size + ii * last_layer_size + thread_idx] = var[0] + (var[1] + (var[2] + (var[3] + (var[4] + var[5] * xx) * xx) * xx) * xx) * xx; + } +} + +template < + typename FPTYPE, + int MTILE, + int KTILE> +__global__ void tabulate_fusion_se_r_grad_fifth_order_polynomial( + FPTYPE * dy_dem, + const FPTYPE * table, + const FPTYPE * em, + const FPTYPE * dy, + const FPTYPE lower, + const FPTYPE upper, + const FPTYPE max, + const FPTYPE stride0, + const FPTYPE stride1, + const int nnei, + const int last_layer_size) +{ + extern __shared__ int _data[]; + const int block_idx = blockIdx.x; // nloc + const int thread_idx = threadIdx.x; // KTILE * WARP_SIZE, usally 128 here~ + int warp_idx = __shfl_sync(0xffffffff, thread_idx / WARP_SIZE, 0); + int lane_idx = thread_idx % WARP_SIZE; + __syncthreads(); + for (int ii = warp_idx; ii < nnei; ii += KTILE) { + FPTYPE xx = em[block_idx * nnei + ii]; + + int table_idx = 0; + FPTYPE Csub = 0.f; + locate_xx_se_r(xx, table_idx, lower, upper, max, stride0, stride1); + + FPTYPE var[6]; + for (int jj = lane_idx; jj < last_layer_size; jj += WARP_SIZE) { + load_polynomial_params(var, table, table_idx, jj, last_layer_size); + Csub += (var[1] + (2 * var[2] + (3 * var[3] + (4 * var[4] + 5 * var[5] * xx) * xx) * xx) * xx) * dy[block_idx * nnei * last_layer_size + ii * last_layer_size + jj]; + } + __syncwarp(); + + warp_reduce(Csub); + if (lane_idx == 0){ + dy_dem[block_idx * nnei + ii] = Csub; + } + + } +} + +template < + typename FPTYPE, + int MTILE, + int KTILE> +__global__ void tabulate_fusion_se_r_grad_grad_fifth_order_polynomial( + FPTYPE * dz_dy, + const FPTYPE * table, + const FPTYPE * em, + const FPTYPE * dz_dy_dem, + const FPTYPE lower, + const FPTYPE upper, + const FPTYPE max, + const FPTYPE stride0, + const FPTYPE stride1, + const int nnei, + const int last_layer_size) +{ + extern __shared__ int _data[]; + const int block_idx = blockIdx.x; // nloc + const int thread_idx = threadIdx.x; // last_layer_size + + int mark_table_idx = -1; + FPTYPE var[6]; + for (int ii = 0; ii < nnei; ii++) { + FPTYPE xx = em[block_idx * nnei + ii]; + int table_idx = 0; + locate_xx_se_r(xx, table_idx, lower, upper, max, stride0, stride1); + if (table_idx != mark_table_idx) { + load_polynomial_params(var, table, table_idx, thread_idx, last_layer_size); + } + FPTYPE res_grad = var[1] + (2 * var[2] + (3 * var[3] + (4 * var[4] + 5 * var[5] * xx) * xx) * xx) * xx; + mark_table_idx = table_idx; + dz_dy[block_idx * nnei * last_layer_size + ii * last_layer_size + thread_idx] = dz_dy_dem[block_idx * nnei + ii]*res_grad; + } +} + namespace deepmd { template void tabulate_fusion_se_a_gpu_cuda( @@ -616,6 +759,69 @@ void tabulate_fusion_se_t_grad_grad_gpu_cuda( DPErrcheck(cudaDeviceSynchronize()); } +template +void tabulate_fusion_se_r_gpu_cuda( + FPTYPE * out, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em, + const int nloc, + const int nnei, + const int last_layer_size) +{ + if (nloc <= 0) {return;} + tabulate_fusion_se_r_fifth_order_polynomial <<>>( + out, + table, em, table_info[0], table_info[1], table_info[2], table_info[3], table_info[4], nnei, last_layer_size); + DPErrcheck(cudaGetLastError()); + DPErrcheck(cudaDeviceSynchronize()); +} + +template +void tabulate_fusion_se_r_grad_gpu_cuda( + FPTYPE * dy_dem, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em, + const FPTYPE * dy, + const int nloc, + const int nnei, + const int last_layer_size) +{ + if (nloc <= 0) {return;} + DPErrcheck(cudaMemset( + dy_dem, + 0.0, sizeof(FPTYPE) * nloc * nnei)); + + tabulate_fusion_se_r_grad_fifth_order_polynomial <<>>( + dy_dem, + table, em, dy, table_info[0], table_info[1], table_info[2], table_info[3], table_info[4], nnei, last_layer_size); + DPErrcheck(cudaGetLastError()); + DPErrcheck(cudaDeviceSynchronize()); +} + +template +void tabulate_fusion_se_r_grad_grad_gpu_cuda( + FPTYPE * dz_dy, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em, + const FPTYPE * dz_dy_dem, + const int nloc, + const int nnei, + const int last_layer_size) +{ + if (nloc <= 0) {return;} + DPErrcheck(cudaMemset( + dz_dy, + 0.0, sizeof(FPTYPE) * nloc * nnei * last_layer_size)); + tabulate_fusion_se_r_grad_grad_fifth_order_polynomial <<>>( + dz_dy, + table, em, dz_dy_dem, table_info[0], table_info[1], table_info[2], table_info[3], table_info[4], nnei, last_layer_size); + DPErrcheck(cudaGetLastError()); + DPErrcheck(cudaDeviceSynchronize()); +} + template void tabulate_fusion_se_a_gpu_cuda(float * out, const float * table, const float * table_info, const float * em_x, const float * em, const int nloc, const int nnei, const int last_layer_size); template void tabulate_fusion_se_a_gpu_cuda(double * out, const double * table, const double * table_info, const double * em_x, const double * em, const int nloc, const int nnei, const int last_layer_size); template void tabulate_fusion_se_a_grad_gpu_cuda (float * dy_dem_x, float * dy_dem, const float * table, const float * table_info, const float * em_x, const float * em, const float * dy, const int nloc, const int nnei, const int last_layer_size); @@ -629,4 +835,12 @@ template void tabulate_fusion_se_t_grad_gpu_cuda (float * dy_dem_x, float template void tabulate_fusion_se_t_grad_gpu_cuda (double * dy_dem_x, double * dy_dem, const double * table, const double * table_info, const double * em_x, const double * em, const double * dy, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); template void tabulate_fusion_se_t_grad_grad_gpu_cuda (float * dz_dy, const float * table, const float * table_info, const float * em_x, const float * em, const float * dz_dy_dem_x, const float * dz_dy_dem, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); template void tabulate_fusion_se_t_grad_grad_gpu_cuda (double * dz_dy, const double * table, const double * table_info, const double * em_x, const double * em, const double * dz_dy_dem_x, const double * dz_dy_dem, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); -} + +template void tabulate_fusion_se_r_gpu_cuda(float * out, const float * table, const float * table_info, const float * em, const int nloc, const int nnei, const int last_layer_size); +template void tabulate_fusion_se_r_gpu_cuda(double * out, const double * table, const double * table_info, const double * em, const int nloc, const int nnei, const int last_layer_size); +template void tabulate_fusion_se_r_grad_gpu_cuda (float * dy_dem, const float * table, const float * table_info, const float * em, const float * dy, const int nloc, const int nnei, const int last_layer_size); +template void tabulate_fusion_se_r_grad_gpu_cuda (double * dy_dem, const double * table, const double * table_info, const double * em, const double * dy, const int nloc, const int nnei, const int last_layer_size); +template void tabulate_fusion_se_r_grad_grad_gpu_cuda (float * dz_dy, const float * table, const float * table_info, const float * em, const float * dz_dy_dem, const int nloc, const int nnei, const int last_layer_size); +template void tabulate_fusion_se_r_grad_grad_gpu_cuda (double * dz_dy, const double * table, const double * table_info, const double * em, const double * dz_dy_dem, const int nloc, const int nnei, const int last_layer_size); + +} \ No newline at end of file diff --git a/source/lib/src/rocm/tabulate.hip.cu b/source/lib/src/rocm/tabulate.hip.cu index edc6bcc4f5..22dbcae916 100644 --- a/source/lib/src/rocm/tabulate.hip.cu +++ b/source/lib/src/rocm/tabulate.hip.cu @@ -465,6 +465,142 @@ __global__ void tabulate_fusion_se_t_grad_grad_fifth_order_polynomial( dz_dy[block_idx * last_layer_size + thread_idx] = sum; } +template < + typename FPTYPE, + int MTILE, + int KTILE> +__global__ void tabulate_fusion_se_r_fifth_order_polynomial( + FPTYPE * out, + const FPTYPE * table, + const FPTYPE * em, + const FPTYPE lower, + const FPTYPE upper, + const FPTYPE max, + const FPTYPE stride0, + const FPTYPE stride1, + const int nnei, + const int last_layer_size) +{ + HIP_DYNAMIC_SHARED( int, _data) + const int block_idx = blockIdx.x; // nloc + const int thread_idx = threadIdx.x; // last_layer_size + + for (int ii = 0; ii < nnei; ii++) { + FPTYPE var[6]; + FPTYPE xx = em[block_idx * nnei + ii]; + int table_idx = 0; + locate_xx(xx, table_idx, lower, upper, max, stride0, stride1); + var[0] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 0]; + var[1] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 1]; + var[2] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 2]; + var[3] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 3]; + var[4] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 4]; + var[5] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 5]; + out[block_idx * nnei * last_layer_size + ii * last_layer_size + thread_idx] = var[0] + (var[1] + (var[2] + (var[3] + (var[4] + var[5] * xx) * xx) * xx) * xx) * xx; + } + +} + +template < + typename FPTYPE, + int MTILE, + int KTILE> +__global__ void tabulate_fusion_se_r_grad_fifth_order_polynomial( + FPTYPE * dy_dem, + const FPTYPE * table, + const FPTYPE * em, + const FPTYPE * dy, + const FPTYPE lower, + const FPTYPE upper, + const FPTYPE max, + const FPTYPE stride0, + const FPTYPE stride1, + const int nnei, + const int last_layer_size) +{ + HIP_DYNAMIC_SHARED( int, _data) + const int block_idx = blockIdx.x; // nloc + const int thread_idx = threadIdx.x; // KTILE * WARP_SIZE, usally 128 here~ + int warp_idx = __shfl(threadIdx.x / 64, 0); + int lane_idx = threadIdx.x % 64; + + bool unloop = false; + FPTYPE * iteratorA = (FPTYPE *)&_data[0]; // dy + for (int ii = 0; ii < MTILE; ii++) { + for (int jj = thread_idx; jj < last_layer_size; jj += blockDim.x) { + iteratorA[ii * last_layer_size + jj] = ; + } + } + __syncthreads(); + + for (int ii = 0; ii < nnei; ii += KTILE) { + FPTYPE xx = em[block_idx * nnei + ii + warp_idx]; + + int table_idx = 0; + locate_xx(xx, table_idx, lower, upper, max, stride0, stride1); + FPTYPE Csub = 0.f; + for (int jj = lane_idx; jj < last_layer_size; jj += WARP_SIZE) { + FPTYPE var[6]; + // load iteratorB through table + var[0] = table[table_idx * last_layer_size * 6 + 6 * jj + 0]; + var[1] = table[table_idx * last_layer_size * 6 + 6 * jj + 1]; + var[2] = table[table_idx * last_layer_size * 6 + 6 * jj + 2]; + var[3] = table[table_idx * last_layer_size * 6 + 6 * jj + 3]; + var[4] = table[table_idx * last_layer_size * 6 + 6 * jj + 4]; + var[5] = table[table_idx * last_layer_size * 6 + 6 * jj + 5]; + Csub +=(var[1] + (2 * var[2] + (3 * var[3] + (4 * var[4] + 5 * var[5] * xx) * xx) * xx) * xx) * dy[block_idx * nnei * last_layer_size + ii * last_layer_size + jj]; + } + //__syncwarp();->syncwrap + __syncthreads(); + warp_reduce(Csub); + if (lane_idx == 0) { + dy_dem[block_idx * nnei + ii + warp_idx] = Csub; + } + + } +} + +template < + typename FPTYPE, + int MTILE, + int KTILE> +__global__ void tabulate_fusion_se_r_grad_grad_fifth_order_polynomial( + FPTYPE * dz_dy, + const FPTYPE * table, + const FPTYPE * em, + const FPTYPE * dz_dy_dem, + const FPTYPE lower, + const FPTYPE upper, + const FPTYPE max, + const FPTYPE stride0, + const FPTYPE stride1, + const int nnei, + const int last_layer_size) +{ + extern __shared__ int _data[]; + const int block_idx = blockIdx.x; // nloc + const int thread_idx = threadIdx.x; // last_layer_size + + __syncthreads(); + + for (int ii = 0; ii < nnei; ii++) { + FPTYPE var[6]; + FPTYPE xx = em[block_idx * nnei + ii]; + int table_idx = 0; + locate_xx(xx, table_idx, lower, upper, max, stride0, stride1); + var[0] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 0]; + var[1] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 1]; + var[2] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 2]; + var[3] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 3]; + var[4] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 4]; + var[5] = table[table_idx * last_layer_size * 6 + thread_idx * 6 + 5]; + FPTYPE res_grad = var[1] + (2 * var[2] + (3 * var[3] + (4 * var[4] + 5 * var[5] * xx) * xx) * xx) * xx; + dz_dy[block_idx * nnei * last_layer_size + ii * last_layer_size + thread_idx] = dz_dy_dem[block_idx * nnei + ii]*res_grad; + + } +} + + namespace deepmd { template void tabulate_fusion_se_a_gpu_rocm( @@ -611,6 +747,69 @@ void tabulate_fusion_se_t_grad_grad_gpu_rocm( DPErrcheck(hipDeviceSynchronize()); } +template +void tabulate_fusion_se_r_gpu_rocm( + FPTYPE * out, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em, + const int nloc, + const int nnei, + const int last_layer_size) +{ + if(nloc <= 0){return;} + hipLaunchKernelGGL(HIP_KERNEL_NAME(tabulate_fusion_se_r_fifth_order_polynomial), nloc, last_layer_size, sizeof(FPTYPE) * MM * last_layer_size, 0, + out, + table, em, table_info[0], table_info[1], table_info[2], table_info[3], table_info[4], nnei, last_layer_size); + DPErrcheck(hipGetLastError()); + DPErrcheck(hipDeviceSynchronize()); +} + +template +void tabulate_fusion_se_r_grad_gpu_rocm( + FPTYPE * dy_dem, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em, + const FPTYPE * dy, + const int nloc, + const int nnei, + const int last_layer_size) +{ + if(nloc <= 0) {return;} + DPErrcheck(hipMemset( + dy_dem, + 0.0, sizeof(FPTYPE) * nloc * nnei)); + + hipLaunchKernelGGL(HIP_KERNEL_NAME(tabulate_fusion_se_r_grad_fifth_order_polynomial), nloc, KK * WARP_SIZE, sizeof(FPTYPE) * MM * last_layer_size, 0, + dy_dem, + table, em, dy, table_info[0], table_info[1], table_info[2], table_info[3], table_info[4], nnei, last_layer_size); + DPErrcheck(hipGetLastError()); + DPErrcheck(hipDeviceSynchronize()); +} + +template +void tabulate_fusion_se_r_grad_grad_gpu_rocm( + FPTYPE * dz_dy, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em, + const FPTYPE * dz_dy_dem, + const int nloc, + const int nnei, + const int last_layer_size) +{ + if(nloc <= 0) {return;} + DPErrcheck(hipMemset( + dz_dy, + 0.0, sizeof(FPTYPE) * nloc * nnei * last_layer_size)); + hipLaunchKernelGGL(HIP_KERNEL_NAME(tabulate_fusion_se_r_grad_grad_fifth_order_polynomial), nloc, last_layer_size, sizeof(FPTYPE) * MM * last_layer_size, 0, + dz_dy, + table, em, dz_dy_dem, table_info[0], table_info[1], table_info[2], table_info[3], table_info[4], nnei, last_layer_size); + DPErrcheck(hipGetLastError()); + DPErrcheck(hipDeviceSynchronize()); +} + template void tabulate_fusion_se_a_gpu_rocm(float * out, const float * table, const float * table_info, const float * em_x, const float * em, const int nloc, const int nnei, const int last_layer_size); template void tabulate_fusion_se_a_gpu_rocm(double * out, const double * table, const double * table_info, const double * em_x, const double * em, const int nloc, const int nnei, const int last_layer_size); template void tabulate_fusion_se_a_grad_gpu_rocm (float * dy_dem_x, float * dy_dem, const float * table, const float * table_info, const float * em_x, const float * em, const float * dy, const int nloc, const int nnei, const int last_layer_size); @@ -624,4 +823,12 @@ template void tabulate_fusion_se_t_grad_gpu_rocm (float * dy_dem_x, float template void tabulate_fusion_se_t_grad_gpu_rocm (double * dy_dem_x, double * dy_dem, const double * table, const double * table_info, const double * em_x, const double * em, const double * dy, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); template void tabulate_fusion_se_t_grad_grad_gpu_rocm (float * dz_dy, const float * table, const float * table_info, const float * em_x, const float * em, const float * dz_dy_dem_x, const float * dz_dy_dem, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); template void tabulate_fusion_se_t_grad_grad_gpu_rocm (double * dz_dy, const double * table, const double * table_info, const double * em_x, const double * em, const double * dz_dy_dem_x, const double * dz_dy_dem, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); -} + +template void tabulate_fusion_se_r_gpu_rocm(float * out, const float * table, const float * table_info, const float * em, const int nloc, const int nnei, const int last_layer_size); +template void tabulate_fusion_se_r_gpu_rocm(double * out, const double * table, const double * table_info,const double * em, const int nloc, const int nnei, const int last_layer_size); +template void tabulate_fusion_se_r_grad_gpu_rocm (float * dy_dem, const float * table, const float * table_info, const float * em, const float * dy, const int nloc, const int nnei, const int last_layer_size); +template void tabulate_fusion_se_r_grad_gpu_rocm (double * dy_dem, const double * table, const double * table_info, const double * em, const double * dy, const int nloc, const int nnei, const int last_layer_size); +template void tabulate_fusion_se_r_grad_grad_gpu_rocm (float * dz_dy, const float * table, const float * table_info, const float * em, const float * dz_dy_dem, const int nloc, const int nnei, const int last_layer_size); +template void tabulate_fusion_se_r_grad_grad_gpu_rocm (double * dz_dy, const double * table, const double * table_info, const double * em, const double * dz_dy_dem, const int nloc, const int nnei, const int last_layer_size); + +} \ No newline at end of file diff --git a/source/lib/src/tabulate.cc b/source/lib/src/tabulate.cc index 840284b93b..29f60684e6 100644 --- a/source/lib/src/tabulate.cc +++ b/source/lib/src/tabulate.cc @@ -468,6 +468,123 @@ void deepmd::tabulate_fusion_se_t_grad_grad_cpu( } } +template +void deepmd::tabulate_fusion_se_r_cpu( + FPTYPE * out, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em, + const int nloc, + const int nnei, + const int last_layer_size) +{ + memset(out, 0.0, sizeof(FPTYPE) * nloc * nnei * last_layer_size); + const FPTYPE lower = table_info[0]; + const FPTYPE upper = table_info[1]; + const FPTYPE _max = table_info[2]; + const FPTYPE stride0 = table_info[3]; + const FPTYPE stride1 = table_info[4]; + // for every atom, execute a small manual gemm ~ + // FPTYPE * res = new FPTYPE[4 * last_layer_size]; + #pragma omp parallel for + for (int ii = 0; ii < nloc; ii++) { + for (int jj = 0; jj < nnei; jj++) { + FPTYPE xx = em[ii * nnei + jj]; + int table_idx = 0; + locate_xx(lower, upper, _max, stride0, stride1, xx, table_idx); + for (int kk = 0; kk < last_layer_size; kk++) { + FPTYPE a0 = table[table_idx * last_layer_size * 6 + 6 * kk + 0]; + FPTYPE a1 = table[table_idx * last_layer_size * 6 + 6 * kk + 1]; + FPTYPE a2 = table[table_idx * last_layer_size * 6 + 6 * kk + 2]; + FPTYPE a3 = table[table_idx * last_layer_size * 6 + 6 * kk + 3]; + FPTYPE a4 = table[table_idx * last_layer_size * 6 + 6 * kk + 4]; + FPTYPE a5 = table[table_idx * last_layer_size * 6 + 6 * kk + 5]; + out[ii * last_layer_size * nnei + jj * last_layer_size + kk] = a0 + (a1 + (a2 + (a3 + (a4 + a5 * xx) * xx) * xx) * xx) * xx; + } + } + } +} + +template +void deepmd::tabulate_fusion_se_r_grad_cpu( + FPTYPE * dy_dem, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em, + const FPTYPE * dy, + const int nloc, + const int nnei, + const int last_layer_size) +{ + memset(dy_dem, 0.0, sizeof(FPTYPE) * nloc * nnei); + FPTYPE const lower = table_info[0]; + FPTYPE const upper = table_info[1]; + FPTYPE const _max = table_info[2]; + FPTYPE const stride0 = table_info[3]; + FPTYPE const stride1 = table_info[4]; + // for every atom, execute a small gemm~ + // FPTYPE * res = new FPTYPE[4 * last_layer_size]; + #pragma omp parallel for + for (int ii = 0; ii < nloc; ii++) { + for (int jj = 0; jj < nnei; jj++) { + // construct the dy/dx + FPTYPE xx = em[ii * nnei + jj]; + int table_idx = 0; + locate_xx(lower, upper, _max, stride0, stride1, xx, table_idx); + FPTYPE grad = 0.0; + for (int kk = 0; kk < last_layer_size; kk++) { + FPTYPE a0 = table[table_idx * last_layer_size * 6 + 6 * kk + 0]; + FPTYPE a1 = table[table_idx * last_layer_size * 6 + 6 * kk + 1]; + FPTYPE a2 = table[table_idx * last_layer_size * 6 + 6 * kk + 2]; + FPTYPE a3 = table[table_idx * last_layer_size * 6 + 6 * kk + 3]; + FPTYPE a4 = table[table_idx * last_layer_size * 6 + 6 * kk + 4]; + FPTYPE a5 = table[table_idx * last_layer_size * 6 + 6 * kk + 5]; + grad += (a1 + (2 * a2 + (3 * a3 + (4 * a4 + 5 * a5 * xx) * xx) * xx) * xx) * dy[ii * last_layer_size * nnei + jj * last_layer_size + kk]; + } + dy_dem[ii * nnei + jj] = grad; + } + } +} + +template +void deepmd::tabulate_fusion_se_r_grad_grad_cpu( + FPTYPE * dz_dy, + const FPTYPE * table, + const FPTYPE * table_info, + const FPTYPE * em, + const FPTYPE * dz_dy_dem, + const int nloc, + const int nnei, + const int last_layer_size) +{ + memset(dz_dy, 0.0, sizeof(FPTYPE) * nloc * nnei * last_layer_size); + const FPTYPE lower = table_info[0]; + const FPTYPE upper = table_info[1]; + const FPTYPE _max = table_info[2]; + const FPTYPE stride0 = table_info[3]; + const FPTYPE stride1 = table_info[4]; + // for every atom, execute a small manual gemm ~ + // FPTYPE * res = new FPTYPE[4 * last_layer_size]; + #pragma omp parallel for + for (int ii = 0; ii < nloc; ii++) { + for (int jj = 0; jj < nnei; jj++) { + FPTYPE xx = em[ii * nnei + jj]; + int table_idx = 0; + locate_xx(lower, upper, _max, stride0, stride1, xx, table_idx); + for (int kk = 0; kk < last_layer_size; kk++) { + FPTYPE a0 = table[table_idx * last_layer_size * 6 + 6 * kk + 0]; + FPTYPE a1 = table[table_idx * last_layer_size * 6 + 6 * kk + 1]; + FPTYPE a2 = table[table_idx * last_layer_size * 6 + 6 * kk + 2]; + FPTYPE a3 = table[table_idx * last_layer_size * 6 + 6 * kk + 3]; + FPTYPE a4 = table[table_idx * last_layer_size * 6 + 6 * kk + 4]; + FPTYPE a5 = table[table_idx * last_layer_size * 6 + 6 * kk + 5]; + FPTYPE var_grad = a1 + (2 * a2 + (3 * a3 + (4 * a4 + 5 * a5 * xx) * xx) * xx) * xx; + dz_dy[ii * last_layer_size * nnei + jj * last_layer_size + kk] = dz_dy_dem[ii * nnei + jj] * var_grad; + } + } + } +} + template void deepmd::tabulate_fusion_se_a_cpu(float * out, const float * table, const float * table_info, const float * em_x, const float * em, const int nloc, const int nnei, const int last_layer_size); template void deepmd::tabulate_fusion_se_a_cpu(double * out, const double * table, const double * table_info, const double * em_x, const double * em, const int nloc, const int nnei, const int last_layer_size); template void deepmd::tabulate_fusion_se_a_grad_cpu (float * dy_dem_x, float * dy_dem, const float * table, const float * table_info, const float * em_x, const float * em, const float * dy, const int nloc, const int nnei, const int last_layer_size); @@ -481,3 +598,10 @@ template void deepmd::tabulate_fusion_se_t_grad_cpu (float * dy_dem_x, fl template void deepmd::tabulate_fusion_se_t_grad_cpu (double * dy_dem_x, double * dy_dem, const double * table, const double * table_info, const double * em_x, const double * em, const double * dy, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); template void deepmd::tabulate_fusion_se_t_grad_grad_cpu(float * dz_dy, const float * table, const float * table_info, const float * em_x, const float * em, const float * dz_dy_dem_x, const float * dz_dy_dem, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); template void deepmd::tabulate_fusion_se_t_grad_grad_cpu(double * dz_dy, const double * table, const double * table_info, const double * em_x, const double * em, const double * dz_dy_dem_x, const double * dz_dy_dem, const int nloc, const int nnei_i, const int nnei_j, const int last_layer_size); + +template void deepmd::tabulate_fusion_se_r_cpu(float * out, const float * table, const float * table_info, const float * em, const int nloc, const int nnei, const int last_layer_size); +template void deepmd::tabulate_fusion_se_r_cpu(double * out, const double * table, const double * table_info, const double * em, const int nloc, const int nnei, const int last_layer_size); +template void deepmd::tabulate_fusion_se_r_grad_cpu (float * dy_dem, const float * table, const float * table_info, const float * em, const float * dy, const int nloc, const int nnei, const int last_layer_size); +template void deepmd::tabulate_fusion_se_r_grad_cpu (double * dy_dem, const double * table, const double * table_info, const double * em, const double * dy, const int nloc, const int nnei, const int last_layer_size); +template void deepmd::tabulate_fusion_se_r_grad_grad_cpu(float * dz_dy, const float * table, const float * table_info, const float * em, const float * dz_dy_dem, const int nloc, const int nnei, const int last_layer_size); +template void deepmd::tabulate_fusion_se_r_grad_grad_cpu(double * dz_dy, const double * table, const double * table_info, const double * em, const double * dz_dy_dem, const int nloc, const int nnei, const int last_layer_size); \ No newline at end of file diff --git a/source/lib/tests/test_tabulate_se_r.cc b/source/lib/tests/test_tabulate_se_r.cc new file mode 100644 index 0000000000..6cc59b02d1 --- /dev/null +++ b/source/lib/tests/test_tabulate_se_r.cc @@ -0,0 +1,192 @@ +#include +#include +#include "device.h" +#include "tabulate.h" +#include +#include "utilities.h" + +class TestTabulateSeR : public ::testing::Test +{ +protected: + // em_x = tf.random.uniform([4, 16], minval=0, maxval=0.2, dtype = tf.float64) + std::vector info = { + 0, 0.2, 0.4, 0.01, 0.1, -1 + }; + std::vector em = { + 0.0343909 , + 0.11357423, + 0.0858676 , + 0.19337772, + 0.1935728 , + 0.0477744 , + 0.05845198, + 0.19080509, + 0.16111261, + 0.07179262, + 0.10078013, + 0.04640909, + 0.10433399, + 0.15650861, + 0.17527857, + 0.04249097 + }; + + std::vector table = { + 6.348551343037398542e-01, 4.209465843706336474e-04, 6.390862740714405368e-03, -1.544448595628262176e-04, -1.891095227974180087e-04, 2.695025951562175852e-05, -1.317549846042939343e+00, -5.624478206903206490e-02, 1.274284553146523905e-02, -6.836227424141475689e-04, -1.438066096020836407e-04, -1.854932873974712940e-06, -9.996964112615246423e-01, 6.928234423723647617e-02, -4.974719973810486084e-03, -2.019584729176823030e-04, 1.077254539742680247e-04, -8.024209768588029797e-06, 3.552689563657350780e-01, -3.578299775339799371e-02, -1.319946251007718743e-03, 1.016701374495701440e-03, -1.057336720791906388e-04, 5.182678943855506567e-06, 1.227750369557627286e+00, 4.100352079064395472e-02, 3.586869164810712295e-03, -4.304540913340443135e-04, -1.269943482892440004e-04, 1.459465404430219674e-05, -1.472642501673147031e+00, -1.611354921283318364e-01, 1.645427874390196360e-02, 2.107392978135091402e-04, -2.193541011180757461e-04, 1.915392497459551146e-05, -2.855174490181606739e-01, 9.774337856626263976e-02, -2.140891880666230714e-03, -7.148328890055103638e-04, 1.965696332267534503e-05,-4.593489654121371453e-06, -1.468441009949382314e+00, -6.360828127262234399e-02, 4.751283295356955282e-03, 8.711899561753186068e-05, -9.937008678852959884e-06, 4.273569346584811685e-07, + 6.348599826995243722e-01, 5.487167506364742930e-04, 6.386116198716365253e-03, -1.619832375568118791e-04, -1.877328309473502049e-04, 2.134130914519164856e-05, -1.318111020264137512e+00, -5.599013082054477008e-02, 1.272225054666903735e-02, -6.893710047488201898e-04, -1.434367581078517366e-04, 3.329508890614227371e-05 , -9.990040854920316793e-01, 6.918278968071900348e-02, -4.980714172967731085e-03, -1.976574487947816198e-04, 1.070037204086153902e-04, -7.859875077388093586e-06, 3.549109954092205532e-01, -3.580909209068139365e-02, -1.289508598157979719e-03, 1.012474257117017967e-03, -1.054418924402112718e-04, -1.245498322204730900e-05, 1.228160763020727630e+00, 4.107512853046493134e-02, 3.573879491390910459e-03, -4.355190226638688713e-04, -1.258433981470396103e-04, 1.610862268100766631e-05, -1.474252210958008291e+00, -1.608063442081248406e-01, 1.646046950167207382e-02, 2.019843636566674109e-04, -2.185756589083626730e-04, 1.978479879983412190e-05, -2.845402300363228942e-01, 9.770034635718018168e-02, -2.162325119197382531e-03, -7.140472215558940627e-04, 1.956302663031799223e-05, 1.932584474244053378e-05, -1.469076617546759334e+00, -6.351322951074317436e-02, 4.753890907276497185e-03, 8.672114560243554321e-05, -1.004574434175897967e-05, -4.345700882560937596e-06, + 6.348661083147921769e-01, 6.763897297752743953e-04, 6.381144275303845745e-03, -1.694690463885140694e-04, -1.868179426353836598e-04, 3.439291082765030046e-05, -1.318669650038090335e+00, -5.573589319299507294e-02, 1.270148368741391351e-02, -6.950749719342792137e-04, -1.422194703304518733e-04, 3.454751241752252323e-05 , -9.983127558632299836e-01, 6.908311652764687061e-02, -4.986579772806746212e-03, -1.933888092529071571e-04, 1.068327546750306073e-04, -2.976978385983384886e-05, 3.545527765488725169e-01, -3.583457894275744043e-02, -1.259197760082061621e-03, 1.008246479193084487e-03, -1.059401869200098984e-04, 1.721968053146218465e-06, 1.228571871257205572e+00, 4.114647496201748883e-02, 3.560738575723638825e-03, -4.405332425718102457e-04, -1.251648759618972115e-04, 3.659080417076460655e-05, -1.475858628153338792e+00, -1.604770750960976822e-01, 1.646639808472218428e-02, 1.932598402043995316e-04, -2.175904819601363058e-04, 1.230256868634094333e-05, -2.835634435191126679e-01, 9.765688571984927624e-02, -2.183734604613508240e-03, -7.132463811570244078e-04, 2.021887442373574272e-05, 1.321401495096886281e-05, -1.469711274366155784e+00, -6.341812571665436660e-02, 4.756486470714936521e-03, 8.631384191910702040e-05, -1.010516500002806932e-05, -1.110874413279218719e-05, + 6.348735101551836735e-01, 8.039610290153098582e-04, 6.375948457075718626e-03, -1.769074132993461279e-04, -1.855677150383903214e-04, 3.421271436711027645e-05, -1.319225739518145257e+00, -5.548207260888919634e-02, 1.268054645200545304e-02, -7.007297564176242621e-04, -1.408885818822980523e-04, 3.124701885930576017e-05 , -9.976224235482542557e-01, 6.898332734138989952e-02, -4.992317635216104131e-03, -1.891404922064061889e-04, 1.053957535708985289e-04, -1.089286646983666076e-06, 3.541943058468561834e-01, -3.585946084769019160e-02, -1.229013912637771933e-03, 1.004009466262262241e-03, -1.059129033455631863e-04, -4.941663399086282537e-06, 1.228983691638902087e+00, 4.121755707472917613e-02, 3.547447845420277635e-03, -4.455036207721562607e-04, -1.239172256532283074e-04, 3.437341080261359686e-05, -1.477461752073406132e+00, -1.601476900261984693e-01, 1.647206544856073471e-02, 1.845724864086241608e-04, -2.173853638475303177e-04, 3.620505631412716563e-05, -2.825870937484175061e-01, 9.761299713537928413e-02, -2.205119732548723246e-03, -7.124245958910824846e-04, 2.074820558303217398e-05, 1.209381466404663338e-05, -1.470344979888463577e+00, -6.332297013406351649e-02, 4.759069711794740656e-03, 8.589935708505183382e-05, -1.045842324058424788e-05, -6.134254562752213537e-06, + 6.348821871815598650e-01, 9.314261853726121809e-04, 6.370530236175125580e-03, -1.842978984547447257e-04, -1.840210089691990327e-04, 2.234897510077387526e-05, -1.319779292891724465e+00, -5.522867246076747227e-02, 1.265944033870337014e-02, -7.063360380236871801e-04, -1.393416734992873119e-04, 1.931167378610719847e-05 , -9.969330896946905218e-01, 6.888342466806646192e-02, -4.997928623431705138e-03, -1.849303524006284602e-04, 1.053651633995249134e-04, -2.870133904891753420e-05, 3.538355893399378616e-01, -3.588374034700148041e-02, -1.198957225773849763e-03, 9.997681359810027708e-04, -1.060678155548662341e-04, -4.107776618240329050e-06, 1.229396221507694564e+00, 4.128837188660083868e-02, 3.534008730169808672e-03, -4.504275777948374090e-04, -1.224778886969254976e-04, 2.455513266683544498e-05, -1.479061581584721008e+00, -1.598181942132129441e-01, 1.647747255391585064e-02, 1.759082956613747337e-04, -2.158335508261176197e-04, 6.406725844410341030e-06, -2.816111850012528728e-01, 9.756868109694678826e-02, -2.226479900633348240e-03, -7.115823288942964460e-04, 2.121038517729223415e-05, 1.358027318850170435e-05, -1.470977733597038872e+00, -6.322776301216057049e-02, 4.761640356162846754e-03, 8.547576468445008296e-05, -1.081874527005240631e-05, -8.845528475774308509e-07, + 6.348921383103013349e-01, 1.058780765759985421e-03, 6.364891110105044131e-03, -1.916363332792569681e-04, -1.827768871456785058e-04, 2.275707291847725182e-05, -1.320330314380025793e+00, -5.497569611120622923e-02, 1.263816684562326688e-02, -7.118908987616576157e-04, -1.380182662155302303e-04, 1.630252530406085050e-05 , -9.962447554247517711e-01, 6.878341103651769428e-02, -5.003413601927745452e-03, -1.807403991329658622e-04, 1.040363362483998831e-04, -4.422604643727719699e-06, 3.534766330394523148e-01, -3.590741998555346121e-02, -1.169027863565602274e-03, 9.955202772264954043e-04, -1.060447700647724903e-04, -1.021743279826507342e-05, 1.229809458175783687e+00, 4.135891644424664892e-02, 3.520422661584679015e-03, -4.553035794622276055e-04, -1.210679214963379874e-04, 1.595827246550979495e-05, -1.480658115605847147e+00, -1.594885928526604546e-01, 1.648262036665308974e-02, 1.672799673730459213e-04, -2.148155690753495697e-04,-1.867405535452657550e-06, -2.806357215496423363e-01, 9.752393810975558408e-02, -2.247814508535729908e-03, -7.107227883497464890e-04, 2.207595560206285042e-05,-1.137331983229785190e-06, -1.471609534977757372e+00, -6.313250460562676303e-02, 4.764198129054059844e-03, 8.503999275315992160e-05, -1.072692568096017848e-05, -1.373273803695183988e-05, + 6.349033624136081189e-01, 1.186020367092407990e-03, 6.359032581545111251e-03, -1.989262833250400370e-04, -1.812752661309344573e-04, 1.302837915648187095e-05, -1.320878808237722746e+00, -5.472314689282183064e-02, 1.261672747063919374e-02, -7.173917679890315846e-04, -1.373052781380030543e-04, 3.768455339511444900e-05 , -9.955574218354472649e-01, 6.868328895828368363e-02, -5.008773436308684712e-03, -1.765844799686671349e-04, 1.034810966435298563e-04, -1.111176255155353207e-05, 3.531174429312692320e-01, -3.593050231143132822e-02, -1.139225984250480384e-03, 9.912704081392112714e-04, -1.064918174657224404e-04, 2.680738443515978403e-06, 1.230223398925979650e+00, 4.142918782293085467e-02, 3.506691073047987512e-03, -4.601302388532728274e-04, -1.198865987378785417e-04, 1.656386182477533959e-05, -1.482251353107205460e+00, -1.591588911206925361e-01, 1.648750985769346228e-02, 1.586901819247656846e-04, -2.147074421644348298e-04, 2.641762503224190698e-05, -2.796607076604977760e-01, 9.747876869099537933e-02, -2.269122958003529523e-03, -7.098388532529275848e-04, 2.226701915637888804e-05, 1.106237844209756009e-05, -1.472240383519069384e+00, -6.303719517464229094e-02, 4.766742755353862819e-03, 8.459962202271287246e-05, -1.132218730142039535e-05, 8.958476322974335592e-07, + 6.349158583197994643e-01, 1.313140616388666637e-03, 6.352956158169477396e-03, -2.061601622854974502e-04, -1.806298821034440756e-04, 3.770936817966389514e-05, -1.321424778752664952e+00, -5.447102810827629538e-02, 1.259512371128685033e-02, -7.228490733933210606e-04, -1.356407402355522122e-04, 2.099832634320949299e-05 , -9.948710899987588396e-01, 6.858306092758209571e-02, -5.014008993202081696e-03, -1.724573933478598642e-04, 1.029144894329912032e-04, -1.738522780636760158e-05, 3.527580249757622521e-01, -3.595298987582695727e-02, -1.109551740263377793e-03, 9.870126155001155040e-04, -1.064931456292656029e-04, -2.059910396978558087e-06, 1.230638041011988815e+00, 4.149918312660194619e-02, 3.492815399561766294e-03, -4.649051157564728157e-04, -1.192927614880224277e-04, 4.072077917749542957e-05, -1.483841293110880866e+00, -1.588290941739924356e-01, 1.649214200293154520e-02, 1.501282794678792006e-04, -2.138853834118830831e-04, 2.633111784219914963e-05, -2.786861475954987011e-01, 9.743317336979973042e-02, -2.290404652904617314e-03, -7.089360554728917595e-04, 2.260180638238835256e-05, 1.741828165826791135e-05, -1.472870278712053782e+00, -6.294183498489253070e-02, 4.769273959660644442e-03, 8.414681093302789892e-05, -1.142905205912834352e-05, -4.014065121916994726e-06, + 6.349296248136164778e-01, 1.440137170869312810e-03, 6.346663352465874847e-03, -2.133510744796659759e-04, -1.788513201196447670e-04, 1.721163944875696416e-05, -1.321968230245579967e+00, -5.421934303028537461e-02, 1.257335706466754244e-02, -7.282542863230233527e-04, -1.343059033644905889e-04, 1.747822893445653714e-05 , -9.941857609618123259e-01, 6.848272942128874607e-02, -5.019121140152461337e-03, -1.683596869525186377e-04, 1.024142382012053007e-04, -2.632719129544749384e-05, 3.523983851077774343e-01, -3.597488523292310947e-02, -1.080005278271846739e-03, 9.827512175914082399e-04, -1.066680880078371994e-04, 3.403258606315080555e-07, 1.231053381658700818e+00, 4.156889948792314576e-02, 3.478797077596604108e-03, -4.696409807358484993e-04, -1.173636798436718986e-04, 1.149931408689037458e-05, -1.485427934690428442e+00, -1.584992071496764965e-01, 1.649651778315383566e-02, 1.415960091521040870e-04, -2.125888038426753843e-04, 7.384582528889821378e-06, -2.777120456109742896e-01, 9.738715268720327112e-02, -2.311658999267464203e-03, -7.080165982958596923e-04, 2.340034491729013294e-05, 5.174033942788913380e-06, -1.473499220050474623e+00, -6.284642430757329812e-02, 4.771791466347353149e-03, 8.368540130389298475e-05, -1.162498575113560591e-05, -5.381585801785509468e-06, + 6.349446606365225509e-01, 1.567005718051586727e-03, 6.340155681555815353e-03, -2.204854663573854625e-04, -1.779502948888764897e-04, 3.196283450610521294e-05, -1.322509167069771951e+00, -5.396809490162747525e-02, 1.255142902735281209e-02, -7.336077414823606981e-04, -1.332538502428148267e-04, 2.525523713666122703e-05 , -9.935014357470516311e-01, 6.838229689892011409e-02, -5.024110745516051704e-03, -1.642860423419652261e-04, 1.011792892256958577e-04, -5.902237032851650630e-06, 3.520385292366049468e-01, -3.599619093977864809e-02, -1.050586739210998023e-03, 9.784837539753422735e-04, -1.066187407206570670e-04, -6.052991441884039902e-06, 1.231469418062474341e+00, 4.163833406830096812e-02, 3.464637544942418459e-03, -4.743218246565151001e-04, -1.164951133813105271e-04, 2.473911917278243621e-05, -1.487011276970676033e+00, -1.581692351651968476e-01, 1.650063818395723983e-02, 1.331001312464952355e-04, -2.118074389246019866e-04, 9.192428068946771109e-06, -2.767384059577842614e-01, 9.734070719609828892e-02, -2.332885405321092481e-03, -7.070743922828596519e-04, 2.373777250910882265e-05, 1.127700884024945933e-05, -1.474127207030835107e+00, -6.275096341939470634e-02, 4.774294999622533293e-03, 8.321347296773265077e-05, -1.162225195759229858e-05, -1.468175407624093560e-05, + 6.349609644870094494e-01, 1.693741975839754832e-03, 6.333434667015966531e-03, -2.275719866012916918e-04, -1.766077012712487378e-04, 2.919052022666632077e-05, -1.323047593610823247e+00, -5.371728693515605280e-02, 1.252934109528984138e-02, -7.389107006611626187e-04, -1.322992615601379437e-04, 3.689337377145077536e-05 , -9.928181153524118230e-01, 6.828176580261838269e-02, -5.028978678356570489e-03, -1.602449667799085492e-04, 1.004819833385002965e-04, -7.012859043909368637e-06, 3.516784632459502014e-01, -3.601690955621394963e-02, -1.021296258318379370e-03, 9.742140050919662845e-04, -1.068837890347894775e-04, 3.261791903209577241e-07, 1.231886147391427544e+00, 4.170748405790913882e-02, 3.450338240560582581e-03, -4.789562532735843967e-04, -1.153902983973557932e-04, 2.856018069496295048e-05, -1.488591319127526624e+00, -1.578391833182464787e-01, 1.650450419566778376e-02, 1.246407552546250339e-04, -2.115332183818513349e-04, 3.149345367837511192e-05, -2.757652328811996956e-01, 9.729383746118988596e-02, -2.354083281534554220e-03, -7.061133365182417328e-04, 2.418809213597686327e-05, 1.280494807360028992e-05, -1.474754239152433311e+00, -6.265545260258377491e-02, 4.776784283590801948e-03, 8.273687806363864625e-05, -1.229952261449745124e-05, 3.204146150058887708e-06, + 6.349785350208994039e-01, 1.820341692612803541e-03, 6.326501834700739083e-03, -2.346100929840904846e-04, -1.748840426396014729e-04, 1.130785525935554482e-05, -1.323583514286295282e+00, -5.346692231381247606e-02, 1.250709476370755191e-02, -7.441705970339035966e-04, -1.303302437099287372e-04, 7.935577538626925858e-06 , -9.921358007514943234e-01, 6.818113855713830995e-02, -5.033725808341922223e-03, -1.562353718150353687e-04, 1.001568149392305130e-04, -2.302258383924021595e-05, 3.513181929939074299e-01, -3.603704364469759169e-02, -9.921339651685744804e-04, 9.699384566370250092e-04, -1.069081013817698415e-04, -2.744679484186812129e-06, 1.232303566785723392e+00, 4.177634667571154814e-02, 3.435900604437185177e-03, -4.835440426346156498e-04, -1.140781768005934266e-04, 2.411509316948267986e-05, -1.490168060387760951e+00, -1.575090566866652331e-01, 1.650811681325956015e-02, 1.162064642248029450e-04, -2.100324946396962247e-04, 4.868837971279583202e-06, -2.747925306207861240e-01, 9.724654405895133413e-02, -2.375252040655950400e-03, -7.051355614741510987e-04, 2.505903781065493165e-05,-2.569082101323676566e-06, -1.475380315917416585e+00, -6.255989214488603956e-02, 4.779259042312647421e-03, 8.224491253736542200e-05, -1.205054378062991984e-05, -1.594987943813344381e-05, + 6.349973708516511994e-01, 1.946800647308156995e-03, 6.319358714566076195e-03, -2.415904693897710526e-04, -1.741570105122868483e-04, 3.342152683043006766e-05, -1.324116933545430141e+00, -5.321700419064152865e-02, 1.248469152702344660e-02, -7.493727578058629766e-04, -1.295525827398787404e-04, 2.659942231629285135e-05 , -9.914544928937398804e-01, 6.808041756983601589e-02, -5.038353005641925050e-03, -1.522500103683389601e-04, 9.911425811568465554e-05, -1.035676665958809070e-05, 3.509577243129330393e-01, -3.605659577023319351e-02, -9.630999837076988784e-04, 9.656594578503095369e-04, -1.070158919994286978e-04, -2.281503112307771063e-06, 1.232721673357858538e+00, 4.184491916948063911e-02, 3.421326077437690516e-03, -4.880823132679394552e-04, -1.129872290747681817e-04, 2.854952342195995698e-05, -1.491741500028839651e+00, -1.571788603283475749e-01, 1.651147703627379656e-02, 1.078118218043548068e-04, -2.094656285123614196e-04, 1.573608604543182341e-05, -2.738203034102859035e-01, 9.719882757757769554e-02, -2.396391097750961291e-03, -7.041328812172977002e-04, 2.511128111671661627e-05, 1.472819566023977703e-05, -1.476005436830838402e+00, -6.246428233956573262e-02, 4.781718999863710830e-03, 8.175246233396933941e-05, -1.310850420537104008e-05, 1.717274673157189222e-05, + 6.350174705506670403e-01, 2.073114649501703322e-03, 6.312006840494438151e-03, -2.485262001215581039e-04, -1.724445833892894095e-04, 1.623821996891234705e-05, -1.324647855868849478e+00, -5.296753568880858964e-02, 1.246213287875118370e-02, -7.545274547770323926e-04, -1.284298383236558551e-04, 3.142127009671183137e-05 , -9.907741927046019859e-01, 6.797960523066012839e-02, -5.042861140826992473e-03, -1.482946605870891395e-04, 9.821987974303589589e-05, -3.593831829470692349e-06, 3.505970630098214080e-01, -3.607556850024738748e-02, -9.341944322877257512e-04, 9.613773761737330267e-04, -1.072343182304808093e-04, 2.791451096706449119e-06, 1.233140464192951757e+00, 4.191319881581374862e-02, 3.406616101162745613e-03, -4.925758895926437772e-04, -1.113902906060245713e-04, 1.275308331152581608e-05, -1.493311637378700762e+00, -1.568485992811522733e-01, 1.651458586873823589e-02, 9.944841367174414462e-05, -2.085492230796830474e-04, 1.276456024245067926e-05, -2.728485554775001987e-01, 9.715068861693920699e-02, -2.417499870240937074e-03, -7.031148500958378164e-04, 2.576543833825076558e-05, 7.841889896124507091e-06, -1.476629601400710978e+00, -6.236862348540499201e-02, 4.784163880393361643e-03, 8.124213252544174404e-05, -1.286332078849730127e-05, -1.821996546344873330e-06, + 6.350388326475970846e-01, 2.199279539485121671e-03, 6.304447750121061969e-03, -2.554047701160370044e-04, -1.716061813901302753e-04, 3.413524324276134592e-05, -1.325176285768258300e+00, -5.271851990161838253e-02, 1.243942031140890699e-02, -7.596346042592860793e-04, -1.269803855069738714e-04, 2.314478643438959578e-05 , -9.900949010857222898e-01, 6.787870391214460841e-02, -5.047251084767826433e-03, -1.443753107913585767e-04, 9.837034053479728221e-05, -3.865274593462701621e-05, 3.502362148656810170e-01, -3.609396440447816545e-02, -9.054174237006253068e-04, 9.570894530963515055e-04, -1.071221722792567601e-04, -5.180134097885568801e-06, 1.233559936349031494e+00, 4.198118292014653419e-02, 3.391772117805412056e-03, -4.970162819604460663e-04, -1.105584293158747960e-04, 2.757032189173095048e-05, -1.494878471815561216e+00, -1.565182785628131401e-01, 1.651744431908664865e-02, 9.112268062696188113e-05, -2.082277461664644284e-04, 3.370820636496137736e-05, -2.718772910441742408e-01, 9.710212778853387350e-02, -2.438577777940475859e-03, -7.020756635958485484e-04, 2.613933618298708639e-05, 1.211520684095310762e-05, -1.477252809138063672e+00, -6.227291588670166161e-02, 4.786593408182711167e-03, 8.072392747742672100e-05, -1.281499371544444526e-05, -1.293175202324119235e-05, + 6.350614556306495295e-01, 2.325291188338546311e-03, 6.296682984661446623e-03, -2.622362895631248896e-04, -1.701076322674243866e-04, 2.573454296903621253e-05, -1.325702227786145437e+00, -5.246995989253622206e-02, 1.241655531642829255e-02, -7.646904682589584622e-04, -1.257704658362481128e-04, 2.439373356208127567e-05 , -9.894166189151047952e-01, 6.777771596940393439e-02, -5.051523708536139086e-03, -1.404733355821404265e-04, 9.677082285072928253e-05, -3.720510878458014501e-06, 3.498751856359115786e-01, -3.611178605486395354e-02, -8.767690652124425499e-04, 9.527998576480508275e-04, -1.072771816869139909e-04, -2.281376475091892258e-06, 1.233980086857325631e+00, 4.204886881676297983e-02, 3.376795570009583514e-03, -5.014114486109571937e-04, -1.092957353261917852e-04, 2.516456964431257380e-05, -1.496442002767713664e+00, -1.561879031708521548e-01, 1.652005340007862977e-02, 8.282284133744905071e-05, -2.067123325224875000e-04, 7.057486539657783089e-06, -2.709065143258797548e-01, 9.705314571543909030e-02, -2.459624243094573216e-03, -7.010187162791577066e-04, 2.672975399789282626e-05, 7.629793933874534523e-06, -1.477875059556995385e+00, -6.217715985326619649e-02, 4.789007307701962507e-03, 8.019935829649041371e-05, -1.318861260046749971e-05, -7.150339348059032240e-06, + 6.350853379468965887e-01, 2.451145498001100487e-03, 6.288714088740080324e-03, -2.690159202421790068e-04, -1.686584359429067433e-04, 1.941481480743946700e-05, -1.326225686495484890e+00, -5.222185869521017709e-02, 1.239353938406437261e-02, -7.696964132049412353e-04, -1.246012242240120604e-04, 2.724071141974432252e-05 , -9.887393470472876089e-01, 6.767664374012982709e-02, -5.055679883306329545e-03, -1.366074591188833347e-04, 9.623033677044332457e-05, -1.113456896173822779e-05, 3.495139810501832756e-01, -3.612903602543367232e-02, -8.482494585971035728e-04, 9.485064841097947883e-04, -1.073561607316583907e-04, -2.239996380309942211e-06, 1.234400912722548371e+00, 4.211625386880359784e-02, 3.361687900729734210e-03, -5.057597926077623488e-04, -1.078411892315765344e-04, 1.508800592977199686e-05, -1.498002229713325750e+00, -1.558574780824932282e-01, 1.652241412871961052e-02, 7.456368677257522147e-05, -2.062001731191939454e-04, 2.069621557469772063e-05, -2.699362295319003291e-01, 9.700374303226286243e-02, -2.480638690415259105e-03, -6.999405672986690023e-04, 2.700789474676622474e-05, 1.556143061449123430e-05, -1.478496352174730522e+00, -6.208135570041733303e-02, 4.791405303667145565e-03, 7.966538051836852740e-05, -1.352687841609079228e-05, -2.789411930543395566e-06, + 6.351104780025849106e-01, 2.576838401336829787e-03, 6.280542610220480118e-03, -2.757414391158645754e-04, -1.675762649448408429e-04, 2.787462665161048641e-05, -1.326746666499438287e+00, -5.197421931349595348e-02, 1.237037400330611749e-02, -7.746541492504023475e-04, -1.232228491818352083e-04, 2.166599538617633252e-05 , -9.880630863135209108e-01, 6.757548954459043078e-02, -5.059720480258220535e-03, -1.327693574508429343e-04, 9.550030312894054513e-05, -1.096549240339310371e-05, 3.491526068124157778e-01, -3.614571689219699124e-02, -8.198587001702131727e-04, 9.442100079790295610e-04, -1.074330339280879455e-04, -2.103241190440061311e-06, 1.234822410923189784e+00, 4.218333546826981417e-02, 3.346450553092000530e-03, -5.100549148199152614e-04, -1.071543306169886722e-04, 3.572075491055831030e-05, -1.499559152180234056e+00, -1.555270082545787691e-01, 1.652452752618108200e-02, 6.633607063542407416e-05, -2.052990867644106118e-04, 1.891505702101457936e-05, -2.689664408651156746e-01, 9.695392038509384469e-02, -2.501620547117759490e-03, -6.988464710389351081e-04, 2.774961528830105395e-05, 4.843681010028069226e-06, -1.479116686511674494e+00, -6.198550374897651011e-02, 4.793787121096219732e-03, 7.912045955652986253e-05, -1.359696279035538403e-05, -9.132339849453571562e-06, + 6.351368741634448867e-01, 2.702365862198193025e-03, 6.272170100036473551e-03, -2.824171711189519380e-04, -1.661976899287730559e-04, 2.457347650017094835e-05, -1.327265172431057128e+00, -5.172704472148267896e-02, 1.234706066178771662e-02, -7.795630288411945592e-04, -1.217395799935142969e-04, 1.184741714306808905e-05 , -9.873878375219384829e-01, 6.747425568563097942e-02, -5.063646370480812467e-03, -1.289626891970745083e-04, 9.513074838211379970e-05, -2.521433322545949321e-05, 3.487910686007592576e-01, -3.616183123303555458e-02, -7.915968808226425679e-04, 9.399119246579864433e-04, -1.077055728285351480e-04, 6.031191175422362627e-06, 1.235244578411804905e+00, 4.225011103602600848e-02, 3.331084970256580589e-03, -5.143079026275864784e-04, -1.055716785023949844e-04, 2.051193936812822612e-05, -1.501112769745742259e+00, -1.551964986234863897e-01, 1.652639461772111712e-02, 5.814089462644928566e-05, -2.041249358339155683e-04, 6.311073191969795411e-06, -2.679971525218879380e-01, 9.690367843145115956e-02, -2.522569242956208650e-03, -6.977319783847560700e-04, 2.827424678587480721e-05, 2.739673941330651616e-06, -1.479736062091468574e+00, -6.188960432526132566e-02, 4.796152485364500034e-03, 7.856828747830194362e-05, -1.395147193446202365e-05, -4.087221013031299888e-06, + 6.351645247550001816e-01, 2.827723875485507743e-03, 6.263598112024793517e-03, -2.890409134869928735e-04, -1.648390823803598971e-04, 2.215887759642637032e-05, -1.327781208952985015e+00, -5.148033786352124164e-02, 1.232360084570068709e-02, -7.844171563535663055e-04, -1.210428935521009746e-04, 3.344327592646507844e-05 , -9.867136014577331249e-01, 6.737294444867666932e-02, -5.067458424877044516e-03, -1.251812701937470213e-04, 9.419473244264059593e-05, -1.679002076268449654e-05, 3.484293720675762929e-01, -3.617738162759492893e-02, -7.634640860539731316e-04, 9.356082122653546981e-04, -1.075431084112703954e-04, -3.044614041061100766e-06, 1.235667412115300623e+00, 4.231657802179918798e-02, 3.315592595281378029e-03, -5.185116053649769336e-04, -1.041674655671950871e-04, 1.242766263135090892e-05, -1.502663082036415076e+00, -1.548659541050484978e-01, 1.652801643260504508e-02, 4.998556989557471122e-05, -2.037688261998792680e-04, 2.657243869390409541e-05, -2.670283686919466826e-01, 9.685301784023310490e-02, -2.543484210258855835e-03, -6.965966582328896994e-04, 2.850491087748043708e-05, 1.232179636112698650e-05, -1.480354478441044286e+00, -6.179365776107784841e-02, 4.798501122259496952e-03, 7.800586916120723585e-05, -1.413851691566035862e-05, -5.727587674967719880e-06, + 6.351934280628791507e-01, 2.952908467203564646e-03, 6.254828202758994093e-03, -2.956111985445306826e-04, -1.636502852942454153e-04, 2.616921494951480123e-05, -1.328294780757159899e+00, -5.123410165425365537e-02, 1.229999603970671068e-02, -7.892274520450543677e-04, -1.195721301312790567e-04, 2.454197033093738297e-05 , -9.860403788833298488e-01, 6.727155810173718331e-02, -5.071157514069617352e-03, -1.214296539729165295e-04, 9.340570341953608358e-05, -1.444050153586573228e-05, 3.480675228394242149e-01, -3.619237065717702262e-02, -7.354603960058733389e-04, 9.313051737393654526e-04, -1.076930273455606579e-04, -7.696053039474192446e-07, 1.236090908935226107e+00, 4.238273390417521269e-02, 3.299974870987111650e-03, -5.226642260988254756e-04, -1.032474625011560351e-04, 2.396475265799989632e-05, -1.504210088727871764e+00, -1.545353795944727493e-01, 1.652939400402650763e-02, 4.186078937618800693e-05, -2.027012231708198600e-04, 1.761148452766873776e-05, -2.660600935582757565e-01, 9.680193929166537592e-02, -2.564364883962782712e-03, -6.954454205710857090e-04, 2.907017700829073683e-05, 9.120785771591908463e-06, -1.480971935090678926e+00, -6.169766439371183325e-02, 4.800832758035045861e-03, 7.743502257440657043e-05, -1.440171540732098418e-05, -4.489324897938611976e-06, + 6.355509554770921721e-01, 4.194364255265300989e-03, 6.156587518227093006e-03, -3.584539136959086518e-04, -1.505562336471176987e-04, 2.631189526673375584e-05, -1.333295991901433553e+00, -4.879824528740911438e-02, 1.205629889598585497e-02, -8.346035033896359156e-04, -1.072962342948566929e-04, 2.412331753624817981e-05 , -9.793640468817854661e-01, 6.625405011186732973e-02, -5.102126473064734317e-03, -8.551069374443776396e-05, 8.618032279329005427e-05, -1.422030758858379208e-05, 3.444418516979214084e-01, -3.631195473807800889e-02, -4.625381215785304145e-04, 8.881537622047225473e-04, -1.080757789189670570e-04, 5.820590714360855199e-08, 1.240361649325028681e+00, 4.302664794411619614e-02, 3.137220402938139478e-03, -5.615677039256951981e-04, -9.125763978623760322e-05, 2.367398552885374808e-05, -1.519498310980496925e+00, -1.512290469691385253e-01, 1.652996628226939199e-02,-3.745688059096337011e-05, -1.938906911473592626e-04, 1.811217640451412989e-05, -2.564062357251438717e-01, 9.626832379335603651e-02, -2.771163091665611831e-03, -6.829069315554202020e-04, 3.363238372709415958e-05, 8.623099725596635004e-06, -1.487093617252511990e+00, -6.073523464295225993e-02, 4.823154268625621383e-03, 7.122599345182346051e-05, -1.664931178025436733e-05, -4.312450972708557703e-06 + }; + std::vector expected_xyz_scatter = { + 0.634877, -1.319469, -0.997320, 0.354037, 1.229165, -1.478165, -0.282159, -1.470623, 0.634985, -1.323774, -0.991892, 0.351189, 1.232453, -1.490731, -0.274445, -1.475604, 0.634938, -1.322286, -0.993784, 0.352187, 1.231297, -1.486357, -0.277141, -1.473868, 0.635174, -1.327955, -0.986486, 0.348307, 1.235810, -1.503186, -0.266701, -1.480563, 0.635175, -1.327965, -0.986473, 0.348300, 1.235819, -1.503216, -0.266682, -1.480575, 0.634890, -1.320208, -0.996398, 0.353557, 1.229717, -1.480303, -0.280853, -1.471469, 0.634902, -1.320794, -0.995664, 0.353173, 1.230159, -1.482005, -0.279812, -1.472143, 0.635167, -1.327823, -0.986659, 0.348400, 1.235701, -1.502788, -0.266950, -1.480404, 0.635088, -1.326284, -0.988664, 0.349474, 1.234448, -1.498176, -0.269828, -1.478565, 0.634918, -1.321522, -0.994748, 0.352694, 1.230712, -1.484126, -0.278511, -1.472983, 0.634962, -1.323089, -0.992765, 0.351650, 1.231919, -1.488714, -0.275689, -1.474803, 0.634888, -1.320133, -0.996492, 0.353606, 1.229661, -1.480085, -0.280986, -1.471383, 0.634968, -1.323280, -0.992522, 0.351522, 1.232067, -1.489275, -0.275344, -1.475026, 0.635077, -1.326043, -0.988976, 0.349640, 1.234254, -1.497458, -0.270275, -1.478280, 0.635124, -1.327021, -0.987707, 0.348962, 1.235045, -1.500380, -0.268455, -1.479444, 0.634885, -1.319917, -0.996762, 0.353746, 1.229499, -1.479460, -0.281368, -1.471135 + }; + std::vector expected_dy_dem = { + -0.105883, -0.100297, -0.102247, -0.094712, -0.094698, -0.104937, -0.104182, -0.094891, -0.096964, -0.103240, -0.101197, -0.105033, -0.100947, -0.097286, -0.095974, -0.105310 + }; + + const int nloc = 4; + const int nnei = 4; + const int last_layer_size = 8; + + void SetUp() override { + } + void TearDown() override { + } +}; + +TEST_F(TestTabulateSeR, tabulate_fusion_se_r_cpu) +{ + std::vector xyz_scatter(nloc * nnei * last_layer_size); + deepmd::tabulate_fusion_se_r_cpu(&xyz_scatter[0], &table[0], &info[0], &em[0], nloc, nnei, last_layer_size); + EXPECT_EQ(xyz_scatter.size(), nloc * nnei * last_layer_size); + EXPECT_EQ(xyz_scatter.size(), expected_xyz_scatter.size()); + for (int jj = 0; jj < xyz_scatter.size(); ++jj){ + EXPECT_LT(fabs(xyz_scatter[jj] - expected_xyz_scatter[jj]) , 1e-5); + } +} + +TEST_F(TestTabulateSeR, tabulate_fusion_se_r_grad_cpu) +{ + std::vector dy_dem(em.size()); + std::vector dy(nloc * nnei * last_layer_size, 1.0); + deepmd::tabulate_fusion_se_r_grad_cpu(&dy_dem[0], &table[0], &info[0], &em[0], &dy[0], nloc, nnei, last_layer_size); + EXPECT_EQ(dy_dem.size(), nloc * nnei); + EXPECT_EQ(dy_dem.size(), expected_dy_dem.size()); + for (int jj = 0; jj < dy_dem.size(); ++jj){ + EXPECT_LT(fabs(dy_dem[jj] - expected_dy_dem[jj]) , 1e-5); + } +} + +#if GOOGLE_CUDA +TEST_F(TestTabulateSeR, tabulate_fusion_se_r_gpu_cuda) +{ + std::vector xyz_scatter(nloc * nnei * last_layer_size, 0.0); + + double * xyz_scatter_dev = NULL, * table_dev = NULL, * em_dev = NULL; + deepmd::malloc_device_memory_sync(xyz_scatter_dev, xyz_scatter); + deepmd::malloc_device_memory_sync(table_dev, table); + deepmd::malloc_device_memory_sync(em_dev, em); + deepmd::tabulate_fusion_se_r_gpu_cuda(xyz_scatter_dev, table_dev, &info[0], em_dev, nloc, nnei, last_layer_size); + deepmd::memcpy_device_to_host(xyz_scatter_dev, xyz_scatter); + deepmd::delete_device_memory(xyz_scatter_dev); + deepmd::delete_device_memory(table_dev); + deepmd::delete_device_memory(em_dev); + + EXPECT_EQ(xyz_scatter.size(), nloc * nnei * last_layer_size); + EXPECT_EQ(xyz_scatter.size(), expected_xyz_scatter.size()); + for (int jj = 0; jj < xyz_scatter.size(); ++jj){ + EXPECT_LT(fabs(xyz_scatter[jj] - expected_xyz_scatter[jj]) , 1e-5); + } +} + +TEST_F(TestTabulateSeR, tabulate_fusion_se_r_grad_gpu_cuda) +{ + std::vector dy_dem(em.size(), 0.0); + std::vector dy(nloc * nnei * last_layer_size, 1.0); + + * dy_dem_dev = NULL, * table_dev = NULL, * em_dev = NULL, * dy_dev = NULL; + deepmd::malloc_device_memory_sync(dy_dem_dev, dy_dem); + deepmd::malloc_device_memory_sync(table_dev, table); + deepmd::malloc_device_memory_sync(em_dev, em); + deepmd::malloc_device_memory_sync(dy_dev, dy); + deepmd::tabulate_fusion_se_r_grad_gpu_cuda(dy_dem_dev, table_dev, &info[0], em_dev, dy_dev, nloc, nnei, last_layer_size); + deepmd::memcpy_device_to_host(dy_dem_dev, dy_dem); + deepmd::delete_device_memory(dy_dem_dev); + deepmd::delete_device_memory(table_dev); + deepmd::delete_device_memory(em_dev); + deepmd::delete_device_memory(dy_dev); + + EXPECT_EQ(dy_dem.size(), nloc * nnei); + EXPECT_EQ(dy_dem.size(), expected_dy_dem.size()); + + for (int jj = 0; jj < dy_dem.size(); ++jj){ + EXPECT_LT(fabs(dy_dem[jj] - expected_dy_dem[jj]) , 1e-5); + } +} +#endif // GOOGLE_CUDA + +#if TENSORFLOW_USE_ROCM +TEST_F(TestTabulateSeR, tabulate_fusion_se_r_gpu_rocm) +{ + std::vector xyz_scatter(nloc * nnei * last_layer_size, 0.0); + + double * xyz_scatter_dev = NULL, * table_dev = NULL, * em_dev = NULL; + deepmd::malloc_device_memory_sync(xyz_scatter_dev, xyz_scatter); + deepmd::malloc_device_memory_sync(table_dev, table); + deepmd::malloc_device_memory_sync(em_dev, em); + deepmd::tabulate_fusion_se_r_gpu_rocm(xyz_scatter_dev, table_dev, &info[0], em_dev, nloc, nnei, last_layer_size); + deepmd::memcpy_device_to_host(xyz_scatter_dev, xyz_scatter); + deepmd::delete_device_memory(xyz_scatter_dev); + deepmd::delete_device_memory(table_dev); + deepmd::delete_device_memory(em_dev); + + EXPECT_EQ(xyz_scatter.size(), nloc * nnei * last_layer_size); + EXPECT_EQ(xyz_scatter.size(), expected_xyz_scatter.size()); + for (int jj = 0; jj < xyz_scatter.size(); ++jj){ + EXPECT_LT(fabs(xyz_scatter[jj] - expected_xyz_scatter[jj]) , 1e-5); + } +} + +TEST_F(TestTabulateSeR, tabulate_fusion_se_r_grad_gpu_rocm) +{ + std::vector dy_dem(em.size(), 0.0); + std::vector dy(nloc * nnei * last_layer_size, 1.0); + + * dy_dem_dev = NULL, * table_dev = NULL, * em_dev = NULL, * dy_dev = NULL; + deepmd::malloc_device_memory_sync(dy_dem_dev, dy_dem); + deepmd::malloc_device_memory_sync(table_dev, table); + deepmd::malloc_device_memory_sync(em_dev, em); + deepmd::malloc_device_memory_sync(dy_dev, dy); + deepmd::tabulate_fusion_se_r_grad_gpu_rocm(dy_dem_dev, table_dev, &info[0], em_dev, dy_dev, nloc, nnei, last_layer_size); + deepmd::memcpy_device_to_host(dy_dem_dev, dy_dem); + deepmd::delete_device_memory(dy_dem_dev); + deepmd::delete_device_memory(table_dev); + deepmd::delete_device_memory(em_dev); + deepmd::delete_device_memory(dy_dev); + + EXPECT_EQ(dy_dem.size(), nloc * nnei); + EXPECT_EQ(dy_dem.size(), expected_dy_dem.size()); + + for (int jj = 0; jj < dy_dem.size(); ++jj){ + EXPECT_LT(fabs(dy_dem[jj] - expected_dy_dem[jj]) , 1e-5); + } +} +#endif // TENSORFLOW_USE_ROCM diff --git a/source/op/_tabulate_grad.py b/source/op/_tabulate_grad.py index 855d9a470e..6fb83966cc 100644 --- a/source/op/_tabulate_grad.py +++ b/source/op/_tabulate_grad.py @@ -28,4 +28,14 @@ def _tabulate_fusion_se_t_grad_cc (op, dy): @ops.RegisterGradient("TabulateFusionSeTGrad") def _tabulate_fusion_se_t_grad_grad_cc (op, dy, dy_): dz_dy = op_module.tabulate_fusion_se_t_grad_grad(op.inputs[0], op.inputs[1], op.inputs[2], op.inputs[3], dy, dy_, op.inputs[5]) - return [None, None, None, None, dz_dy, None] \ No newline at end of file + return [None, None, None, None, dz_dy, None] + +@ops.RegisterGradient("TabulateFusionSeR") +def _tabulate_fusion_se_r_grad_cc (op, dy): + dy_df = op_module.tabulate_fusion_se_r_grad(op.inputs[0], op.inputs[1], op.inputs[2], dy, op.outputs[0]) + return [None, None, dy_df] + +@ops.RegisterGradient("TabulateFusionSeRGrad") +def _tabulate_fusion_se_r_grad_grad_cc (op, dy): + dz_dy = op_module.tabulate_fusion_se_r_grad_grad(op.inputs[0], op.inputs[1], op.inputs[2], dy, op.inputs[4]) + return [None, None, None, dz_dy, None] \ No newline at end of file diff --git a/source/op/tabulate_multi_device.cc b/source/op/tabulate_multi_device.cc index aba6efc5b5..7bf190661c 100644 --- a/source/op/tabulate_multi_device.cc +++ b/source/op/tabulate_multi_device.cc @@ -94,6 +94,32 @@ REGISTER_OP("TabulateFusionSeTGradGrad") .Input("descriptor: T") .Output("dz_dy: T"); +REGISTER_OP("TabulateFusionSeR") + .Attr("T: {float, double} = DT_DOUBLE") + .Input("table: T") + .Input("table_info: T") + .Input("em: T") + .Attr("last_layer_size: int") + .Output("descriptor: T"); + +REGISTER_OP("TabulateFusionSeRGrad") + .Attr("T: {float, double} = DT_DOUBLE") + .Input("table: T") + .Input("table_info: T") + .Input("em: T") + .Input("dy: T") + .Input("descriptor: T") + .Output("dy_dem: T"); + +REGISTER_OP("TabulateFusionSeRGradGrad") + .Attr("T: {float, double}") + .Input("table: T") + .Input("table_info: T") + .Input("em: T") + .Input("dz_dy_dem: T") + .Input("descriptor: T") + .Output("dz_dy: T"); + template class TabulateFusionSeAOp : public OpKernel { public: @@ -499,6 +525,191 @@ class TabulateFusionSeTGradGradOp : public OpKernel { private: std::string device; }; +template +class TabulateFusionSeROp : public OpKernel { + public: + explicit TabulateFusionSeROp(OpKernelConstruction* context) : OpKernel(context) { + OP_REQUIRES_OK(context, context->GetAttr("last_layer_size", &last_layer_size)); + } + void Compute(OpKernelContext* context) override { + deepmd::safe_compute(context, [this](OpKernelContext* context) {this->_Compute(context);}); + } + + void _Compute(OpKernelContext* context) { + // Grab the input tensor + int context_input_index = 0; + const Tensor& table_tensor = context->input(context_input_index++); + const Tensor& table_info_tensor = context->input(context_input_index++); + const Tensor& em_tensor = context->input(context_input_index++); + // set size of the sample + OP_REQUIRES (context, (table_tensor.shape().dims() == 2), errors::InvalidArgument ("Dim of table should be 2")); + OP_REQUIRES (context, (em_tensor.shape().dims() == 2), errors::InvalidArgument ("Dim of input should be 2")); + TensorShape descriptor_shape; + descriptor_shape.AddDim (em_tensor.shape().dim_size(0)); + descriptor_shape.AddDim (em_tensor.shape().dim_size(1)); // TODO: be careful here; + descriptor_shape.AddDim (last_layer_size); + int context_output_index = 0; + Tensor* descriptor_tensor = NULL; + OP_REQUIRES_OK(context, context->allocate_output( + context_output_index++, + descriptor_shape, + &descriptor_tensor)); + DeviceFunctor() ( + device, + context->eigen_device() + ); + // flat the tensors + FPTYPE * descriptor = descriptor_tensor->flat().data(); + const FPTYPE * table = table_tensor.flat().data(); + const FPTYPE * table_info = table_info_tensor.flat().data(); + const FPTYPE * em = em_tensor.flat().data(); + const int nloc = em_tensor.shape().dim_size(0); + const int nnei = em_tensor.shape().dim_size(1); + + if (device == "GPU") { + #if GOOGLE_CUDA + deepmd::tabulate_fusion_se_r_gpu_cuda( + descriptor, + table, table_info, em, nloc, nnei, last_layer_size); + #endif // GOOGLE_CUDA + + #if TENSORFLOW_USE_ROCM + deepmd::tabulate_fusion_se_r_gpu_rocm( + descriptor, + table, table_info, em, nloc, nnei, last_layer_size); + #endif // TENSORFLOW_USE_ROCM + } + else if (device == "CPU") { + deepmd::tabulate_fusion_se_r_cpu( + descriptor, + table, table_info, em, nloc, nnei, last_layer_size); + } + } +private: + int last_layer_size; + std::string device; +}; + +template +class TabulateFusionSeRGradOp : public OpKernel { + public: + explicit TabulateFusionSeRGradOp(OpKernelConstruction* context) : OpKernel(context) {} + void Compute(OpKernelContext* context) override { + deepmd::safe_compute(context, [this](OpKernelContext* context) {this->_Compute(context);}); + } + + void _Compute(OpKernelContext* context) { + // Grab the input tensor + int context_input_index = 0; + const Tensor& table_tensor = context->input(context_input_index++); + const Tensor& table_info_tensor = context->input(context_input_index++); + const Tensor& em_tensor = context->input(context_input_index++); + const Tensor& dy_tensor = context->input(context_input_index++); + const Tensor& descriptor_tensor = context->input(context_input_index++); + // set size of the sample + OP_REQUIRES (context, (dy_tensor.shape().dims() == 3), errors::InvalidArgument ("Dim of table should be 3")); + int context_output_index = 0; + Tensor* dy_dem_tensor = NULL; + OP_REQUIRES_OK(context, context->allocate_output( + context_output_index++, + em_tensor.shape(), + &dy_dem_tensor)); + DeviceFunctor() ( + device, + context->eigen_device() + ); + + // flat the tensors + FPTYPE * dy_dem = dy_dem_tensor->flat().data(); + const FPTYPE * descriptor = descriptor_tensor.flat().data(); + const FPTYPE * table = table_tensor.flat().data(); + const FPTYPE * table_info = table_info_tensor.flat().data(); + const FPTYPE * em = em_tensor.flat().data(); + const FPTYPE * dy = dy_tensor.flat().data(); + const int nloc = em_tensor.shape().dim_size(0); + const int nnei = em_tensor.shape().dim_size(1); + const int last_layer_size = descriptor_tensor.shape().dim_size(2); + + if (device == "GPU") { + #if GOOGLE_CUDA + deepmd::tabulate_fusion_se_r_grad_gpu_cuda( + dy_dem, + table, table_info, em, dy, nloc, nnei, last_layer_size); + #endif // GOOGLE_CUDA + + #if TENSORFLOW_USE_ROCM + deepmd::tabulate_fusion_se_r_grad_gpu_rocm( + dy_dem, + table, table_info, em, dy, nloc, nnei, last_layer_size); + #endif // TENSORFLOW_USE_ROCM + } + else if (device == "CPU") { + deepmd::tabulate_fusion_se_r_grad_cpu( + dy_dem, + table, table_info, em, dy, nloc, nnei, last_layer_size); + } + } +private: + std::string device; +}; + +template +class TabulateFusionSeRGradGradOp : public OpKernel { + public: + explicit TabulateFusionSeRGradGradOp(OpKernelConstruction* context) : OpKernel(context) {} + void Compute(OpKernelContext* context) override { + // Grab the input tensor + int context_input_index = 0; + const Tensor& table_tensor = context->input(context_input_index++); + const Tensor& table_info_tensor = context->input(context_input_index++); + const Tensor& em_tensor = context->input(context_input_index++); + const Tensor& dz_dy_dem_tensor = context->input(context_input_index++); + const Tensor& descriptor_tensor = context->input(context_input_index++); + // set size of the sample + OP_REQUIRES (context, (dz_dy_dem_tensor.shape().dims() == 3), errors::InvalidArgument ("Dim of input should be 3")); + int context_output_index = 0; + Tensor* dz_dy_tensor = NULL; + OP_REQUIRES_OK(context, context->allocate_output( + context_output_index++, + descriptor_tensor.shape(), + &dz_dy_tensor)); + DeviceFunctor() ( + device, + context->eigen_device() + ); + + // flat the tensors + FPTYPE * dz_dy = dz_dy_tensor->flat().data(); + const FPTYPE * table = table_tensor.flat().data(); + const FPTYPE * table_info = table_info_tensor.flat().data(); + const FPTYPE * em = em_tensor.flat().data(); + const FPTYPE * dz_dy_dem = dz_dy_dem_tensor.flat().data(); + const int nloc = em_tensor.shape().dim_size(0); + const int nnei = em_tensor.shape().dim_size(1); + const int last_layer_size = descriptor_tensor.shape().dim_size(2); + + if (device == "GPU") { + #if GOOGLE_CUDA + deepmd::tabulate_fusion_se_r_grad_grad_gpu_cuda( + dz_dy, + table, table_info, em, dz_dy_dem, nloc, nnei, last_layer_size); + #endif // GOOGLE_CUDA + #if TENSORFLOW_USE_ROCM + deepmd::tabulate_fusion_se_r_grad_grad_gpu_rocm( + dz_dy, + table, table_info, em, dz_dy_dem, nloc, nnei, last_layer_size); + #endif // TENSORFLOW_USE_ROCM + OP_REQUIRES (context, (last_layer_size <= 1024), errors::InvalidArgument ("In the process of model compression, the size of the last layer of embedding net must be less than 1024!")); + } + else if (device == "CPU") { + deepmd::tabulate_fusion_se_r_grad_grad_cpu( + dz_dy, + table, table_info, em, dz_dy_dem, nloc, nnei, last_layer_size); + } + } +private: + std::string device; +}; #define REGISTER_CPU(T) \ REGISTER_KERNEL_BUILDER( \ @@ -527,7 +738,16 @@ REGISTER_KERNEL_BUILDER( TabulateFusionSeTGradOp); \ REGISTER_KERNEL_BUILDER( \ Name("TabulateFusionSeTGradGrad").Device(DEVICE_CPU).TypeConstraint("T"), \ - TabulateFusionSeTGradGradOp); + TabulateFusionSeTGradGradOp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionSeR").Device(DEVICE_CPU).TypeConstraint("T"), \ + TabulateFusionSeROp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionSeRGrad").Device(DEVICE_CPU).TypeConstraint("T"), \ + TabulateFusionSeRGradOp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionSeRGradGrad").Device(DEVICE_CPU).TypeConstraint("T"), \ + TabulateFusionSeRGradGradOp); REGISTER_CPU(float); REGISTER_CPU(double); @@ -559,7 +779,16 @@ REGISTER_KERNEL_BUILDER( TabulateFusionSeTGradOp); \ REGISTER_KERNEL_BUILDER( \ Name("TabulateFusionSeTGradGrad").Device(DEVICE_GPU).TypeConstraint("T").HostMemory("table_info"), \ - TabulateFusionSeTGradGradOp); + TabulateFusionSeTGradGradOp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionSeR").Device(DEVICE_GPU).TypeConstraint("T").HostMemory("table_info"), \ + TabulateFusionSeROp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionSeRGrad").Device(DEVICE_GPU).TypeConstraint("T").HostMemory("table_info"), \ + TabulateFusionSeRGradOp); \ +REGISTER_KERNEL_BUILDER( \ + Name("TabulateFusionSeRGradGrad").Device(DEVICE_GPU).TypeConstraint("T").HostMemory("table_info"), \ + TabulateFusionSeRGradGradOp); REGISTER_GPU(float); REGISTER_GPU(double); #endif // GOOGLE_CUDA || TENSORFLOW_USE_ROCM diff --git a/source/tests/test_model_compression_se_a.py b/source/tests/test_model_compression_se_a.py index 2f42134409..1c13a9d610 100644 --- a/source/tests/test_model_compression_se_a.py +++ b/source/tests/test_model_compression_se_a.py @@ -331,6 +331,8 @@ def tearDownClass(self): _file_delete("model-compression/model.ckpt.index") _file_delete("model-compression/model.ckpt.data-00000-of-00001") _file_delete("model-compression") + _file_delete("input_v2_compat.json") + _file_delete("lcurve.out") def test_attrs(self): self.assertEqual(self.dp_original.get_ntypes(), 2) diff --git a/source/tests/test_model_compression_se_r.py b/source/tests/test_model_compression_se_r.py new file mode 100644 index 0000000000..9b9218d175 --- /dev/null +++ b/source/tests/test_model_compression_se_r.py @@ -0,0 +1,422 @@ +import os,sys,platform,shutil,dpdata,json +import numpy as np +import unittest +import subprocess as sp + +from deepmd.infer import DeepPot +from deepmd.env import MODEL_VERSION +# from deepmd.entrypoints.compress import compress +from common import j_loader, tests_path + +from deepmd.env import GLOBAL_NP_FLOAT_PRECISION +if GLOBAL_NP_FLOAT_PRECISION == np.float32 : + default_places = 4 +else : + default_places = 10 + +def _file_delete(file) : + if os.path.isdir(file): + os.rmdir(file) + elif os.path.isfile(file): + os.remove(file) + +def _subprocess_run(command): + popen = sp.Popen(command.split(), shell=False, stdout=sp.PIPE, stderr=sp.STDOUT) + for line in iter(popen.stdout.readline, b''): + if hasattr(line, 'decode'): + line = line.decode('utf-8') + line = line.rstrip() + print(line) + popen.wait() + return popen.returncode + +def _init_models(): + data_file = str(tests_path / os.path.join("model_compression", "data")) + frozen_model = str(tests_path / "dp-original-se-r.pb") + compressed_model = str(tests_path / "dp-compressed-se-r.pb") + INPUT = str(tests_path / "input.json") + jdata = j_loader(str(tests_path / os.path.join("model_compression", "input.json"))) + jdata["model"]["descriptor"] = {} + jdata["model"]["descriptor"]["type"] = "se_e2_r" + jdata["model"]["descriptor"]["sel"] = [46, 92] + jdata["model"]["descriptor"]["rcut_smth"] = 0.5 + jdata["model"]["descriptor"]["rcut"] = 6.0 + jdata["model"]["descriptor"]["neuron"] = [5,10,20] + jdata["model"]["descriptor"]["resnet_dt"] = False + jdata["model"]["descriptor"]["seed"] = 1 + jdata["training"]["training_data"]["systems"] = data_file + jdata["training"]["validation_data"]["systems"] = data_file + with open(INPUT, "w") as fp: + json.dump(jdata, fp, indent=4) + + ret = _subprocess_run("dp train " + INPUT) + np.testing.assert_equal(ret, 0, 'DP train failed!') + ret = _subprocess_run("dp freeze -o " + frozen_model) + np.testing.assert_equal(ret, 0, 'DP freeze failed!') + ret = _subprocess_run("dp compress " + " -i " + frozen_model + " -o " + compressed_model) + np.testing.assert_equal(ret, 0, 'DP model compression failed!') + return INPUT, frozen_model, compressed_model + +INPUT, FROZEN_MODEL, COMPRESSED_MODEL = _init_models() + +class TestDeepPotAPBC(unittest.TestCase) : + @classmethod + def setUpClass(self): + self.dp_original = DeepPot(FROZEN_MODEL) + self.dp_compressed = DeepPot(COMPRESSED_MODEL) + self.coords = np.array([12.83, 2.56, 2.18, + 12.09, 2.87, 2.74, + 00.25, 3.32, 1.68, + 3.36, 3.00, 1.81, + 3.51, 2.51, 2.60, + 4.27, 3.22, 1.56]) + self.atype = [0, 1, 1, 0, 1, 1] + self.box = np.array([13., 0., 0., 0., 13., 0., 0., 0., 13.]) + + def test_attrs(self): + self.assertEqual(self.dp_original.get_ntypes(), 2) + self.assertAlmostEqual(self.dp_original.get_rcut(), 6.0, places = default_places) + self.assertEqual(self.dp_original.get_type_map(), ['O', 'H']) + self.assertEqual(self.dp_original.get_dim_fparam(), 0) + self.assertEqual(self.dp_original.get_dim_aparam(), 0) + + self.assertEqual(self.dp_compressed.get_ntypes(), 2) + self.assertAlmostEqual(self.dp_compressed.get_rcut(), 6.0, places = default_places) + self.assertEqual(self.dp_compressed.get_type_map(), ['O', 'H']) + self.assertEqual(self.dp_compressed.get_dim_fparam(), 0) + self.assertEqual(self.dp_compressed.get_dim_aparam(), 0) + + def test_1frame(self): + ee0, ff0, vv0 = self.dp_original.eval(self.coords, self.box, self.atype, atomic = False) + ee1, ff1, vv1 = self.dp_compressed.eval(self.coords, self.box, self.atype, atomic = False) + # check shape of the returns + nframes = 1 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) + + def test_1frame_atm(self): + ee0, ff0, vv0, ae0, av0 = self.dp_original.eval(self.coords, self.box, self.atype, atomic = True) + ee1, ff1, vv1, ae1, av1 = self.dp_compressed.eval(self.coords, self.box, self.atype, atomic = True) + # check shape of the returns + nframes = 1 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ae0.shape, (nframes,natoms,1)) + self.assertEqual(av0.shape, (nframes,natoms,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + self.assertEqual(ae1.shape, (nframes,natoms,1)) + self.assertEqual(av1.shape, (nframes,natoms,9)) + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ae0, ae1, default_places) + np.testing.assert_almost_equal(av0, av1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) + + def test_2frame_atm(self): + coords2 = np.concatenate((self.coords, self.coords)) + box2 = np.concatenate((self.box, self.box)) + ee0, ff0, vv0, ae0, av0 = self.dp_original.eval(coords2, box2, self.atype, atomic = True) + ee1, ff1, vv1, ae1, av1 = self.dp_compressed.eval(coords2, box2, self.atype, atomic = True) + # check shape of the returns + nframes = 2 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ae0.shape, (nframes,natoms,1)) + self.assertEqual(av0.shape, (nframes,natoms,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + self.assertEqual(ae1.shape, (nframes,natoms,1)) + self.assertEqual(av1.shape, (nframes,natoms,9)) + + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ae0, ae1, default_places) + np.testing.assert_almost_equal(av0, av1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) + + +class TestDeepPotANoPBC(unittest.TestCase) : + @classmethod + def setUpClass(self): + self.dp_original = DeepPot(FROZEN_MODEL) + self.dp_compressed = DeepPot(COMPRESSED_MODEL) + self.coords = np.array([12.83, 2.56, 2.18, + 12.09, 2.87, 2.74, + 00.25, 3.32, 1.68, + 3.36, 3.00, 1.81, + 3.51, 2.51, 2.60, + 4.27, 3.22, 1.56]) + self.atype = [0, 1, 1, 0, 1, 1] + self.box = None + + def test_1frame(self): + ee0, ff0, vv0 = self.dp_original.eval(self.coords, self.box, self.atype, atomic = False) + ee1, ff1, vv1 = self.dp_compressed.eval(self.coords, self.box, self.atype, atomic = False) + # check shape of the returns + nframes = 1 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) + + def test_1frame_atm(self): + ee0, ff0, vv0, ae0, av0 = self.dp_original.eval(self.coords, self.box, self.atype, atomic = True) + ee1, ff1, vv1, ae1, av1 = self.dp_compressed.eval(self.coords, self.box, self.atype, atomic = True) + # check shape of the returns + nframes = 1 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ae0.shape, (nframes,natoms,1)) + self.assertEqual(av0.shape, (nframes,natoms,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + self.assertEqual(ae1.shape, (nframes,natoms,1)) + self.assertEqual(av1.shape, (nframes,natoms,9)) + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ae0, ae1, default_places) + np.testing.assert_almost_equal(av0, av1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) + + def test_2frame_atm(self): + coords2 = np.concatenate((self.coords, self.coords)) + ee0, ff0, vv0, ae0, av0 = self.dp_original.eval(coords2, self.box, self.atype, atomic = True) + ee1, ff1, vv1, ae1, av1 = self.dp_compressed.eval(coords2, self.box, self.atype, atomic = True) + # check shape of the returns + nframes = 2 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ae0.shape, (nframes,natoms,1)) + self.assertEqual(av0.shape, (nframes,natoms,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + self.assertEqual(ae1.shape, (nframes,natoms,1)) + self.assertEqual(av1.shape, (nframes,natoms,9)) + + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ae0, ae1, default_places) + np.testing.assert_almost_equal(av0, av1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) + + +class TestDeepPotALargeBoxNoPBC(unittest.TestCase) : + @classmethod + def setUpClass(self): + self.dp_original = DeepPot(FROZEN_MODEL) + self.dp_compressed = DeepPot(COMPRESSED_MODEL) + self.coords = np.array([12.83, 2.56, 2.18, + 12.09, 2.87, 2.74, + 00.25, 3.32, 1.68, + 3.36, 3.00, 1.81, + 3.51, 2.51, 2.60, + 4.27, 3.22, 1.56]) + self.atype = [0, 1, 1, 0, 1, 1] + self.box = np.array([19., 0., 0., 0., 13., 0., 0., 0., 13.]) + + def test_1frame(self): + ee0, ff0, vv0 = self.dp_original.eval(self.coords, self.box, self.atype, atomic = False) + ee1, ff1, vv1 = self.dp_compressed.eval(self.coords, self.box, self.atype, atomic = False) + # check shape of the returns + nframes = 1 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) + + def test_1frame_atm(self): + ee0, ff0, vv0, ae0, av0 = self.dp_original.eval(self.coords, self.box, self.atype, atomic = True) + ee1, ff1, vv1, ae1, av1 = self.dp_compressed.eval(self.coords, self.box, self.atype, atomic = True) + # check shape of the returns + nframes = 1 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ae0.shape, (nframes,natoms,1)) + self.assertEqual(av0.shape, (nframes,natoms,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + self.assertEqual(ae1.shape, (nframes,natoms,1)) + self.assertEqual(av1.shape, (nframes,natoms,9)) + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ae0, ae1, default_places) + np.testing.assert_almost_equal(av0, av1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) + + def test_ase(self): + from ase import Atoms + from deepmd.calculator import DP + water0 = Atoms('OHHOHH', + positions=self.coords.reshape((-1,3)), + cell=self.box.reshape((3,3)), + calculator=DP(FROZEN_MODEL)) + water1 = Atoms('OHHOHH', + positions=self.coords.reshape((-1,3)), + cell=self.box.reshape((3,3)), + calculator=DP(COMPRESSED_MODEL)) + ee0 = water0.get_potential_energy() + ff0 = water0.get_forces() + ee1 = water1.get_potential_energy() + ff1 = water1.get_forces() + nframes = 1 + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + +class TestDeepPotAPBCExcludeTypes(unittest.TestCase) : + @classmethod + def setUpClass(self): + self.dp_original = DeepPot(FROZEN_MODEL) + self.dp_compressed = DeepPot(COMPRESSED_MODEL) + self.coords = np.array([12.83, 2.56, 2.18, + 12.09, 2.87, 2.74, + 00.25, 3.32, 1.68, + 3.36, 3.00, 1.81, + 3.51, 2.51, 2.60, + 4.27, 3.22, 1.56]) + self.atype = [0, 1, 1, 0, 1, 1] + self.box = np.array([13., 0., 0., 0., 13., 0., 0., 0., 13.]) + + @classmethod + def tearDownClass(self): + _file_delete(INPUT) + _file_delete(FROZEN_MODEL) + _file_delete(COMPRESSED_MODEL) + _file_delete("out.json") + _file_delete("compress.json") + _file_delete("checkpoint") + _file_delete("model.ckpt.meta") + _file_delete("model.ckpt.index") + _file_delete("model.ckpt.data-00000-of-00001") + _file_delete("model.ckpt-100.meta") + _file_delete("model.ckpt-100.index") + _file_delete("model.ckpt-100.data-00000-of-00001") + _file_delete("model-compression/checkpoint") + _file_delete("model-compression/model.ckpt.meta") + _file_delete("model-compression/model.ckpt.index") + _file_delete("model-compression/model.ckpt.data-00000-of-00001") + _file_delete("model-compression") + _file_delete("input_v2_compat.json") + _file_delete("lcurve.out") + + def test_attrs(self): + self.assertEqual(self.dp_original.get_ntypes(), 2) + self.assertAlmostEqual(self.dp_original.get_rcut(), 6.0, places = default_places) + self.assertEqual(self.dp_original.get_type_map(), ['O', 'H']) + self.assertEqual(self.dp_original.get_dim_fparam(), 0) + self.assertEqual(self.dp_original.get_dim_aparam(), 0) + + self.assertEqual(self.dp_compressed.get_ntypes(), 2) + self.assertAlmostEqual(self.dp_compressed.get_rcut(), 6.0, places = default_places) + self.assertEqual(self.dp_compressed.get_type_map(), ['O', 'H']) + self.assertEqual(self.dp_compressed.get_dim_fparam(), 0) + self.assertEqual(self.dp_compressed.get_dim_aparam(), 0) + + def test_1frame(self): + ee0, ff0, vv0 = self.dp_original.eval(self.coords, self.box, self.atype, atomic = False) + ee1, ff1, vv1 = self.dp_compressed.eval(self.coords, self.box, self.atype, atomic = False) + # check shape of the returns + nframes = 1 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) + + def test_1frame_atm(self): + ee0, ff0, vv0, ae0, av0 = self.dp_original.eval(self.coords, self.box, self.atype, atomic = True) + ee1, ff1, vv1, ae1, av1 = self.dp_compressed.eval(self.coords, self.box, self.atype, atomic = True) + # check shape of the returns + nframes = 1 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ae0.shape, (nframes,natoms,1)) + self.assertEqual(av0.shape, (nframes,natoms,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + self.assertEqual(ae1.shape, (nframes,natoms,1)) + self.assertEqual(av1.shape, (nframes,natoms,9)) + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ae0, ae1, default_places) + np.testing.assert_almost_equal(av0, av1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) + + def test_2frame_atm(self): + coords2 = np.concatenate((self.coords, self.coords)) + box2 = np.concatenate((self.box, self.box)) + ee0, ff0, vv0, ae0, av0 = self.dp_original.eval(coords2, box2, self.atype, atomic = True) + ee1, ff1, vv1, ae1, av1 = self.dp_compressed.eval(coords2, box2, self.atype, atomic = True) + # check shape of the returns + nframes = 2 + natoms = len(self.atype) + self.assertEqual(ee0.shape, (nframes,1)) + self.assertEqual(ff0.shape, (nframes,natoms,3)) + self.assertEqual(vv0.shape, (nframes,9)) + self.assertEqual(ae0.shape, (nframes,natoms,1)) + self.assertEqual(av0.shape, (nframes,natoms,9)) + self.assertEqual(ee1.shape, (nframes,1)) + self.assertEqual(ff1.shape, (nframes,natoms,3)) + self.assertEqual(vv1.shape, (nframes,9)) + self.assertEqual(ae1.shape, (nframes,natoms,1)) + self.assertEqual(av1.shape, (nframes,natoms,9)) + + # check values + np.testing.assert_almost_equal(ff0, ff1, default_places) + np.testing.assert_almost_equal(ae0, ae1, default_places) + np.testing.assert_almost_equal(av0, av1, default_places) + np.testing.assert_almost_equal(ee0, ee1, default_places) + np.testing.assert_almost_equal(vv0, vv1, default_places) \ No newline at end of file diff --git a/source/tests/test_model_compression_se_t.py b/source/tests/test_model_compression_se_t.py index 5d1efc08a2..4c77a392c7 100644 --- a/source/tests/test_model_compression_se_t.py +++ b/source/tests/test_model_compression_se_t.py @@ -339,6 +339,8 @@ def tearDownClass(self): _file_delete("model-compression/model.ckpt.index") _file_delete("model-compression/model.ckpt.data-00000-of-00001") _file_delete("model-compression") + _file_delete("input_v2_compat.json") + _file_delete("lcurve.out") def test_attrs(self): self.assertEqual(self.dp_original.get_ntypes(), 2) From ceb9f16813c18415b56b1be12b349dfc72a1d8b0 Mon Sep 17 00:00:00 2001 From: Denghui Lu Date: Thu, 13 Jan 2022 10:38:29 +0800 Subject: [PATCH 148/161] fix gelu grad multi definitions error (#1406) * fix gelu grad multi definitions * use approximate gelu function Co-authored-by: Jinzhe Zeng * fix UT error Co-authored-by: Jinzhe Zeng --- deepmd/common.py | 8 +++++++- source/op/_gelu.py | 17 ++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/deepmd/common.py b/deepmd/common.py index 695fee1a93..1f9d3afb0c 100644 --- a/deepmd/common.py +++ b/deepmd/common.py @@ -2,6 +2,7 @@ import json import warnings +import tensorflow from functools import wraps from pathlib import Path from typing import ( @@ -64,7 +65,12 @@ def gelu(x: tf.Tensor) -> tf.Tensor: Original paper https://arxiv.org/abs/1606.08415 """ - return op_module.gelu(x) + def gelu_wrapper(x): + try: + return tensorflow.nn.gelu(x, approximate=True) + except AttributeError: + return op_module.gelu(x) + return (lambda x: gelu_wrapper(x))(x) # TODO this is not a good way to do things. This is some global variable to which diff --git a/source/op/_gelu.py b/source/op/_gelu.py index ac0585da78..db45ef798e 100644 --- a/source/op/_gelu.py +++ b/source/op/_gelu.py @@ -2,14 +2,17 @@ """ First-order derivatives and second-order derivatives for gelu function. """ - +import tensorflow from tensorflow.python.framework import ops from deepmd.env import op_module -@ops.RegisterGradient("Gelu") -def _gelu_cc (op, dy) : - return op_module.gelu_grad(dy, op.inputs[0]) +try: + gelu = tensorflow.nn.gelu +except AttributeError: + @ops.RegisterGradient("Gelu") + def _gelu_cc (op, dy) : + return op_module.gelu_grad(dy, op.inputs[0]) -@ops.RegisterGradient("GeluGrad") -def _gelu_grad_cc (op, dy) : - return [op_module.gelu_grad(dy, op.inputs[1]), op_module.gelu_grad_grad(dy, op.inputs[0], op.inputs[1])] + @ops.RegisterGradient("GeluGrad") + def _gelu_grad_cc (op, dy) : + return [op_module.gelu_grad(dy, op.inputs[1]), op_module.gelu_grad_grad(dy, op.inputs[0], op.inputs[1])] From 0f6d644e085b4f5419fb58402c6b02c808d322c5 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Wed, 12 Jan 2022 21:41:34 -0500 Subject: [PATCH 149/161] introduce TensorFlow Profiler (#1414) Enable by setting `enable_profiler` to true. Fix #1407. --- deepmd/entrypoints/freeze.py | 1 + deepmd/env.py | 4 ++++ deepmd/train/trainer.py | 8 +++++++- deepmd/utils/argcheck.py | 2 ++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/deepmd/entrypoints/freeze.py b/deepmd/entrypoints/freeze.py index 172c765646..1f816d083e 100755 --- a/deepmd/entrypoints/freeze.py +++ b/deepmd/entrypoints/freeze.py @@ -9,6 +9,7 @@ import logging import google.protobuf.message from deepmd.env import tf, FITTING_NET_PATTERN +from deepmd.utils.errors import GraphTooLargeError from deepmd.utils.sess import run_sess from deepmd.utils.graph import get_pattern_nodes_from_graph_def from os.path import abspath diff --git a/deepmd/env.py b/deepmd/env.py index f8088c69d4..5942b4c062 100644 --- a/deepmd/env.py +++ b/deepmd/env.py @@ -21,6 +21,10 @@ tf.disable_v2_behavior() except ImportError: import tensorflow as tf +try: + import tensorflow.compat.v2 as tfv2 +except ImportError: + tfv2 = None __all__ = [ "GLOBAL_CONFIG", diff --git a/deepmd/train/trainer.py b/deepmd/train/trainer.py index ea75b30bd1..927074c1c6 100644 --- a/deepmd/train/trainer.py +++ b/deepmd/train/trainer.py @@ -9,7 +9,7 @@ import numpy as np from packaging.version import Version -from deepmd.env import tf +from deepmd.env import tf, tfv2 from deepmd.env import get_tf_session_config from deepmd.env import GLOBAL_TF_FLOAT_PRECISION from deepmd.env import GLOBAL_ENER_FLOAT_PRECISION @@ -226,6 +226,7 @@ def _init_param(self, jdata): self.timing_in_training = tr_data.get('time_training', True) self.profiling = self.run_opt.is_chief and tr_data.get('profiling', False) self.profiling_file = tr_data.get('profiling_file', 'timeline.json') + self.enable_profiler = tr_data.get('enable_profiler', False) self.tensorboard = self.run_opt.is_chief and tr_data.get('tensorboard', False) self.tensorboard_log_dir = tr_data.get('tensorboard_log_dir', 'log') self.tensorboard_freq = tr_data.get('tensorboard_freq', 1) @@ -480,6 +481,9 @@ def train (self, train_data = None, valid_data=None) : else: tb_train_writer = None tb_valid_writer = None + if self.enable_profiler: + # https://www.tensorflow.org/guide/profiler + tfv2.profiler.experimental.start(self.tensorboard_log_dir) train_time = 0 @@ -550,6 +554,8 @@ def train (self, train_data = None, valid_data=None) : chrome_trace = fetched_timeline.generate_chrome_trace_format() with open(self.profiling_file, 'w') as f: f.write(chrome_trace) + if self.enable_profiler and self.run_opt.is_chief: + tfv2.profiler.experimental.stop() def get_feed_dict(self, batch, is_training): feed_dict = {} diff --git a/deepmd/utils/argcheck.py b/deepmd/utils/argcheck.py index 847eccc52e..3c99b58196 100644 --- a/deepmd/utils/argcheck.py +++ b/deepmd/utils/argcheck.py @@ -629,6 +629,7 @@ def training_args(): # ! modified by Ziyao: data configuration isolated. doc_time_training = 'Timing durining training.' doc_profiling = 'Profiling during training.' doc_profiling_file = 'Output file for profiling.' + doc_enable_profiler = 'Enable TensorFlow Profiler (available in TensorFlow 2.3) to analyze performance. The log will be saved to `tensorboard_log_dir`.' doc_tensorboard = 'Enable tensorboard' doc_tensorboard_log_dir = 'The log directory of tensorboard outputs' doc_tensorboard_freq = 'The frequency of writing tensorboard events.' @@ -651,6 +652,7 @@ def training_args(): # ! modified by Ziyao: data configuration isolated. Argument("time_training", bool, optional=True, default=True, doc=doc_time_training), Argument("profiling", bool, optional=True, default=False, doc=doc_profiling), Argument("profiling_file", str, optional=True, default='timeline.json', doc=doc_profiling_file), + Argument("enable_profiler", bool, optional=True, default=False, doc=doc_enable_profiler), Argument("tensorboard", bool, optional=True, default=False, doc=doc_tensorboard), Argument("tensorboard_log_dir", str, optional=True, default='log', doc=doc_tensorboard_log_dir), Argument("tensorboard_freq", int, optional=True, default=1, doc=doc_tensorboard_freq), From e80e2228afec9874e5fffa8108f3954fee8a76eb Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Thu, 13 Jan 2022 20:49:14 -0500 Subject: [PATCH 150/161] only test/eval fitting properties (#1416) During profiling, I found that the virial will be calculated even if I don't provide virial data. This is meaningless and the the viral has never been outputted. This commit removes such behavior. (cherry picked from commit 9a55069b40dc2892adeed8b153c67ba2d407a62b) --- deepmd/loss/ener.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/deepmd/loss/ener.py b/deepmd/loss/ener.py index f3edac8800..e6c359b701 100644 --- a/deepmd/loss/ener.py +++ b/deepmd/loss/ener.py @@ -129,13 +129,14 @@ def build (self, return l2_loss, more_loss def eval(self, sess, feed_dict, natoms): + placeholder = tf.no_op() run_data = [ self.l2_l, - self.l2_more['l2_ener_loss'], - self.l2_more['l2_force_loss'], - self.l2_more['l2_virial_loss'], - self.l2_more['l2_atom_ener_loss'], - self.l2_more['l2_pref_force_loss'] + self.l2_more['l2_ener_loss'] if self.has_e else placeholder, + self.l2_more['l2_force_loss'] if self.has_f else placeholder, + self.l2_more['l2_virial_loss'] if self.has_v else placeholder, + self.l2_more['l2_atom_ener_loss'] if self.has_ae else placeholder, + self.l2_more['l2_pref_force_loss'] if self.has_pf else placeholder, ] error, error_e, error_f, error_v, error_ae, error_pf = run_sess(sess, run_data, feed_dict=feed_dict) results = {"natoms": natoms[0], "rmse": np.sqrt(error)} From d7795ca096ea29939ece5f8886e2389ab066467c Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sat, 15 Jan 2022 00:32:03 -0500 Subject: [PATCH 151/161] replace `tf.no_op` (#1419) `tf.no_op` has a strange pefermance issue... We need to use another tensor as placeholder. (cherry picked from commit f3b7189f8b3f9df85172cea51c84148423bc4bf8) --- deepmd/loss/ener.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deepmd/loss/ener.py b/deepmd/loss/ener.py index e6c359b701..29e1fa4068 100644 --- a/deepmd/loss/ener.py +++ b/deepmd/loss/ener.py @@ -129,7 +129,7 @@ def build (self, return l2_loss, more_loss def eval(self, sess, feed_dict, natoms): - placeholder = tf.no_op() + placeholder = self.l2_l run_data = [ self.l2_l, self.l2_more['l2_ener_loss'] if self.has_e else placeholder, From b88c1da26ac88b6d16c9071af5470428d8fc910c Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sat, 15 Jan 2022 00:42:12 -0500 Subject: [PATCH 152/161] remove the dependency on `inputs` from `inputs_zero` (#1417) * remove the dependency on `inputs` from `inputs_zero` When profiling on multiple CPU threads, I notice that `inputs_zero` always runs after descriptor is computed. However, Some threads have nothing to do during ProdEnvMatA. (cherry picked from commit e704b4af1df7c23465615454bf7dfc0fc2f8d18f) * fix UT * revert changes to the interface of the EnerFitting (cherry picked from commit 4f95d0152dc2f689a84e8bd89d5fc7e5e09c30f4) * Revert "fix UT" This reverts commit c43ac3a0e8856f4d0d6394100fdb0dacbad7fb18. * fix typo (cherry picked from commit a4c32c7d14757f4e84142ac9cc7b2f64e42efe76) * initialize input_dict * init input_dict --- deepmd/fit/ener.py | 16 ++++++++++------ deepmd/model/ener.py | 7 +++++-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/deepmd/fit/ener.py b/deepmd/fit/ener.py index dca4e0d353..71657e1b98 100644 --- a/deepmd/fit/ener.py +++ b/deepmd/fit/ener.py @@ -332,7 +332,7 @@ def _build_lower( def build (self, inputs : tf.Tensor, natoms : tf.Tensor, - input_dict : dict = {}, + input_dict : dict = None, reuse : bool = None, suffix : str = '', ) -> tf.Tensor: @@ -362,6 +362,8 @@ def build (self, ener The system energy """ + if input_dict is None: + input_dict = {} bias_atom_e = self.bias_atom_e if self.numb_fparam > 0 and ( self.fparam_avg is None or self.fparam_inv_std is None ): raise RuntimeError('No data stat result. one should do data statisitic, before build') @@ -401,7 +403,12 @@ def build (self, inputs = tf.reshape(inputs, [-1, self.dim_descrpt * natoms[0]]) if len(self.atom_ener): # only for atom_ener - inputs_zero = tf.zeros_like(inputs, dtype=self.fitting_precision) + nframes = input_dict.get('nframes') + if nframes is not None: + # like inputs, but we don't want to add a dependency on inputs + inputs_zero = tf.zeros((nframes, self.dim_descrpt * natoms[0]), dtype=self.fitting_precision) + else: + inputs_zero = tf.zeros_like(inputs, dtype=self.fitting_precision) if bias_atom_e is not None : @@ -419,10 +426,7 @@ def build (self, aparam = (aparam - t_aparam_avg) * t_aparam_istd aparam = tf.reshape(aparam, [-1, self.numb_aparam * natoms[0]]) - if input_dict is not None: - type_embedding = input_dict.get('type_embedding', None) - else: - type_embedding = None + type_embedding = input_dict.get('type_embedding', None) if type_embedding is not None: atype_embed = embed_atom_type(self.ntypes, natoms, type_embedding) atype_embed = tf.tile(atype_embed,[tf.shape(inputs)[0],1]) diff --git a/deepmd/model/ener.py b/deepmd/model/ener.py index c179f00f7b..574fbd9f16 100644 --- a/deepmd/model/ener.py +++ b/deepmd/model/ener.py @@ -117,7 +117,9 @@ def build (self, frz_model = None, suffix = '', reuse = None): - + + if input_dict is None: + input_dict = {} with tf.variable_scope('model_attr' + suffix, reuse = reuse) : t_tmap = tf.constant(' '.join(self.type_map), name = 'tmap', @@ -144,6 +146,7 @@ def build (self, coord = tf.reshape (coord_, [-1, natoms[1] * 3]) atype = tf.reshape (atype_, [-1, natoms[1]]) + input_dict['nframes'] = tf.shape(coord)[0] # type embedding if any if self.typeebd is not None: @@ -270,4 +273,4 @@ def build (self, def _import_graph_def_from_frz_model(self, frz_model, feed_dict, return_elements): graph, graph_def = load_graph_def(frz_model) - return tf.import_graph_def(graph_def, input_map = feed_dict, return_elements = return_elements, name = "") \ No newline at end of file + return tf.import_graph_def(graph_def, input_map = feed_dict, return_elements = return_elements, name = "") From a9d08a7061f214a4d95ffa4dfa12253bacd3c30c Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sun, 16 Jan 2022 19:58:14 -0500 Subject: [PATCH 153/161] support recursive detection for the system of model_devi (#1424) --- deepmd/entrypoints/main.py | 2 +- deepmd/infer/model_devi.py | 60 +++++++++++++++++++++----------------- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/deepmd/entrypoints/main.py b/deepmd/entrypoints/main.py index 632d35fd71..0f7372e0b9 100644 --- a/deepmd/entrypoints/main.py +++ b/deepmd/entrypoints/main.py @@ -361,7 +361,7 @@ def parse_args(args: Optional[List[str]] = None): "--system", default=".", type=str, - help="The system directory, not support recursive detection.", + help="The system directory. Recursively detect systems in this directory.", ) parser_model_devi.add_argument( "-S", "--set-prefix", default="set", type=str, help="The set prefix" diff --git a/deepmd/infer/model_devi.py b/deepmd/infer/model_devi.py index 3a9c7f0cc4..a859aadbc6 100644 --- a/deepmd/infer/model_devi.py +++ b/deepmd/infer/model_devi.py @@ -2,6 +2,7 @@ from .deep_pot import DeepPot from ..utils.data import DeepmdData from ..utils.batch_size import AutoBatchSize +from deepmd.common import expand_sys_str def calc_model_devi_f(fs: np.ndarray): @@ -56,11 +57,12 @@ def write_model_devi_out(devi: np.ndarray, fname: str): header = "%10s" % "step" for item in 'vf': header += "%19s%19s%19s" % (f"max_devi_{item}", f"min_devi_{item}", f"avg_devi_{item}") - np.savetxt(fname, - devi, - fmt=['%12d'] + ['%19.6e' for _ in range(6)], - delimiter='', - header=header) + with open(fname, "ab") as fp: + np.savetxt(fp, + devi, + fmt=['%12d'] + ['%19.6e' for _ in range(6)], + delimiter='', + header=header) return devi def _check_tmaps(tmaps, ref_tmap=None): @@ -185,25 +187,31 @@ def make_model_devi( tmap = tmaps[0] else: raise RuntimeError("The models does not have the same type map.") - - # create data-system - dp_data = DeepmdData(system, set_prefix, shuffle_test=False, type_map=tmap) - if dp_data.pbc: - nopbc = False - else: - nopbc = True - data_sets = [dp_data._load_set(set_name) for set_name in dp_data.dirs] - nframes_tot = 0 - devis = [] - for data in data_sets: - coord = data["coord"] - box = data["box"] - atype = data["type"][0] - devi = calc_model_devi(coord, box, atype, dp_models, nopbc=nopbc) - nframes_tot += coord.shape[0] - devis.append(devi) - devis = np.vstack(devis) - devis[:, 0] = np.arange(nframes_tot) * frequency - write_model_devi_out(devis, output) - return devis + all_sys = expand_sys_str(system) + if len(all_sys) == 0: + raise RuntimeError("Did not find valid system") + devis_coll = [] + for system in all_sys: + # create data-system + dp_data = DeepmdData(system, set_prefix, shuffle_test=False, type_map=tmap) + if dp_data.pbc: + nopbc = False + else: + nopbc = True + + data_sets = [dp_data._load_set(set_name) for set_name in dp_data.dirs] + nframes_tot = 0 + devis = [] + for data in data_sets: + coord = data["coord"] + box = data["box"] + atype = data["type"][0] + devi = calc_model_devi(coord, box, atype, dp_models, nopbc=nopbc) + nframes_tot += coord.shape[0] + devis.append(devi) + devis = np.vstack(devis) + devis[:, 0] = np.arange(nframes_tot) * frequency + write_model_devi_out(devis, output) + devis_coll.append(devis) + return devis_coll From 057e6ab3dba70733e035aac543c21d23dba2a506 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sun, 16 Jan 2022 20:01:43 -0500 Subject: [PATCH 154/161] enable TF remapper optimizer (#1418) TF supports a remapper optimizer which remaps subgraphs onto more efficient implementations by replacing commonly occuring subgraphs with optimized fused monolithic kernels. However, its support is limited: (1) MatMul + BiasAdd (not Add) + Activation; (2) Float32 (but not float64); (3) Activation is Tanh; (4) MKL is built and used. This commit replaces Add by BiasAdd in the NN. The speed of a single op can be improved by about 20% when TF is using MKL and precision is set to float32. One can find `_MklNativeFusedMatMul` op in the profiler. See also: - https://www.tensorflow.org/guide/graph_optimization - https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/grappler/optimizers/remapper.cc (cherry picked from commit 8f2dc4404ddfc6308e3d42d29a35ee98f5d5cddd) --- deepmd/utils/network.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deepmd/utils/network.py b/deepmd/utils/network.py index c82721becb..57dd90f893 100644 --- a/deepmd/utils/network.py +++ b/deepmd/utils/network.py @@ -56,7 +56,7 @@ def one_layer(inputs, w = tf.cast(w, get_precision(mixed_prec['compute_prec'])) b = tf.cast(b, get_precision(mixed_prec['compute_prec'])) - hidden = tf.matmul(inputs, w) + b + hidden = tf.nn.bias_add(tf.matmul(inputs, w), b) if activation_fn != None and use_timestep : idt_initializer = tf.random_normal_initializer( stddev=0.001, @@ -196,7 +196,7 @@ def embedding_net(xx, variable_summaries(w, 'matrix_'+str(ii)+name_suffix) b = tf.get_variable('bias_'+str(ii)+name_suffix, - [1, outputs_size[ii]], + [outputs_size[ii]], precision, b_initializer, trainable = trainable) @@ -206,7 +206,7 @@ def embedding_net(xx, xx = tf.cast(xx, get_precision(mixed_prec['compute_prec'])) w = tf.cast(w, get_precision(mixed_prec['compute_prec'])) b = tf.cast(b, get_precision(mixed_prec['compute_prec'])) - hidden = tf.reshape(activation_fn(tf.matmul(xx, w) + b), [-1, outputs_size[ii]]) + hidden = tf.reshape(activation_fn(tf.nn.bias_add(tf.matmul(xx, w), b)), [-1, outputs_size[ii]]) if resnet_dt : idt_initializer = tf.random_normal_initializer( stddev=0.001, From 7068698f0b9664c9941c17b58a4f95f8a8f9f304 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Thu, 20 Jan 2022 20:53:23 -0500 Subject: [PATCH 155/161] dynamically load op library in C++ interface (#1384) * dynamically load op library in C++ interface C++ interface will dynamically load OP libraries, just like Python interface, so it no longer needs linking. * version cannot be NULL... * set LD_LIBRARY_PATH * install runUnitTest * remove CMAKE_LINK_WHAT_YOU_USE It is not necessary anymore. * also remove CMAKE_LINK_WHAT_YOU_USE in api_cc/tests * change the type of op library to MODULE * add the absolute path of library directory to cc rpath * Revert "add the absolute path of library directory to cc rpath" This reverts commit fabdac91d42004409042c87456930797fdc39880. * add `-Wl,--disable-new-dtags` * Revert "add `-Wl,--disable-new-dtags`" This reverts commit ecccb578ed959b44fea7aa52222f821b65739239. * dlopen from dp lib but not TF --- .gitignore | 2 ++ source/CMakeLists.txt | 3 --- source/api_cc/CMakeLists.txt | 1 - source/api_cc/include/common.h | 6 ++++++ source/api_cc/src/DataModifier.cc | 1 + source/api_cc/src/DeepPot.cc | 1 + source/api_cc/src/DeepTensor.cc | 1 + source/api_cc/src/common.cc | 13 +++++++++++++ source/api_cc/tests/CMakeLists.txt | 20 ++++++++++++++++---- source/install/test_cc.sh | 11 +++++++---- source/install/test_cc_local.sh | 14 ++++++++------ source/lib/tests/CMakeLists.txt | 3 +++ source/op/CMakeLists.txt | 6 +++--- 13 files changed, 61 insertions(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index b5c08d1f7a..d515e1c5ca 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,8 @@ _templates API_CC doc/api_py/ dp/ +dp_test/ +dp_test_cc/ build_lammps/ .idea/ build_tests/ diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index a3d62c09b4..d706a6d292 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -1,8 +1,5 @@ cmake_minimum_required(VERSION 3.7) project(DeePMD) -if (CMAKE_COMPILER_IS_GNUCXX) - set(CMAKE_LINK_WHAT_YOU_USE TRUE) -endif () # build cpp or python interfaces if (NOT DEFINED BUILD_CPP_IF) diff --git a/source/api_cc/CMakeLists.txt b/source/api_cc/CMakeLists.txt index 895a06baea..830ecd6b78 100644 --- a/source/api_cc/CMakeLists.txt +++ b/source/api_cc/CMakeLists.txt @@ -17,7 +17,6 @@ add_library(${libname} SHARED ${LIB_SRC}) # link: libdeepmd libdeepmd_op libtensorflow_cc libtensorflow_framework target_link_libraries (${libname} PUBLIC ${LIB_DEEPMD} ${TensorFlow_LIBRARY} ${TensorFlowFramework_LIBRARY}) -target_link_libraries (${libname} PRIVATE ${LIB_DEEPMD_OP}) target_include_directories(${libname} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR} ${TensorFlow_INCLUDE_DIRS}) set_target_properties( diff --git a/source/api_cc/include/common.h b/source/api_cc/include/common.h index 96f6057358..a07caac546 100644 --- a/source/api_cc/include/common.h +++ b/source/api_cc/include/common.h @@ -116,6 +116,12 @@ void get_env_nthreads(int & num_intra_nthreads, int & num_inter_nthreads); +/** + * @brief Dynamically load OP library. This should be called before loading graphs. + */ +void +load_op_library(); + /** @struct deepmd::deepmd_exception **/ diff --git a/source/api_cc/src/DataModifier.cc b/source/api_cc/src/DataModifier.cc index f704ba599e..c6c009b0d8 100644 --- a/source/api_cc/src/DataModifier.cc +++ b/source/api_cc/src/DataModifier.cc @@ -33,6 +33,7 @@ init (const std::string & model, get_env_nthreads(num_intra_nthreads, num_inter_nthreads); options.config.set_inter_op_parallelism_threads(num_inter_nthreads); options.config.set_intra_op_parallelism_threads(num_intra_nthreads); + deepmd::load_op_library(); deepmd::check_status(NewSession(options, &session)); deepmd::check_status(ReadBinaryProto(Env::Default(), model, &graph_def)); deepmd::check_status(session->Create(graph_def)); diff --git a/source/api_cc/src/DeepPot.cc b/source/api_cc/src/DeepPot.cc index 8bf212e520..047c665e8d 100644 --- a/source/api_cc/src/DeepPot.cc +++ b/source/api_cc/src/DeepPot.cc @@ -189,6 +189,7 @@ init (const std::string & model, const int & gpu_rank, const std::string & file_ get_env_nthreads(num_intra_nthreads, num_inter_nthreads); options.config.set_inter_op_parallelism_threads(num_inter_nthreads); options.config.set_intra_op_parallelism_threads(num_intra_nthreads); + deepmd::load_op_library(); if(file_content.size() == 0) check_status (ReadBinaryProto(Env::Default(), model, &graph_def)); diff --git a/source/api_cc/src/DeepTensor.cc b/source/api_cc/src/DeepTensor.cc index 419c97aa65..316b6ec3a9 100644 --- a/source/api_cc/src/DeepTensor.cc +++ b/source/api_cc/src/DeepTensor.cc @@ -33,6 +33,7 @@ init (const std::string & model, get_env_nthreads(num_intra_nthreads, num_inter_nthreads); options.config.set_inter_op_parallelism_threads(num_inter_nthreads); options.config.set_intra_op_parallelism_threads(num_intra_nthreads); + deepmd::load_op_library(); deepmd::check_status (NewSession(options, &session)); deepmd::check_status (ReadBinaryProto(Env::Default(), model, &graph_def)); deepmd::check_status (session->Create(graph_def)); diff --git a/source/api_cc/src/common.cc b/source/api_cc/src/common.cc index 8693c14b5c..b19f79d42e 100644 --- a/source/api_cc/src/common.cc +++ b/source/api_cc/src/common.cc @@ -1,6 +1,7 @@ #include "common.h" #include "AtomMap.h" #include "device.h" +#include using namespace tensorflow; @@ -228,6 +229,18 @@ get_env_nthreads(int & num_intra_nthreads, } } +void +deepmd:: +load_op_library() +{ + tensorflow::Env* env = tensorflow::Env::Default(); + std::string dso_path = env->FormatLibraryFileName("deepmd_op", ""); + void* dso_handle = dlopen(dso_path.c_str(), RTLD_NOW | RTLD_LOCAL); + if (!dso_handle) { + throw deepmd::deepmd_exception(dso_path + " is not found! You can add the library directory to LD_LIBRARY_PATH"); + } +} + std::string deepmd:: name_prefix(const std::string & scope) diff --git a/source/api_cc/tests/CMakeLists.txt b/source/api_cc/tests/CMakeLists.txt index caa62c82f8..bd7c23a5a3 100644 --- a/source/api_cc/tests/CMakeLists.txt +++ b/source/api_cc/tests/CMakeLists.txt @@ -1,6 +1,5 @@ cmake_minimum_required(VERSION 3.9) project(deepmd_api_test) -set(CMAKE_LINK_WHAT_YOU_USE TRUE) if (NOT DEFINED BUILD_CPP_IF) set(BUILD_CPP_IF TRUE) @@ -99,11 +98,11 @@ else() endif() if (USE_CUDA_TOOLKIT) - target_link_libraries(runUnitTests gtest gtest_main ${libname} ${apiname} ${opname} pthread ${TensorFlow_LIBRARY} rt deepmd_op_cuda coverage_config) + target_link_libraries(runUnitTests gtest gtest_main ${libname} ${apiname} pthread ${TensorFlow_LIBRARY} rt deepmd_op_cuda coverage_config) elseif(USE_ROCM_TOOLKIT) - target_link_libraries(runUnitTests gtest gtest_main ${libname} ${apiname} ${opname} pthread ${TensorFlow_LIBRARY} rt deepmd_op_rocm coverage_config) + target_link_libraries(runUnitTests gtest gtest_main ${libname} ${apiname} pthread ${TensorFlow_LIBRARY} rt deepmd_op_rocm coverage_config) else() - target_link_libraries(runUnitTests gtest gtest_main ${libname} ${apiname} ${opname} pthread ${TensorFlow_LIBRARY} rt coverage_config) + target_link_libraries(runUnitTests gtest gtest_main ${libname} ${apiname} pthread ${TensorFlow_LIBRARY} rt coverage_config) endif() find_package(Protobuf) @@ -134,3 +133,16 @@ if(NOT GTEST_LIBRARIES) else () include_directories(${GTEST_INCLUDE_DIRS}) endif () + +set_target_properties( + runUnitTests + PROPERTIES + INSTALL_RPATH "$ORIGIN/../lib" +) +set_target_properties( + ${apiname} + PROPERTIES + INSTALL_RPATH "$ORIGIN;${TensorFlow_LIBRARY_PATH}" +) +install(TARGETS runUnitTests DESTINATION bin/) +install(TARGETS ${libname} ${apiname} ${opname} DESTINATION lib/) diff --git a/source/install/test_cc.sh b/source/install/test_cc.sh index 60463c5bc9..6b07b802ba 100755 --- a/source/install/test_cc.sh +++ b/source/install/test_cc.sh @@ -7,29 +7,32 @@ NPROC=$(nproc --all) #------------------ +INSTALL_PREFIX=${SCRIPT_PATH}/../../dp_test BUILD_TMP_DIR=${SCRIPT_PATH}/../build_tests mkdir -p ${BUILD_TMP_DIR} cd ${BUILD_TMP_DIR} -cmake ../lib/tests +cmake ../lib/tests -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} make -j${NPROC} +make install #------------------ -${BUILD_TMP_DIR}/runUnitTests +${INSTALL_PREFIX}/bin/runUnitTests #------------------ BUILD_TMP_DIR=${SCRIPT_PATH}/../build_cc_tests -INSTALL_PREFIX=${SCRIPT_PATH}/../../dp +INSTALL_PREFIX=${SCRIPT_PATH}/../../dp_test_cc mkdir -p ${BUILD_TMP_DIR} mkdir -p ${INSTALL_PREFIX} cd ${BUILD_TMP_DIR} cmake -DINSTALL_TENSORFLOW=TRUE -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} ../api_cc/tests make -j${NPROC} +make install #------------------ cd ${SCRIPT_PATH}/../api_cc/tests -${BUILD_TMP_DIR}/runUnitTests +${INSTALL_PREFIX}/bin/runUnitTests #------------------ # upload to codecov diff --git a/source/install/test_cc_local.sh b/source/install/test_cc_local.sh index 71d6954d46..e5a1070186 100755 --- a/source/install/test_cc_local.sh +++ b/source/install/test_cc_local.sh @@ -6,27 +6,29 @@ SCRIPT_PATH=$(dirname $(realpath -s $0)) NPROC=$(nproc --all) #------------------ - +INSTALL_PREFIX=${SCRIPT_PATH}/../../dp_test BUILD_TMP_DIR=${SCRIPT_PATH}/../build_tests mkdir -p ${BUILD_TMP_DIR} cd ${BUILD_TMP_DIR} -cmake ../lib/tests +cmake ../lib/tests -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} make -j${NPROC} +make install #------------------ -${BUILD_TMP_DIR}/runUnitTests +${INSTALL_PREFIX}/bin/runUnitTests #------------------ echo "try to find tensorflow in ${tensorflow_root}" BUILD_TMP_DIR=${SCRIPT_PATH}/../build_cc_tests -INSTALL_PREFIX=${SCRIPT_PATH}/../../dp +INSTALL_PREFIX=${SCRIPT_PATH}/../../dp_test_cc mkdir -p ${BUILD_TMP_DIR} cd ${BUILD_TMP_DIR} -cmake -DINSTALL_TENSORFLOW=FALSE -DTENSORFLOW_ROOT=${tensorflow_root} ../api_cc/tests +cmake -DINSTALL_TENSORFLOW=FALSE -DTENSORFLOW_ROOT=${tensorflow_root} ../api_cc/tests -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} make -j${NPROC} +make install #------------------ cd ${SCRIPT_PATH}/../api_cc/tests -${BUILD_TMP_DIR}/runUnitTests +${INSTALL_PREFIX}/bin/runUnitTests diff --git a/source/lib/tests/CMakeLists.txt b/source/lib/tests/CMakeLists.txt index b5a0460c54..8c3a3e4c16 100644 --- a/source/lib/tests/CMakeLists.txt +++ b/source/lib/tests/CMakeLists.txt @@ -106,3 +106,6 @@ if(NOT GTEST_LIBRARIES) else () include_directories(${GTEST_INCLUDE_DIRS}) endif () + +install(TARGETS runUnitTests DESTINATION bin/) +install(TARGETS runUnitTests DESTINATION lib/) diff --git a/source/op/CMakeLists.txt b/source/op/CMakeLists.txt index 96c02ec71c..8b483b2e5d 100644 --- a/source/op/CMakeLists.txt +++ b/source/op/CMakeLists.txt @@ -8,7 +8,7 @@ file(GLOB OP_GRADS_SRC custom_op.cc prod_force_grad.cc prod_force_grad_multi_dev file(GLOB OP_PY *.py) if (BUILD_CPP_IF) - add_library(${LIB_DEEPMD_OP} SHARED ${OP_SRC}) + add_library(${LIB_DEEPMD_OP} MODULE ${OP_SRC}) # link: libdeepmd libtensorflow_cc libtensorflow_framework target_link_libraries (${LIB_DEEPMD_OP} PUBLIC ${TensorFlow_LIBRARY} ${TensorFlowFramework_LIBRARY}) target_link_libraries (${LIB_DEEPMD_OP} PRIVATE ${LIB_DEEPMD}) @@ -17,8 +17,8 @@ if (BUILD_CPP_IF) endif (BUILD_CPP_IF) if (BUILD_PY_IF) - add_library(op_abi SHARED ${OP_SRC} ${OP_LIB}) - add_library(op_grads SHARED ${OP_GRADS_SRC}) + add_library(op_abi MODULE ${OP_SRC} ${OP_LIB}) + add_library(op_grads MODULE ${OP_GRADS_SRC}) message(STATUS ${TensorFlowFramework_LIBRARY}) # link: libdeepmd libtensorflow_framework From f5b0abb7acc63aec7bd144ee914f0290bc690f05 Mon Sep 17 00:00:00 2001 From: readthedocs-assistant <96542097+readthedocs-assistant@users.noreply.github.com> Date: Fri, 21 Jan 2022 02:55:08 +0100 Subject: [PATCH 156/161] Update Read the Docs configuration (automatic) (#1431) The following migrators were applied: - Migrate to `build.tools` configuration. This uses the new base Docker image based on Ubuntu 20.04 introduced in October 2021 and picks an appropriate Python version for your project (read [our blog post](https://blog.readthedocs.com/new-build-specification/) for details). Notice that now you can specify the Node.js, Rust, and Go versions as well. *Note:* Some system dependencies are not preinstalled anymore, so this might require manually adding them to `build.apt_packages` (see [our documentation](https://docs.readthedocs.io/en/stable/config-file/v2.html#build-apt-packages>)). - Migrate to Mamba as a drop-in replacement for Conda. Your project requested using Mamba instead of Conda for performance reasons. Now this is included in your configuration and you can change it without our intervention. Co-authored-by: Han Wang --- .readthedocs.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.readthedocs.yml b/.readthedocs.yml index 90fedfd292..6f3ff6be3f 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,4 +1,8 @@ version: 2 +build: + os: ubuntu-20.04 + tools: + python: mambaforge-4.10 conda: environment: doc/environment.yml formats: all From 83f5dc8e2e42a1bafec5eef026c6759c715b8dbb Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sat, 22 Jan 2022 04:03:55 -0500 Subject: [PATCH 157/161] fix `cast_precision` breaking docstring (#1437) --- deepmd/common.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/deepmd/common.py b/deepmd/common.py index 1f9d3afb0c..d32422f0db 100644 --- a/deepmd/common.py +++ b/deepmd/common.py @@ -538,11 +538,6 @@ def cast_precision(func: Callable) -> Callable: If it does not match (e.g. it is an integer), the decorator will do nothing on it. - Parameters - ---------- - precision : tf.DType - Tensor data type that casts to - Returns ------- Callable @@ -560,6 +555,7 @@ def cast_precision(func: Callable) -> Callable: ... def f(x: tf.Tensor, y: tf.Tensor) -> tf.Tensor: ... return x ** 2 + y """ + @wraps(func) def wrapper(self, *args, **kwargs): # only convert tensors returned_tensor = func( From 6238482cf2e83b467fe8bdeb43ede1897a9861d0 Mon Sep 17 00:00:00 2001 From: liangadam <45301969+liangadam@users.noreply.github.com> Date: Sat, 22 Jan 2022 17:06:12 +0800 Subject: [PATCH 158/161] Add image link of ROCm version. (#1432) * Add image link of ROCm version. * Update easy-install.md Co-authored-by: Han Wang --- doc/install/easy-install.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/install/easy-install.md b/doc/install/easy-install.md index 55720b59e4..b33adf980d 100644 --- a/doc/install/easy-install.md +++ b/doc/install/easy-install.md @@ -53,3 +53,8 @@ To pull the GPU version: ```bash docker pull ghcr.io/deepmodeling/deepmd-kit:2.0.0_cuda10.1_gpu ``` + +To pull the ROCm version: +```bash +docker pull deepmodeling/dpmdkit-rocm:dp2.0.3-rocm4.5.2-tf2.6-lmp29Sep2021 +``` From bcfb127c4785e5f0783b3264acf31cba58bd1ba9 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 24 Jan 2022 01:31:39 -0500 Subject: [PATCH 159/161] fix a typo in document (#1445) Fix #1339. --- doc/data/data-conv.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/data/data-conv.md b/doc/data/data-conv.md index 96a0cb4e30..4458e4eb17 100644 --- a/doc/data/data-conv.md +++ b/doc/data/data-conv.md @@ -51,5 +51,5 @@ box.raw coord.raw energy.raw force.raw set.000 set.001 set.002 type.raw ``` It generates three sets `set.000`, `set.001` and `set.002`, with each set contains 2000 frames. One do not need to take care of the binary data files in each of the `set.*` directories. The path containing `set.*` and `type.raw` is called a *system*. -If one needs to train a non-periodic system, an empty `nopbc` file should be put under the system directory. `box.raw` is not necessary is a non-periodic system. +If one needs to train a non-periodic system, an empty `nopbc` file should be put under the system directory. `box.raw` is not necessary in a non-periodic system. From 1ee6987954076e563ebbaac76191d5fd9f009a6c Mon Sep 17 00:00:00 2001 From: Han Wang Date: Fri, 28 Jan 2022 07:51:25 +0800 Subject: [PATCH 160/161] Dplr doc and examples (#1458) * add doc and examples for deep potential long-range * add "* *" to pair style. use warning section of RTD for warning message * rst syntax does not work in md. changed Co-authored-by: Han Wang --- README.md | 1 + doc/model/dplr.md | 171 +++++ doc/model/index.md | 3 +- doc/model/index.rst | 3 +- examples/water/dplr/lmp/conf.lmp | 663 ++++++++++++++++++ examples/water/dplr/lmp/in.lammps | 71 ++ .../dplr/train/data/set.000/atomic_dipole.npy | Bin 0 -> 92288 bytes .../water/dplr/train/data/set.000/box.npy | Bin 0 -> 1208 bytes .../water/dplr/train/data/set.000/coord.npy | Bin 0 -> 138368 bytes .../water/dplr/train/data/set.000/energy.npy | Bin 0 -> 248 bytes .../water/dplr/train/data/set.000/force.npy | Bin 0 -> 138368 bytes .../water/dplr/train/data/set.000/virial.npy | Bin 0 -> 1208 bytes examples/water/dplr/train/data/type.raw | 384 ++++++++++ examples/water/dplr/train/data/type_map.raw | 2 + examples/water/dplr/train/dw.json | 67 ++ examples/water/dplr/train/ener.json | 68 ++ 16 files changed, 1431 insertions(+), 2 deletions(-) create mode 100644 doc/model/dplr.md create mode 100644 examples/water/dplr/lmp/conf.lmp create mode 100644 examples/water/dplr/lmp/in.lammps create mode 100644 examples/water/dplr/train/data/set.000/atomic_dipole.npy create mode 100644 examples/water/dplr/train/data/set.000/box.npy create mode 100644 examples/water/dplr/train/data/set.000/coord.npy create mode 100644 examples/water/dplr/train/data/set.000/energy.npy create mode 100644 examples/water/dplr/train/data/set.000/force.npy create mode 100644 examples/water/dplr/train/data/set.000/virial.npy create mode 100644 examples/water/dplr/train/data/type.raw create mode 100644 examples/water/dplr/train/data/type_map.raw create mode 100644 examples/water/dplr/train/dw.json create mode 100644 examples/water/dplr/train/ener.json diff --git a/README.md b/README.md index 6e9d43648b..0b84c705dd 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,7 @@ A full [document](doc/train/train-input-auto.rst) on options in the training inp - [Fit energy](doc/model/train-energy.md) - [Fit `tensor` like `Dipole` and `Polarizability`](doc/model/train-fitting-tensor.md) - [Train a Deep Potential model using `type embedding` approach](doc/model/train-se-e2-a-tebd.md) + - [Deep potential long-range](doc/model/dplr.md) - [Training](doc/train/index.md) - [Training a model](doc/train/training.md) - [Advanced options](doc/train/training-advanced.md) diff --git a/doc/model/dplr.md b/doc/model/dplr.md new file mode 100644 index 0000000000..07184dc9cd --- /dev/null +++ b/doc/model/dplr.md @@ -0,0 +1,171 @@ +# Deep potential long-range (DPLR) + +Notice: **The interfaces of DPLR are not stable and subject to change** + +The method of DPLR is described in [this paper][1]. One is recommended to read the paper before using the DPLR. + +In the following, we take the DPLR model for example to introduce the training and LAMMPS simulation with the DPLR model. The DPLR model is training in two steps. + +### Train a deep Wannier model for Wannier centroids + +We use the deep Wannier model (DW) to represent the relative position of the Wannier centroid (WC) with the atom to which it is associated. One may consult the introduction of the [dipole model](train-fitting-tensor.md) for a detailed introduction. An example input `wc.json` and a small dataset `data` for tutorial purposes can be found in +```bash +$deepmd_source_dir/examples/water/dplr/train/ +``` +It is noted that **the tutorial dataset is not enough for training a productive model**. +Two settings make the training input script different from an energy training input: +```json + "fitting_net": { + "type": "dipole", + "dipole_type": [0], + "neuron": [128, 128, 128], + "seed": 1 + }, +``` +The type of fitting is set to `"dipole"`. The dipole is associate to type 0 atoms (oxygens), by the setting `"dipole_type": [0]`. What we trained is the displacement of the WC from the corresponding oxygen atom. It shares the same training input as atomic dipole because both are 3-dimensional vectors defined on atoms. +The loss section is provided as follows +```json + "loss": { + "type": "tensor", + "pref": 0.0, + "pref_atomic": 1.0 + }, +``` +so that the atomic dipole is trained as labels. Note that the numpy compressed file `atomic_dipole.npy` should be provided in each dataset. + +The training and freezing can be started from the example directory by +```bash +dp train dw.json && dp freeze -o dw.pb +``` + +### Train the DPLR model + +The training of the DPLR model is very similar to the standard short-range DP models. An example input script can be found in the example directory. The following section is introduced to compute the long-range energy contribution of the DPLR model, and modify the short-range DP model by this part. +```json + "modifier": { + "type": "dipole_charge", + "model_name": "dw.pb", + "model_charge_map": [-8], + "sys_charge_map": [6, 1], + "ewald_h": 1.00, + "ewald_beta": 0.40 + }, +``` +The `"model_name"` specifies which DW model is used to predict the position of WCs. `"model_charge_map"` gives the amount of charge assigned to WCs. `"sys_charge_map"` provides the nuclear charge of oxygen (type 0) and hydrogen (type 1) atoms. `"ewald_beta"` (unit A^{-1}) gives the spread parameter controls the spread of Gaussian charges, and `"ewald_h"` (unit A) assigns the grid size of Fourier transform. +The DPLR model can be trained and frozen by (from the example directory) +``` +dp train ener.json && dp freeze -o ener.pb +``` + +## Molecular dynamics simulation with DPLR + +In MD simulations, the long-range part of the DPLR is calculated by the LAMMPS `kspace` support. Then the long-range interaction is back-propagated to atoms by DeePMD-kit. This setup is commonly used in classical molecular dynamics simulations as the "virtual site". Unfortunately, LAMMPS does not natively support virtual sites, so we have to hack the LAMMPS code, which makes the input configuration and script a little wired. + +An example of input configuration file and script can be found in +```bash +$deepmd_source_dir/examples/water/dplr/lmp/ +``` + +We use `atom_style full` for DPLR simulations. the coordinates of the WCs are explicitly written to the configuration file. Moreover, a virtual bond is established between the oxygens and the WCs to indicate they are associated together. The configuration file containing 128 H2O molecules is thus written as +``` + +512 atoms +3 atom types +128 bonds +1 bond types + +0 16.421037674 xlo xhi +0 16.421037674 ylo yhi +0 16.421037674 zlo zhi +0 0 0 xy xz yz + +Masses + +1 16 +2 2 +3 16 + +Atoms + + 1 1 1 6 8.4960699081e+00 7.5073699951e+00 9.6371297836e+00 + 2 2 1 6 4.0597701073e+00 6.8156299591e+00 1.2051420212e+01 +... + + 385 1 3 -8 8.4960699081e+00 7.5073699951e+00 9.6371297836e+00 + 386 2 3 -8 4.0597701073e+00 6.8156299591e+00 1.2051420212e+01 +... + +Bonds + +1 1 1 385 +2 1 2 386 +... +``` +The oxygens and hydrogens are assigned with atom types 1 and 2 (corresponding to training atom types 0 and 1), respectively. The WCs are assigned with atom type 3. We want to simulate heavy water so the mass of hydrogens is set to 2. + +An example input script is provided in +```bash +$deepmd_source_dir/examples/water/dplr/lmp/in.lammps +``` +Here are some explanations +``` +# groups of real and virtual atoms +group real_atom type 1 2 +group virtual_atom type 3 + +# bond between real and its corresponding virtual site should be given +# to setup a map between real and virtual atoms. However, no real +# bonded interaction is applied, thus bond_sytle "zero" is used. +pair_style deepmd ener.pb +pair_coeff * * +bond_style zero +bond_coeff * +special_bonds lj/coul 1 1 1 angle no +``` +Type 1 and 2 (O and H) are `real_atom`s, while type 3 (WCs) are `virtual_atom`s. The model file `ener.pb` stores both the DW and DPLR models, so the position of WCs and the energy can be inferred from it. A virtual bond type is specified by `bond_style zero`. The `special_bonds` command switches off the exclusion of intramolecular interactions. + +``` +# kspace_style "pppm/dplr" should be used. in addition the +# gewald(1/distance) should be set the same as that used in +# training. Currently only ik differentiation is supported. +kspace_style pppm/dplr 1e-5 +kspace_modify gewald ${BETA} diff ik mesh ${KMESH} ${KMESH} ${KMESH} +``` +The long-range part is calculated by the `kspace` support of LAMMPS. The `kspace_style` `pppm/dplr` is required. The spread parameter set by variable `BETA` should be set the same as that used in training. The `KMESH` should be set dense enough so the long-range calculation is converged. + +``` +# "fix dplr" set the position of the virtual atom, and spread the +# electrostatic interaction asserting on the virtual atom to the real +# atoms. "type_associate" associates the real atom type its +# corresponding virtual atom type. "bond_type" gives the type of the +# bond between the real and virtual atoms. +fix 0 all dplr model ener.pb type_associate 1 3 bond_type 1 +fix_modify 0 virial yes +``` +The fix command `dplr` calculates the position of WCs by the DW model and back-propagates the long-range interaction on virtual atoms to real toms. + +``` +# compute the temperature of real atoms, excluding virtual atom contribution +compute real_temp real_atom temp +compute real_press all pressure real_temp +fix 1 real_atom nvt temp ${TEMP} ${TEMP} ${TAU_T} +fix_modify 1 temp real_temp +``` +The temperature of the system should be computed from the real atoms. The kinetic contribution in the pressure tensor is also computed from the real atoms. The thermostat is applied to only real atoms. The computed temperature and pressure of real atoms can be accessed by, e.g. +``` +fix thermo_print all print ${THERMO_FREQ} "$(step) $(pe) $(ke) $(etotal) $(enthalpy) $(c_real_temp) $(c_real_press) $(vol) $(c_real_press[1]) $(c_real_press[2]) $(c_real_press[3])" append thermo.out screen no title "# step pe ke etotal enthalpy temp press vol pxx pyy pzz" +``` + +The LAMMPS simulation can be started from the example directory by +``` +lmp -i in.lammps +``` +If LAMMPS complains that no model file `ener.pb` exists, it can be copied from the training example directory. + +The MD simulation lasts for only 20 steps. If one runs a longer simulation, it will blow up, because the model is trained with a very limited dataset for a very short training steps, thus is of poor quality. + +Another restriction should be noted is that the energies printed at the zero step is not correct. This is because at the zero step the position of the WC has not been updated with the DW model. The energies printed in later steps are correct. + + + +[1]: https://arxiv.org/abs/2112.13327 diff --git a/doc/model/index.md b/doc/model/index.md index 715860f7b1..cce3ec57ec 100644 --- a/doc/model/index.md +++ b/doc/model/index.md @@ -7,4 +7,5 @@ - [Descriptor `"hybrid"`](train-hybrid.md) - [Fit energy](train-energy.md) - [Fit `tensor` like `Dipole` and `Polarizability`](train-fitting-tensor.md) -- [Train a Deep Potential model using `type embedding` approach](train-se-e2-a-tebd.md) \ No newline at end of file +- [Train a Deep Potential model using `type embedding` approach](train-se-e2-a-tebd.md) +- [Deep potential long-range](dplr.md) diff --git a/doc/model/index.rst b/doc/model/index.rst index b264d95eca..1cc0f7e6ea 100644 --- a/doc/model/index.rst +++ b/doc/model/index.rst @@ -11,4 +11,5 @@ Model train-hybrid train-energy train-fitting-tensor - train-se-e2-a-tebd \ No newline at end of file + train-se-e2-a-tebd + dplr diff --git a/examples/water/dplr/lmp/conf.lmp b/examples/water/dplr/lmp/conf.lmp new file mode 100644 index 0000000000..11905861c9 --- /dev/null +++ b/examples/water/dplr/lmp/conf.lmp @@ -0,0 +1,663 @@ + + +512 atoms +3 atom types +128 bonds +1 bond types + +0 16.421037674 xlo xhi +0 16.421037674 ylo yhi +0 16.421037674 zlo zhi +0 0 0 xy xz yz + +Masses + +1 16 +2 2 +3 16 + +Atoms + + 1 1 1 6 8.4960699081e+00 7.5073699951e+00 9.6371297836e+00 + 2 2 1 6 4.0597701073e+00 6.8156299591e+00 1.2051420212e+01 + 3 3 1 6 2.6408200264e+00 1.0598219871e+01 9.3506002426e+00 + 4 4 1 6 1.1694219589e+01 1.4214019775e+01 1.3693120003e+01 + 5 5 1 6 8.7306203842e+00 1.3607620239e+01 1.2758020401e+01 + 6 6 1 6 1.0342249870e+01 2.8897099495e+00 1.3001319885e+01 + 7 7 1 6 1.6010500193e+00 6.4406399727e+00 5.3241600990e+00 + 8 8 1 6 3.1527600288e+00 1.2283720017e+01 1.5380720138e+01 + 9 9 1 6 1.4798919678e+01 1.5740220070e+01 6.1582899094e+00 + 10 10 1 6 9.2309398651e+00 1.5580719948e+01 8.5355997090e-01 + 11 11 1 6 9.1436700821e+00 1.6327819824e+01 1.0490420342e+01 + 12 12 1 6 4.6231899261e+00 4.7593097687e+00 1.5846320152e+01 + 13 13 1 6 1.1337719917e+01 1.1849120140e+01 1.7001099586e+00 + 14 14 1 6 1.0370820046e+01 9.8193197250e+00 1.3924719810e+01 + 15 15 1 6 9.5739898682e+00 9.3357095718e+00 5.4412398338e+00 + 16 16 1 6 5.6118898392e+00 7.8216400146e+00 4.7569899559e+00 + 17 17 1 6 8.1148195267e+00 6.3689699173e+00 1.4318619728e+01 + 18 18 1 6 5.1289701462e+00 1.3673819542e+01 6.8581700325e+00 + 19 19 1 6 1.0273889542e+01 1.6011220932e+01 3.5431900024e+00 + 20 20 1 6 9.8264398575e+00 3.6420900822e+00 6.4606800079e+00 + 21 21 1 6 1.1179019928e+01 9.7553701401e+00 8.1035804749e+00 + 22 22 1 6 9.3739500046e+00 7.7072300911e+00 1.2156920433e+01 + 23 23 1 6 3.4909999371e+00 3.5429000850e-01 1.0146929741e+01 + 24 24 1 6 3.9963800907e+00 1.5806999800e-01 1.5749620438e+01 + 25 25 1 6 5.4874998330e-01 1.1597120285e+01 1.5405719757e+01 + 26 26 1 6 1.5839019775e+01 3.2634301186e+00 5.8823900223e+00 + 27 27 1 6 1.4367620468e+01 1.4510419846e+01 3.6194500923e+00 + 28 28 1 6 1.5029319763e+01 4.1238398552e+00 1.0885919571e+01 + 29 29 1 6 1.0278700590e+00 1.2713120461e+01 1.0780119896e+01 + 30 30 1 6 6.3524799347e+00 7.2390997410e-01 3.3308401108e+00 + 31 31 1 6 1.2130220413e+01 7.2178502083e+00 1.2014419556e+01 + 32 32 1 6 5.8344697952e+00 3.0445299149e+00 1.3951720238e+01 + 33 33 1 6 1.7648099661e+00 1.0562520027e+01 6.7235598564e+00 + 34 34 1 6 2.0747799873e+00 8.8539199829e+00 1.1305419922e+01 + 35 35 1 6 8.0390000340e-01 3.5488500595e+00 9.6619701385e+00 + 36 36 1 6 6.5141301155e+00 7.8425202370e+00 1.5955419540e+01 + 37 37 1 6 1.3925120354e+01 1.4959919929e+01 1.1965120316e+01 + 38 38 1 6 8.1237401962e+00 9.8311595917e+00 7.9719200134e+00 + 39 39 1 6 1.5912420273e+01 8.1848001480e+00 1.0240790367e+01 + 40 40 1 6 3.1178801060e+00 1.4391320229e+01 1.1324919701e+01 + 41 41 1 6 5.1615700722e+00 1.1410120010e+01 8.8125000000e+00 + 42 42 1 6 1.1742119789e+01 9.6195096970e+00 3.2075099945e+00 + 43 43 1 6 2.4253900051e+00 1.3426119804e+01 6.1721601486e+00 + 44 44 1 6 1.1472120285e+01 5.3505101204e+00 4.8002500534e+00 + 45 45 1 6 1.3761919975e+01 1.7784299850e+00 6.7494101524e+00 + 46 46 1 6 2.5328700542e+00 1.4649620056e+01 1.3887319565e+01 + 47 47 1 6 2.1921999750e-01 5.2320299149e+00 7.4821400642e+00 + 48 48 1 6 1.0269989967e+01 2.8220400810e+00 7.4199998380e-01 + 49 49 1 6 7.5452399254e+00 3.3903999329e+00 7.7205500603e+00 + 50 50 1 6 8.7138998510e-01 5.8262997870e-01 9.4447698593e+00 + 51 51 1 6 1.1744099855e+00 1.5949020386e+01 6.9449901581e+00 + 52 52 1 6 1.3198619842e+01 9.5628995895e+00 5.7105498314e+00 + 53 53 1 6 6.7880001068e+00 5.0305002930e-01 1.5319120407e+01 + 54 54 1 6 6.0509200096e+00 1.0372619629e+01 1.3448100090e+00 + 55 55 1 6 9.7995500565e+00 1.3804120064e+01 1.0197730064e+01 + 56 56 1 6 1.1547519684e+01 1.7948499918e+00 5.4134202003e+00 + 57 57 1 6 2.1266100407e+00 2.8618299961e+00 2.8285100460e+00 + 58 58 1 6 3.4466700554e+00 1.1507419586e+01 1.6255700588e+00 + 59 59 1 6 1.5572520256e+01 5.6202101707e+00 4.0818700790e+00 + 60 60 1 6 7.9180498123e+00 6.5438399315e+00 1.5129300356e+00 + 61 61 1 6 1.0336020470e+01 2.2085900307e+00 1.0415019989e+01 + 62 62 1 6 7.6401200294e+00 1.6198020935e+01 1.2845120430e+01 + 63 63 1 6 1.5935819626e+01 6.3507499695e+00 1.2157620430e+01 + 64 64 1 6 7.7594199181e+00 4.3740999700e-01 7.9513401985e+00 + 65 65 1 6 7.9136900902e+00 4.1400299072e+00 1.2954119682e+01 + 66 66 1 6 1.2321920395e+01 7.5979700089e+00 1.5551420212e+01 + 67 67 1 6 4.3533902168e+00 6.3721599579e+00 7.6649599075e+00 + 68 68 1 6 7.2718601227e+00 4.8287901878e+00 4.5960597992e+00 + 69 69 1 6 5.0663599968e+00 1.1232520104e+01 1.4095520020e+01 + 70 70 1 6 7.7993898392e+00 1.1234820366e+01 3.3244199753e+00 + 71 71 1 6 2.9732899666e+00 6.9290499687e+00 1.6317020416e+01 + 72 72 1 6 4.6515598297e+00 2.9142398834e+00 3.4287199974e+00 + 73 73 1 6 1.1702320099e+01 2.9646999836e+00 3.0991001129e+00 + 74 74 1 6 5.0074200630e+00 1.6359720230e+01 7.8598198891e+00 + 75 75 1 6 1.0431719780e+01 4.6139597893e+00 9.0203399658e+00 + 76 76 1 6 1.1848919868e+01 1.5337519646e+01 1.6234619141e+01 + 77 77 1 6 1.3410420418e+01 2.0997400284e+00 9.4832496643e+00 + 78 78 1 6 1.6251020431e+01 1.2126300335e+00 1.3413220406e+01 + 79 79 1 6 1.6055120468e+01 1.5006719589e+01 1.3628820419e+01 + 80 80 1 6 9.0406103134e+00 1.2871720314e+01 9.0289002660e-01 + 81 81 1 6 1.4765319824e+01 7.2231397629e+00 1.4444020271e+01 + 82 82 1 6 1.4350720406e+01 1.5747619629e+01 9.5329999920e-01 + 83 83 1 6 1.5393919945e+01 1.0530920029e+01 1.0076500177e+00 + 84 84 1 6 4.4979600906e+00 2.7500700951e+00 1.1533820152e+01 + 85 85 1 6 7.3879699707e+00 1.3108019829e+01 5.5275897980e+00 + 86 86 1 6 7.7684597969e+00 1.9555100203e+00 1.1057100296e+00 + 87 87 1 6 1.1727020264e+01 1.4331799746e+00 1.5136019707e+01 + 88 88 1 6 7.2639000420e-01 1.0847220421e+01 1.2769020081e+01 + 89 89 1 6 5.3962998390e+00 1.2726819992e+01 1.1805319786e+01 + 90 90 1 6 1.3733420372e+01 8.3645095825e+00 1.3737399578e+00 + 91 91 1 6 1.4374320030e+01 9.7617797852e+00 1.2146220207e+01 + 92 92 1 6 8.8880002500e-01 3.5487198830e+00 1.4416020393e+01 + 93 93 1 6 1.4642219543e+01 1.9025100470e+00 1.5868320465e+01 + 94 94 1 6 4.3711099625e+00 8.5390195847e+00 1.4339819908e+01 + 95 95 1 6 1.5293419838e+01 1.3746620178e+01 1.5926719666e+01 + 96 96 1 6 1.5850919724e+01 1.2248820305e+01 3.2916700840e+00 + 97 97 1 6 2.7166600227e+00 2.7059700489e+00 1.0255999860e-01 + 98 98 1 6 1.4993220329e+01 7.3605000970e-01 1.1033320427e+01 + 99 99 1 6 1.3668519974e+01 6.8897800446e+00 5.6276798248e+00 + 100 100 1 6 3.3622899055e+00 7.7265000340e-01 5.6924500465e+00 + 101 101 1 6 1.1762220383e+01 1.3709520340e+01 3.8691298962e+00 + 102 102 1 6 1.2165019989e+01 6.9615201950e+00 9.1231498718e+00 + 103 103 1 6 1.0004650116e+01 7.5758500099e+00 3.3191499710e+00 + 104 104 1 6 6.6179699898e+00 3.8664200306e+00 1.0511619568e+01 + 105 105 1 6 1.4655719757e+01 6.8960099220e+00 8.0900297165e+00 + 106 106 1 6 8.0601100922e+00 1.5797120094e+01 5.2414698601e+00 + 107 107 1 6 6.0947999954e+00 6.3980898857e+00 9.9862604141e+00 + 108 108 1 6 4.5113998650e-01 5.9147200584e+00 1.5922619820e+01 + 109 109 1 6 3.7371599674e+00 1.6143920898e+01 2.0300900936e+00 + 110 110 1 6 1.2564620018e+01 1.1477720261e+01 1.3465620041e+01 + 111 111 1 6 1.4317420006e+01 1.3356220245e+01 7.6144399643e+00 + 112 112 1 6 1.0066490173e+01 5.6166300774e+00 1.6302320480e+01 + 113 113 1 6 1.2119720459e+01 1.2075320244e+01 9.8997097015e+00 + 114 114 1 6 9.2635898590e+00 1.2541020393e+01 7.4875102043e+00 + 115 115 1 6 2.7872700691e+00 1.3628319740e+01 3.2878000736e+00 + 116 116 1 6 4.7283701897e+00 3.3856899738e+00 6.1632699966e+00 + 117 117 1 6 1.3272120476e+01 1.1820219994e+01 1.6092720032e+01 + 118 118 1 6 1.4806119919e+01 5.9120202065e+00 1.4654799700e+00 + 119 119 1 6 1.3160920143e+01 4.1334700584e+00 1.5967320442e+01 + 120 120 1 6 1.1785019875e+01 1.3233220100e+01 6.6698698997e+00 + 121 121 1 6 2.6090600491e+00 4.4231100082e+00 1.2107819557e+01 + 122 122 1 6 8.1236295700e+00 1.1675419807e+01 1.4719220161e+01 + 123 123 1 6 2.6272299290e+00 8.7129201889e+00 4.6780500412e+00 + 124 124 1 6 1.4748519897e+01 1.3087220192e+01 1.0362170219e+01 + 125 125 1 6 2.8109200001e+00 8.5564899445e+00 1.8962999582e+00 + 126 126 1 6 1.2588219643e+01 4.6191601753e+00 1.3375519753e+01 + 127 127 1 6 6.4855799675e+00 4.2895698547e+00 1.6029200554e+00 + 128 128 1 6 1.5604319572e+01 1.1363820076e+01 6.3081698418e+00 + 129 1 2 1 8.9833898544e+00 6.6978502274e+00 9.3566198349e+00 + 130 2 2 1 4.8980598450e+00 6.6616702080e+00 1.1580619812e+01 + 131 3 2 1 2.5167601109e+00 9.8685197830e+00 1.0053870201e+01 + 132 4 2 1 1.1625419617e+01 1.4605520248e+01 1.4570719719e+01 + 133 1 2 1 8.7812099457e+00 7.5310301781e+00 1.0652919769e+01 + 134 2 2 1 3.6520099640e+00 5.9442501068e+00 1.1991120338e+01 + 135 3 2 1 2.1983499527e+00 1.0503620148e+01 8.5153703690e+00 + 136 4 2 1 1.0855019569e+01 1.4289420128e+01 1.3238619804e+01 + 137 5 2 1 9.1278400421e+00 1.3341420174e+01 1.1896820068e+01 + 138 6 2 1 1.0721520424e+01 2.2120599747e+00 1.3622420311e+01 + 139 7 2 1 2.0169000626e+00 5.8068799973e+00 4.6991801262e+00 + 140 8 2 1 3.1446700096e+00 1.3172120094e+01 1.4906220436e+01 + 141 5 2 1 8.6417503357e+00 1.2844820023e+01 1.3426919937e+01 + 142 6 2 1 1.0330559730e+01 2.4245500565e+00 1.2134420395e+01 + 143 7 2 1 2.0318698883e+00 7.3054199219e+00 5.1022901535e+00 + 144 8 2 1 3.9576599598e+00 1.1933119774e+01 1.4951519966e+01 + 145 9 2 1 1.4631719589e+01 1.5565719604e+01 5.1605300903e+00 + 146 68 2 1 7.4450201988e+00 4.5667200089e+00 3.6701600552e+00 + 147 11 2 1 8.6056604385e+00 1.6310230255e+01 9.6991500854e+00 + 148 12 2 1 3.9433300495e+00 4.0550398827e+00 1.6003820419e+01 + 149 9 2 1 1.4719019890e+01 1.4945119858e+01 6.7024598122e+00 + 150 10 2 1 8.9614000320e+00 1.4647820473e+01 9.9216997620e-01 + 151 11 2 1 9.4715404510e+00 1.5370120049e+01 1.0521920204e+01 + 152 12 2 1 5.2754101753e+00 4.6207900047e+00 1.6777999700e-01 + 153 13 2 1 1.2066619873e+01 1.1748920441e+01 1.0094300508e+00 + 154 14 2 1 1.0682120323e+01 9.0943899155e+00 1.4503820419e+01 + 155 15 2 1 9.2523899078e+00 9.4730100632e+00 6.4063901901e+00 + 156 16 2 1 6.3058099747e+00 8.4648799896e+00 4.8531198502e+00 + 157 13 2 1 1.1386320114e+01 1.1041720390e+01 2.2529900074e+00 + 158 14 2 1 9.9100103378e+00 9.3395099640e+00 1.3211020470e+01 + 159 15 2 1 1.0520219803e+01 9.6425199509e+00 5.5945601463e+00 + 160 16 2 1 5.9983100891e+00 7.0285601616e+00 4.3523797989e+00 + 161 17 2 1 8.6656799316e+00 5.7231001854e+00 1.4888019562e+01 + 162 18 2 1 5.0128102303e+00 1.4644319534e+01 7.1735601425e+00 + 163 19 2 1 9.7614402771e+00 1.5982419968e+01 2.6491699219e+00 + 164 20 2 1 1.0326459885e+01 2.8723800182e+00 6.1583700180e+00 + 165 17 2 1 7.7509999275e+00 5.6568298340e+00 1.3748020172e+01 + 166 18 2 1 5.0189099312e+00 1.3044019699e+01 7.6463098526e+00 + 167 19 2 1 9.5978298187e+00 1.5936019897e+01 4.2281298637e+00 + 168 20 2 1 8.9388103485e+00 3.3201599121e+00 6.8107600212e+00 + 169 21 2 1 1.1794819832e+01 9.5571203232e+00 7.3678297997e+00 + 170 22 2 1 8.9509897232e+00 7.3347401619e+00 1.2953720093e+01 + 171 23 2 1 4.1151099205e+00 1.5550999340e-01 9.3323602676e+00 + 172 24 2 1 3.8847401142e+00 1.6128919601e+01 1.6872000690e-01 + 173 21 2 1 1.1296219826e+01 8.9417800903e+00 8.6366195679e+00 + 174 22 2 1 1.0346710205e+01 7.4265298843e+00 1.2019120216e+01 + 175 23 2 1 3.7601399422e+00 1.1172599792e+00 1.0668919563e+01 + 176 24 2 1 3.4846899509e+00 1.5978619575e+01 1.5130120277e+01 + 177 25 2 1 1.4922000170e+00 1.1914620400e+01 1.5522919655e+01 + 178 26 2 1 1.6305919647e+01 3.9318199158e+00 6.4722700119e+00 + 179 27 2 1 1.4441320419e+01 1.4880920410e+01 2.7076001167e+00 + 180 10 2 1 8.5916700363e+00 1.6022619247e+01 2.4647000430e-01 + 181 25 2 1 7.3871999980e-01 1.1288020134e+01 1.4508020401e+01 + 182 26 2 1 1.5428219795e+01 3.7610800266e+00 5.1725101471e+00 + 183 91 2 1 1.5182820320e+01 1.0374320030e+01 1.2291319847e+01 + 184 28 2 1 1.5872119904e+01 3.7566399574e+00 1.0512319565e+01 + 185 29 2 1 1.6182099581e+00 1.2163519859e+01 1.0220399857e+01 + 186 30 2 1 5.6696801186e+00 1.4778699875e+00 3.4357900620e+00 + 187 31 2 1 1.2433919907e+01 7.0990200043e+00 1.1087019920e+01 + 188 32 2 1 5.4505701065e+00 3.7250699997e+00 1.4646719933e+01 + 189 29 2 1 1.5755000114e+00 1.3518320084e+01 1.0882019997e+01 + 190 30 2 1 6.7958998680e+00 1.0680600405e+00 2.5091300011e+00 + 191 31 2 1 1.2772419929e+01 7.7946300507e+00 1.2458120346e+01 + 192 32 2 1 6.1269598007e+00 2.2396099567e+00 1.4419819832e+01 + 193 125 2 1 3.0232501030e+00 9.4820899963e+00 1.6625900269e+00 + 194 34 2 1 2.7110199928e+00 8.1157398224e+00 1.1506420136e+01 + 195 35 2 1 6.8174999950e-01 2.6035299301e+00 9.5699195862e+00 + 196 36 2 1 5.9391999245e+00 8.2750902176e+00 1.5360919952e+01 + 197 33 2 1 1.9904099703e+00 1.0008769989e+01 5.9505600929e+00 + 198 34 2 1 1.2736799717e+00 8.3656702042e+00 1.0950119972e+01 + 199 84 2 1 5.0100698471e+00 2.7086699009e+00 1.2365019798e+01 + 200 36 2 1 6.9829897881e+00 7.1937699318e+00 1.5381119728e+01 + 201 37 2 1 1.4031620026e+01 1.5911520004e+01 1.1621919632e+01 + 202 38 2 1 8.2020797729e+00 9.0278196335e+00 8.5738897324e+00 + 203 39 2 1 1.5381420136e+01 8.8655700684e+00 1.0669719696e+01 + 204 40 2 1 3.2877900600e+00 1.5282320023e+01 1.0905819893e+01 + 205 37 2 1 1.3238719940e+01 1.4824319839e+01 1.2608920097e+01 + 206 38 2 1 7.3083300591e+00 1.0290280342e+01 8.3123397827e+00 + 207 39 2 1 1.5842820167e+01 7.3599700928e+00 1.0882220268e+01 + 208 40 2 1 3.9607300758e+00 1.3851119995e+01 1.1463520050e+01 + 209 41 2 1 5.3205699921e+00 1.1845919609e+01 9.7145500183e+00 + 210 42 2 1 1.2454819679e+01 9.1883096695e+00 2.7001800537e+00 + 211 43 2 1 3.3241400719e+00 1.3519720078e+01 6.5152602196e+00 + 212 44 2 1 1.1767720222e+01 4.6335101128e+00 4.1996397972e+00 + 213 41 2 1 4.1373000145e+00 1.1154219627e+01 8.9612703323e+00 + 214 42 2 1 1.1002719879e+01 8.9355802536e+00 3.1770300865e+00 + 215 43 2 1 2.2577600479e+00 1.2391619682e+01 6.2320799828e+00 + 216 44 2 1 1.0875419617e+01 4.9362201691e+00 5.4710001945e+00 + 217 45 2 1 1.4054019928e+01 8.4421998260e-01 6.6780400276e+00 + 218 46 2 1 2.8359398842e+00 1.4530520439e+01 1.2932620049e+01 + 219 47 2 1 7.3829001190e-01 5.5997800827e+00 6.7205901146e+00 + 220 48 2 1 1.0802419663e+01 2.7474200726e+00 1.5978499651e+00 + 221 67 2 1 3.5227699280e+00 6.8623600006e+00 7.8746500015e+00 + 222 46 2 1 1.5763700008e+00 1.4882320404e+01 1.3684320450e+01 + 223 47 2 1 1.5830120087e+01 5.7819399834e+00 7.5954799652e+00 + 224 48 2 1 1.0861619949e+01 2.4902698994e+00 4.8030000200e-02 + 225 49 2 1 7.3649501801e+00 3.7047998905e+00 8.6432199478e+00 + 226 50 2 1 1.8013199568e+00 6.3723999260e-01 9.7321596146e+00 + 227 51 2 1 2.7112999560e-01 1.5899319649e+01 6.4777598381e+00 + 228 52 2 1 1.3923020363e+01 1.0171250343e+01 5.8901700974e+00 + 229 49 2 1 6.7048702240e+00 3.5456199646e+00 7.2065601349e+00 + 230 50 2 1 9.6384000780e-01 2.0401999350e-01 8.5252799988e+00 + 231 51 2 1 1.4794100523e+00 1.5020719528e+01 6.7109599113e+00 + 232 86 2 1 8.6926097870e+00 2.2595798969e+00 1.1261299849e+00 + 233 53 2 1 7.2568001747e+00 1.6729999300e-02 1.4558620453e+01 + 234 54 2 1 5.9468998909e+00 9.9700603485e+00 4.9887999890e-01 + 235 55 2 1 9.4702997208e+00 1.3590519905e+01 9.3124103546e+00 + 236 56 2 1 1.1317219734e+01 8.5253000260e-01 5.1829299927e+00 + 237 45 2 1 1.4580120087e+01 2.3664000034e+00 6.5427999496e+00 + 238 54 2 1 5.1660299301e+00 1.0864319801e+01 1.3880699873e+00 + 239 55 2 1 1.0634619713e+01 1.3232119560e+01 1.0217060089e+01 + 240 56 2 1 1.2400620461e+01 1.8054300547e+00 5.8815498352e+00 + 241 57 2 1 2.0691499710e+00 2.7709600925e+00 1.8279900551e+00 + 242 58 2 1 3.2105300426e+00 1.1787919998e+01 7.2491997480e-01 + 243 59 2 1 3.1279999800e-02 6.0399599075e+00 4.3540101051e+00 + 244 60 2 1 8.5852003098e+00 6.9994001389e+00 2.0702099800e+00 + 245 57 2 1 1.2432500124e+00 2.9883599281e+00 3.2575500011e+00 + 246 58 2 1 3.2410800457e+00 1.2206419945e+01 2.2994999886e+00 + 247 59 2 1 1.4809020042e+01 6.1193399429e+00 4.6470098495e+00 + 248 60 2 1 7.3514499664e+00 7.2554898262e+00 1.1103899479e+00 + 249 35 2 1 4.8684999350e-01 4.0633001328e+00 8.8592395782e+00 + 250 62 2 1 7.9498701096e+00 1.5261420250e+01 1.2922719955e+01 + 251 63 2 1 1.5412019730e+01 5.5930900574e+00 1.1741220474e+01 + 252 64 2 1 7.8202199936e+00 1.4425899982e+00 7.9145197868e+00 + 253 61 2 1 1.0117500305e+01 1.2597399950e+00 1.0254810333e+01 + 254 62 2 1 8.1101999283e+00 1.0818000140e-01 1.2047419548e+01 + 255 63 2 1 1.5350720406e+01 6.6539201736e+00 1.2942620277e+01 + 256 64 2 1 8.0458202362e+00 1.6690000900e-02 7.0975699425e+00 + 257 65 2 1 8.8812704086e+00 3.7375900745e+00 1.2946419716e+01 + 258 66 2 1 1.1661419868e+01 6.8551998138e+00 1.5732119560e+01 + 259 112 2 1 1.0303730011e+01 4.6875200272e+00 1.0958000270e-01 + 260 68 2 1 8.0857801437e+00 5.1339402199e+00 4.9711499214e+00 + 261 65 2 1 7.3292498589e+00 3.4946200848e+00 1.3401419640e+01 + 262 66 2 1 1.2469420433e+01 7.9732799530e+00 4.4800001000e-03 + 263 67 2 1 4.7779297829e+00 7.0013298988e+00 7.0437197685e+00 + 264 128 2 1 1.5624520302e+01 1.1705419540e+01 5.4044299126e+00 + 265 69 2 1 6.0917301178e+00 1.1232319832e+01 1.4278220177e+01 + 266 70 2 1 8.5067901611e+00 1.0618320465e+01 3.4443600178e+00 + 267 52 2 1 1.2798419952e+01 9.8584299088e+00 4.8854298592e+00 + 268 72 2 1 3.7830400467e+00 2.8905000687e+00 2.9046800137e+00 + 269 69 2 1 4.7820301056e+00 1.0290060043e+01 1.4038120270e+01 + 270 70 2 1 7.1761598587e+00 1.0848719597e+01 2.6528699398e+00 + 271 71 2 1 2.0748100281e+00 6.4666399956e+00 1.6033420563e+01 + 272 72 2 1 5.3769598007e+00 3.4491899014e+00 2.9945800304e+00 + 273 73 2 1 1.2564920425e+01 2.6808400154e+00 2.7539699078e+00 + 274 74 2 1 5.9225301743e+00 1.4830000700e-01 8.0135698318e+00 + 275 75 2 1 1.0296210289e+01 3.8671300411e+00 9.6246004105e+00 + 276 76 2 1 1.2619020462e+01 1.5370320320e+01 4.4828000660e-01 + 277 73 2 1 1.1586319923e+01 2.4203500748e+00 3.8918399811e+00 + 278 74 2 1 4.7386298180e+00 3.9197000860e-01 7.0593600273e+00 + 279 61 2 1 1.1277219772e+01 2.1224400997e+00 1.0184889793e+01 + 280 76 2 1 1.1020719528e+01 1.5446720123e+01 2.8446999190e-01 + 281 77 2 1 1.3655920029e+01 1.8968100548e+00 8.5830802917e+00 + 282 78 2 1 1.6295520782e+01 2.5802001360e-01 1.3609720230e+01 + 283 79 2 1 1.5330220222e+01 1.4777919769e+01 1.3040320396e+01 + 284 80 2 1 8.6059103012e+00 1.2488019943e+01 1.7353899479e+00 + 285 77 2 1 1.3900719643e+01 2.9670000076e+00 9.6906995773e+00 + 286 78 2 1 1.5705820084e+01 1.3114099503e+00 1.2601019859e+01 + 287 79 2 1 1.5717619896e+01 1.4634420395e+01 1.4502820015e+01 + 288 80 2 1 9.9461803436e+00 1.2731419563e+01 1.1440900564e+00 + 289 81 2 1 1.3893219948e+01 7.1291799545e+00 1.4947420120e+01 + 290 82 2 1 1.4587019920e+01 2.4594999850e-01 6.0628002880e-01 + 291 83 2 1 1.6088520050e+01 1.0804920196e+01 2.6616999510e-01 + 292 84 2 1 5.1763000488e+00 3.4711999893e+00 1.1168419838e+01 + 293 81 2 1 1.4825119972e+01 8.2068500519e+00 1.4172519684e+01 + 294 82 2 1 1.4860520363e+01 1.5066820145e+01 4.4203001260e-01 + 295 28 2 1 1.4724519730e+01 3.6320500374e+00 1.1651720047e+01 + 296 27 2 1 1.3466219902e+01 1.4150520325e+01 3.5702099800e+00 + 297 85 2 1 6.5515198708e+00 1.3216520309e+01 6.1123600006e+00 + 298 86 2 1 7.6973600388e+00 1.4915200472e+00 2.4017000200e-01 + 299 87 2 1 1.2593219757e+01 1.8808300495e+00 1.5259619713e+01 + 300 88 2 1 7.3618000750e-01 1.1578419685e+01 1.2098219871e+01 + 301 85 2 1 7.3069300652e+00 1.2532219887e+01 4.7495198250e+00 + 302 98 2 1 1.5836020470e+01 4.2184999590e-01 1.0527520180e+01 + 303 87 2 1 1.1869919777e+01 5.3706002240e-01 1.5508219719e+01 + 304 88 2 1 1.4253400564e+00 1.0251770020e+01 1.2411520004e+01 + 305 89 2 1 5.3722500801e+00 1.1966919899e+01 1.2482020378e+01 + 306 90 2 1 1.4492819786e+01 8.9310102463e+00 1.0563800335e+00 + 307 75 2 1 1.0363510132e+01 4.2603898048e+00 8.1301498413e+00 + 308 92 2 1 1.5456600189e+00 3.9554400444e+00 1.3748620033e+01 + 309 89 2 1 6.1985301971e+00 1.3199919701e+01 1.1969320297e+01 + 310 90 2 1 1.4102219582e+01 7.4810199738e+00 1.5541800261e+00 + 311 33 2 1 8.0773001910e-01 1.0852319717e+01 6.7046999931e+00 + 312 92 2 1 6.2181001900e-01 2.6401300430e+00 1.4070420265e+01 + 313 93 2 1 1.5174420357e+01 1.8615599871e+00 1.5055919647e+01 + 314 94 2 1 3.6487100124e+00 8.2751598358e+00 1.4984820366e+01 + 315 95 2 1 1.5923620224e+01 1.3078720093e+01 1.5688119888e+01 + 316 96 2 1 3.5888001320e-01 1.2424619675e+01 3.4643499851e+00 + 317 93 2 1 1.4115719795e+01 2.7545700073e+00 1.5949219704e+01 + 318 94 2 1 4.2683801651e+00 7.8965401649e+00 1.3621120453e+01 + 319 95 2 1 1.4403719902e+01 1.3273520470e+01 1.5991419792e+01 + 320 96 2 1 1.5336720467e+01 1.3109519958e+01 3.3662500381e+00 + 321 97 2 1 1.9847400188e+00 2.7817499638e+00 1.5771320343e+01 + 322 98 2 1 1.4483920097e+01 1.4346200228e+00 1.0555120468e+01 + 323 99 2 1 1.3508119583e+01 7.8685002327e+00 5.4123601913e+00 + 324 100 2 1 2.4792299271e+00 6.0350000860e-01 6.0694098473e+00 + 325 97 2 1 3.2279798985e+00 1.8559700251e+00 1.6312019348e+01 + 326 114 2 1 9.1920099258e+00 1.1619520187e+01 7.8817501068e+00 + 327 99 2 1 1.2783220291e+01 6.2986998558e+00 5.5348701477e+00 + 328 100 2 1 3.4992098808e+00 1.7462600470e+00 5.8084602356e+00 + 329 53 2 1 5.8817400932e+00 1.9909000400e-01 1.5449219704e+01 + 330 102 2 1 1.3145520210e+01 6.9865198135e+00 8.8036203384e+00 + 331 103 2 1 1.0535420418e+01 6.7340798378e+00 3.5914199352e+00 + 332 104 2 1 6.3808898926e+00 4.8206801414e+00 1.0301219940e+01 + 333 101 2 1 1.1491220474e+01 1.3142720222e+01 3.0838599205e+00 + 334 102 2 1 1.1731120110e+01 6.0981597900e+00 9.1941795349e+00 + 335 103 2 1 9.8622798920e+00 8.0136804581e+00 4.2209300995e+00 + 336 104 2 1 7.1787199974e+00 4.0091600418e+00 1.1322520256e+01 + 337 105 2 1 1.4423520088e+01 7.0153598785e+00 7.1517400742e+00 + 338 106 2 1 7.3862299919e+00 1.6174720764e+01 4.5710802078e+00 + 339 107 2 1 5.4857201576e+00 6.4821300507e+00 9.1844701767e+00 + 340 108 2 1 5.5193001030e-01 5.0368700027e+00 1.5419619560e+01 + 341 105 2 1 1.5115119934e+01 7.5903301239e+00 8.7755403519e+00 + 342 106 2 1 7.7120099068e+00 1.4885020256e+01 5.4720201492e+00 + 343 107 2 1 6.9038000107e+00 6.9323601723e+00 9.9094200134e+00 + 344 108 2 1 1.6193820953e+01 6.4124398232e+00 1.5437820435e+01 + 345 109 2 1 2.9763300419e+00 2.2721000020e-01 2.4249598980e+00 + 346 110 2 1 1.1779319763e+01 1.0850020409e+01 1.3386219978e+01 + 347 111 2 1 1.4293620110e+01 1.3235719681e+01 8.6815595627e+00 + 348 83 2 1 1.5740819931e+01 1.0903120041e+01 1.8280800581e+00 + 349 109 2 1 4.5491800308e+00 1.6289049149e+01 2.5430600643e+00 + 350 110 2 1 1.2258119583e+01 1.2381119728e+01 1.3270719528e+01 + 351 111 2 1 1.3349320412e+01 1.3233119965e+01 7.3614602089e+00 + 352 112 2 1 9.4850797653e+00 5.8311300278e+00 6.3769000770e-01 + 353 113 2 1 1.1956620216e+01 1.1378520012e+01 9.2546501160e+00 + 354 114 2 1 8.5540304184e+00 1.2609120369e+01 6.8035402298e+00 + 355 115 2 1 3.2311999798e+00 1.4374819756e+01 2.8146998882e+00 + 356 116 2 1 4.6851601601e+00 3.3364698887e+00 5.1848101616e+00 + 357 113 2 1 1.3013420105e+01 1.2028120041e+01 1.0340200424e+01 + 358 101 2 1 1.1209520340e+01 1.4499620438e+01 3.8401799202e+00 + 359 115 2 1 2.6903600693e+00 1.3911120415e+01 4.2274699211e+00 + 360 116 2 1 4.4161000252e+00 4.3293700218e+00 6.3396601677e+00 + 361 117 2 1 1.4096520424e+01 1.1322219849e+01 1.6284219742e+01 + 362 118 2 1 1.5069120407e+01 5.9094500542e+00 2.4706599712e+00 + 363 119 2 1 1.3306019783e+01 4.8613901138e+00 1.4027999340e-01 + 364 120 2 1 1.1697520256e+01 1.3341520309e+01 5.6620597839e+00 + 365 117 2 1 1.3045220375e+01 1.1688320160e+01 1.5167619705e+01 + 366 118 2 1 1.5689319611e+01 5.9145097733e+00 9.9571001530e-01 + 367 119 2 1 1.3112319946e+01 4.4439301491e+00 1.4978520393e+01 + 368 120 2 1 1.0954119682e+01 1.2922719955e+01 7.0095100403e+00 + 369 121 2 1 3.2885100842e+00 3.6702098846e+00 1.2011819839e+01 + 370 122 2 1 8.3817996979e+00 1.2046719551e+01 1.5625220299e+01 + 371 123 2 1 3.5898699760e+00 8.5167198181e+00 4.8271698952e+00 + 372 124 2 1 1.5741620064e+01 1.3004520416e+01 1.0440320015e+01 + 373 121 2 1 1.8930799961e+00 4.2027997971e+00 1.1450420380e+01 + 374 122 2 1 8.6922197342e+00 1.0858920097e+01 1.4582619667e+01 + 375 123 2 1 2.4883399010e+00 8.7543201447e+00 3.6568799019e+00 + 376 124 2 1 1.4388019562e+01 1.3729920387e+01 1.1048520088e+01 + 377 91 2 1 1.3699919701e+01 1.0364080429e+01 1.2549420357e+01 + 378 126 2 1 1.1916020393e+01 4.0477600098e+00 1.2936719894e+01 + 379 127 2 1 7.0719299316e+00 5.0637001991e+00 1.5687400103e+00 + 380 71 2 1 3.6430799961e+00 6.3007497787e+00 1.6387020111e+01 + 381 125 2 1 2.8608500957e+00 8.0535602570e+00 1.0195100307e+00 + 382 126 2 1 1.2277319908e+01 5.4857201576e+00 1.3040019989e+01 + 383 127 2 1 7.0190601349e+00 3.5070400238e+00 1.3909499645e+00 + 384 128 2 1 1.5247819900e+01 1.2120220184e+01 6.8465099335e+00 + 385 1 3 -8 8.4960699081e+00 7.5073699951e+00 9.6371297836e+00 + 386 2 3 -8 4.0597701073e+00 6.8156299591e+00 1.2051420212e+01 + 387 3 3 -8 2.6408200264e+00 1.0598219871e+01 9.3506002426e+00 + 388 4 3 -8 1.1694219589e+01 1.4214019775e+01 1.3693120003e+01 + 389 5 3 -8 8.7306203842e+00 1.3607620239e+01 1.2758020401e+01 + 390 6 3 -8 1.0342249870e+01 2.8897099495e+00 1.3001319885e+01 + 391 7 3 -8 1.6010500193e+00 6.4406399727e+00 5.3241600990e+00 + 392 8 3 -8 3.1527600288e+00 1.2283720017e+01 1.5380720138e+01 + 393 9 3 -8 1.4798919678e+01 1.5740220070e+01 6.1582899094e+00 + 394 10 3 -8 9.2309398651e+00 1.5580719948e+01 8.5355997090e-01 + 395 11 3 -8 9.1436700821e+00 1.6327819824e+01 1.0490420342e+01 + 396 12 3 -8 4.6231899261e+00 4.7593097687e+00 1.5846320152e+01 + 397 13 3 -8 1.1337719917e+01 1.1849120140e+01 1.7001099586e+00 + 398 14 3 -8 1.0370820046e+01 9.8193197250e+00 1.3924719810e+01 + 399 15 3 -8 9.5739898682e+00 9.3357095718e+00 5.4412398338e+00 + 400 16 3 -8 5.6118898392e+00 7.8216400146e+00 4.7569899559e+00 + 401 17 3 -8 8.1148195267e+00 6.3689699173e+00 1.4318619728e+01 + 402 18 3 -8 5.1289701462e+00 1.3673819542e+01 6.8581700325e+00 + 403 19 3 -8 1.0273889542e+01 1.6011220932e+01 3.5431900024e+00 + 404 20 3 -8 9.8264398575e+00 3.6420900822e+00 6.4606800079e+00 + 405 21 3 -8 1.1179019928e+01 9.7553701401e+00 8.1035804749e+00 + 406 22 3 -8 9.3739500046e+00 7.7072300911e+00 1.2156920433e+01 + 407 23 3 -8 3.4909999371e+00 3.5429000850e-01 1.0146929741e+01 + 408 24 3 -8 3.9963800907e+00 1.5806999800e-01 1.5749620438e+01 + 409 25 3 -8 5.4874998330e-01 1.1597120285e+01 1.5405719757e+01 + 410 26 3 -8 1.5839019775e+01 3.2634301186e+00 5.8823900223e+00 + 411 27 3 -8 1.4367620468e+01 1.4510419846e+01 3.6194500923e+00 + 412 28 3 -8 1.5029319763e+01 4.1238398552e+00 1.0885919571e+01 + 413 29 3 -8 1.0278700590e+00 1.2713120461e+01 1.0780119896e+01 + 414 30 3 -8 6.3524799347e+00 7.2390997410e-01 3.3308401108e+00 + 415 31 3 -8 1.2130220413e+01 7.2178502083e+00 1.2014419556e+01 + 416 32 3 -8 5.8344697952e+00 3.0445299149e+00 1.3951720238e+01 + 417 33 3 -8 1.7648099661e+00 1.0562520027e+01 6.7235598564e+00 + 418 34 3 -8 2.0747799873e+00 8.8539199829e+00 1.1305419922e+01 + 419 35 3 -8 8.0390000340e-01 3.5488500595e+00 9.6619701385e+00 + 420 36 3 -8 6.5141301155e+00 7.8425202370e+00 1.5955419540e+01 + 421 37 3 -8 1.3925120354e+01 1.4959919929e+01 1.1965120316e+01 + 422 38 3 -8 8.1237401962e+00 9.8311595917e+00 7.9719200134e+00 + 423 39 3 -8 1.5912420273e+01 8.1848001480e+00 1.0240790367e+01 + 424 40 3 -8 3.1178801060e+00 1.4391320229e+01 1.1324919701e+01 + 425 41 3 -8 5.1615700722e+00 1.1410120010e+01 8.8125000000e+00 + 426 42 3 -8 1.1742119789e+01 9.6195096970e+00 3.2075099945e+00 + 427 43 3 -8 2.4253900051e+00 1.3426119804e+01 6.1721601486e+00 + 428 44 3 -8 1.1472120285e+01 5.3505101204e+00 4.8002500534e+00 + 429 45 3 -8 1.3761919975e+01 1.7784299850e+00 6.7494101524e+00 + 430 46 3 -8 2.5328700542e+00 1.4649620056e+01 1.3887319565e+01 + 431 47 3 -8 2.1921999750e-01 5.2320299149e+00 7.4821400642e+00 + 432 48 3 -8 1.0269989967e+01 2.8220400810e+00 7.4199998380e-01 + 433 49 3 -8 7.5452399254e+00 3.3903999329e+00 7.7205500603e+00 + 434 50 3 -8 8.7138998510e-01 5.8262997870e-01 9.4447698593e+00 + 435 51 3 -8 1.1744099855e+00 1.5949020386e+01 6.9449901581e+00 + 436 52 3 -8 1.3198619842e+01 9.5628995895e+00 5.7105498314e+00 + 437 53 3 -8 6.7880001068e+00 5.0305002930e-01 1.5319120407e+01 + 438 54 3 -8 6.0509200096e+00 1.0372619629e+01 1.3448100090e+00 + 439 55 3 -8 9.7995500565e+00 1.3804120064e+01 1.0197730064e+01 + 440 56 3 -8 1.1547519684e+01 1.7948499918e+00 5.4134202003e+00 + 441 57 3 -8 2.1266100407e+00 2.8618299961e+00 2.8285100460e+00 + 442 58 3 -8 3.4466700554e+00 1.1507419586e+01 1.6255700588e+00 + 443 59 3 -8 1.5572520256e+01 5.6202101707e+00 4.0818700790e+00 + 444 60 3 -8 7.9180498123e+00 6.5438399315e+00 1.5129300356e+00 + 445 61 3 -8 1.0336020470e+01 2.2085900307e+00 1.0415019989e+01 + 446 62 3 -8 7.6401200294e+00 1.6198020935e+01 1.2845120430e+01 + 447 63 3 -8 1.5935819626e+01 6.3507499695e+00 1.2157620430e+01 + 448 64 3 -8 7.7594199181e+00 4.3740999700e-01 7.9513401985e+00 + 449 65 3 -8 7.9136900902e+00 4.1400299072e+00 1.2954119682e+01 + 450 66 3 -8 1.2321920395e+01 7.5979700089e+00 1.5551420212e+01 + 451 67 3 -8 4.3533902168e+00 6.3721599579e+00 7.6649599075e+00 + 452 68 3 -8 7.2718601227e+00 4.8287901878e+00 4.5960597992e+00 + 453 69 3 -8 5.0663599968e+00 1.1232520104e+01 1.4095520020e+01 + 454 70 3 -8 7.7993898392e+00 1.1234820366e+01 3.3244199753e+00 + 455 71 3 -8 2.9732899666e+00 6.9290499687e+00 1.6317020416e+01 + 456 72 3 -8 4.6515598297e+00 2.9142398834e+00 3.4287199974e+00 + 457 73 3 -8 1.1702320099e+01 2.9646999836e+00 3.0991001129e+00 + 458 74 3 -8 5.0074200630e+00 1.6359720230e+01 7.8598198891e+00 + 459 75 3 -8 1.0431719780e+01 4.6139597893e+00 9.0203399658e+00 + 460 76 3 -8 1.1848919868e+01 1.5337519646e+01 1.6234619141e+01 + 461 77 3 -8 1.3410420418e+01 2.0997400284e+00 9.4832496643e+00 + 462 78 3 -8 1.6251020431e+01 1.2126300335e+00 1.3413220406e+01 + 463 79 3 -8 1.6055120468e+01 1.5006719589e+01 1.3628820419e+01 + 464 80 3 -8 9.0406103134e+00 1.2871720314e+01 9.0289002660e-01 + 465 81 3 -8 1.4765319824e+01 7.2231397629e+00 1.4444020271e+01 + 466 82 3 -8 1.4350720406e+01 1.5747619629e+01 9.5329999920e-01 + 467 83 3 -8 1.5393919945e+01 1.0530920029e+01 1.0076500177e+00 + 468 84 3 -8 4.4979600906e+00 2.7500700951e+00 1.1533820152e+01 + 469 85 3 -8 7.3879699707e+00 1.3108019829e+01 5.5275897980e+00 + 470 86 3 -8 7.7684597969e+00 1.9555100203e+00 1.1057100296e+00 + 471 87 3 -8 1.1727020264e+01 1.4331799746e+00 1.5136019707e+01 + 472 88 3 -8 7.2639000420e-01 1.0847220421e+01 1.2769020081e+01 + 473 89 3 -8 5.3962998390e+00 1.2726819992e+01 1.1805319786e+01 + 474 90 3 -8 1.3733420372e+01 8.3645095825e+00 1.3737399578e+00 + 475 91 3 -8 1.4374320030e+01 9.7617797852e+00 1.2146220207e+01 + 476 92 3 -8 8.8880002500e-01 3.5487198830e+00 1.4416020393e+01 + 477 93 3 -8 1.4642219543e+01 1.9025100470e+00 1.5868320465e+01 + 478 94 3 -8 4.3711099625e+00 8.5390195847e+00 1.4339819908e+01 + 479 95 3 -8 1.5293419838e+01 1.3746620178e+01 1.5926719666e+01 + 480 96 3 -8 1.5850919724e+01 1.2248820305e+01 3.2916700840e+00 + 481 97 3 -8 2.7166600227e+00 2.7059700489e+00 1.0255999860e-01 + 482 98 3 -8 1.4993220329e+01 7.3605000970e-01 1.1033320427e+01 + 483 99 3 -8 1.3668519974e+01 6.8897800446e+00 5.6276798248e+00 + 484 100 3 -8 3.3622899055e+00 7.7265000340e-01 5.6924500465e+00 + 485 101 3 -8 1.1762220383e+01 1.3709520340e+01 3.8691298962e+00 + 486 102 3 -8 1.2165019989e+01 6.9615201950e+00 9.1231498718e+00 + 487 103 3 -8 1.0004650116e+01 7.5758500099e+00 3.3191499710e+00 + 488 104 3 -8 6.6179699898e+00 3.8664200306e+00 1.0511619568e+01 + 489 105 3 -8 1.4655719757e+01 6.8960099220e+00 8.0900297165e+00 + 490 106 3 -8 8.0601100922e+00 1.5797120094e+01 5.2414698601e+00 + 491 107 3 -8 6.0947999954e+00 6.3980898857e+00 9.9862604141e+00 + 492 108 3 -8 4.5113998650e-01 5.9147200584e+00 1.5922619820e+01 + 493 109 3 -8 3.7371599674e+00 1.6143920898e+01 2.0300900936e+00 + 494 110 3 -8 1.2564620018e+01 1.1477720261e+01 1.3465620041e+01 + 495 111 3 -8 1.4317420006e+01 1.3356220245e+01 7.6144399643e+00 + 496 112 3 -8 1.0066490173e+01 5.6166300774e+00 1.6302320480e+01 + 497 113 3 -8 1.2119720459e+01 1.2075320244e+01 9.8997097015e+00 + 498 114 3 -8 9.2635898590e+00 1.2541020393e+01 7.4875102043e+00 + 499 115 3 -8 2.7872700691e+00 1.3628319740e+01 3.2878000736e+00 + 500 116 3 -8 4.7283701897e+00 3.3856899738e+00 6.1632699966e+00 + 501 117 3 -8 1.3272120476e+01 1.1820219994e+01 1.6092720032e+01 + 502 118 3 -8 1.4806119919e+01 5.9120202065e+00 1.4654799700e+00 + 503 119 3 -8 1.3160920143e+01 4.1334700584e+00 1.5967320442e+01 + 504 120 3 -8 1.1785019875e+01 1.3233220100e+01 6.6698698997e+00 + 505 121 3 -8 2.6090600491e+00 4.4231100082e+00 1.2107819557e+01 + 506 122 3 -8 8.1236295700e+00 1.1675419807e+01 1.4719220161e+01 + 507 123 3 -8 2.6272299290e+00 8.7129201889e+00 4.6780500412e+00 + 508 124 3 -8 1.4748519897e+01 1.3087220192e+01 1.0362170219e+01 + 509 125 3 -8 2.8109200001e+00 8.5564899445e+00 1.8962999582e+00 + 510 126 3 -8 1.2588219643e+01 4.6191601753e+00 1.3375519753e+01 + 511 127 3 -8 6.4855799675e+00 4.2895698547e+00 1.6029200554e+00 + 512 128 3 -8 1.5604319572e+01 1.1363820076e+01 6.3081698418e+00 + +Bonds + +1 1 1 385 +2 1 2 386 +3 1 3 387 +4 1 4 388 +5 1 5 389 +6 1 6 390 +7 1 7 391 +8 1 8 392 +9 1 9 393 +10 1 10 394 +11 1 11 395 +12 1 12 396 +13 1 13 397 +14 1 14 398 +15 1 15 399 +16 1 16 400 +17 1 17 401 +18 1 18 402 +19 1 19 403 +20 1 20 404 +21 1 21 405 +22 1 22 406 +23 1 23 407 +24 1 24 408 +25 1 25 409 +26 1 26 410 +27 1 27 411 +28 1 28 412 +29 1 29 413 +30 1 30 414 +31 1 31 415 +32 1 32 416 +33 1 33 417 +34 1 34 418 +35 1 35 419 +36 1 36 420 +37 1 37 421 +38 1 38 422 +39 1 39 423 +40 1 40 424 +41 1 41 425 +42 1 42 426 +43 1 43 427 +44 1 44 428 +45 1 45 429 +46 1 46 430 +47 1 47 431 +48 1 48 432 +49 1 49 433 +50 1 50 434 +51 1 51 435 +52 1 52 436 +53 1 53 437 +54 1 54 438 +55 1 55 439 +56 1 56 440 +57 1 57 441 +58 1 58 442 +59 1 59 443 +60 1 60 444 +61 1 61 445 +62 1 62 446 +63 1 63 447 +64 1 64 448 +65 1 65 449 +66 1 66 450 +67 1 67 451 +68 1 68 452 +69 1 69 453 +70 1 70 454 +71 1 71 455 +72 1 72 456 +73 1 73 457 +74 1 74 458 +75 1 75 459 +76 1 76 460 +77 1 77 461 +78 1 78 462 +79 1 79 463 +80 1 80 464 +81 1 81 465 +82 1 82 466 +83 1 83 467 +84 1 84 468 +85 1 85 469 +86 1 86 470 +87 1 87 471 +88 1 88 472 +89 1 89 473 +90 1 90 474 +91 1 91 475 +92 1 92 476 +93 1 93 477 +94 1 94 478 +95 1 95 479 +96 1 96 480 +97 1 97 481 +98 1 98 482 +99 1 99 483 +100 1 100 484 +101 1 101 485 +102 1 102 486 +103 1 103 487 +104 1 104 488 +105 1 105 489 +106 1 106 490 +107 1 107 491 +108 1 108 492 +109 1 109 493 +110 1 110 494 +111 1 111 495 +112 1 112 496 +113 1 113 497 +114 1 114 498 +115 1 115 499 +116 1 116 500 +117 1 117 501 +118 1 118 502 +119 1 119 503 +120 1 120 504 +121 1 121 505 +122 1 122 506 +123 1 123 507 +124 1 124 508 +125 1 125 509 +126 1 126 510 +127 1 127 511 +128 1 128 512 diff --git a/examples/water/dplr/lmp/in.lammps b/examples/water/dplr/lmp/in.lammps new file mode 100644 index 0000000000..eb082ea5ab --- /dev/null +++ b/examples/water/dplr/lmp/in.lammps @@ -0,0 +1,71 @@ +# bulk water +variable NSTEPS equal 20 +variable DT equal 0.0005 +variable BETA equal 0.4 +variable KMESH equal 32 +variable THERMO_FREQ equal 2 +variable DUMP_FREQ equal 20 +variable TEMP equal 300.000000 +variable PRES equal 1.000000 +variable TAU_T equal 0.100000 +variable TAU_P equal 0.500000 + +units metal +boundary p p p +atom_style full + +# the neighbor list between real (1) and its corresponding virtual +# type (3) should be excluded +neighbor 2.0 bin +neigh_modify every 10 delay 0 check no exclude type 1 3 + +read_data conf.lmp +write_data init.lmp + +# groups of real and virtual atoms +group real_atom type 1 2 +group virtual_atom type 3 + +# bond between real and its corresponding virtual site should be given +# to setup a map between real and virtual atoms. However, no real +# bonded interaction is applied, thus bond_sytle "zero" is used. +pair_style deepmd ener.pb +pair_coeff * * +bond_style zero +bond_coeff * +special_bonds lj/coul 1 1 1 angle no + +# kspace_style "pppm/dplr" should be used. in addition the +# gewald(1/distance) should be set the same as that used in +# training. Currently only ik differentiation is supported. +kspace_style pppm/dplr 1e-5 +kspace_modify gewald ${BETA} diff ik mesh ${KMESH} ${KMESH} ${KMESH} + +# "fix dplr" set the position of the virtual atom, and spread the +# electrostatic interaction asserting on the virtual atom to the real +# atoms. "type_associate" associates the real atom type the its +# corresponding virtual atom type. "bond_type" gives the type of the +# bond between the real and virtual atoms. +fix 0 all dplr model ener.pb type_associate 1 3 bond_type 1 +fix_modify 0 virial yes + +# compute the temperature of real atoms, excluding virtual atom contribution +compute real_temp real_atom temp +compute real_press all pressure real_temp +fix 1 real_atom nvt temp ${TEMP} ${TEMP} ${TAU_T} +fix_modify 1 temp real_temp + +timestep ${DT} +thermo ${THERMO_FREQ} +thermo_style custom step ke pe etotal enthalpy temp press vol lx ly lz pxx pyy pzz +thermo_modify temp real_temp press real_press +dump 1 real_atom custom ${DUMP_FREQ} water.dump id type x y z vx vy vz +fix thermo_print all print ${THERMO_FREQ} "$(step) $(pe) $(ke) $(etotal) $(enthalpy) $(c_real_temp) $(c_real_press) $(vol) $(c_real_press[1]) $(c_real_press[2]) $(c_real_press[3])" append thermo.out screen no title "# step pe ke etotal enthalpy temp press vol pxx pyy pzz" + +velocity real_atom create ${TEMP} 23456784 +velocity real_atom zero linear + +run ${NSTEPS} +write_data out.lmp nocoeff + + diff --git a/examples/water/dplr/train/data/set.000/atomic_dipole.npy b/examples/water/dplr/train/data/set.000/atomic_dipole.npy new file mode 100644 index 0000000000000000000000000000000000000000..d7e58260f2382fdc6e69d79afe6e55eb5b4ccd64 GIT binary patch literal 92288 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

Hs6da$^ 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 U5F5XW1JxXC{1xq}ZpOw303O$}MgRZ+ literal 0 HcmV?d00001 diff --git a/examples/water/dplr/train/data/set.000/coord.npy b/examples/water/dplr/train/data/set.000/coord.npy new file mode 100644 index 0000000000000000000000000000000000000000..a585ece57a31025e656572d6772d6632e90d7f4b GIT binary patch literal 138368 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

tu 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�Ib?WcaBu@(_NH;<+En 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 XnleMWw6T&0%%yuA@z#|?j>-Q3;_Vs1 literal 0 HcmV?d00001 diff --git a/examples/water/dplr/train/data/set.000/energy.npy b/examples/water/dplr/train/data/set.000/energy.npy new file mode 100644 index 0000000000000000000000000000000000000000..f3ab9de59159938b229848ded7de5fb6a0fdf737 GIT binary patch literal 248 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

3qrNk{+I)k}^Q<`H z^cQM>KOaLb4&$&pKft+j1ubvhi%V}GW!n|9_R*dLa#ZKavNC zJ`mc4plVUT`d{)8_8vJyu^HZGCUfx6FQG*g=J+&#XYH=aAf$=`Qst< zbK7csm;9D`4iPM#7c)rtvqZA3cIMHa(^X+sg3o;-3a;38l`395fx`viJXqNXo=@#A z7*S5(b50N4RvAj)=7vyL^O;yN=@+yJ*&{6(*pt(GKZW1{E;N6T2bK)==3CFMbI0dn zMMu_-9tGTDe6GuX1FWEf{~NgY;WFJh{90N(zd1WRFyJY5F=Ed7Kzh7m30@fWh9<6z zlIol4xT60lV81^xcV(RqjE_>BImGLE^1=0%5~&R+V=C@b0T;S zQo*$QU10FK*Q{@oAV%Li1KZx5AW0VQ;a}OqdJj8D+^EbBIt)hhxv^CAy+K%66iu^! zcBZF$R$^A*4l!;yv3Zj$SytcYWlB%dxKqTJsU9q459s=TIZ^0$a17JyWX1QVu3)J& ziAJ|6VD~{Lyw6^Od%i3ng-zSZ`l&4>EW6D2*W;OClplzjt+CE99PKCfr05UcaA8k0 zjY!A^znrD8Wt0!KE*}h4K5sb-vk^G2L^h_iE4-ef%X3OqSmHK<#wAs(y!3t~0Pbb|@MTUM+eKJOaUA_`PDCKijf#D1K9SL!X#BwyD=g(D;xI&9D6F zXoCr7f7e3kpNsHqh=KHP_glOV@uu|1q6$zuXCefJuEz+)eD=@iCX>n)X}Gl$31(%a z@4}yl%SKcGz4!5?V+v=_a<57nQr@lM(!rl^kkx_iQWF;o%D&hWJCcG~56x6uFl97U zhc3c(_lwy4oDg>Y(J#(Lxq$o5?!v7eJ=ufH2{`*s3RVX9=ZuCmlvHKM%p@OKYLf&{ zgqX?BJaVFKlTw6NRf|yNh9){#&!b$QCbsS~!ljNSusuqHRwrv=h|qv%9_^K-7}fCm zuQT>PQioQ)<7sa~BKI^nQMh^xKAB=pvod;$U48lrktbu=j{)Jh)z5=xJk#0Rh8<$~ z?yEvc%{KPgCQKOoPz4hcec+c28TSFE9@V4o$0wwWN~E#XD)sf|8qF3?Ra1go18_z4d!EonJ+|y+O&%AGl zTV-2dwZ=VRoo_JP9wVXMmoBkM&qCNn&uKFIAv5S*pDNma?I!4Fzh>i$Tye~z{#5xm zgtgRLu46nU|%#dpKbns`kNz4{C_c5lHWCsn6z}AeG(xM1&_^S~Qr~gJW zyCf^>lrD#;a(&Jdc*?U-nv~N0JiI`ZIV*X z7r(;qzuNToei*I1br?6w`|%y!4xIOS5$^*YLA`Byv=k_<5@a_kuoRr>yMIqRm{${W64ZugJwt8?)JWY+)Ar za#7bVSEAbMDrR2UjWu}_@Xtd9-sx!;FDHgDmFSx^f5s`!3GHCzHgXtov=|OI`*N1% z%KtLqpl7QBJ1_KxnZH8m!@|i}Bwx+O$9!O$+HG|V-vvN%5u`X8-^ z`!4sHhH5%z=1yma-u59jvNLxTccmq7Ls)X3N};Q7AntNg!FP|H$>wJO3%stw{`5=| zq8=aRnc`OdZ|aK}dEzwdnBd7y4tIo+jX%UgOLc|&ZWCk)ez9cT5zYPdpV+Q51H|~O zW3tqi3AkY8Aw0Rdl-2D~rJ(F9-~%I=zI?eb?;`K1CCRDt2W`e(%KSTh06xpRFIm^_ zA`UzgOlcm+#J#0U;N5eg%RS5CzuevQy`v#HD^pmSZvgH-k1*(Y0J8e!)YruY?z`HO zhRJKODtRIA`)ZTVnUlhwAEwfkwsBO@Yd)S&(?^p^b8PuOpHe0aBZr2$U{E%ZvZmW` z4?#R>^(|#7lM99B0Y^DQa}1_0T?HTRtj3W$wm`Q(=edLG7?~M$qDOOgV%c(4tTTRf z@l0}~(Ah2+YV~fwr;8wHloZ%nZ)^vTf1`L7Kv7%L-KRW-Vn@!RlKC>BG`p9yk8qsbkoK4U-ZUAoUeuLuUbqLk7fpjr$A;s>NvrUAPzBl9ALq}#+nm4A%7#Br zgxMwfd=}>(yt-)md|d@2imt^1yjSTRH)Pue)j+U5TiKf794_V(%H*nS0-gIQbPV`x{ky755 zqSBKY*njU~__xpxjqC#`dCV02Hra%#rt;nPy%%s}q6wR#uOdq~-HX4gqA;~7jBOb+ zS^DKiIF=Rv&%u|5+jC|5;3t4OVnudM}o5unueDIthP|oq{!+ zI!TKT9mn^Re!|OmZOp-;*puH{>IPfOqYvyr_2=`Xd;C_@HJdBcr+6R!H`{v^-2J00x?~|$H>9xp3Lb*d zF%OztS4YYwD>(Or^Bx|2!Q?r{Slu^;_r8YW3;vBGGb-RYr5xs{KLs;x8cIu=E$MKL z9<`crkHx`SaY^w2eExd}T-|qo-z?|%*YM=S$qT!WZ2OHvw_lMbtV|M zY>}9{V<7#m=t*a$B-87=IW*yPCH|Oof&QKKmO5M1VpH=xxH`B6lE!Xi1MiwMhdGvP z{h+b5G5ZmF;;{~A$A5>7{pYgI#&vLCcO_x;94g`87wc8S*y7*wDe~oe%-E)f+pm2D zn?CLkGCmrg=d|FCQ6F$R=PUgTC(ylw zU;Oqx70-Nr1`}GgVOFXQcgmc^n9Dh$vtu#*kz3-cxh1r)NCQ+aS~A?$Nl1I-OPh<& zGKu3y5t?^U`r03nh z%;%0lmy_nwiuq=AwYY@)-geRL_ncky+L%VDx}&Phntl261zM*j$QJ+F0%;xs^K-5M zmbx1g{$n2aX=6q7LQJO5q}Z#P{m2~+HQq*KWo`!6F2+>6Fb@*K)&pTeGO!p?68;x+I2P`fdhG*&F2>&H3~_la{) z?+aEpFqysHQz=s-C4BjQ8MKr+LS%yqx{D!Yy~a->ug=5Zao-X;5$;Z3a=dV|pBpVv zQIwy|2!;Jsn`MI?Wfbx%9&2-Vup92#Y|pCqlo{HWdta60DK|wpyJrg;&Z~lJ*G7^1 zG*O5iqKzxso0#s9W1OjY2(`!mQ?8>J_b01MeRJqu> z+Z*wAd^9`Tb(8E%|D(9`LLk0#@}i%Q{|Xw#U2(yu0PdCK-OS7H@j(3_%5t1RmU}Le zo|`@0UFF2KEsCQM{_Pf#kirGmE0664doC?s|swN)A7jdvQfF(;gP6+YoS zbX95R^Lb#y7@Y5YnBBhcTI~Jk4J&`?K}+srv6#z4KqH|Y?kyY5euQN*r;ajF@#1iC z?aW4eiX-GRz3!7+(`%Sl7bG@pNXJ7;Wd#iwt%bTd$`X37#Gm4&7?qKt#?BMS7C`xk`gu_1l{**y*6qRPGj`$J)uS(F#}CJI zGedX=op;X8pBMM;bB5_{zM|?-CHONV0(jR~c69qKSU6}kxW*K62G0xTvPnf6Kf;4T zvs6T2zU2yjWV<43pRlOx+RBp7<$nls}aT z%e%1op|@b(zKtwslO=AM^`7s?gUJ(`#j0Z?VIzCVOv_5eOJX&&1gPPVLw3~J#0@L- zg5a=s3KJ`b(Uc|+l6F+W+T+?}G4%-rP0xXb{Ey6f&KWq98V;u`cfs0At9a+OgtG4k z;NN6@q639!Ud>%USBdA1+Symj0o1&y7OY=Rp|2~iqFdz$HvQZ)mh$E_t2n8}IazJC z%brc7=1WnOkkXTRI~-!o;cp?=JXZYk!%=WvC1YRB+r@4(Y;fK3PK@(>fp=uaB?q=J z+h2+7=#?UPG%yqz*F-aU%NBum#)az|y!-e*j+RDd@LT6W%2)KG@w=_?bhND;|K-qK z$9LieMegCrkV9VUVfMK)n%QnK5)Yr*&UcbYqVG6gh#3<}z5V2}`xUcDCwLakU!%kK zJUii;VPC2_^-)Ogktf9SUW8SO9;z!$X5B`qz^We)z&CFZd@qcrvICyv*lJ15dHzE1 zG-d8(z0S5b*Ws*s7jjV?E4Lapnfv?XaCMm#b#eQ`PCVaE=d43$TX8O$nIPy`nZnh$ zX|VeII$U3K1n}ima-O;X-<*kJRu*a)mpX}Y=RC9E^Q{BVP?g|nc7o^E!}!G`eqA_L zjH?!myTrg?r*c*u{E8Xx+K7GP$K%-G`(;7d#aMs(AV#h3jCMm?Kxu|C&HXVEy1&j8 zmPPbn#)^O7?btcO!AaIkW6A?rutlxxOiH3yKF&|H(n#YjHW}T%y^g-Az7;&Vcc5~O zgJ5`b4jfp!S9lOCWm{91(i8uFc(vPBu%4L)M;2@nKd$Y@`QASGXnYB63|xpofs@&@ zsx*j7tzgyVf5}|`fjDYq7~CA&o9%19Aog;3FaA~65!z0q!Ag;VK!<-YKl1; zIH`(J>onmVjl_RlT!oldd=7XSPH^<3`2K!6`+mI*`n3g#aqYA5WQQ}>=8QmUv!*%T z%Xmjp4!UX)IKRI+!QxU(uQOx!tM<_JJsm78Z71y>+nYX*8!OJIdNeLd=H8zrq`WVV zj0|>eA{1j&Hr3V&CX{`Q3F#VocNd1qR zm)Sf_8vn#`EqwL6DC_yg1fLeQuoL}P3+;9@*y6rd1+(`xLhaabv@-Srd*u>?=Hphw z*ctEHiN>Bd@Y4Y3T_41jmH)A^`!s-5nHkf|cjezDXN6t)>NHYe8*7NF5q2GZ$GW#Z zgkTMx<$7d8H_k-zy~}PqGSP?6u)R5_=_(Yek;Ep`nSzH8W(wQa5#1N~-E$YK$^XNS zR;5#~-h?=+H4)L9_hw@dPK1VSrE!>htc=fjnKqr z(dHAwQT{HLUd;~R97<<8^8OdUUHD*1*Ol0iZwkr3hVuXRm8tj3!L+pJa4mlo=Z-ni zw*+g^{7(o;#9X)=m;pJR>t)+J9iRjLFU2&!6vo*!;)lVj;Ypc2Yu=g-S;Jjm>$eX= z`P>xdZ|_8=7v4+SBwoV!(=x&CJm)%d@`~2RxmYZG!7=*%v2CC)?a-;A(4V~T(tJtS zY5oR}hn)ng;pee5xQflQ*a&J9jUeIPZQB^_qwprCk+qBrN8>m%^4z|h?RoM5TI*uz zrTtpA&BRj3{aga&xjoR|&zM~F`TlU|3E0qNiUp=wqQCuZv5$8!@iY>owB?AKELV_< z_Z0SEH9$?{WBz_ur)BHYAU-39-TTxZ2K~$AjE({LBrOOpg$!mc+f1>o@fP>83o!n$ zA6cvlqqX(v%p)Q|Y^{?Em47b4?GLx%>uL?~?%Tlb_4v+K46(xT+Y*JIH-|{L1B}mI z{c)F3CAy$}N~7+Bs5CN-W?$Gu3T_)nQKP$1<#~#wIoMM3i%_yFea~tS z4W!Ly*K^L!S`0t0g{7Bus7YUkbFF)DXUzuqCpfU7PD_NPu@*Q@`2cQOwV69FW|DX6 z3J7VbKt{8e^L-V}a-PfIEvMNU*OxFc?=JhhZwG7cca9l2O=D9!+dx<^ZTh@Gppp6Z zxNLnnv?f=<{D7$#m0`x3H=AM2O@2GmF&CX>Nsy|i0Oqdo?Dx0?Tz7sX`|-1x3kYVz zg#&+BZsKM*<3F0Unt0IsrwiEiwCyaZa~PX+B^)eoXwmiIznElZkjOh5g4Nfvu(du| z{1;t_SGxAYN3MT`L(7I>g{uKIS1n;zCe#RnE)B;eZ7FySe!z^Vl{8FY95%n&h;biB z(T)v;e4ZZ9XE$Tw`5fC`&r9LyxgQi=8bz%p>hc+<+&SYqiS?BPNX%^)b8fyn@O-my zcGn5A>V+gbvKdkh0tLy}(`=l^0IV|q3dxyDC|A~C{nRb-wyHm~-0jW0UfWQ>U>oTZ zo@M&`@hh#EK6BhK;Voz2+VV5;-iqrQc)0HKIfqz-Un8xAsa76=)`@E05(+=Sb zqj=eV+fugRxE<&I#4_vtL#1t%oOP$WMEGrUo|dmEfr0AH?DBFqI#Tlv%&)HFd(QXp z#B(6KnWK-(<@ppLX!72gmNcaCCuRN%AiA)a{GRjY_?<#__Gl7*mh{K#k4N)6{!hVi z+yUq|%9OQ@J6-n4KZ5mZ--d%OuNBK}$5CCYqV(yWD2NJf6-_@Va7Np3Sp3owBv!%T zmj9DyE_t`EiF*@+xG(%&1RS&MN&fbSaq_XpG{irhK0aObUnhx)n;PNQjaD}3X@w6Yxrm(7+vY%7PwbNtD_eLCx5)tNFP zCv$#Y7)^I~fdkq0S&`-UF_bC4R!hRwl&uN@YVINokt8K-0Uc$oqMuF#U{53{kp) z$!kS$esGs(!|DacZQPMm5y#R>KX8BCHQLm^n(99^agRy{O|ZKr?tk4$;Ou{~{+u!X z{JH|qT(skUivVyMGo9z_6y-N(7=r%hIt&^A7=}L?K?M&l;JJo=IJ$VdG-!GoXdG=K z#{xxKW8=qNmL~XciZya#8Fx1J!$PA^kgPL`>>fN5GQ(TDDvrjTLzW10vp<5vGj*!Yxxkr+mOPVmA64d?U`17qV91+crb&rl zo$-|7+U7&!^c;L_r6{$oDCTaORh$bDjSh46!JL~%amsc@j9S%2zOcJLI<{)F%zqUy zHq@3{v<6Xl{8W0;=Lm_f8e!0-W0Vxt74)LCS5qv?v(fvNmmy? zq03)a;--rtcCQJddl$yZ>x_p}#^7-h&2x>QZq2im;}h}Vha|G!_lA;OC)4|ngR#;7 z4wDA&C5!zl*f5(6q2mJ5nK~cT)>Wbl2|DaZi;TJVO6K>jk4$~kEq)8kU?;9*vB#86 z|C+*y=V(DGCq)?bJs6HT`cO+oF-yulL~p%_ofyaYYLhw3e9&~3-J{+1dY>3!!RI*M zFYQVxOA%@-1?nC(i3)}eL+(;#!re5UsXC7S;xfz}GeWqPewLm;zY2dpEN1s3Mb6M#_T_$kFzs{m97m;yMf=v*DS#y z;vYK3j^JW#H>~}ogNU!t9TYut}pU#-?e} z6vI~WyQ?FrFbKE~^Hv4pg}N~I)Mz)Z zztbSD*~FPwpEU4koI2lAq%wDLHxBSnr^RD@p~AqBcJ9~1ZKi7SF*RkZ@9WvJ`mPbo zDa=E3ukQ=efURtpkjvz|b=mrw9;Eipi^WqQz4-i@t^NKUlY`T7T5SLe>R(DjGfh#L zIvzT9PiHeX_kz)9Z!)zdE9iza4z?}tOud7LP@g?UG;zKP_lFw8u1+6dzwR-ZW2i5d zcj`on?Q@wAT!Spx7~DC*fRdkE!K(Clwrs-{7XR#-Xs&IFurmPGjD5q##28SZVI}>x z8P9y?8^W^~Wm(f}E4;b6jQMV!iz*5?q5W=u{Exf5`g{`WFat<979bY4zJLoKx-tug zDfDb!hop4oIQHvpUr?#u2PYz4b3SIH*!j&q5I(D-1#MlIGr@Skh?@HV;?^!3S>O63(i5P61@` z<)zSb@k4fug^+pWU7T=kg*+%k8}F&R2nA)Ipdm(|xm4-kFU2!#_v~kMt@#fd-}H`M zi~b9yTsGJ`6auwT0=+<12s1Lw-b0`<{s$ud1$<#usa^f3v~***qa9`n(v^(3z#HrL+ET7n0e#6ne907WRGZ zj@uW6qsOLeg5%b=qVBmtI833C+WC#j?{T8=?IzDQ7G4I+iQU5uHpkPHjZG0+n{?#MSs9r}80+yiX;hikXuQvw7M#5yva4fj!4pTkPLWjx| z!7pPkjoCVo?)BV-dCJ{|g5|SdfoF4>N7glA_~=A9x9$=P?eORR+sBfsgZV(Ui%Bmg z5;rf>p|dw_NGOw#VO+WpA&o_o*WcLZxoc6&s+aWT(j{#0lbuw0xd}?Q_|n=8U%2=r z82%pUPj~&(DE8Me*?*4Zo3E~d%C!!-KGh8smuX=E#Vs?GrSac3 zfc~a-nQxvhI6GVyHm*s))2AL_?W=!m)?+P}?{XEs_Kf4cSsiDSc>M-)K5?&N%V)Ll$(6rrO+_qc|SG?+(M{2g_9+jhFVoL4$(Jg6`}!!p)qjkY zGw!m!9p%%994MM3KRIhSpB-9h*BqS(&bk#N{(C$zoR$5|u)Vf?x~!dicGT0JS6 z)b6Cx0{_{het8bPn6h1bv;DfT-g|}^>vxBpx748l0oy`4MEQDE9?5LKRem` zD|5f!NorQ`MhJZ6K|5yFGn4RgIM9&;15F+2Ua2|V+0FZPAJwJCcm|UDRf%DJ&#~)e zI&A0UK6KzGBOm2JQDsmejr$bN%zRo};9T|CID1R;=Y>pmtCy6IB zBe0?KI(qmi6?WRm*kSJ;;!Q0Dw&Bk^HX&vbetdkJ9vsROkBLEO^0zy7E!D)giu>Wk zmq9p7^EIrt(30kWC((z^II2zq=NSHFnKqn_=eZ8;94f)ESrFqsS4c*BJqF!w{qfcp zRot7X#=nCXlgs6e?B2@hbgO42>t9>WJ_c#9F*o<~8PtH!mVvytT*0`GnSaA6keSLn zD!#gkg_aM%k%e37*LGzbv%?Nc3S7|f6z@l=9H%A4u5f7Z1^DcCR?y46CRX(@r!~KQ z!Gn7%JO}Q^o^Dfdszx|^w##+Eb8#rj&k>X?vY%r*VJ*{O}-St5JM z{(paA+Zh)av!xKr$G+vAtDYTKsct11+WEaSH*!x9pK9xnQ*<=a<*ZM z9MT+uSl&A|P%~LYs?!9#-Mu%xxZjD|kItro5;xpk)haqY-$RGrs?zjN_elIx%Ab+F z@yo9!IG@^`W{ycCZ$CLR_CClKI4FQ5;}5+4I0CP)o<%o`weehqItA(-q(+Z@EX>h^ zIXxearpHR4?c_GPe4q!u)xAR-Jy&5T{_eUxiT9_fmx~_ZweY6PN|;mdkR{IuppOCN zu1r?CL6zICNR8-aI6N#>xfPU7YqVOd2tmc62qGbUcb z5Kj&6J9!6%6W_DvPpf5hQdiP?QcgvjZKWfhh|X&Zu(zmy%JK8T+e#H16dGWqa-6Mg zUL@&q*S6M1Gqx|*jQc2+Xxq|OHg;Th4BVzb-xs9NhB*qf;@(_bP@}j2B z6NGwiC3-z18mE0ahV%ACKvLB;$Wgp0CbY*;X?UO{E+LW6fg`Bg1u5p$1e$qhEa%WE zOa0{U`0ptk-%qo~mLGl4c(;oDc{VcPGj~l4h!bv4jHH_|gGFXu`iY?mmkUk$X@9-mu&=!kd`afk!HkQ<9X9B<5r(*9u3Ha!rl=HdX2y?a{qE$0G zIQK;ffAE{G?KovTIa3>Fo7eOF;zist!k*ook&m}nEP56GhIMzN*yG?K@bG{ieTbV+ zd4|zK?dD3HIa5xW=BIfttCZRB%wtH`Pwd6xOPq^0R(SYn28QYw;nj(n)TclhG^b8t zGY{EsmJCfec`OB6&PljbP!s$F`DzN9v z5xA9Uhm8+AIJy~E$L{kSaO>2&6sI;F~qnKmS46Gcz5oGf-*v%`&ELJm)1xSn0V%aIqAG}T0BL`BzEE1an2>E(YWo-3zUb?I)LcF<^9enLuSD=ytbg z=KZ7vtUk4YPDUNX{~n6!FS|*_jZ>*|-Eo?j`;qfi!-T`{%2{tM54JP$9_R$^C-s*< z*tg|ZMdzSEh{=hdit$wxI_5b%*SbilubnaB;05OMJ{E6$6uDP^K0TY8h9jd4uuow- zPBTh?x+96OFzyQYjogRZCbS5Xo?Ky5U@Wd&*dz|Sc8IlI{UIJL=t4Hm%JPQ8mr<|L zgY!6EP}kwvc;&4Ly0+G$`@;rYcxRR5In_hwCFQIo^&s@;_rZhb`-(IB-vi&MC>*gY zR@fTg&GL$s(D&sN))xL!Z2a5-aCRUb{j(NkdUeMu2?X}b`iXtsD`F3e)#Ns=xa>pC zaB+as3Tol3{}UBU1%Ps4QeXw#Yr7#5(hEx&&9d$$R~xcMSZG&7_fqCj!oz!Pmn2+p3fs=+?6T!QYcW&1wTH<;ky@M= zljYJ}2#B{PY4&?|_SRtjzK}5d=ZAIG{<4z6$r7XGhoK_uAf%tuK`O1{zLU<#{cS?G z-{mZQ#aQU8A4son`@*&6Lfo1*4{g$3u!;IUtfM55KJhHx_-4>4^ctu_o`wPZxg!dTA~d=XHB*y%9V5*)W?3dwgoM&1ML+W+dyrh2;cdLh7@O~03TW9{)&lGjlcEd~Q zPPQReA56C15K9VIK=X;taADUHyrwho}W6 zl{D0^gRQPWDLbFB1KAPKep{Ixe?ElNbG2#wTMd$q2*#!bli2)Vf3ywBV2MhPWRgcJ ztVv%HRy~Yo9*uk9)r&#kHb4WD3Wk!G*Fc)sI1m$7b%;7SO2qpUY{Ft)+SA)j62H!l z#ZMngZd+bMvF=*#ny%0}bJ z{Dr=_E}@t_uFhg%{`R1@XFO*ZsnhP4=i$=jD6G5rQCwCr4i^kF!0!c|L-QBI{^ zJlzYMZH@|NM>O!;$77uB7!O;M9huh-AJTITAkKKCK!cuSWOAH&ocvmrzW6ojPBf+1 zL%)PqvrmCxlPcY5_7lSQ6|sA56Zp+tjWgi`g?kQ;LQH=nNOk?f=d^5^`)H6@y}kwX z2D)HPz6ab|lfteHRzk~|WB9hy05a&4&rV1E6edq~0A(x^_N1>9Ckm&?%y>ObvGv7W zi+cY5cKa~thhV*qGoM;@g!|fK$v`uSwv4<6M+aoGL%X!Zmy-r^7VuWv=BrwK*4MH3oIODC4MeL3~k zT*>*B-t6gzr)+8KZ}Gc=Gl*k1;^Mw7?A!TP2=m;7)nk8&W%09V@KbA2|91_ZtO~*Z z<|xevhR}V190rd(jpIr+Sc+X9Y7RNdEG8dj6>dPqhZE82%T-a)pMM`(Z(|?MszTe` zb4&&WU(vWX1m?No%L17bVVX*?#OFP7bds4Qqpa2GZ-iRf!Wss=6 znR8>hGTo26n9e98kawHQ?Az{%v)`YixC@sB%e{YO%W_H}uS=e2QFfcV;b!t@k0GDy z9t#H(b%a0z73$!($R^vXOup+7)V}&&cCPY;cy?kKTrhdc3WnvBt+zYCS+ikmQA8?B zd%zu*W=ha>#t@7qgt8v89^|*73mev$0Nr`sRCP@!ytOJ0KDFQDtej+~IZ030BTwe{ zXARCav|;uO!`TD>F0kT`6&f~oBkl!|tooje`a$Kw_k3lk(T5STh-xW>lCWH!WZnEH>x1hveGwc2HgOIzo5TZto zM|X`BuveZ$kz)ec*~ztKo$U_FGHujw=yCNfj^fG~IpE?WL zW4{WLAG^RQgkb1QHBjnEW;Q(!v+a4Q7_mr=^Q+@=(}ST97T^Wx6GjVr)`ZwTU-yIa z2J}(o74Kf03nqnVApOl1kT^&IvK}DoURw(rbx;bpf!qQ8SZcR1XI`&T9ica;4=d1ozL_vj2u zPph$AHE*Hg)^Yr{4b88QR&fGt1gHAw|~F>m9d3y27+(pJs5k)iRRagzkvb%ns6#ohmr3djt7}3__g|gW=AN%V57pBAV9u zliO7zy6`rJ^Xb*G;LBR}wcS?e^=A;D9qx*HT^2LRqbPQIoEjZ5F~0aG(-(`@>B~L- zb!XR^8ZN4G;LL;$Vcgy^oQu03^SH0jX|p1=sy(F~Z!gXQ_Q6jFRVYo;oNRV+eqxfIqueuDQd?xKWYSIQ1AD8LP4d@23h zAj#vvHX1hHhfXLwWc*zN^-Ff}*<6Mj=J?{^uR5r#+E@N6^P||Ddy@PA@?d-{&pd62 zz%UE$`OxI7S#o=PeZmVPMd7{CXne}4QW#2U7Dc6^OTVhxOYW9 zB}t5VelHqEULAoK5^Hd1|8aDzq$lsJe-R~~-y{|q_PDUPghdERVveB>>v{T!*h_mY zj{cY-QGMhfzq^jJd-L@$IdC&gQ4A*|_v7TUEmM3IIvdA8jBL!7k7lY`FE?F z8DERW5erX~f!Gb_)Vvd|GxenwD|=&8oB_4;iKU#~KxceLOBHvBd%I1XJqiq5KC#iW)M@Ht`~O>v9G$0Nqde_HY# z!xcr0A0EV_>ZAE>@D${5Zsv*0)3N51ntcD|O#1wZOmRV*CiOm4^VjP~0&;4W_$KBGl36@>z*4t~qr zHZP%{)16>{#2%`A7AkWc5=kBEG2#*Y&dwxvk-g)$)x~qp5Wlx^Us^8+om0WqDDJ|9 zb_e-J_7iVUna}dCX|hD+cslpbM;x*$iS}?#b=U8X)U97Ecizv&kIrxDT8jcHm1$VpS z$@HDV$>ZPf(xE8kdDxSw6m)=k_A&-FoiI6QkTk;kC~og8!hoKqxT{xFHvEJwijj9{ ziuY!a91ml{P;E?!dxUP`HMrVz3?7;|3qA?P*6xA9>nTcM5Ze621Yy!= z?(ObHeNTLa8E=x<;pPspKtow>cIyiB_RB=;kYkXRTEuU_`EWe6t8H+d0dwd(nY(|Y z#Th#^+0a8$I;y9G!BcI--rqI|P12)Kyu%TEw@#qVJ3GPjrRHGr`yybdvG8-{T+V{& z%(Fhq(uTr2kiJ|>^Lh@(eLF|~ucI>!%jxUla4F3sq7^p()sg&{Mz@w5O^Ax4yI`@I_ zFc$|r7>LCeq;%9d^FPK5T)KA|b+nH{-~Kt!-Zuv`5)2^fY%<<-ZRhW#JYrKLL81`= zmkl4X@H98F&mKz|U;FbJ<#}}CU4m(v?)b@hJKPSdgyKXK%FEf$oPJetX2Vk3)pPpu z9Elh1&QQf^BVO|R`f|x?**~JoxC`uh@pV{szKzWioT#VkWjJJeomwv1)6VZrl68Y8 zkl~mEOttwod)wsBtgkx3>enOj-bPIvlcrGgAU8w!ydn~sy!XN0?SA6wu3JLrHqQ6* z^$ ztF8(&{ghb28c*JT)`66aOQ3c*4a(#mqvtaTt>|-?4&*#zdyL;nf_ri{rSA)VuewGL zT8N+8=-F@aq7b+8Q`Cn;x)FMO^YfOEFru)X0km9F|8WY=$x1N)c_V$nw> zLCgPv5ZZ87NZ-KoZ?AKi=dh)0>etC|=$#ITvz9W4phI-jOoHQGcY#lnHa)fL$K8Sx z*_kvo&h<^Bm}}XbuMSLGQIT_tOz7{L?PT0riN5umwk_HiUDhRG zaNtYQ+>rvW)!S%H>oV9^mJ3-2Q`xqGo%C~gf9AbxJq>s;4Vuy>vAIzlV#Vi^V5KLe zOGBgagq|hHjva`nORa263%%KNkJ|);B{(R)2ma_(rk}mC(fY9mcfe*r+s6}vOt==h zw~ru089yfKA|07}hvy}2FuiXr3!W@T!uM06i>?(u+x!cR>OvrB##eDoA0u@70o(<% z81*^|ag%i#Y$4ZagLQ+V%B>>~4*{NJbN zX+f}axHxk9WX^sHWE&rEwG4ExERB39HnXMp)f0!_wWvD zQ_Q|FID7GnB*r_Lt^HQUxy|ml*YLb(;kA(r%8%g6VT!ayJSMi-&*Zzfr$W>9d>ANv zr3#ZPg4WancyGW6Ji31jIrTgZQR`0Au;+*2`LajQ=aG+e#|kfYY;!Lx8l1}bu0%{O zy}@_TV{u%G9F+GSj-L+*JQLlFRo{J3X^TIyQEEjV>Mm$v`c>@UxmcBq5uBB427`^O z#lG%Q5OYNX*De{xM4fe{61bJtZ#j;a@_XB5t@%rHyA*Nb$o}k@UL7==IzxtiFUYOc z7rF<>(}y1taax@^&sH1brUqHbx$%>*>(UDLPEikjraGhQVq}{>1mUsRNYb7#nyIkW z*fr!Jr7had`_@uCI>H6#JyQnPVco?MgB;1*WTg<*@Lgis^oC_DQ4q=?kEI=5MNgX) z$=zj$ARnE?zN;KzKMO6`)5RW44F}Vx-Sb4PuQQ?M_9}LBda1Z{v<0@lHNkHy0@>6r z`N-M#(CD>f`+YexAi01g3%$Tia-Q8uJjljruaTAqbg?bs1^SZX2O+BI zV&>ULf{pJD@NYiA_R4wV*{UP7b?{1dyfl-W13)L?mzh3=MbLQIf~i~wIJ>25+QSg zB6AApiQbO};*ab)R`E{o9cqi2zg;zs^T~h5>a1jN#?;%~+j(BHY-cEz+zx=X*X2NVG>IdF zK0}+eD~?gmgN!6E_PORQQ+|;pzAg-88h_8=2Qh^H?CuYpo9fu>fVX0nbC>Wd?mlGo zm80+XZc*pP$82u22g^L!AB8Cm5{Kz~aYdhgcFyw-@l1hn(cOrlcE6U!@*Ywjwq(J6 zwkF+__b&&qCl*`4t-CqXd)=3019f=!{30#sK8SoTuB4pNFNG&hT`)P?yy#2rH`=*m zgI(>gG~}~jNdXJP^DU3CFv*eoKG#A=&I=*0;Te>;52s>-=cq@ODu+2)M8h*S5;L#_8_ zSRHC7NGvQcq3o;lpZzZJ^W#&(vCVs!bY~lP6L+zt5h3Ki$bgL7c8f!`6>#Z*Zbj{m z6RG?_e^%i=kS#Nk;dcgO`gi@bV0c*^lEM+~Hn+SV%T_f==XO)R@tU_4@ zUMyCoCHlv?+qXpM-LE^7>BgAW(hNup3Siw26*H43COFJuFe?bU%=a&;^yQ2`?bEv@ z4xPD%jhUjto!s$k>z7o#5tD?1Ul)0$AET)C1L^$d?lgJGL2R9SknI^(h3h6PhRX(9 zu-n)BZ07zK?3YRoMSLE@EUo)8+w~EJ*Te3>vLkV1sNDs3Dt&~vIakSD+Zf}A=x}G? zA-FgqS6KYeg+gPs=%Q018V-oVQ6F1pxc>T_F!F{eOjf=N^}46= zacmjbeDnk9A|t{aGwSxFlJ9e~(U$LXHkzM?lZ)Sq7PVP?o}f&d|IMTfha9rMdyIwG zOyIjTU#kAImgQcpENqv4WN+dXab#x^Keyb&r@p#k+Yu?A^ou0l54x<`J%x=cP6X47 zH6WRm#vYW`tDGq>(aSeQJ8 z>iX&cbG6~Kv|xx{WkuclzkwCcHN`s%^tf-vj};sm%ky)$*u9&-*lr7JHgLPXxX)$; zGg_`i8(gl!O#8D;je8QGr>8-z&wUE{ya8O+z2whj5NgcIkgVWYs2|0b_)Iv8-Q8%4 zr)c`BAe(Jjg0MG0%=!AEPwdkXT(O+ozjCiur}q{F<{22PDD59VZ}jI)j-<<%&M7 z`Hk7>-JsDsfo6=DMAGh|`0YR<3@ss6{ab^!I2ux}+El*FP^IR-emqatLhQt3o&#;7 zAFq%Wz8fQ6nKpyjC_0mh<|{lQr$WC&B5=UOpKQ^edU`d(h^Z`_kEaZmvxTw(c|Pj} z-&U2g#rqrZ>VDOt#uG`zWA zys#lfSaSHIpfy>A??@XdJGffX)=KQ~1mc@c)j2=dhT!aUM z%3$lzflOng$U^MrW5l;)Jlf--_~JA7sQY(goo7qoOzT`U);h}~c*im2k8;tvgp(ju zbinH;eb|YL$r~9{y>ehF+IQjZiG%2`a!ODg(%bGs?Ka%7>HxY9J%WD2Uf}Uz z8iG-}4si~maME)Y8Zz4L}7T_ z7FafK00#JTrs>(QG}OJ7tsk_Udz}U`qg9a}>4-c1gl&W5Q0MnRnq+JP{s?N8VHU7)jr3qW2zR$#x*QG)Dy$pU$57rO3Emp&RM%Nbstp>{Z?rXHaq$CDUc3t-tlJ$O#JoJEg( zBXrg;qFKsY@Rg1>tW3<{yq8$~{k9w=&qvuczc~T+x`SB3+9q*Jpb zWZ}Nzt8CHweAs+LndR=+5(}Q5Vy$PnA7fcD7MeZ8@&h5z?y_C-G|?Efp8S@CHm#vQ zJwH&!o@}OWugmfd-xnhXHL_L05}IA&z!qf(2|HFcvXm26boO3vWbNC~Re2AMGV#au z8gJei8IDsb^5{PO6(>3OqhduH&fOo!c{rgoFft7$naNP^n>%<=Sm64o zmoRX_6R=w~mOi(xVgu(d6E)}8NG@B)!?-`uoFV=KF06?b{Rf*<$Kf90F^?OZm6Xgr zIdkV}n80&!sp8!+I<_mn3}E`lZwny{<7wa>GjtjKLf9AGDB3psVmJQ!V8XeN!rkW* zD(}4>f`5&YPMTsV$Of)qH+*_hOp+m%dA#6zNJW%hsD;egHzhqae@k3^=A(JfabUXb zD05BL05_w0-cj+QtJNo&_ZL%2f3b+~RowVYpi}HIGa4Q}@utOU6UbNoKCn6ylG&V& z{yRcp>1-Lhj-9~}k&?<8S{IpIbD}WjfHmDR^Rzu#^;GEcRpEZtm2_x+7#sgA1fX&g zRe3gYHXCQ0U)E-!3jUBe{uonL^@PZ6-`E)ELs&O05gYe;W3RG2I{j)n<>_V=bzz2_W<#kg>568G0n#$lSKFg|@apA8;> z==SN%v%pDs*|>r>_D;aML)nz)!FMTNFTu@#Y?iAY23}8wa_5UZ=OvtAgZ8ea-9d$d zPUl{!7_lCF(1H~l&zATvaK^zsuZyj=rnoe;Hw<#vEUL|&2)A#oq#4IQviwuC;mOHS z(&GF2Y@%@r`#ZWE6l#s3?Q9;%PWwc9ZVPx0_L{{0Ul}XD6hJ0CQ#8`zEW4v}2utJ( z>B0s_v}-G;S@BV%n7kPyzFOh^17mO^6tO*fZc26j#Nm&dqPRZdHQR8@j}!t&!G=y{ z;gF3CZa$eUPW3%OrRIv56gQ2vw;g4c+cSXkj#%*O1=#BTkadRGz!nw^$|r5e(>V{C z9v%~mjV_43vAVR7=QZ7wdca1r6;%FnGFy1&B=1pO7HsCNAbNNdf}e%c6~{l6+4-Dv z><>VQas>{#|6hhuiP_||!YPBhurJJ2Xdk|Y6&@Xe=EFibqh=u0X-}bc&KgZMdnKOE z*0cMlP{sFTTm$@9n|=G)gX&K^(5|drcz&^kRR8CDmR+Pl5(_0due1+sx~1UKTdm>{ z^b#u@x%Veam08|RpiBEk(w)8K!mp#wI3@ZntNpo!`s=yTqA}Ac^=Al(rA>@;jD-Nl zo;)uUg;%fDz+1yMK{@z1T(-U^TKjp>qa+Ww{>l{J%T|a<@g{7}qN`AU$^fT6DhIzW z{qWMze{e|e8}~O~hs{N<+{flht+zwODLG20XuJ=v?(#sB>MB;h>4H$t^L^JEjf8Ga z*F?YM63H@&4O9KBO)`=UsiLt8t=n;5*jN7>It(ZOEkRIq-dA9DBh zWp+T%ld~xGP}L!j@?=BkQ0#M7-ERw~4x34{5?0X{UEYvV9?3$kuAu29dtv*j7}_z- z0w*un#(rM!i3xYTNzZB^I&c2Xb}ToBf7R1qihddIde<`db}y3VEW=HD0Q&Tb89AlH zpFQ$y+H*s?lmu+u?hfc?rp8n!$5QOrNc!e{jQTt_A_cR0c5YKRF81ZD?vom5*i|89 zv2}bmo?W1HN41FekqU=DIL;z%l7wr6W|8bFO?>dqk!400v8@m5Ve~pB>XX#T#9Nin zxY!m9jvyv2x(_~{d#E674EfJ{!yusymfors&rJ5Gc!O>@C3h`rFUsYNp(@ckxsz#e zzo^D*CA$enRZ}5xTCa6{4blPPg)qXJ{kJ!A7Zbc$m4i_C)PFJ723A! zVm5PkP(iOc_@$ID>3sQ%?{8OduM6*Mq)&jlte)XLBRaB>+0gstuyOW^6zx#dSv{-?71fr~VVc)a21@&)6*kQyy){AZm zTNe(;jG?;b`Dq-^;5_l*Hr^4R>p-n%1%9{t3_*Wun3ftaY5a5{(x|3z za#A82@9fL!|5+9B9FVXi&lb{@HK=-|hS>XNGE+HzP&%~qAgzxMVahZ7sK?5#tXS3iq<=gG0wm^0*IZix4P|01#Ynqaf7MBKer8~=`a47%Uq z>7`r)EB?3xleaJ84%#`i_^J%$u)DD4WXd+1Cp@0;;8=jIF30-v-no;rAz5Qqx-jF;$bnO6~|Gd)$SamM7uXmF{e5h%SBH zbp<)&iD@c5z^{LEuUqyu)f)OXrMR*M$g z8CLk|cs?a5ro+$g#uRZh1w8!cLzqu2cc|2|->>7KV(?utTRMYsGV5@JkR+*|ewIDZ zPZHmimy4m>BVYv2FaMXjnD@%AaQ@VAa(h3YEz`5+xpjB=_A}qMye(YtbQ}i_&cVQY zq?E9!TAbfojXK>E*=MKeY>tYC-J^%+xNEqXO;}lk{YDSPJ$?u9_S#5(U)qPOc8(^i z?d`B}N* zw}-ed-#}*L1aYYD7cpvv0?O@rzycR<=Ce9|n)!_=+9DVyFV4Z*6}sFtmn$Kkv8s)7J=^y93)| zWQrzF3g+yBR?4Wd6h2t;?p0(e`7F3VH~NpEGY;!XZpbSNjao~&kM;1l%N90eM>K@F zdeWjW1`S1J)O>3*Ian~}WRXf!_um%}j#MM!-n<_ndUiK925~O(O>rU21kRxn7M>M^ z0YeS(MDAx6zPtuEYHY>Rox_;11VE>m##vvziLg#xw=QL!sr{Fi&dd?-9!*3KjK z5)HRY#@e3YXt3h}BeOZ8QcO5}5KYl;R6JI%7>^aES!f%10nMv-kmZL4+;PZ>C1x!W zqcdm2>gb2CeLx%MFO9W}8=iq3afhk)YX$R}xKwPuZ7Z>JQ9$LO8qUad#2rs$$*<-S zn`^V56iq6{Z@)CDtx}e1!o%^$2?Ie@`5}qAnsz&tufRFp_mhmO2iu!X(%b#(;g-4% z{nZG@;=$&)x&JZ9aTza`#YBoTMzquLOXK0!LOq-{*VNW)M47O6+fR6QI#PJad8o24 z#=$@JUeJH5AzMH0jTqcI9ab*BPRDla$2FsSik5f1S+`bg?A`CD&{}$v_TTihyBYY2 z9qw*U2DvHHDf%6dr#DWV5~W3h?c>4fhz^+z?1ZJ`qN%&p94hS@$_{P415umKQh;9z z?_p2F9W}u?D>hwt7_kI)Kh>jd*DW|ZDwOZr-RvCsIV4Y#$W*K6fUn#S_|a}}_fC<| zL4U*w%?;0?s#*qk_L1EW2te(fkErO#fTGUuQ~3Du4LlI;N3M%`9^Q}hG83Fx=9q&x z#p4V{e10q@_6y=!HW}J6+yKw%q~o{R`_ezvJ#p7xCvo4{Qt@h%2BP~3TKa+OhalcELVgH+L@jr@Je$B^MMT2>-I9kZN z)5!BKm7K9=PfNO0<9}R`Uhl35X^L&a`o^E^yz&fGYS5wWGnUZ0sy6Nn)UxaP5P~O7 z!*P6VHqH1koj>zqVc8%z?&SL=25Z9C7xk(Xgnml&F<=>9NA^Vl%L=$r78-0dAl55a%zC zrkz!{G3Z4Q==v}Df1Yr~(@VmZZ@r6dM3~c_mP}FSbvMDo)Zo920ItzGL4OaPAuWwX zG!c{8@;fm?7e?|9vtm)VM+!ynn$E%OcZS$v9|^~fXwbxb1J=}IoanEtSrj#E3Gr;E z(EVf`d-l%&dl#AFOcPtyx@|ob#cQ#r3;wVIXD{1jHmm57srnX2cjGQwbb2_e)VoNW2^>+qbJ|mE-12OU6I&uTE zqUfNBc(-H~Y;?{PGCUno>C8}yPdp{2XJ%52;|>fQ8ijW-mYrxa2NOr`;3^-=_ACcD zot+AWLwx!BGmtV82cThx9}GNWDtb(Wplw^HU}^pk@uGYfEi*;JkrT1h&ikMcd`A4s!1 ziT%&FOQKx``Z^jo<4_G(C7ePxyTvHCa17@&#>3jyKwJ@jx6VzP>D29KR_#9h=OwuDpbn1^I09Qgza-k{2Fsbf$YRefgQ3vs~46 z*fRaC)cb@R_ie}Yz&7b+PS&Wui!vkv)ae5&Liwrds%)}Q@Zl^@=>7 zBTM%ki$z_hKa6v?ZHg;?v2NE)A@|1tR%z>nit!^UHJMZ6S2c2f%5C_anS+~O52qcg z^{KVOS(?&b32Qp8f_OR~E}Z-hlPk5v`Nvk!0DBo>nuS`?+iotjI&=V9e|s!k(BfJ2 z#86nLFs4ZH(`~XcI*6r?3h;K&Jhbcz7Ok)L$DkYQAkCPu+bjk0ZKA@mqfWjpI$6z3H?iEZ-Mw6Gz~_SD@c!e5v5ESo%8 z`|+9ZbUWvl#J|Dss!_Nup&twz)y96-}A6fm|kosR<-mJ zmTh?~Rj;~0wMoMZ59t>}xOo%X=-n#jdb_Z08eT%hl{sXt{)h7z2O!K7ajIP=D}OQ# z8%=G2b0b)@)mQfP{#%L9B3U-V$CsTQtU$b*BPkqqf*m)1&Sp4o!R{ShP-tR>>-G41 zQ(&yJ$&eW+#=(>>qoStCGWc>^IQrxd#Xj5*`8eK2Sg=qI3e8s092IK>-zsK!atuTc ziow%eCe$t96!Y|(#;)t+P_Lb@*#+&s>|vxX9NT<^=}o@^1ulK*)y;7Ha@Uai-RdAe z+l)=>{TDWVdkqsGNW~>PF2dtD0{^@)R&qEA$~+Rp&YNom+c6bl+qOYM&huL={jUT^ zzbe76uSb(yQ#k6(x=eDOyHIxh7M$MY%dVDT&Kc^Yw$^*zrw`rdSd3QnUwRVZ_%tlXW`T$ zHJBH6iY?b}lUn@B1&5QLSnzNSoSNiJYt4>P)z{vX(s3LoKGb4&b<@SheL;fKwivpPf;wFT8bNw{&QPEQEX>fjb}CkjxxU z^j2wsGm}rT$AcY7-ltXcu5uDqkLyiId9t{uV;|mpSuCj-5=(vcyTp{$KpL_}GucP zQSa7B?);gCW0%&6vtSI^>=*`a7u}_9Tk}~DYb!8oI3u2)f7E77%|7YZFh#T|oWe|| zw1ebj3dG<4$o}MSqNF9OnDX>3Y-m_A?>qIPm+vZJo8%d#?OlprI~2i9@gZm6Z|C!m z8}v8mInQ%g;wT3{GMu@RKCBX9`Dq1OYUK~nnVM+sT@NFRdcu{!aiF>nNw3&do zRndbkKKlWyw@k;T)Twa5<_)!H9HX=G^1QLFiM=M|i|=Po#XE|NIA{JaOl#XErg=RU zCoZt!t|wpGHM6f>llL#!?c5HDnTB{xArnVFFXOqOMmFyy-`h9Mg6~Ova9(jbZqEwh zj8Z=uS9+Y^xyx*O<-G(8-i-_WaEf~*MR96EKe{pZJac(73Odd`6yj?3la`Y$|5+u^ ze{Y0@E%N|kAHz!Hjkqo93~gZ9&^?H|O?9H7{CJ7DH91evb4{azMF-&N$0WG=;2Kll z-$8xUmALEMi)SZF_*~;Bt6o_u_$%0BRsg;QVHubD5LiSHN#?po&G^n>TL34s$#|I5^}OIXEXdfpeO zmtKNF6H~;_zlNMApTs`yP^Mds>P#o3N?g9a6@r%=!I$#u%#iP34wZY*?opa>C_9He ze=>?C=;bh-(+kP9$7Sh;w%)9A{$0Vo%nxOQ3niN~=TMA8lyH36cd7E|O2PB{EUbB> zjaehduzR8Mbm^!Z8Q-V}b@dp~3wNY=76_MJ4(UPW=vow8%FV+t@MyGWmSX-$oh1VWq z^)o_6=l2^)eNqofc%z7A)^C`P>SWl^p2$6H1RqxPp?)uPFrfN7tF2x^+-o4sS(||x zZ&cA>;U;GN`3<}Od>FQPwy?a-AB1m@>e!2+(e&wMsBL-b1*Ye-7y7MJq-_R9MFGbw z(C2|bKf+UmXYcE1YmZy-~>G~=2yc;s>VrXGfuW#ut1+M3DjI?i@$MzXKe7v<%{ zF!!J;cRH5Qp~KZ!oY1Xk$M7tyJ-D8d%eRrv^tEI@fpmON|v17jX- z$L^ioaBRzcY7PsBJ&uX^dSo~no!Ej`l?r%HV+i#QEW$NGui%{S7ol+RQQCAnkp-Px z$4bScg3|j~_RCj|<%P#V4)dh61SthSCLHcNv}i+6BArQn05V&~qG&yyUO&!YtGAxV zpTGKGW9nH-ZWxD6Pio-8x}J9X|D0#gPwOeV?keMbSFt>01fE=+OAoV; z2vp6`MetJ98v|n@ZOtl8>G|d;cn6I5Lh+$_+S@0-S@&E%~yokdE_itPiM@h3bzCIV}yNAyWIgJsPps@ z^mfq0KGjadEUcl1KU)=EmMohyDt3*Rm2My10oTqabJy-wan~z(`s?fk_H!8Z3~+#T zJ3=u3bO_ye{hAV8#zIZWB$~20m1a!MM0=iVIj}W`J^grw&)MHI<)D3_R_RXHOC;il z^g?#cb`Jehm8V}-X+nNhi_me;7_TlpBCXTB3g^z8$G;Yvsa3{`dVTsWWcH7v`_7e6 zzsZzxm)|5O&F9FTX>hNYCVeWJ2cK@97lzzbp@qCh9^-e4Qd3va_yFK8(i50}vljbq z9s)Lsh<{op;{H*w+~JbQnd?~?YvoRNcE`}r((agcFHSNgS66r+)z0!en)vQAR{XtT z4!VRDkSp(9j-h8lVrCJ_?b**cNX6{4MsErnOnjG^D-2Glz&Yqe;+^TF9A-)1A65c* z-jVk1%{wt$+^8k-GKK64;k(>M&V-tZW-$YJ9)2}zAE`+#6Qb!(^+(9pdLZ3xv6{)A zJjWU@t%K4PNKw!fm%_=f;05bvx0l|XA>(-96=`(4XKaU z2wK>sLtcf5{zDd{U#|yjZns-#rn?;q%sc65k{{3g?$EfGR8UaGj?+w(@S; zn$a_{tn4y(iVXtWaf?{8^oE>_*A`|rM)KT{w#z2 z-gH^oVmy+5hu&tn*RBbdC4H!-zbfw)k3!>-6PYrf2M^1v0Fz`Z(vW%1WaO69${I7? z6FAFMH2&enbcLd!C4p4B>MWo?=B!(?B!Um|j#hz;xZ^%y_gVTJPzNI_t->H9v=d#HB+J9y}I0 zyY8aPX&+{5T@DQ=L}tBM1{1SV3$GNO5++?M6IPcFXRiy^&?}#-&~tbOo1N_sS3l*6 zCl*d+<3=l>BWF5|&eWl>*9ow<${v27@x>#W@}%X@EieZE;AwYzu{`YvWGxR6?d8w0 znU6n##;MUbr28-7__3of_l`Nc6t9Pa9xZ2vsVo6dj5dog-Sk4gP6Ed!m^O{{0#6*2YTN?~cZqj<~Q5x+g<=l6R$ zcJ3phIb;4K89lxHOU#6>0D_<6O0XJG0COWx~E%By5fPcy{B?+Zk=IARAwNc>RuR`g$ZhWQR| zVe{Gp#oaCq!n^LhXn5rdmN6ubb3wb~jN{K>ba6ZLbehSo9PdqAeXGUh{ue>j;XWI3 z|FHl=)iLLT~*x^`#8cn-UOI;s(yp9kjnSY15CCNCU;5FMc zZwOQ#8Or+4ox?rq_r=^t$8fmeUBL}!b8gmNsMw`}ao$O6&6w3J!gw<-J0D077DUta z&LGxmAcOipT=2B635)LYPVl~F1HJC)qv5(*vHnF2YkIgJ`nlz>j`?!f<6k6fUwa%Z z3XO&G+S#N$UY_y1H%qo@gUnf{;KuENsOx-9JXdmRTuPWO%8nh&osv$tcuxr1S(U=x z|FcBx`HQ$SyBy5k4W{U+G3;$_4O5aVVS_#6xf?T9lp57@;CwJ{b5ECc9l6V9rup)F z#1$6ax&uaPJ>YEdRH!hM=dSfomM8fN9zQor-16Rot!xi*s(laP;Hu$;XAKvM=93J; zAT|T1G|uGN=y}*&+{n7s+!a>xJ3)GfC3Bx45fbK>!A<5)>Kyu;+K9k8!nmst$^A!bb4E^eMavGDCj6P~x0 z(?+Lxi3h9rsAiL0W?=9nB9G$ zkJ|Yi!unht;nU40Q2Jy8=Y$dH_B5tBmt2|O^1s}F^-1#h!cZFFZjB`k0^9ARO94H1 ziCb6_o{Lmt@070d-S&NUYlWY*-2Wb1-7pI(7R!R{DSPD3Z>HSWj%H1WL*X&+?7Mcu zL5l*oPvRG*XB&~RRw}(ZaR@k*P2@&W*un3(>Q`+srbnhAogXROa@4|c{g;DTu{}J% zGG^@ZnhpLQ0g+pD#jdIf;iY>N=L0N;hEZW`o>m(;Uoa9EZV3{`S=qw$`YX^fq=)49 z%_AVUQ&oIEyh-@@aU!ZMC}gp3)Hnyb3k-FGLDBOsWVw~Y>Ii?)Axi@tPliFCVF%fF z`#?HB;h6B^vmx63iDXmG2cX@HB5|5VIy>LufHCTK2`X}7XToSqns9`r&vN8F$)Rj+ zqQv<{b3%RN`z-*FiB zSy|!O{R7zA;d|i8Z%ta6Y>Sq;KHRmLgsP9c1dTA>e--@L_`Ww_f^>u=cDycoy<(Ke zy*(h?CJ}?eGJ$2v(zFr%DBF#*0?SSdbLS<)eE$hd{`^p=v9{rEl-_*LeT3n;Hn#iN zWNg_{g%f(IVQjZ8_-R@uYi;D5#*EdV6mt&7n}3GcHYRYnd=9v#Zp03|nQUKZA*eqx z6)U{>J^=Sa?W09xyFo&E=Vf`vK_72Kx-og#i(=5JQh-l0u&!MPw{Hl=e#ghN8Na%r zIAac-oEV9c;DOLQ$IvchkCvEH>_h9r7opaVmxc2}M)9oGFiHJe-lnTcgbzQjD?$KL;!U^F_Aq+6E<*@Hof*9ait4pBcB=Gre0lq32XcYiR)E zX}3gs8AlwSd4WDo)TEy6fpqN585U|APos{Mvv&Vs;$_Kwn6YXU9XhYbIn=>y(JUV@ z$hjayMgg6ecbsXQJT2%&+>%_d{39m$ZiYI|pE#{8iQE>x1BI03LTX?HtJk(>=Z*Ve z@0)$t?t|x{S!O)UcT5-L;-3rMGfJV`6AjGxq6gyb4N$z>jINaz5qYn}+8>F`@ZSw~ zJHwRpdrim3yFK`>uSojqp{DrG*Bp1nveQh_J|NA09Prgdly4;uvT1lhnl4rQ& zVwwt;P8o^0tzV?2$(j7`bVATt(az}0dARaBlcgDjV&%fE^zK{`T8zknFs)nA*E)qg zd%FfUd1!)Cjux$}XqIf;uRwo)JYmhHmiV$;CB1z&n`eW1VWeR__+8r#@AM9{;mXtT z@2XWSY_tm5d??@9L4eB@i26XBu#LNZ6a;PBvKDw)VB9qPu4j_aXRi4m951F>_pJnAkL|o zU;@e)IRsOYk~##2+wtIc7ueo$JZQtF zRHHU?y-~YI`+v7s|M4+uQm2{A+>F62rZ>Jy`a(O2q8b2CwR;tnzxY%Vp`guZ2H_-jQUDIGT6Y&{@7a-omGz0MoV9C1B> zhPHdHT%DoeNqf(*hZzYN*R15~Dg&Y6bZIKrkH1M7DI4gi!y6eq;D4Ix$`;5CM=!+C zsq16pU8dnt4R4!a?PIOfFVzb)%;~&JKk1f`w4z^>e5RBgs!9tipYt*_RDFFs@PjXl z+ipM3raiw%@YRtO{Kf1ua>I)s6!95DCav7pYZ{DSC0n@vBl^?xuRz0^P&pl)dyDLm zb)xLhxCDp84r+WYEH`Y3HBkDt7FMY#poclEUvr%ZhFT#g&E4 zXqb_5lC3$PKqm{9^3wbi+2oczq^@NQ4I?Vg)9t07Qper>%-U_2V9|ziG&}vJK*IrN zEj=@QignLlV}ot^92&3Y@)3hcZn(Q~9eMBe2xs&Wrp-)}-CW#6_NtITLuII)5-oRx zqCUiQ<24LcO$6C}tCrAk^rbhb(_s-8HBayp9oe#tGYYD9ek0JZ_}nY3s>eaA+Sc)1 zp1&kG6xb)ao}FcA*cT3vY3mW*@`6#iq>h$gMM4L!OrK2I0P@SwF?d{OfuDf3gs4plsZH^&A=L;rVj!#T@w3QR4aYeUsyvN@K+uD&Ib zIcrjG7^{e(G;fG5-?$a Date: Fri, 28 Jan 2022 21:55:25 -0500 Subject: [PATCH 161/161] skip `data_stat` in `init_from_model` and `restart` mode (#1463) `std` and `avg` are stored as variables. `saver.restore` will restore and override it. So `data_stat` before `saver.restore` is useless. Note that currently `init_from_frz_model` does not restore these variables. I also add a message before `data_stat`. It sometimes takes long time. --- deepmd/train/trainer.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/deepmd/train/trainer.py b/deepmd/train/trainer.py index 927074c1c6..21f078b763 100644 --- a/deepmd/train/trainer.py +++ b/deepmd/train/trainer.py @@ -282,7 +282,12 @@ def build (self, )) self.type_map = data.get_type_map() self.batch_size = data.get_batch_size() - self.model.data_stat(data) + if self.run_opt.init_mode not in ('init_from_model', 'restart'): + # self.saver.restore (in self._init_session) will restore avg and std variables, so data_stat is useless + # currently init_from_frz_model does not restore data_stat variables + # TODO: restore avg and std in the init_from_frz_model mode + log.info("data stating... (this step may take long time)") + self.model.data_stat(data) # config the init_frz_model command if self.run_opt.init_mode == 'init_from_frz_model':