Skip to content

Commit d04365a

Browse files
committed
Add JSON serialization and deserialization of xtensor containers
1 parent b2b4884 commit d04365a

File tree

11 files changed

+322
-8
lines changed

11 files changed

+322
-8
lines changed

.appveyor.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ platform:
55
- x86
66

77
image:
8-
- Visual Studio 2017
8+
- Visual Studio 2017
99
- Visual Studio 2015
1010

1111
environment:
@@ -15,7 +15,7 @@ environment:
1515
init:
1616
- "ECHO %MINICONDA%"
1717
- if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2015" set VCVARPATH="C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
18-
- if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2015" set VCARGUMENT=%PLATFORM%
18+
- if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2015" set VCARGUMENT=%PLATFORM%
1919
- if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2017" if "%PLATFORM%" == "x64" set VCVARPATH="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
2020
- if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2017" if "%PLATFORM%" == "x86" set VCVARPATH="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat"
2121
- echo "%VCVARPATH% %VCARGUMENT%"
@@ -32,7 +32,8 @@ install:
3232
- conda install gtest cmake -c conda-forge
3333
- conda install xtl==0.4.1 -c QuantStack
3434
- conda install xsimd -c QuantStack
35-
#- cmake -G "NMake Makefiles" -D CMAKE_INSTALL_PREFIX=%MINICONDA%\\LIBRARY -DBUILD_TESTS=ON -DXTENSOR_USE_XSIMD=ON -DDISABLE_VS2017=ON .
35+
- conda install nlohmann_json -c QuantStack
36+
# - cmake -G "NMake Makefiles" -D CMAKE_INSTALL_PREFIX=%MINICONDA%\\LIBRARY -DBUILD_TESTS=ON -DXTENSOR_USE_XSIMD=ON -DDISABLE_VS2017=ON .
3637
- cmake -G "NMake Makefiles" -D CMAKE_INSTALL_PREFIX=%MINICONDA%\\LIBRARY -DBUILD_TESTS=ON -DDISABLE_VS2017=ON .
3738
- nmake test_xtensor_lib
3839
- cd test

.travis.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,10 @@ install:
114114
- hash -r
115115
- conda config --set always_yes yes --set changeps1 no
116116
- conda update -q conda
117-
- conda install gtest cmake -c conda-forge
118-
- conda install xtl==0.4.1 -c QuantStack
117+
- conda install gtest cmake -c conda-forge
118+
- conda install xtl==0.4.8 -c QuantStack
119119
- conda install xsimd -c QuantStack
120+
- conda install nlohmann_json -c QuantStack
120121
# Testing
121122
- mkdir build
122123
- cd build

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,11 @@ message(STATUS "Building xtensor v${${PROJECT_NAME}_VERSION}")
2929
# ============
3030

3131
find_package(xtl 0.4.1 REQUIRED)
32+
3233
message(STATUS "Found xtl: ${xtl_INCLUDE_DIRS}/xtl")
3334

35+
find_package(nlohmann_json 3.1.1)
36+
3437
# Build
3538
# =====
3639

@@ -59,6 +62,7 @@ set(XTENSOR_HEADERS
5962
${XTENSOR_INCLUDE_DIR}/xtensor/xio.hpp
6063
${XTENSOR_INCLUDE_DIR}/xtensor/xiterable.hpp
6164
${XTENSOR_INCLUDE_DIR}/xtensor/xiterator.hpp
65+
${XTENSOR_INCLUDE_DIR}/xtensor/xjson.hpp
6266
${XTENSOR_INCLUDE_DIR}/xtensor/xlayout.hpp
6367
${XTENSOR_INCLUDE_DIR}/xtensor/xmath.hpp
6468
${XTENSOR_INCLUDE_DIR}/xtensor/xnoalias.hpp

docs/source/changelog.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
Changelog
88
=========
99

10+
0.16.0
11+
------
12+
13+
- Support for JSON serialization and deserialization of xtensor expressions
14+
`#830 <https://github.com/QuantStack/xtensor/pull/830>`_.
15+
1016
0.15.9
1117
------
1218

include/xtensor/xcontainer.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1311,7 +1311,7 @@ namespace xt
13111311
{
13121312
if (compute_size(shape) != this->size())
13131313
{
1314-
throw std::runtime_error("Cannot reshape with incorrect number of elements.");
1314+
throw std::runtime_error("Cannot reshape with incorrect number of elements. Do you mean to resize?");
13151315
}
13161316
if (layout == layout_type::dynamic || layout == layout_type::any)
13171317
{

include/xtensor/xexpression.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ namespace xt
113113
template <class E>
114114
using is_xexpression = detail::is_xexpression_impl<E>;
115115

116+
template <class E, class R = void>
117+
using enable_xexpression = typename std::enable_if<is_xexpression<E>::value, R>::type;
118+
116119
template <class E, class R = void>
117120
using disable_xexpression = typename std::enable_if<!is_xexpression<E>::value, R>::type;
118121

include/xtensor/xjson.hpp

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/***************************************************************************
2+
* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht *
3+
* *
4+
* Distributed under the terms of the BSD 3-Clause License. *
5+
* *
6+
* The full license is in the file LICENSE, distributed with this software. *
7+
****************************************************************************/
8+
9+
#ifndef XTENSOR_JSON_HPP
10+
#define XTENSOR_JSON_HPP
11+
12+
#include <cstddef>
13+
#include <stdexcept>
14+
#include <utility>
15+
16+
#include <nlohmann/json.hpp>
17+
18+
#include "xstrided_view.hpp"
19+
20+
namespace xt
21+
{
22+
/*************************************
23+
* to_json and from_json declaration *
24+
*************************************/
25+
26+
template <class E>
27+
enable_xexpression<E> to_json(nlohmann::json&, const E&);
28+
29+
template <class E>
30+
enable_xcontainer<E> from_json(const nlohmann::json&, E&);
31+
32+
template <class E>
33+
enable_xview<E> from_json(const nlohmann::json&, E&);
34+
35+
/****************************************
36+
* to_json and from_json implementation *
37+
****************************************/
38+
39+
namespace detail
40+
{
41+
template <class D>
42+
void to_json_impl(nlohmann::json& j, const xexpression<D>& e, slice_vector& slices)
43+
{
44+
const auto view = dynamic_view(e.derived_cast(), slices);
45+
if (view.dimension() == 0)
46+
{
47+
j = view();
48+
}
49+
else
50+
{
51+
j = nlohmann::json::array();
52+
using size_type = typename D::size_type;
53+
size_type nrows = view.shape()[0];
54+
for (size_type i = 0; i != nrows; ++i)
55+
{
56+
slices.push_back(i);
57+
nlohmann::json k;
58+
to_json_impl(k, e, slices);
59+
j.push_back(std::move(k));
60+
slices.pop_back();
61+
}
62+
}
63+
}
64+
65+
template <class D>
66+
inline void from_json_impl(const nlohmann::json& j, xexpression<D>& e, slice_vector& slices)
67+
{
68+
auto view = dynamic_view(e.derived_cast(), slices);
69+
70+
if (view.dimension() == 0)
71+
{
72+
view() = j;
73+
}
74+
else
75+
{
76+
using size_type = typename D::size_type;
77+
size_type nrows = view.shape()[0];
78+
for (auto i = 0; i != nrows; ++i)
79+
{
80+
slices.push_back(i);
81+
const nlohmann::json& k = j[i];
82+
from_json_impl(k, e, slices);
83+
slices.pop_back();
84+
}
85+
}
86+
}
87+
88+
inline unsigned int json_dimension(const nlohmann::json& j)
89+
{
90+
if (j.is_array() && j.size())
91+
{
92+
return 1 + json_dimension(j[0]);
93+
}
94+
else
95+
{
96+
return 0;
97+
}
98+
}
99+
100+
template <class S>
101+
inline void json_shape(const nlohmann::json& j, S& s, std::size_t pos = 0)
102+
{
103+
if (j.is_array())
104+
{
105+
auto size = j.size();
106+
s[pos] = size;
107+
if (size)
108+
{
109+
json_shape(j[0], s, pos + 1);
110+
}
111+
}
112+
}
113+
}
114+
115+
/**
116+
* @brief JSON serialization of an xtensor expression.
117+
*
118+
* The to_json method is used by the nlohmann_json package for automatic
119+
* serialization of user-defined types. The method is picked up by
120+
* argument-dependent lookup.
121+
*
122+
* @param j a JSON object
123+
* @param e a const \ref xexpression
124+
*/
125+
template <class E>
126+
inline enable_xexpression<E> to_json(nlohmann::json& j, const E& e)
127+
{
128+
auto sv = slice_vector();
129+
detail::to_json_impl(j, e, sv);
130+
}
131+
132+
/**
133+
* @brief JSON deserialization of a xtensor expression with a container
134+
* semantics.
135+
*
136+
* The from_json method is used by the nlohmann_json library for automatic
137+
* serialization of user-defined types. The method is picked up by
138+
* argument-dependent lookup.
139+
*
140+
* @param j a const JSON object
141+
* @param e an \ref xexpression
142+
*/
143+
template <class E>
144+
inline enable_xcontainer<E> from_json(const nlohmann::json& j, E& e)
145+
{
146+
auto dimension = detail::json_dimension(j);
147+
auto s = xtl::make_sequence<typename E::shape_type>(dimension);
148+
detail::json_shape(j, s);
149+
150+
// In the case of a container, we resize the container.
151+
e.resize(s);
152+
153+
auto sv = slice_vector();
154+
detail::from_json_impl(j, e, sv);
155+
}
156+
157+
/**
158+
* @brief JSON deserialization of a xtensor expression with a view
159+
* semantics.
160+
*
161+
* The from_json method is used by the nlohmann_json library for automatic
162+
* serialization of user-defined types. The method is picked up by
163+
* argument-dependent lookup.
164+
*
165+
* However, for converting a JSON object to a value, nlohmann_json requires
166+
* the value type to be default constructible, which is typically not the
167+
* case for expressions with a view semantics.
168+
*
169+
* For this usecase, one can call from_json directly.
170+
*
171+
* @param j a const JSON object
172+
* @param e an \ref xexpression
173+
*/
174+
template <class E>
175+
inline enable_xview<E> from_json(const nlohmann::json& j, E& e)
176+
{
177+
typename E::shape_type s;
178+
detail::json_shape(j, s);
179+
180+
// In the case of a view, we check the size of the container.
181+
if (!std::equal(s.cbegin(), s.cend(), e.shape().cbegin()))
182+
{
183+
throw std::runtime_error("Shape mismatch when deserializing JSON to view");
184+
}
185+
186+
auto sv = slice_vector();
187+
detail::from_json_impl(j, e, sv);
188+
}
189+
}
190+
191+
#endif

include/xtensor/xnorm.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
namespace xt
2323
{
24-
/*************************************
24+
/*************************************
2525
* norm functions for built-in types *
2626
*************************************/
2727

include/xtensor/xsemantic.hpp

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
namespace xt
1919
{
20-
2120
/**
2221
* @class xsemantic_base
2322
* @brief Base interface for assignable xexpressions.
@@ -165,6 +164,28 @@ namespace xt
165164
derived_type& operator=(const xexpression<E>&);
166165
};
167166

167+
namespace detail
168+
{
169+
template <class E>
170+
struct has_container_semantic_impl : std::is_base_of<xcontainer_semantic<std::decay_t<E>>, std::decay_t<E>>
171+
{
172+
};
173+
174+
template <class E>
175+
struct has_container_semantic_impl<xcontainer_semantic<E>> : std::true_type
176+
{
177+
};
178+
}
179+
180+
template <class E>
181+
using has_container_semantic = detail::has_container_semantic_impl<E>;
182+
183+
template <class E, class R = void>
184+
using enable_xcontainer = typename std::enable_if<has_container_semantic<E>::value, R>::type;
185+
186+
template <class E, class R = void>
187+
using disable_xcontainer = typename std::enable_if<!has_container_semantic<E>::value, R>::type;
188+
168189
/**
169190
* @class xview_semantic
170191
* @brief Implementation of the xsemantic_base interface for
@@ -210,6 +231,28 @@ namespace xt
210231
derived_type& operator=(const xexpression<E>&);
211232
};
212233

234+
namespace detail
235+
{
236+
template <class E>
237+
struct has_view_semantic_impl : std::is_base_of<xview_semantic<std::decay_t<E>>, std::decay_t<E>>
238+
{
239+
};
240+
241+
template <class E>
242+
struct has_view_semantic_impl<xview_semantic<E>> : std::true_type
243+
{
244+
};
245+
}
246+
247+
template <class E>
248+
using has_view_semantic = detail::has_view_semantic_impl<E>;
249+
250+
template <class E, class R = void>
251+
using enable_xview = typename std::enable_if<has_view_semantic<E>::value, R>::type;
252+
253+
template <class E, class R = void>
254+
using disable_xview = typename std::enable_if<!has_view_semantic<E>::value, R>::type;
255+
213256
/*********************************
214257
* xsemantic_base implementation *
215258
*********************************/

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ set(XTENSOR_TESTS
107107
test_xinfo.cpp
108108
test_xiterator.cpp
109109
test_xio.cpp
110+
test_xjson.cpp
110111
test_xlayout.cpp
111112
test_xmath.cpp
112113
test_xnan_functions.cpp

0 commit comments

Comments
 (0)