diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index a4de87fb8..1a66aa273 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -12,17 +12,21 @@ defaults: shell: bash -e -l {0} jobs: build: - runs-on: ubuntu-24.04 - name: ${{ matrix.sys.compiler }} ${{ matrix.sys.version }} - ${{ matrix.sys.name }} + runs-on: ${{ matrix.os }} + name: ${{ matrix.os }} ${{ matrix.sys.compiler }} ${{ matrix.sys.version }} - ${{ matrix.sys.name }} strategy: fail-fast: false matrix: + os: [ubuntu-24.04, ubuntu-24.04-arm] sys: - {compiler: clang, version: '17', name: assert, flags: -DXTENSOR_ENABLE_ASSERT=ON} - {compiler: clang, version: '18', name: column-major, flags: -DDEFAULT_COLUMN_MAJOR=ON} - {compiler: clang, version: '19', name: assert, flags: -DXTENSOR_ENABLE_ASSERT=ON} - {compiler: clang, version: '20', name: column-major, flags: -DDEFAULT_COLUMN_MAJOR=ON} - {compiler: clang, version: '21', name: assert, flags: -DXTENSOR_ENABLE_ASSERT=ON} + - {compiler: clang, version: '21', name: column-major, flags: -DDEFAULT_COLUMN_MAJOR=ON} + - {compiler: clang, version: '22', name: assert, flags: -DXTENSOR_ENABLE_ASSERT=ON} + - {compiler: clang, version: '22', name: column-major, flags: -DDEFAULT_COLUMN_MAJOR=ON} - {compiler: gcc, version: '11', name: openmp, flags: -DXTENSOR_USE_OPENMP=ON} - {compiler: gcc, version: '11', name: noexcept, flags: -DXTENSOR_DISABLE_EXCEPTIONS=ON} - {compiler: gcc, version: '12', name: xsimd, flags: -DXTENSOR_USE_XSIMD=ON} @@ -30,10 +34,25 @@ jobs: - {compiler: gcc, version: '13', name: tbb, flags: -DXTENSOR_USE_TBB=ON -DTBB_INCLUDE_DIR=$CONDA_PREFIX/include -DTBB_LIBRARY=$CONDA_PREFIX/lib} - {compiler: gcc, version: '14', name: xsimd-tbb, flags: -DXTENSOR_USE_XSIMD=ON -DXTENSOR_USE_TBB=ON} - {compiler: gcc, version: '14', name: tbb, flags: -DXTENSOR_USE_TBB=ON -DTBB_INCLUDE_DIR=$CONDA_PREFIX/include -DTBB_LIBRARY=$CONDA_PREFIX/lib} + # TODO: Activate following gcc versions when switching github runner to ubuntu 26.04 + # - {compiler: gcc, version: '15', name: xsimd-tbb, flags: -DXTENSOR_USE_XSIMD=ON -DXTENSOR_USE_TBB=ON} + # - {compiler: gcc, version: '15', name: tbb, flags: -DXTENSOR_USE_TBB=ON -DTBB_INCLUDE_DIR=$CONDA_PREFIX/include -DTBB_LIBRARY=$CONDA_PREFIX/lib} + # - {compiler: gcc, version: '16', name: xsimd-tbb, flags: -DXTENSOR_USE_XSIMD=ON -DXTENSOR_USE_TBB=ON} + # - {compiler: gcc, version: '16', name: tbb, flags: -DXTENSOR_USE_TBB=ON -DTBB_INCLUDE_DIR=$CONDA_PREFIX/include -DTBB_LIBRARY=$CONDA_PREFIX/lib} + exclude: + - os: ubuntu-24.04-arm + sys: + compiler: gcc + version: '12' + - os: ubuntu-24.04-arm + sys: + compiler: gcc + version: '11' + steps: - name: Install GCC if: matrix.sys.compiler == 'gcc' - uses: egor-tensin/setup-gcc@v1 + uses: egor-tensin/setup-gcc@v2 with: version: ${{matrix.sys.version}} platform: x64 @@ -53,7 +72,7 @@ jobs: sudo update-alternatives --set clang-scan-deps /usr/bin/clang-scan-deps-${{matrix.sys.version}} - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set conda environment uses: mamba-org/setup-micromamba@main diff --git a/.github/workflows/osx.yml b/.github/workflows/osx.yml index c278216b0..5dbce477e 100644 --- a/.github/workflows/osx.yml +++ b/.github/workflows/osx.yml @@ -18,7 +18,6 @@ jobs: fail-fast: false matrix: os: - - 13 - 14 - 15 diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 5441a3371..64fe21244 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -10,5 +10,13 @@ jobs: pre-commit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: pre-commit/action@v3.0.0 + - uses: actions/checkout@v6 + - uses: pre-commit/action@v3.0.1 + + include-check: + runs-on: ubuntu-latest + name: Check unused standard includes + steps: + - uses: actions/checkout@v6 + - run: pip install diskarzhan + - run: diskarzhan `find include -name '*.hpp'` diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d67461681..bfc98b9cf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,12 +24,13 @@ repos: - id: remove-tabs args: [--whitespaces-count, '4'] - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks - rev: v2.11.0 + rev: v2.16.0 hooks: - id: pretty-format-yaml args: [--autofix, --indent, '2'] types: [file] files: \.(yaml|yml|clang-format) + additional_dependencies: [setuptools] - repo: https://github.com/tdegeus/cpp_comment_format rev: v0.2.1 hooks: diff --git a/docs/source/api/accumulating_functions.rst b/docs/source/api/accumulating_functions.rst index edcc26909..1842912e1 100644 --- a/docs/source/api/accumulating_functions.rst +++ b/docs/source/api/accumulating_functions.rst @@ -11,10 +11,5 @@ Accumulating functions Defined in ``xtensor/core/xmath.hpp`` -.. doxygenfunction:: cumsum(E&&) - -.. doxygenfunction:: cumsum(E&&, std::ptrdiff_t) - -.. doxygenfunction:: cumprod(E&&) - -.. doxygenfunction:: cumprod(E&&, std::ptrdiff_t) +.. doxygengroup:: acc_functions + :members: diff --git a/docs/source/api/basic_functions.rst b/docs/source/api/basic_functions.rst index c96e34d66..c6e8020b8 100644 --- a/docs/source/api/basic_functions.rst +++ b/docs/source/api/basic_functions.rst @@ -11,26 +11,5 @@ Basic functions Defined in ``xtensor/core/xmath.hpp`` -.. doxygenfunction:: abs(E&&) - -.. doxygenfunction:: fabs(E&&) - -.. doxygenfunction:: fmod(E1&&, E2&&) - -.. doxygenfunction:: remainder(E1&&, E2&&) - -.. doxygenfunction:: fma(E1&&, E2&&, E3&&) - -.. doxygenfunction:: maximum(E1&&, E2&&) - -.. doxygenfunction:: minimum(E1&&, E2&&) - -.. doxygenfunction:: fmax(E1&&, E2&&) - -.. doxygenfunction:: fmin(E1&&, E2&&) - -.. doxygenfunction:: fdim(E1&&, E2&&) - -.. doxygenfunction:: clip(E1&&, E2&&, E3&&) - -.. doxygenfunction:: sign(E&&) +.. doxygengroup:: basic_functions + :members: diff --git a/docs/source/api/classif_functions.rst b/docs/source/api/classif_functions.rst index d170f737b..f39602b2b 100644 --- a/docs/source/api/classif_functions.rst +++ b/docs/source/api/classif_functions.rst @@ -11,12 +11,5 @@ Classification functions Defined in ``xtensor/core/xmath.hpp`` -.. doxygenfunction:: isfinite(E&&) - -.. doxygenfunction:: isinf(E&&) - -.. doxygenfunction:: isnan(E&&) - -.. doxygenfunction:: isclose(E1&&, E2&&, double, double, bool) - -.. doxygenfunction:: allclose(E1&&, E2&, double, double) +.. doxygengroup:: classif_functions + :members: diff --git a/docs/source/api/error_functions.rst b/docs/source/api/error_functions.rst index 7b335e4cc..be2312f08 100644 --- a/docs/source/api/error_functions.rst +++ b/docs/source/api/error_functions.rst @@ -11,10 +11,5 @@ Error and gamma functions Defined in ``xtensor/core/xmath.hpp`` -.. doxygenfunction:: erf(E&&) - -.. doxygenfunction:: erfc(E&&) - -.. doxygenfunction:: tgamma(E&&) - -.. doxygenfunction:: lgamma(E&&) +.. doxygengroup:: err_functions + :members: diff --git a/docs/source/api/exponential_functions.rst b/docs/source/api/exponential_functions.rst index e2202dd67..65caeed74 100644 --- a/docs/source/api/exponential_functions.rst +++ b/docs/source/api/exponential_functions.rst @@ -11,16 +11,5 @@ Exponential functions Defined in ``xtensor/core/xmath.hpp`` -.. doxygenfunction:: exp(E&&) - -.. doxygenfunction:: exp2(E&&) - -.. doxygenfunction:: expm1(E&&) - -.. doxygenfunction:: log(E&&) - -.. doxygenfunction:: log2(E&&) - -.. doxygenfunction:: log10(E&&) - -.. doxygenfunction:: log1p(E&&) +.. doxygengroup:: exp_functions + :members: diff --git a/docs/source/api/hyperbolic_functions.rst b/docs/source/api/hyperbolic_functions.rst index e7e835104..757c77409 100644 --- a/docs/source/api/hyperbolic_functions.rst +++ b/docs/source/api/hyperbolic_functions.rst @@ -11,20 +11,5 @@ Hyperbolic functions Defined in ``xtensor/core/xmath.hpp`` -.. _sinh-function-reference: -.. doxygenfunction:: sinh(E&&) - -.. _cosh-function-reference: -.. doxygenfunction:: cosh(E&&) - -.. _tanh-function-reference: -.. doxygenfunction:: tanh(E&&) - -.. _asinh-func-ref: -.. doxygenfunction:: asinh(E&&) - -.. _acosh-func-ref: -.. doxygenfunction:: acosh(E&&) - -.. _atanh-func-ref: -.. doxygenfunction:: atanh(E&&) +.. doxygengroup:: hyper_functions + :members: diff --git a/docs/source/api/index_related.rst b/docs/source/api/index_related.rst index 7f74c758e..f7de8fdc2 100644 --- a/docs/source/api/index_related.rst +++ b/docs/source/api/index_related.rst @@ -9,10 +9,7 @@ Index related functions Defined in ``xtensor/core/xoperation.hpp`` -.. doxygenfunction:: where(const T&) +The logical operator group documents the index-producing overloads of +``xt::where``, ``xt::nonzero`` and ``xt::argwhere``. -.. doxygenfunction:: nonzero(const T&) - -.. doxygenfunction:: argwhere - -.. doxygenfunction:: from_indices +``xt::from_indices`` is documented on the ``xtensor`` API page. diff --git a/docs/source/api/nan_functions.rst b/docs/source/api/nan_functions.rst index 5b35e5c0f..918039bce 100644 --- a/docs/source/api/nan_functions.rst +++ b/docs/source/api/nan_functions.rst @@ -11,26 +11,5 @@ NaN functions Defined in ``xtensor/core/xmath.hpp`` -.. doxygenfunction:: nan_to_num(E&&) - -.. doxygenfunction:: nanmin(E&&, X&&, EVS) - -.. doxygenfunction:: nanmax(E&&, X&&, EVS) - -.. doxygenfunction:: nansum(E&&, X&&, EVS) - -.. doxygenfunction:: nanmean(E&&, X&&, EVS) - -.. doxygenfunction:: nanvar(E&&, X&&, EVS) - -.. doxygenfunction:: nanstd(E&&, X&&, EVS) - -.. doxygenfunction:: nanprod(E&&, X&&, EVS) - -.. doxygenfunction:: nancumsum(E&&) - -.. doxygenfunction:: nancumsum(E&&, std::ptrdiff_t) - -.. doxygenfunction:: nancumprod(E&&) - -.. doxygenfunction:: nancumprod(E&&, std::ptrdiff_t) +.. doxygengroup:: nan_functions + :members: diff --git a/docs/source/api/nearint_operations.rst b/docs/source/api/nearint_operations.rst index c4ba6832c..59dd61d19 100644 --- a/docs/source/api/nearint_operations.rst +++ b/docs/source/api/nearint_operations.rst @@ -11,14 +11,5 @@ Nearest integer floating point operations Defined in ``xtensor/core/xmath.hpp`` -.. doxygenfunction:: ceil(E&&) - -.. doxygenfunction:: floor(E&&) - -.. doxygenfunction:: trunc(E&&) - -.. doxygenfunction:: round(E&&) - -.. doxygenfunction:: nearbyint(E&&) - -.. doxygenfunction:: rint(E&&) +.. doxygengroup:: nearint_functions + :members: diff --git a/docs/source/api/operators.rst b/docs/source/api/operators.rst index 32a474221..00df9458d 100644 --- a/docs/source/api/operators.rst +++ b/docs/source/api/operators.rst @@ -9,68 +9,17 @@ Operators and related functions Defined in ``xtensor/core/xmath.hpp`` and ``xtensor/core/xoperation.hpp`` -.. doxygenfunction:: operator+(E&&) +.. doxygengroup:: arithmetic_operators + :members: -.. doxygenfunction:: operator-(E&&) +.. doxygengroup:: logical_operators + :members: -.. doxygenfunction:: operator+(E1&&, E2&&) +.. doxygengroup:: comparison_operators + :members: -.. doxygenfunction:: operator-(E1&&, E2&&) +.. doxygengroup:: bitwise_operators + :members: -.. doxygenfunction:: operator*(E1&&, E2&&) - -.. doxygenfunction:: operator/(E1&&, E2&&) - -.. doxygenfunction:: operator||(E1&&, E2&&) - -.. doxygenfunction:: operator&&(E1&&, E2&&) - -.. doxygenfunction:: operator!(E&&) - -.. doxygenfunction:: where(E1&&, E2&&, E3&&) - -.. doxygenfunction:: any(E&&) - -.. doxygenfunction:: all(E&&) - -.. doxygenfunction:: operator<(E1&&, E2&&) - -.. doxygenfunction:: operator<=(E1&&, E2&&) - -.. doxygenfunction:: operator>(E1&&, E2&&) - -.. doxygenfunction:: operator>=(E1&&, E2&&) - -.. doxygenfunction:: operator==(const xexpression&, const xexpression&) - -.. doxygenfunction:: operator!=(const xexpression&, const xexpression&) - -.. doxygenfunction:: equal(E1&&, E2&&) - -.. doxygenfunction:: not_equal(E1&&, E2&&) - -.. doxygenfunction:: less(E1&& e1, E2&& e2) - -.. doxygenfunction:: less_equal(E1&& e1, E2&& e2) - -.. doxygenfunction:: greater(E1&& e1, E2&& e2) - -.. doxygenfunction:: greater_equal(E1&& e1, E2&& e2) - -.. doxygenfunction:: operator&(E1&&, E2&&) - -.. doxygenfunction:: operator|(E1&&, E2&&) - -.. doxygenfunction:: operator^(E1&&, E2&&) - -.. doxygenfunction:: operator~(E&&) - -.. doxygenfunction:: left_shift(E1&&, E2&&) - -.. doxygenfunction:: right_shift(E1&&, E2&&) - -.. doxygenfunction:: operator<<(E1&&, E2&&) - -.. doxygenfunction:: operator>>(E1&&, E2&&) - -.. doxygenfunction:: cast(E&&) +.. doxygengroup:: casting_operators + :members: diff --git a/docs/source/api/power_functions.rst b/docs/source/api/power_functions.rst index a11b5ba27..5ac5a1546 100644 --- a/docs/source/api/power_functions.rst +++ b/docs/source/api/power_functions.rst @@ -12,16 +12,5 @@ Power functions Defined in ``xtensor/core/xmath.hpp`` -.. doxygenfunction:: pow(E1&&, E2&&) - -.. doxygenfunction:: pow(E&&) - -.. doxygenfunction:: square(E1&&) - -.. doxygenfunction:: cube(E1&&) - -.. doxygenfunction:: sqrt(E&&) - -.. doxygenfunction:: cbrt(E&&) - -.. doxygenfunction:: hypot(E1&&, E2&&) +.. doxygengroup:: pow_functions + :members: diff --git a/docs/source/api/reducing_functions.rst b/docs/source/api/reducing_functions.rst index 223e57a92..89c0baff5 100644 --- a/docs/source/api/reducing_functions.rst +++ b/docs/source/api/reducing_functions.rst @@ -9,62 +9,7 @@ Reducing functions **xtensor** provides the following reducing functions for xexpressions: -Defined in ``xtensor/core/xmath.hpp`` +Defined in ``xtensor/core/xmath.hpp`` and ``xtensor/reducers/xnorm.hpp``. -.. doxygenfunction:: sum(E&&, EVS) - -.. doxygenfunction:: sum(E&&, X&&, EVS) - -.. doxygenfunction:: prod(E&&, EVS) - -.. doxygenfunction:: prod(E&&, X&&, EVS) - -.. doxygenfunction:: mean(E&&, EVS) - -.. doxygenfunction:: mean(E&&, X&&, EVS) - -.. doxygenfunction:: average(E&&, EVS) - -.. doxygenfunction:: variance(E&&, EVS) - -.. doxygenfunction:: variance(E&&, X&&, EVS) - -.. doxygenfunction:: variance(E&&, X&&, const D&, EVS) - -.. doxygenfunction:: stddev(E&&, EVS) - -.. doxygenfunction:: stddev(E&&, X&&, EVS) - -.. doxygenfunction:: diff(const xexpression&, unsigned int, std::ptrdiff_t) - -.. doxygenfunction:: amax(E&&, EVS) - -.. doxygenfunction:: amax(E&&, X&&, EVS) - -.. doxygenfunction:: amin(E&&, EVS) - -.. doxygenfunction:: amin(E&&, X&&, EVS) - -.. doxygenfunction:: trapz(const xexpression&, double, std::ptrdiff_t) - -.. doxygenfunction:: trapz(const xexpression&, const xexpression&, std::ptrdiff_t) - -Defined in ``xtensor/reducers/xnorm.hpp`` - -.. doxygenfunction:: norm_l0(E&&, X&&, EVS) - -.. doxygenfunction:: norm_l1(E&&, X&&, EVS) - -.. doxygenfunction:: norm_sq(E&&, X&&, EVS) - -.. doxygenfunction:: norm_l2(E&&, X&&, EVS) - -.. doxygenfunction:: norm_linf(E&&, X&&, EVS) - -.. doxygenfunction:: norm_lp_to_p(E&&, double, X&&, EVS) - -.. doxygenfunction:: norm_lp(E&&, double, X&&, EVS) - -.. doxygenfunction:: norm_induced_l1(E&&, EVS) - -.. doxygenfunction:: norm_induced_linf(E&&, EVS) +.. doxygengroup:: red_functions + :members: diff --git a/docs/source/api/trigonometric_functions.rst b/docs/source/api/trigonometric_functions.rst index 6234470d0..e76d151b5 100644 --- a/docs/source/api/trigonometric_functions.rst +++ b/docs/source/api/trigonometric_functions.rst @@ -11,16 +11,5 @@ Trigonometric functions Defined in ``xtensor/core/xmath.hpp`` -.. doxygenfunction:: sin(E&&) - -.. doxygenfunction:: cos(E&&) - -.. doxygenfunction:: tan(E&&) - -.. doxygenfunction:: asin(E&&) - -.. doxygenfunction:: acos(E&&) - -.. doxygenfunction:: atan(E&&) - -.. doxygenfunction:: atan2(E1&&, E2&&) +.. doxygengroup:: trigo_functions + :members: diff --git a/docs/source/api/xaccessible.rst b/docs/source/api/xaccessible.rst new file mode 100644 index 000000000..237fc029d --- /dev/null +++ b/docs/source/api/xaccessible.rst @@ -0,0 +1,16 @@ +.. Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht + + Distributed under the terms of the BSD 3-Clause License. + + The full license is in the file LICENSE, distributed with this software. + +xaccessible +=========== + +Defined in ``xtensor/core/xaccessible.hpp`` + +.. doxygenclass:: xt::xconst_accessible + :members: + +.. doxygenclass:: xt::xaccessible + :members: diff --git a/docs/source/api/xbuilder.rst b/docs/source/api/xbuilder.rst index db358f88e..b315c3730 100644 --- a/docs/source/api/xbuilder.rst +++ b/docs/source/api/xbuilder.rst @@ -19,7 +19,7 @@ Defined in ``xtensor/generators/xbuilder.hpp`` .. doxygenfunction:: xt::empty(const S&) -.. doxygenfunction:: xt::full_like(const xexpression&) +.. doxygenfunction:: xt::full_like(const xexpression&, typename E::value_type) .. doxygenfunction:: xt::empty_like(const xexpression&) diff --git a/docs/source/api/xfft.rst b/docs/source/api/xfft.rst new file mode 100644 index 000000000..a05766ff7 --- /dev/null +++ b/docs/source/api/xfft.rst @@ -0,0 +1,16 @@ +.. Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht + + Distributed under the terms of the BSD 3-Clause License. + + The full license is in the file LICENSE, distributed with this software. + +xfft +==== + +Defined in ``xtensor/misc/xfft.hpp`` + +.. doxygenfunction:: xt::fft::fft(E&&, std::ptrdiff_t) + +.. doxygenfunction:: xt::fft::ifft(E&&, std::ptrdiff_t) + +.. doxygenfunction:: xt::fft::convolve(E1&&, E2&&, std::ptrdiff_t) diff --git a/docs/source/api/xhistogram.rst b/docs/source/api/xhistogram.rst index 71b05c89b..4d1e3e639 100644 --- a/docs/source/api/xhistogram.rst +++ b/docs/source/api/xhistogram.rst @@ -9,35 +9,14 @@ xhistogram Defined in ``xtensor/misc/xhistogram.hpp`` -.. doxygenenum:: xt::histogram_algorithm +.. doxygengroup:: digitize + :members: -.. doxygenfunction:: xt::histogram(E1&&, E2&&, E3&&, bool) +.. doxygengroup:: histogram + :members: .. doxygenfunction:: xt::bincount(E1&&, E2&&, std::size_t) -.. doxygenfunction:: xt::histogram_bin_edges(E1&&, E2&&, E3, E3, std::size_t, histogram_algorithm) - -.. doxygenfunction:: xt::digitize(E1&&, E2&&, E3&&, bool, bool) - .. doxygenfunction:: xt::bin_items(size_t, E&&) -Further overloads ------------------ - -.. doxygenfunction:: xt::histogram(E1&&, E2&&, bool) - -.. doxygenfunction:: xt::histogram(E1&&, std::size_t, bool) - -.. doxygenfunction:: xt::histogram(E1&&, std::size_t, E2, E2, bool) - -.. doxygenfunction:: xt::histogram(E1&&, std::size_t, E2&&, bool) - -.. doxygenfunction:: xt::histogram(E1&&, std::size_t, E2&&, E3, E3, bool) - -.. doxygenfunction:: xt::histogram_bin_edges(E1&&, E2, E2, std::size_t, histogram_algorithm) - -.. doxygenfunction:: xt::histogram_bin_edges(E1&&, E2&&, std::size_t, histogram_algorithm) - -.. doxygenfunction:: xt::histogram_bin_edges(E1&&, std::size_t, histogram_algorithm) - .. doxygenfunction:: xt::bin_items(size_t, size_t) diff --git a/docs/source/api/xjson.rst b/docs/source/api/xjson.rst index 7a11957a5..138c5b9de 100644 --- a/docs/source/api/xjson.rst +++ b/docs/source/api/xjson.rst @@ -9,6 +9,10 @@ xjson: serialize to/from JSON Defined in ``xtensor/io/xjson.hpp`` -.. doxygenfunction:: xt::to_json(nlohmann::json&, const E&); +Available overload families +--------------------------- -.. doxygenfunction:: xt::from_json(const nlohmann::json&, E&); +- ``xt::to_json(nlohmann::basic_json&, const E&)`` +- ``xt::from_json(const nlohmann::basic_json&, E&)`` + +``xt::from_json`` is provided for both container and view semantics. diff --git a/docs/source/api/xreducer.rst b/docs/source/api/xreducer.rst index 85ee2a09a..4e896e099 100644 --- a/docs/source/api/xreducer.rst +++ b/docs/source/api/xreducer.rst @@ -12,4 +12,9 @@ Defined in ``xtensor/reducers/xreducer.hpp`` .. doxygenclass:: xt::xreducer :members: -.. doxygenfunction:: xt::reduce(F&&, E&&, X&&, EVS&&) +Available overload families +--------------------------- + +- ``xt::reduce(f, e, axes, options)`` +- ``xt::reduce(f, e, options)`` +- ``xt::reduce(f, e, const I (&axes)[N], options)`` diff --git a/docs/source/api/xset_operation.rst b/docs/source/api/xset_operation.rst index 31b25126c..14ea65cda 100644 --- a/docs/source/api/xset_operation.rst +++ b/docs/source/api/xset_operation.rst @@ -9,19 +9,13 @@ xset_operation Defined in ``xtensor/misc/xset_operation.hpp`` -.. doxygenenum:: xt::isin(E&&, F&&) +``xt::searchsorted(a, v, right)`` returns insertion indices for values ``v`` +in the sorted array ``a``. -.. doxygenenum:: xt::in1d(E&&, F&&) +Available functions +------------------- -.. doxygenenum:: xt::searchsorted(E1&&, E2&&, bool) - -Further overloads ------------------ - -.. doxygenenum:: xt::isin(E&&, std::initializer_list) - -.. doxygenenum:: xt::isin(E&&, I&&, I&&) - -.. doxygenenum:: xt::in1d(E&&, std::initializer_list) - -.. doxygenenum:: xt::in1d(E&&, I&&, I&&) +- ``xt::isin(element, test_elements)`` +- ``xt::in1d(element, test_elements)`` +- ``xt::isin(element, begin, end)`` +- ``xt::in1d(element, begin, end)`` diff --git a/docs/source/api/xview.rst b/docs/source/api/xview.rst index 9652ea26a..d9e72ad72 100644 --- a/docs/source/api/xview.rst +++ b/docs/source/api/xview.rst @@ -10,7 +10,6 @@ xview Defined in ``xtensor/views/xview.hpp`` .. doxygenclass:: xt::xview - :members: .. doxygenfunction:: xt::view diff --git a/docs/source/build-options.rst b/docs/source/build-options.rst index b076c422b..25e32ceb9 100644 --- a/docs/source/build-options.rst +++ b/docs/source/build-options.rst @@ -120,4 +120,4 @@ Notice that this option prevents building on a machine and distributing the resu a different architecture (i.e. not supporting the same instruction set). .. _xsimd: https://github.com/xtensor-stack/xsimd -.. _tbb: https://www.threadingbuildingblocks.org +.. _tbb: https://github.com/uxlfoundation/oneTBB diff --git a/docs/source/dev-build-options.rst b/docs/source/dev-build-options.rst index 67fbab9a6..c4b12a8fe 100644 --- a/docs/source/dev-build-options.rst +++ b/docs/source/dev-build-options.rst @@ -100,4 +100,4 @@ You can then build the documentation: Type ``make help`` to see the list of available documentation targets. .. _xsimd: https://github.com/xtensor-stack/xsimd -.. _tbb: https://www.threadingbuildingblocks.org +.. _tbb: https://github.com/uxlfoundation/oneTBB diff --git a/docs/source/expression.rst b/docs/source/expression.rst index 0fec517f7..8aa5d7324 100644 --- a/docs/source/expression.rst +++ b/docs/source/expression.rst @@ -232,5 +232,5 @@ Iterators :cpp:func:`begin(shape) ` and :cpp:func:`end(shape) `. -.. _NumPy: http://www.numpy.org +.. _NumPy: https://numpy.org/ .. _libdynd: http://libdynd.org diff --git a/docs/source/external-structures.rst b/docs/source/external-structures.rst index 1eb048138..4d82738f2 100644 --- a/docs/source/external-structures.rst +++ b/docs/source/external-structures.rst @@ -47,7 +47,7 @@ Adapting a pointer ------------------ Suppose that you want to use the *xtensor* machinery on a small contiguous subset of a large tensor. -You can, of course, use :ref:`Views`, but for efficiency you can also use pointers to the right bit of memory. +You can, of course, use :ref:`view-description`, but for efficiency you can also use pointers to the right bit of memory. Consider an example of an ``[M, 2, 2]`` tensor ``A``, for which you want to operate on ``A[i, :, :]`` for different ``i``. In this case the most efficient *xtensor* has to offer is: diff --git a/docs/source/index.rst b/docs/source/index.rst index 2aea4cb71..d28e135ea 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -119,7 +119,7 @@ for details. related bindings -.. _NumPy: http://www.numpy.org +.. _NumPy: https://numpy.org/ .. _Buffer Protocol: https://docs.python.org/3/c-api/buffer.html .. _libdynd: http://libdynd.org .. _xtensor-python: https://github.com/xtensor-stack/xtensor-python diff --git a/docs/source/pitfall.rst b/docs/source/pitfall.rst index 0c404007f..b9e40a2a3 100644 --- a/docs/source/pitfall.rst +++ b/docs/source/pitfall.rst @@ -70,6 +70,45 @@ in the returned expression. Replacing ``auto tmp`` with ``xt::xarray tmp`` does not change anything, ``tmp`` is still an lvalue and thus captured by reference. +.. warning:: + + This issue is particularly subtle with reducer functions like :cpp:func:`xt::amax`, + :cpp:func:`xt::sum`, etc. Consider the following function: + + .. code:: + + template + xt::xtensor logSoftmax(const xt::xtensor &matrix) + { + xt::xtensor maxVals = xt::amax(matrix, {1}, xt::keep_dims); + auto shifted = matrix - maxVals; + auto expVals = xt::exp(shifted); + auto sumExp = xt::sum(expVals, {1}, xt::keep_dims); + return shifted - xt::log(sumExp); + } + + This function may produce incorrect results or crash, especially in optimized builds. + The issue is that ``shifted``, ``expVals``, and ``sumExp`` are all lazy expressions + that hold references to local variables. When the function returns, these local + variables are destroyed, and the returned expression contains dangling references. + + The fix is to evaluate reducer results and the returned expression explicitly. + Element-wise lazy expressions (like ``shifted`` and ``expVals``) are safe to + leave as ``auto``, but reducer results (like ``sumExp``) must be materialized + before being used in a subsequent element-wise expression: + + .. code:: + + template + xt::xtensor logSoftmax(const xt::xtensor &matrix) + { + xt::xtensor maxVals = xt::amax(matrix, {1}, xt::keep_dims); + auto shifted = matrix - maxVals; + auto expVals = xt::exp(shifted); + xt::xtensor sumExp = xt::sum(expVals, {1}, xt::keep_dims); + return xt::xtensor(shifted - xt::log(sumExp)); + } + Random numbers not consistent ----------------------------- diff --git a/docs/source/quickref/chunked_arrays.rst b/docs/source/quickref/chunked_arrays.rst index 2f25c9f3f..e645cea73 100644 --- a/docs/source/quickref/chunked_arrays.rst +++ b/docs/source/quickref/chunked_arrays.rst @@ -17,7 +17,7 @@ the chunks fit comfortably in memory, but this also allows to process them in parallel, including in a distributed environment (although this is not supported yet). -Formats for the storage of arrays such as `Zarr `_ +Formats for the storage of arrays such as `Zarr `_ specifically target chunked arrays. Such formats are becoming increasingly popular in the field of big data, since the chunks can be stored in the cloud. @@ -66,4 +66,4 @@ persistence of data. In particular, they are used as a building block for the `xtensor-zarr `_ library. For further details, please refer to the documentation -of `xtensor-io `_. +of `xtensor-io `_. diff --git a/docs/source/random.rst b/docs/source/random.rst index 69e51384a..c0f555664 100644 --- a/docs/source/random.rst +++ b/docs/source/random.rst @@ -67,8 +67,8 @@ where :math:`\alpha` is the shape (also known as :math:`k`) and :math:`\beta` th .. seealso:: * :any:`numpy.random.gamma` - * `std::gamma_distribution `_ - * `Weisstein, Eric W. "Gamma Distribution." From MathWorld – A Wolfram Web Resource. `_ + * `std::gamma_distribution `_ + * `Weisstein, Eric W. "Gamma Distribution." From MathWorld – A Wolfram Web Resource. `_ * `Wikipedia, "Gamma distribution". `_ :cpp:func:`xt::random::weibull` @@ -100,7 +100,7 @@ Note that you can specify only :math:`a` while choosing the default for :math:`b .. seealso:: * :any:`numpy.random.weibull` - * `std::weibull_distribution `_ + * `std::weibull_distribution `_ * `Wikipedia, "Weibull distribution". `_ :cpp:func:`xt::random::extreme_value` diff --git a/docs/source/related.rst b/docs/source/related.rst index 97fa5ab30..60c640272 100644 --- a/docs/source/related.rst +++ b/docs/source/related.rst @@ -453,12 +453,12 @@ and also provides a python wrapper based on ``xtensor-python``. .. _xtensor-r: https://github.com/xtensor-stack/xtensor-r .. _xtensor-blas: https://github.com/xtensor-stack/xtensor-blas .. _xtensor-io: https://github.com/xtensor-stack/xtensor-io -.. _xtensor-fftw: https://github.com/egpbos/xtensor-fftw -.. _xtensor-ros: https://github.com/wolfv/xtensor_ros +.. _xtensor-fftw: https://github.com/xtensor-stack/xtensor-fftw +.. _xtensor-ros: https://github.com/RoboStack/xtensor-ros .. _xsimd: https://github.com/xtensor-stack/xsimd .. _xtl: https://github.com/xtensor-stack/xtl .. _xframe: https://github.com/xtensor-stack/xframe .. _z5: https://github.com/constantinpape/z5 -.. _zarr: https://github.com/zarr-developers/zarr -.. _n5: https://github.com/saalfeldlab/n5i -.. _xarray: http://xarray.pydata.org +.. _zarr: https://github.com/zarr-developers/zarr-python +.. _n5: https://github.com/saalfeldlab/n5 +.. _xarray: https://docs.xarray.dev/en/stable/ diff --git a/docs/source/view.rst b/docs/source/view.rst index 5348860b6..95b6365b8 100644 --- a/docs/source/view.rst +++ b/docs/source/view.rst @@ -159,9 +159,9 @@ Since ``xtensor 0.16.3``, a new range syntax can be used with strided views: // The previous line is equivalent to auto v2 = xt::strided_view(a, {xt::range(0, 1), 1, xt::range(_, 2), xt::range(_, _, -1)}); -The :cpp:type:`xt::xstrided_view` is very efficient on contigous memory -(e.g. :cpp:type:`xt::xtensor` or :cpp:type:`xt::xarray`) but less efficient on\ -:cpp:type:`xt::xexpression`s. +The :cpp:type:`xt::xstrided_view` type is very efficient on contigous memory +(e.g. :cpp:type:`xt::xtensor` or :cpp:type:`xt::xarray`) but less efficient on +generic :cpp:type:`xt::xexpression` objects. Transposed views ---------------- diff --git a/include/xtensor/chunk/xchunked_array.hpp b/include/xtensor/chunk/xchunked_array.hpp index 53a53aba1..dc0c16355 100644 --- a/include/xtensor/chunk/xchunked_array.hpp +++ b/include/xtensor/chunk/xchunked_array.hpp @@ -19,7 +19,7 @@ namespace xt { /** - * @defgroup xt_xchunked_array + * @defgroup xt_xchunked_array Chunked array * * Chunked array container. * Defined in ``xtensor/xchunked_array.hpp``. diff --git a/include/xtensor/containers/xarray.hpp b/include/xtensor/containers/xarray.hpp index 7455b8854..7f27bd296 100644 --- a/include/xtensor/containers/xarray.hpp +++ b/include/xtensor/containers/xarray.hpp @@ -11,7 +11,6 @@ #define XTENSOR_ARRAY_HPP #include -#include #include #include diff --git a/include/xtensor/containers/xbuffer_adaptor.hpp b/include/xtensor/containers/xbuffer_adaptor.hpp index f9ac409a1..e59503f3a 100644 --- a/include/xtensor/containers/xbuffer_adaptor.hpp +++ b/include/xtensor/containers/xbuffer_adaptor.hpp @@ -709,7 +709,7 @@ namespace xt rhs.get_allocator() ); pointer tmp = safe_init_allocate(al, rhs.m_size); - if (xtrivially_default_constructible::value) + if (std::is_trivially_default_constructible::value) { std::uninitialized_copy(rhs.m_data.get(), rhs.m_data.get() + rhs.m_size, tmp); } diff --git a/include/xtensor/containers/xcontainer.hpp b/include/xtensor/containers/xcontainer.hpp index 70ebaa798..7f17ffe01 100644 --- a/include/xtensor/containers/xcontainer.hpp +++ b/include/xtensor/containers/xcontainer.hpp @@ -11,9 +11,7 @@ #define XTENSOR_CONTAINER_HPP #include -#include #include -#include #include #include @@ -116,11 +114,11 @@ namespace xt size_type size() const noexcept; - XTENSOR_CONSTEXPR_RETURN size_type dimension() const noexcept; + constexpr size_type dimension() const noexcept; - XTENSOR_CONSTEXPR_RETURN const inner_shape_type& shape() const noexcept; - XTENSOR_CONSTEXPR_RETURN const inner_strides_type& strides() const noexcept; - XTENSOR_CONSTEXPR_RETURN const inner_backstrides_type& backstrides() const noexcept; + constexpr const inner_shape_type& shape() const noexcept; + constexpr const inner_strides_type& strides() const noexcept; + constexpr const inner_backstrides_type& backstrides() const noexcept; template void fill(const T& value); @@ -375,7 +373,7 @@ namespace xt * Returns the number of dimensions of the container. */ template - XTENSOR_CONSTEXPR_RETURN auto xcontainer::dimension() const noexcept -> size_type + constexpr auto xcontainer::dimension() const noexcept -> size_type { return shape().size(); } @@ -384,7 +382,7 @@ namespace xt * Returns the shape of the container. */ template - XTENSOR_CONSTEXPR_RETURN auto xcontainer::shape() const noexcept -> const inner_shape_type& + constexpr auto xcontainer::shape() const noexcept -> const inner_shape_type& { return derived_cast().shape_impl(); } @@ -393,7 +391,7 @@ namespace xt * Returns the strides of the container. */ template - XTENSOR_CONSTEXPR_RETURN auto xcontainer::strides() const noexcept -> const inner_strides_type& + constexpr auto xcontainer::strides() const noexcept -> const inner_strides_type& { return derived_cast().strides_impl(); } @@ -402,7 +400,7 @@ namespace xt * Returns the backstrides of the container. */ template - XTENSOR_CONSTEXPR_RETURN auto xcontainer::backstrides() const noexcept -> const inner_backstrides_type& + constexpr auto xcontainer::backstrides() const noexcept -> const inner_backstrides_type& { return derived_cast().backstrides_impl(); } @@ -999,10 +997,10 @@ namespace xt template inline void xstrided_container::resize(S&& shape, bool force) { - XTENSOR_ASSERT_MSG( + XTENSOR_PRECONDITION( detail::check_resize_dimension(m_shape, shape), "cannot change the number of dimensions of xtensor" - ) + ); std::size_t dim = shape.size(); if (m_shape.size() != dim || !std::equal(std::begin(shape), std::end(shape), std::begin(m_shape)) || force) diff --git a/include/xtensor/containers/xfixed.hpp b/include/xtensor/containers/xfixed.hpp index 6ac029881..e1b02867d 100644 --- a/include/xtensor/containers/xfixed.hpp +++ b/include/xtensor/containers/xfixed.hpp @@ -14,7 +14,6 @@ #include #include #include -#include #include @@ -257,12 +256,7 @@ namespace xt using inner_backstrides_type = backstrides_type; // NOTE: 0D (S::size() == 0) results in storage for 1 element (scalar) -#if defined(_MSC_VER) && _MSC_VER < 1910 && !defined(_WIN64) - // WORKAROUND FOR MSVC 2015 32 bit, fallback to unaligned container for 0D scalar case - using storage_type = std::array::value>; -#else using storage_type = aligned_array::value>; -#endif using reference = typename storage_type::reference; using const_reference = typename storage_type::const_reference; @@ -374,9 +368,9 @@ namespace xt storage_type& storage_impl() noexcept; const storage_type& storage_impl() const noexcept; - XTENSOR_CONSTEXPR_RETURN const inner_shape_type& shape_impl() const noexcept; - XTENSOR_CONSTEXPR_RETURN const inner_strides_type& strides_impl() const noexcept; - XTENSOR_CONSTEXPR_RETURN const inner_backstrides_type& backstrides_impl() const noexcept; + constexpr const inner_shape_type& shape_impl() const noexcept; + constexpr const inner_strides_type& strides_impl() const noexcept; + constexpr const inner_backstrides_type& backstrides_impl() const noexcept; friend class xcontainer>; }; @@ -496,9 +490,9 @@ namespace xt storage_type& storage_impl() noexcept; const storage_type& storage_impl() const noexcept; - XTENSOR_CONSTEXPR_RETURN const inner_shape_type& shape_impl() const noexcept; - XTENSOR_CONSTEXPR_RETURN const inner_strides_type& strides_impl() const noexcept; - XTENSOR_CONSTEXPR_RETURN const inner_backstrides_type& backstrides_impl() const noexcept; + constexpr const inner_shape_type& shape_impl() const noexcept; + constexpr const inner_strides_type& strides_impl() const noexcept; + constexpr const inner_backstrides_type& backstrides_impl() const noexcept; friend class xcontainer>; }; @@ -741,21 +735,20 @@ namespace xt } template - XTENSOR_CONSTEXPR_RETURN auto xfixed_container::shape_impl() const noexcept - -> const inner_shape_type& + constexpr auto xfixed_container::shape_impl() const noexcept -> const inner_shape_type& { return m_shape; } template - XTENSOR_CONSTEXPR_RETURN auto xfixed_container::strides_impl() const noexcept + constexpr auto xfixed_container::strides_impl() const noexcept -> const inner_strides_type& { return m_strides; } template - XTENSOR_CONSTEXPR_RETURN auto xfixed_container::backstrides_impl() const noexcept + constexpr auto xfixed_container::backstrides_impl() const noexcept -> const inner_backstrides_type& { return m_backstrides; @@ -938,21 +931,20 @@ namespace xt } template - XTENSOR_CONSTEXPR_RETURN auto xfixed_adaptor::shape_impl() const noexcept - -> const inner_shape_type& + constexpr auto xfixed_adaptor::shape_impl() const noexcept -> const inner_shape_type& { return m_shape; } template - XTENSOR_CONSTEXPR_RETURN auto xfixed_adaptor::strides_impl() const noexcept + constexpr auto xfixed_adaptor::strides_impl() const noexcept -> const inner_strides_type& { return m_strides; } template - XTENSOR_CONSTEXPR_RETURN auto xfixed_adaptor::backstrides_impl() const noexcept + constexpr auto xfixed_adaptor::backstrides_impl() const noexcept -> const inner_backstrides_type& { return m_backstrides; diff --git a/include/xtensor/containers/xscalar.hpp b/include/xtensor/containers/xscalar.hpp index 9464f4721..106a65923 100644 --- a/include/xtensor/containers/xscalar.hpp +++ b/include/xtensor/containers/xscalar.hpp @@ -12,7 +12,6 @@ #include #include -#include #include @@ -467,25 +466,25 @@ namespace xt *****************************/ template - XTENSOR_CONSTEXPR_RETURN auto linear_begin(xscalar& c) noexcept -> decltype(c.dummy_begin()) + constexpr auto linear_begin(xscalar& c) noexcept -> decltype(c.dummy_begin()) { return c.dummy_begin(); } template - XTENSOR_CONSTEXPR_RETURN auto linear_end(xscalar& c) noexcept -> decltype(c.dummy_end()) + constexpr auto linear_end(xscalar& c) noexcept -> decltype(c.dummy_end()) { return c.dummy_end(); } template - XTENSOR_CONSTEXPR_RETURN auto linear_begin(const xscalar& c) noexcept -> decltype(c.dummy_begin()) + constexpr auto linear_begin(const xscalar& c) noexcept -> decltype(c.dummy_begin()) { return c.dummy_begin(); } template - XTENSOR_CONSTEXPR_RETURN auto linear_end(const xscalar& c) noexcept -> decltype(c.dummy_end()) + constexpr auto linear_end(const xscalar& c) noexcept -> decltype(c.dummy_end()) { return c.dummy_end(); } diff --git a/include/xtensor/containers/xstorage.hpp b/include/xtensor/containers/xstorage.hpp index 811ab80b7..26d4a8ccd 100644 --- a/include/xtensor/containers/xstorage.hpp +++ b/include/xtensor/containers/xstorage.hpp @@ -167,7 +167,7 @@ namespace xt using pointer = typename traits::pointer; using value_type = typename traits::value_type; pointer res = alloc.allocate(size); - if (!xtrivially_default_constructible::value) + if (!std::is_trivially_default_constructible::value) { for (pointer p = res; p != res + size; ++p) { @@ -189,7 +189,7 @@ namespace xt using value_type = typename traits::value_type; if (ptr != nullptr) { - if (!xtrivially_default_constructible::value) + if (!std::is_trivially_default_constructible::value) { for (pointer p = ptr; p != ptr + size; ++p) { @@ -325,7 +325,7 @@ namespace xt rhs.get_allocator() ); resize_impl(rhs.size()); - if (xtrivially_default_constructible::value) + if (std::is_trivially_default_constructible::value) { std::uninitialized_copy(rhs.p_begin, rhs.p_end, p_begin); } @@ -649,13 +649,9 @@ namespace xt using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; -#if defined(_MSC_VER) && _MSC_VER < 1910 - static constexpr std::size_t alignment = detail::allocator_alignment::value; -#else static constexpr std::size_t alignment = detail::allocator_alignment::value != 0 ? detail::allocator_alignment::value : alignof(T); -#endif svector() noexcept; ~svector(); @@ -1259,7 +1255,7 @@ namespace xt template inline void svector::destroy_range(T* begin, T* end) { - if (!xtrivially_default_constructible::value) + if (!std::is_trivially_default_constructible::value) { while (begin != end) { @@ -1437,49 +1433,6 @@ namespace xt #define XTENSOR_CONST const #endif -#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__) -#define GCC4_FALLBACK - - namespace const_array_detail - { - template - struct array_traits - { - using storage_type = T[N]; - - static constexpr T& ref(const storage_type& t, std::size_t n) noexcept - { - return const_cast(t[n]); - } - - static constexpr T* ptr(const storage_type& t) noexcept - { - return const_cast(t); - } - }; - - template - struct array_traits - { - struct empty - { - }; - - using storage_type = empty; - - static constexpr T& ref(const storage_type& /*t*/, std::size_t /*n*/) noexcept - { - return *static_cast(nullptr); - } - - static constexpr T* ptr(const storage_type& /*t*/) noexcept - { - return nullptr; - } - }; - } -#endif - /** * A std::array like class with all member function (except reverse iterators) * as constexpr. The data is immutable once set. @@ -1502,11 +1455,7 @@ namespace xt constexpr const_reference operator[](std::size_t idx) const { -#ifdef GCC4_FALLBACK - return const_array_detail::array_traits::ref(m_data, idx); -#else return m_data[idx]; -#endif } constexpr const_iterator begin() const noexcept @@ -1552,30 +1501,17 @@ namespace xt constexpr const_pointer data() const noexcept { -#ifdef GCC4_FALLBACK - return const_array_detail::array_traits::ptr(m_data); -#else return m_data; -#endif } constexpr const_reference front() const noexcept { -#ifdef GCC4_FALLBACK - return const_array_detail::array_traits::ref(m_data, 0); -#else return m_data[0]; -#endif } constexpr const_reference back() const noexcept { -#ifdef GCC4_FALLBACK - return N ? const_array_detail::array_traits::ref(m_data, N - 1) - : const_array_detail::array_traits::ref(m_data, 0); -#else return m_data[size() - 1]; -#endif } constexpr bool empty() const noexcept @@ -1588,15 +1524,9 @@ namespace xt return N; } -#ifdef GCC4_FALLBACK - XTENSOR_CONST typename const_array_detail::array_traits::storage_type m_data; -#else XTENSOR_CONST T m_data[N > 0 ? N : 1]; -#endif }; -#undef GCC4_FALLBACK - template inline bool operator==(const const_array& lhs, const const_array& rhs) { @@ -1658,13 +1588,7 @@ namespace xt { public: -#if defined(_MSC_VER) - using cast_type = std::array; -#define XTENSOR_FIXED_SHAPE_CONSTEXPR inline -#else using cast_type = const_array; -#define XTENSOR_FIXED_SHAPE_CONSTEXPR constexpr -#endif using value_type = std::size_t; using size_type = std::size_t; using const_iterator = typename cast_type::const_iterator; @@ -1681,17 +1605,17 @@ namespace xt return std::get(tmp_cast_type{X...}); } - XTENSOR_FIXED_SHAPE_CONSTEXPR operator cast_type() const + constexpr operator cast_type() const { return cast_type({X...}); } - XTENSOR_FIXED_SHAPE_CONSTEXPR auto begin() const + constexpr auto begin() const { return m_array.begin(); } - XTENSOR_FIXED_SHAPE_CONSTEXPR auto end() const + constexpr auto end() const { return m_array.end(); } @@ -1706,22 +1630,32 @@ namespace xt return m_array.rend(); } - XTENSOR_FIXED_SHAPE_CONSTEXPR auto cbegin() const + constexpr auto cbegin() const { return m_array.cbegin(); } - XTENSOR_FIXED_SHAPE_CONSTEXPR auto cend() const + constexpr auto cend() const { return m_array.cend(); } - XTENSOR_FIXED_SHAPE_CONSTEXPR std::size_t operator[](std::size_t idx) const + auto crbegin() const + { + return m_array.crbegin(); + } + + auto crend() const + { + return m_array.crend(); + } + + constexpr std::size_t operator[](std::size_t idx) const { return m_array[idx]; } - XTENSOR_FIXED_SHAPE_CONSTEXPR bool empty() const + constexpr bool empty() const { return sizeof...(X) == 0; } @@ -1731,8 +1665,6 @@ namespace xt XTENSOR_CONSTEXPR_ENHANCED_STATIC cast_type m_array = cast_type({X...}); }; -#undef XTENSOR_FIXED_SHAPE_CONSTEXPR - template class sequence_view { diff --git a/include/xtensor/core/xeval.hpp b/include/xtensor/core/xeval.hpp index 231f47312..5ccbc1db9 100644 --- a/include/xtensor/core/xeval.hpp +++ b/include/xtensor/core/xeval.hpp @@ -18,7 +18,7 @@ namespace xt { /** - * @defgroup xt_xeval + * @defgroup xt_xeval Evaluation * * Evaluation functions. * Defined in ``xtensor/xeval.hpp`` diff --git a/include/xtensor/core/xexpression.hpp b/include/xtensor/core/xexpression.hpp index 1f8c98194..9b133315d 100644 --- a/include/xtensor/core/xexpression.hpp +++ b/include/xtensor/core/xexpression.hpp @@ -10,9 +10,7 @@ #ifndef XTENSOR_EXPRESSION_HPP #define XTENSOR_EXPRESSION_HPP -#include #include -#include #include #include diff --git a/include/xtensor/core/xfunction.hpp b/include/xtensor/core/xfunction.hpp index 200a35d47..6459e998d 100644 --- a/include/xtensor/core/xfunction.hpp +++ b/include/xtensor/core/xfunction.hpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -609,7 +608,7 @@ namespace xt // leading to warning about signed/unsigned conversions in the deeper layers of the access methods return std::apply( - [&](auto&... e) + [&](auto&... e) -> const_reference { XTENSOR_TRY(check_index(shape(), args...)); XTENSOR_CHECK_DIMENSION(shape(), args...); @@ -632,7 +631,7 @@ namespace xt inline auto xfunction::flat(size_type index) const -> const_reference { return std::apply( - [&](auto&... e) + [&](auto&... e) -> const_reference { return m_f(e.data_element(index)...); }, @@ -666,7 +665,7 @@ namespace xt // The static cast prevents the compiler from instantiating the template methods with signed integers, // leading to warning about signed/unsigned conversions in the deeper layers of the access methods return std::apply( - [&](const auto&... e) + [&](const auto&... e) -> const_reference { return m_f(e.unchecked(static_cast(args)...)...); }, @@ -686,7 +685,7 @@ namespace xt inline auto xfunction::element(It first, It last) const -> const_reference { return std::apply( - [&](auto&... e) + [&](auto&... e) -> const_reference { XTENSOR_TRY(check_element_index(shape(), first, last)); return m_f(e.element(first, last)...); @@ -827,7 +826,7 @@ namespace xt inline auto xfunction::data_element(size_type i) const -> const_reference { return std::apply( - [&](auto&... e) + [&](auto&... e) -> const_reference { return m_f(e.data_element(i)...); }, @@ -958,7 +957,7 @@ namespace xt inline auto xfunction_iterator::operator*() const -> reference { return std::apply( - [&](auto&... it) + [&](auto&... it) -> reference { return (p_f->m_f)(*it...); }, @@ -1110,7 +1109,7 @@ namespace xt inline auto xfunction_stepper::operator*() const -> reference { return std::apply( - [&](auto&... e) + [&](auto&... e) -> reference { return (p_f->m_f)(*e...); }, diff --git a/include/xtensor/core/xiterable.hpp b/include/xtensor/core/xiterable.hpp index d0caf9d6f..14d386207 100644 --- a/include/xtensor/core/xiterable.hpp +++ b/include/xtensor/core/xiterable.hpp @@ -322,21 +322,6 @@ namespace xt static constexpr layout_type static_layout = inner_types::layout; -#if defined(_MSC_VER) && _MSC_VER >= 1910 - // Workaround for compiler bug in Visual Studio 2017 with respect to alias templates with non-type - // parameters. - template - using layout_iterator = xiterator; - template - using const_layout_iterator = xiterator< - typename iterable_base::const_stepper, - typename iterable_base::inner_shape_type*, - L>; - template - using reverse_layout_iterator = std::reverse_iterator>; - template - using const_reverse_layout_iterator = std::reverse_iterator>; -#else template using layout_iterator = typename iterable_base::template layout_iterator; template @@ -345,7 +330,6 @@ namespace xt using reverse_layout_iterator = typename iterable_base::template reverse_layout_iterator; template using const_reverse_layout_iterator = typename iterable_base::template const_reverse_layout_iterator; -#endif template using broadcast_iterator = typename iterable_base::template broadcast_iterator; diff --git a/include/xtensor/core/xiterator.hpp b/include/xtensor/core/xiterator.hpp index ca3faef21..448f6093d 100644 --- a/include/xtensor/core/xiterator.hpp +++ b/include/xtensor/core/xiterator.hpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -418,7 +417,7 @@ namespace xt } template - XTENSOR_CONSTEXPR_RETURN auto linear_begin(C& c) noexcept + constexpr auto linear_begin(C& c) noexcept { if constexpr (detail::has_linear_iterator::value) { @@ -431,7 +430,7 @@ namespace xt } template - XTENSOR_CONSTEXPR_RETURN auto linear_end(C& c) noexcept + constexpr auto linear_end(C& c) noexcept { if constexpr (detail::has_linear_iterator::value) { @@ -444,7 +443,7 @@ namespace xt } template - XTENSOR_CONSTEXPR_RETURN auto linear_begin(const C& c) noexcept + constexpr auto linear_begin(const C& c) noexcept { if constexpr (detail::has_linear_iterator::value) { @@ -457,7 +456,7 @@ namespace xt } template - XTENSOR_CONSTEXPR_RETURN auto linear_end(const C& c) noexcept + constexpr auto linear_end(const C& c) noexcept { if constexpr (detail::has_linear_iterator::value) { diff --git a/include/xtensor/core/xmath.hpp b/include/xtensor/core/xmath.hpp index 77f864929..827e84219 100644 --- a/include/xtensor/core/xmath.hpp +++ b/include/xtensor/core/xmath.hpp @@ -606,13 +606,13 @@ namespace xt template constexpr auto operator()(const A1& v, const A2& lo, const A3& hi) const { - return xtl::select(v < lo, lo, xtl::select(hi < v, hi, v)); + return xtl::select(lo < hi, xtl::select(v < lo, lo, xtl::select(hi < v, hi, v)), hi); } template constexpr auto simd_apply(const A1& v, const A2& lo, const A3& hi) const { - return xt_simd::select(v < lo, lo, xt_simd::select(hi < v, hi, v)); + return xt_simd::select(lo < hi, xt_simd::select(v < lo, lo, xt_simd::select(hi < v, hi, v)), hi); } }; @@ -1088,31 +1088,6 @@ namespace xt return xfunction_type(detail::lambda_adapt(std::forward(lambda)), std::forward(args)...); } -// Workaround for MSVC 2015 & GCC 4.9 -#if (defined(_MSC_VER) && _MSC_VER < 1910) || (defined(__GNUC__) && __GNUC__ < 5) -#define XTENSOR_DISABLE_LAMBDA_FCT -#endif - -#ifdef XTENSOR_DISABLE_LAMBDA_FCT - struct square_fct - { - template - auto operator()(T x) const -> decltype(x * x) - { - return x * x; - } - }; - - struct cube_fct - { - template - auto operator()(T x) const -> decltype(x * x * x) - { - return x * x * x; - } - }; -#endif - /** * @ingroup pow_functions * @brief Square power function, equivalent to e1 * e1. @@ -1125,15 +1100,11 @@ namespace xt template inline auto square(E1&& e1) noexcept { -#ifdef XTENSOR_DISABLE_LAMBDA_FCT - return make_lambda_xfunction(square_fct{}, std::forward(e1)); -#else auto fnct = [](auto x) -> decltype(x * x) { return x * x; }; return make_lambda_xfunction(std::move(fnct), std::forward(e1)); -#endif } /** @@ -1148,19 +1119,13 @@ namespace xt template inline auto cube(E1&& e1) noexcept { -#ifdef XTENSOR_DISABLE_LAMBDA_FCT - return make_lambda_xfunction(cube_fct{}, std::forward(e1)); -#else auto fnct = [](auto x) -> decltype(x * x * x) { return x * x * x; }; return make_lambda_xfunction(std::move(fnct), std::forward(e1)); -#endif } -#undef XTENSOR_DISABLE_LAMBDA_FCT - namespace detail { // Thanks to Matt Pharr in http://pbrt.org/hair.pdf diff --git a/include/xtensor/core/xoperation.hpp b/include/xtensor/core/xoperation.hpp index 533ad6513..76bfc7c50 100644 --- a/include/xtensor/core/xoperation.hpp +++ b/include/xtensor/core/xoperation.hpp @@ -11,7 +11,6 @@ #define XTENSOR_OPERATION_HPP #include -#include #include #include diff --git a/include/xtensor/core/xshape.hpp b/include/xtensor/core/xshape.hpp index 28fbe43cc..6988e69f3 100644 --- a/include/xtensor/core/xshape.hpp +++ b/include/xtensor/core/xshape.hpp @@ -11,13 +11,10 @@ #define XTENSOR_XSHAPE_HPP #include -#include #include #include -#include #include #include -#include #include "../containers/xstorage.hpp" #include "../core/xlayout.hpp" @@ -122,7 +119,7 @@ namespace xt * Check if an object has a certain shape. * * @ingroup xt_xshape - * @param a an array + * @param e an array-like object * @param shape the shape to test * @return bool */ @@ -136,8 +133,8 @@ namespace xt /** * Check if an object has a certain shape. * - * @ingroup has_shape - * @param a an array + * @ingroup xt_xshape + * @param e an array-like object * @param shape the shape to test * @return bool */ diff --git a/include/xtensor/core/xstrides.hpp b/include/xtensor/core/xstrides.hpp index f8735f7e3..d413fcd26 100644 --- a/include/xtensor/core/xstrides.hpp +++ b/include/xtensor/core/xstrides.hpp @@ -59,7 +59,8 @@ namespace xt * * @ingroup xt_xstrides * @param strides Strides of the array. - * @param args Array index. + * @param arg First array index. + * @param args Remaining array indices. * @return The flat index. */ template @@ -241,7 +242,8 @@ namespace xt * @brief Get strides of an object. * * @ingroup xt_xstrides - * @param a an array + * @param e an array + * @param type output stride convention * @return array */ template @@ -285,7 +287,9 @@ namespace xt * @brief Get stride of an object along an axis. * * @ingroup xt_xstrides - * @param a an array + * @param e an array + * @param axis axis along which to query the stride + * @param type output stride convention * @return integer */ template diff --git a/include/xtensor/core/xtensor_config.hpp b/include/xtensor/core/xtensor_config.hpp index 6a74112df..959a3b2a4 100644 --- a/include/xtensor/core/xtensor_config.hpp +++ b/include/xtensor/core/xtensor_config.hpp @@ -35,17 +35,12 @@ // Workaround for some missing constexpr functionality in MSVC 2015 and MSVC 2017 x86 #if defined(_MSC_VER) -#define XTENSOR_CONSTEXPR_ENHANCED const // The following must not be defined to const, otherwise // it prevents generation of copy operators of classes // containing XTENSOR_CONSTEXPR_ENHANCED_STATIC members #define XTENSOR_CONSTEXPR_ENHANCED_STATIC -#define XTENSOR_CONSTEXPR_RETURN inline #else -#define XTENSOR_CONSTEXPR_ENHANCED constexpr -#define XTENSOR_CONSTEXPR_RETURN constexpr #define XTENSOR_CONSTEXPR_ENHANCED_STATIC constexpr static -#define XTENSOR_HAS_CONSTEXPR_ENHANCED #endif #ifndef XTENSOR_DEFAULT_DATA_CONTAINER diff --git a/include/xtensor/core/xvectorize.hpp b/include/xtensor/core/xvectorize.hpp index 053d7c92b..edd199021 100644 --- a/include/xtensor/core/xvectorize.hpp +++ b/include/xtensor/core/xvectorize.hpp @@ -54,14 +54,9 @@ namespace xt template xvectorizer vectorize(F&& f, R (*)(Args...)); -// Workaround for Visual Studio 15.7.1. -// Error C2668 (ambiguous call to overloaded function) mistaking a declarations -// for the definition of another overload. -#ifndef _MSC_VER template auto vectorize(F&& f) -> decltype(vectorize(std::forward(f), std::declval*>())); -#endif /****************************** * xvectorizer implementation * diff --git a/include/xtensor/generators/xbuilder.hpp b/include/xtensor/generators/xbuilder.hpp index 8a2f61147..4b9bed942 100644 --- a/include/xtensor/generators/xbuilder.hpp +++ b/include/xtensor/generators/xbuilder.hpp @@ -146,7 +146,7 @@ namespace xt * the same shape, value type and layout as the input xexpression *e*. * * Note: contrary to zeros(shape), this function returns a non-lazy, allocated container! - * Use ``xt::zeros(e.shape());` for a lazy version. + * Use ``xt::zeros(e.shape());`` for a lazy version. * * @param e the xexpression from which to extract shape, value type and layout. */ @@ -574,18 +574,18 @@ namespace xt } const auto& shape = arr.shape(); const size_t stride = std::accumulate( - shape.begin() + i + 1, + shape.begin() + static_cast(i) + 1, shape.end(), - 1, + size_t(1), std::multiplies() ); - const auto len = (*(first + i + after_axis)); + const auto len = (*(first + static_cast(i + after_axis))); offset += len * stride; } - const auto element = arr.begin() + offset; + const auto element = arr.begin() + static_cast(offset); return *element; }; - size_type i = *(first + axis); + size_type i = *(first + static_cast(axis)); return apply(i, get_item, t); } }; @@ -910,6 +910,32 @@ namespace xt namespace detail { + template + struct vstack_fixed_shape_impl; + + template + struct vstack_fixed_shape_impl> + { + using type = fixed_shape<1, N>; + }; + + template + struct vstack_fixed_shape_impl> + { + using type = fixed_shape; + }; + + template + struct vstack_fixed_shape + { + using type = concat_fixed_shape_t< + 0, + typename vstack_fixed_shape_impl::shape_type>::type...>; + }; + + template + using vstack_fixed_shape_t = typename vstack_fixed_shape::type; + template inline auto vstack_shape(std::tuple& t, const S& shape) { @@ -948,23 +974,31 @@ namespace xt return detail::make_xgenerator(detail::vstack_impl(std::move(t), size_t(0)), new_shape); } + /** + * @brief Stack fixed-shape xexpressions in sequence vertically (row wise). + * This overload preserves the result shape at compile time by treating + * 1-D fixed shapes as ``(1, N)`` row vectors before concatenation. + * + * @param t \ref xtuple of fixed-shape xexpressions to stack + * @return xgenerator evaluating to stacked elements with a fixed compile-time shape + */ + template + inline auto vstack(std::tuple&& t) + { + using shape_type = detail::vstack_fixed_shape_t; + return detail::make_xgenerator(detail::vstack_impl(std::move(t), size_t(0)), shape_type{}); + } + namespace detail { template inline auto meshgrid_impl(std::index_sequence, E&&... e) noexcept { -#if defined _MSC_VER - const std::array shape = {e.shape()[0]...}; - return std::make_tuple( - detail::make_xgenerator(detail::repeat_impl>(std::forward(e), I), shape)... - ); -#else return std::make_tuple(detail::make_xgenerator( detail::repeat_impl>(std::forward(e), I), {e.shape()[0]...} )...); -#endif } } diff --git a/include/xtensor/generators/xgenerator.hpp b/include/xtensor/generators/xgenerator.hpp index f4dfc191f..66922c5be 100644 --- a/include/xtensor/generators/xgenerator.hpp +++ b/include/xtensor/generators/xgenerator.hpp @@ -12,8 +12,6 @@ #include #include -#include -#include #include #include diff --git a/include/xtensor/generators/xrandom.hpp b/include/xtensor/generators/xrandom.hpp index 5e365f667..73b1763ae 100644 --- a/include/xtensor/generators/xrandom.hpp +++ b/include/xtensor/generators/xrandom.hpp @@ -63,14 +63,14 @@ namespace xt auto binomial(const S& shape, T trials = 1, D prob = 0.5, E& engine = random::get_default_random_engine()); - template + template auto geometric(const S& shape, D prob = 0.5, E& engine = random::get_default_random_engine()); template auto negative_binomial(const S& shape, T k = 1, D prob = 0.5, E& engine = random::get_default_random_engine()); - template + template auto poisson(const S& shape, D rate = 1.0, E& engine = random::get_default_random_engine()); template @@ -123,7 +123,7 @@ namespace xt auto binomial(const I (&shape)[L], T trials = 1, D prob = 0.5, E& engine = random::get_default_random_engine()); - template + template auto geometric(const I (&shape)[L], D prob = 0.5, E& engine = random::get_default_random_engine()); template @@ -134,7 +134,7 @@ namespace xt E& engine = random::get_default_random_engine() ); - template + template auto poisson(const I (&shape)[L], D rate = 1.0, E& engine = random::get_default_random_engine()); template @@ -914,15 +914,16 @@ namespace xt * * For weighted random sampling with replacement, binary search with cumulative weights alogrithm is * used. For weighted random sampling without replacement, the algorithm used is the exponential sort - * from [Efraimidis and Spirakis](https://doi.org/10.1016/j.ipl.2005.11.003) (2006) with the ``weight - * / randexp(1)`` [trick](https://web.archive.org/web/20201021162211/https://krlmlr.github.io/wrswoR/) - * from Kirill Müller. + * from [Efraimidis and Spirakis](https://linkinghub.elsevier.com/retrieve/pii/S002001900500298X) + * (2006) with the ``weight / randexp(1)`` + * [trick](https://web.archive.org/web/20201021162211/https://krlmlr.github.io/wrswoR/) from Kirill + * Müller. * * Note: this function makes a copy of your data, and only 1D data is accepted. * * @param e expression to sample from * @param n number of elements to sample - * @param w expression for the weight distribution. + * @param weights expression for the weight distribution. * Weights must be positive and real-valued but need not sum to 1. * @param replace set true to sample with replacement * @param engine random number engine diff --git a/include/xtensor/io/xcsv.hpp b/include/xtensor/io/xcsv.hpp index 2b758bd9e..70472625e 100644 --- a/include/xtensor/io/xcsv.hpp +++ b/include/xtensor/io/xcsv.hpp @@ -10,7 +10,6 @@ #ifndef XTENSOR_CSV_HPP #define XTENSOR_CSV_HPP -#include #include #include #include @@ -67,7 +66,7 @@ namespace xt } size_t last = cell.find_last_not_of(' '); - return cell.substr(first, last == std::string::npos ? cell.size() : last + 1); + return cell.substr(first, last == std::string::npos ? cell.size() : last - first + 1); } template <> @@ -94,6 +93,18 @@ namespace xt return std::stoi(cell); } + template <> + inline signed char lexical_cast(const std::string& cell) + { + return static_cast(std::stoi(cell)); + } + + template <> + inline unsigned char lexical_cast(const std::string& cell) + { + return static_cast(std::stoul(cell)); + } + template <> inline long lexical_cast(const std::string& cell) { @@ -211,30 +222,40 @@ namespace xt { using size_type = typename E::size_type; const E& ex = e.derived_cast(); - if (ex.dimension() != 2) - { - XTENSOR_THROW(std::runtime_error, "Only 2-D expressions can be serialized to CSV"); - } - size_type nbrows = ex.shape()[0], nbcols = ex.shape()[1]; - auto st = ex.stepper_begin(ex.shape()); - for (size_type r = 0; r != nbrows; ++r) + if (ex.dimension() == 1) { - for (size_type c = 0; c != nbcols; ++c) + const size_type n = ex.shape()[0]; + for (size_type i = 0; i != n; ++i) { - stream << *st; - if (c != nbcols - 1) + stream << ex(i); + if (i != n - 1) { - st.step(1); stream << ','; } - else + } + stream << std::endl; + } + else if (ex.dimension() == 2) + { + const size_type nbrows = ex.shape()[0]; + const size_type nbcols = ex.shape()[1]; + for (size_type r = 0; r != nbrows; ++r) + { + for (size_type c = 0; c != nbcols; ++c) { - st.reset(1); - st.step(0); - stream << std::endl; + stream << ex(r, c); + if (c != nbcols - 1) + { + stream << ','; + } } + stream << std::endl; } } + else + { + XTENSOR_THROW(std::runtime_error, "Only 1-D and 2-D expressions can be serialized to CSV"); + } } struct xcsv_config @@ -253,6 +274,47 @@ namespace xt } }; + template + void dump_csv(std::ostream& stream, const xexpression& e, const xcsv_config& config) + { + using size_type = typename E::size_type; + const E& ex = e.derived_cast(); + if (ex.dimension() == 1) + { + const size_type n = ex.shape()[0]; + for (size_type i = 0; i != n; ++i) + { + stream << ex(i); + if (i != n - 1) + { + stream << config.delimiter; + } + } + stream << std::endl; + } + else if (ex.dimension() == 2) + { + const size_type nbrows = ex.shape()[0]; + const size_type nbcols = ex.shape()[1]; + for (size_type r = 0; r != nbrows; ++r) + { + for (size_type c = 0; c != nbcols; ++c) + { + stream << ex(r, c); + if (c != nbcols - 1) + { + stream << config.delimiter; + } + } + stream << std::endl; + } + } + else + { + XTENSOR_THROW(std::runtime_error, "Only 1-D and 2-D expressions can be serialized to CSV"); + } + } + template void load_file(std::istream& stream, xexpression& e, const xcsv_config& config) { @@ -266,9 +328,9 @@ namespace xt } template - void dump_file(std::ostream& stream, const xexpression& e, const xcsv_config&) + void dump_file(std::ostream& stream, const xexpression& e, const xcsv_config& config) { - dump_csv(stream, e); + dump_csv(stream, e, config); } } diff --git a/include/xtensor/io/xinfo.hpp b/include/xtensor/io/xinfo.hpp index f78cc3c76..478569cc7 100644 --- a/include/xtensor/io/xinfo.hpp +++ b/include/xtensor/io/xinfo.hpp @@ -14,49 +14,21 @@ #include "../core/xlayout.hpp" -#ifndef _MSC_VER -#if __cplusplus < 201103 -#define CONSTEXPR11_TN -#define CONSTEXPR14_TN -#define NOEXCEPT_TN -#elif __cplusplus < 201402 -#define CONSTEXPR11_TN constexpr -#define CONSTEXPR14_TN -#define NOEXCEPT_TN noexcept -#else -#define CONSTEXPR11_TN constexpr -#define CONSTEXPR14_TN constexpr -#define NOEXCEPT_TN noexcept -#endif -#else // _MSC_VER -#if _MSC_VER < 1900 -#define CONSTEXPR11_TN -#define CONSTEXPR14_TN -#define NOEXCEPT_TN -#elif _MSC_VER < 2000 -#define CONSTEXPR11_TN constexpr -#define CONSTEXPR14_TN -#define NOEXCEPT_TN noexcept -#else -#define CONSTEXPR11_TN constexpr -#define CONSTEXPR14_TN constexpr -#define NOEXCEPT_TN noexcept -#endif -#endif - namespace xt { // see http://stackoverflow.com/a/20170989 struct static_string { template - explicit CONSTEXPR11_TN static_string(const char (&a)[N]) NOEXCEPT_TN : data(a), - size(N - 1) + explicit constexpr static_string(const char (&a)[N]) noexcept + : data(a) + , size(N - 1) { } - CONSTEXPR11_TN static_string(const char* a, const std::size_t sz) NOEXCEPT_TN : data(a), - size(sz) + constexpr static_string(const char* a, const std::size_t sz) noexcept + : data(a) + , size(sz) { } @@ -65,18 +37,14 @@ namespace xt }; template - CONSTEXPR14_TN static_string type_name() + constexpr static_string type_name() { #ifdef __clang__ static_string p(__PRETTY_FUNCTION__); return static_string(p.data + 39, p.size - 39 - 1); #elif defined(__GNUC__) static_string p(__PRETTY_FUNCTION__); -#if __cplusplus < 201402 - return static_string(p.data + 36, p.size - 36 - 1); -#else return static_string(p.data + 54, p.size - 54 - 1); -#endif #elif defined(_MSC_VER) static const static_string p(__FUNCSIG__); return static_string(p.data + 47, p.size - 47 - 7); diff --git a/include/xtensor/io/xio.hpp b/include/xtensor/io/xio.hpp index 345edc4d4..09ee4da58 100644 --- a/include/xtensor/io/xio.hpp +++ b/include/xtensor/io/xio.hpp @@ -14,13 +14,13 @@ #include #include #include -#include #include #include #include "../core/xexpression.hpp" #include "../core/xmath.hpp" #include "../views/xstrided_view.hpp" +#include "xtl/xmasked_value_meta.hpp" namespace xt { @@ -647,7 +647,14 @@ namespace xt void update(const_reference val) { std::stringstream buf; - buf << val; + if constexpr (xtl::is_xmasked_value::value) + { + buf << +val; + } + else + { + buf << val; + } std::string s = buf.str(); if (int(s.size()) > m_width) { diff --git a/include/xtensor/io/xnpy.hpp b/include/xtensor/io/xnpy.hpp index 369d68c9d..ef517f068 100644 --- a/include/xtensor/io/xnpy.hpp +++ b/include/xtensor/io/xnpy.hpp @@ -17,15 +17,12 @@ #include #include #include -#include #include -#include #include #include #include #include #include -#include #include #include @@ -482,7 +479,9 @@ namespace xt char header_len_le16[2]; istream.read(header_len_le16, 2); - uint16_t header_length = uint16_t(header_len_le16[0] << 0) | uint16_t(header_len_le16[1] << 8); + uint16_t header_b0 = static_cast(static_cast(header_len_le16[0])); + uint16_t header_b1 = static_cast(static_cast(header_len_le16[1])) << 8; + uint16_t header_length = header_b0 | header_b1; if ((magic_string_length + 2 + 2 + header_length) % 16 != 0) { @@ -502,8 +501,11 @@ namespace xt char header_len_le32[4]; istream.read(header_len_le32, 4); - uint32_t header_length = uint32_t(header_len_le32[0] << 0) | uint32_t(header_len_le32[1] << 8) - | uint32_t(header_len_le32[2] << 16) | uint32_t(header_len_le32[3] << 24); + uint32_t header_b0 = static_cast(static_cast(header_len_le32[0])); + uint32_t header_b1 = static_cast(static_cast(header_len_le32[1])) << 8; + uint32_t header_b2 = static_cast(static_cast(header_len_le32[2])) << 16; + uint32_t header_b3 = static_cast(static_cast(header_len_le32[3])) << 24; + uint32_t header_length = header_b0 | header_b1 | header_b2 | header_b3; if ((magic_string_length + 2 + 4 + header_length) % 16 != 0) { diff --git a/include/xtensor/misc/xcomplex.hpp b/include/xtensor/misc/xcomplex.hpp index 308218f60..c803bb77c 100644 --- a/include/xtensor/misc/xcomplex.hpp +++ b/include/xtensor/misc/xcomplex.hpp @@ -23,7 +23,7 @@ namespace xt { /** - * @defgroup xt_xcomplex + * @defgroup xt_xcomplex Complex numbers * * Defined in ``xtensor/xcomplex.hpp`` */ diff --git a/include/xtensor/misc/xhistogram.hpp b/include/xtensor/misc/xhistogram.hpp index b8fdd9da1..a389c4fb9 100644 --- a/include/xtensor/misc/xhistogram.hpp +++ b/include/xtensor/misc/xhistogram.hpp @@ -23,6 +23,11 @@ using namespace xt::placeholders; namespace xt { + /** + * @defgroup digitize Digitize helpers + * @brief Helpers for assigning values to histogram bins. + */ + /** * @ingroup digitize * @brief Return the indices of the bins to which each value in input array belongs. @@ -128,6 +133,11 @@ namespace xt } // detail + /** + * @defgroup histogram Histogram functions + * @brief Helpers for computing histograms and histogram bin edges. + */ + /** * @ingroup histogram * @brief Compute the histogram of a set of data. diff --git a/include/xtensor/misc/xmanipulation.hpp b/include/xtensor/misc/xmanipulation.hpp index f100744ac..2d540539b 100644 --- a/include/xtensor/misc/xmanipulation.hpp +++ b/include/xtensor/misc/xmanipulation.hpp @@ -27,7 +27,7 @@ namespace xt { /** - * @defgroup xt_xmanipulation + * @defgroup xt_xmanipulation Array manipulation */ namespace check_policy @@ -1099,7 +1099,7 @@ namespace xt * @param repeats The number of repetition of each elements. * @p repeats is broadcasted to fit the shape of the given @p axis. * @param axis the axis along which to repeat the value - * @return an expression which as the same shape as \ref e, except along the given \ref axis + * @return an expression with the same shape as ``e``, except along the given ``axis`` */ template inline auto repeat(E&& e, std::size_t repeats, std::size_t axis) @@ -1119,7 +1119,7 @@ namespace xt * The size of @p repeats must match the shape of the given @p axis. * @param axis the axis along which to repeat the value * - * @return an expression which as the same shape as \ref e, except along the given \ref axis + * @return an expression with the same shape as ``e``, except along the given ``axis`` */ template inline auto repeat(E&& e, const std::vector& repeats, std::size_t axis) @@ -1135,7 +1135,7 @@ namespace xt * @param repeats The number of repetition of each elements. * The size of @p repeats must match the shape of the given @p axis. * @param axis the axis along which to repeat the value - * @return an expression which as the same shape as \ref e, except along the given \ref axis + * @return an expression with the same shape as ``e``, except along the given ``axis`` */ template inline auto repeat(E&& e, std::vector&& repeats, std::size_t axis) diff --git a/include/xtensor/misc/xset_operation.hpp b/include/xtensor/misc/xset_operation.hpp index 94b7e23a7..2a29a6b12 100644 --- a/include/xtensor/misc/xset_operation.hpp +++ b/include/xtensor/misc/xset_operation.hpp @@ -11,7 +11,6 @@ #define XTENSOR_XSET_OPERATION_HPP #include -#include #include #include @@ -25,6 +24,11 @@ namespace xt { + /** + * @defgroup searchsorted Searchsorted helpers + * @brief Helpers for locating insertion indices in sorted arrays. + */ + namespace detail { diff --git a/include/xtensor/misc/xsort.hpp b/include/xtensor/misc/xsort.hpp index aa9ea4cfa..196ec45cd 100644 --- a/include/xtensor/misc/xsort.hpp +++ b/include/xtensor/misc/xsort.hpp @@ -556,7 +556,7 @@ namespace xt * @ingroup xt_xsort * @param e input xexpression * @param kth_container a container of ``indices`` that should contain the correctly sorted value - * @param axis either integer (default = -1) to sort along last axis or ``xnone()`` to flatten before + * @param ax placeholder indicating that the input is flattened before sorting * sorting * * @return partially sorted xcontainer @@ -647,7 +647,7 @@ namespace xt * @ingroup xt_xsort * @param e input xexpression * @param kth_container a container of ``indices`` that should contain the correctly sorted value - * @param axis either integer (default = -1) to sort along last axis or ``xnone()`` to flatten before + * @param axis placeholder indicating that the input is flattened before sorting * sorting * * @return xcontainer with indices of partial sort of input diff --git a/include/xtensor/optional/xoptional_assembly_base.hpp b/include/xtensor/optional/xoptional_assembly_base.hpp index 3f60cc359..5df618f0b 100644 --- a/include/xtensor/optional/xoptional_assembly_base.hpp +++ b/include/xtensor/optional/xoptional_assembly_base.hpp @@ -700,7 +700,7 @@ namespace xt /** * Returns a reference to the element at the specified position * of the underlying storage in the optional assembly. - * @param index index to underlying flat storage. + * @param i index to underlying flat storage. */ template inline auto xoptional_assembly_base::flat(size_type i) -> reference @@ -711,7 +711,7 @@ namespace xt /** * Returns a constant reference to the element at the specified position * of the underlying storage in the optional assembly. - * @param index index to underlying flat storage. + * @param i index to underlying flat storage. */ template inline auto xoptional_assembly_base::flat(size_type i) const -> const_reference diff --git a/include/xtensor/reducers/xblockwise_reducer_functors.hpp b/include/xtensor/reducers/xblockwise_reducer_functors.hpp index 4ac9e649d..d6216b2da 100644 --- a/include/xtensor/reducers/xblockwise_reducer_functors.hpp +++ b/include/xtensor/reducers/xblockwise_reducer_functors.hpp @@ -2,10 +2,7 @@ #define XTENSOR_XBLOCKWISE_REDUCER_FUNCTORS_HPP -#include -#include #include -#include #include "../chunk/xchunked_array.hpp" #include "../chunk/xchunked_assign.hpp" diff --git a/include/xtensor/reducers/xreducer.hpp b/include/xtensor/reducers/xreducer.hpp index 3cf8ec76f..e99522b5e 100644 --- a/include/xtensor/reducers/xreducer.hpp +++ b/include/xtensor/reducers/xreducer.hpp @@ -12,7 +12,6 @@ #include #include -#include #include #include #include @@ -1400,6 +1399,7 @@ namespace xt * @param func the function to apply * @param e the expression to reduce * @param axes the axes along which the reduction is performed + * @param options reducer options controlling evaluation strategy and related settings */ template template diff --git a/include/xtensor/utils/xexception.hpp b/include/xtensor/utils/xexception.hpp index 10a75d190..025836ce2 100644 --- a/include/xtensor/utils/xexception.hpp +++ b/include/xtensor/utils/xexception.hpp @@ -10,7 +10,6 @@ #ifndef XTENSOR_EXCEPTION_HPP #define XTENSOR_EXCEPTION_HPP -#include #include #include #include diff --git a/include/xtensor/utils/xutils.hpp b/include/xtensor/utils/xutils.hpp index 2e97f67db..e93d8e688 100644 --- a/include/xtensor/utils/xutils.hpp +++ b/include/xtensor/utils/xutils.hpp @@ -12,8 +12,6 @@ #include #include -#include -#include #include #include #include @@ -30,12 +28,6 @@ #include "../core/xtensor_config.hpp" -#if (defined(_MSC_VER) && _MSC_VER >= 1910) -#define NOEXCEPT(T) -#else -#define NOEXCEPT(T) noexcept(T) -#endif - namespace xt { /**************** @@ -55,7 +47,7 @@ namespace xt constexpr decltype(auto) argument(Args&&... args) noexcept; template - R apply(std::size_t index, F&& func, const std::tuple& s) NOEXCEPT(noexcept(func(std::get<0>(s)))); + R apply(std::size_t index, F&& func, const std::tuple& s) noexcept(noexcept(func(std::get<0>(s)))); template void nested_copy(T&& iter, const S& s); @@ -200,7 +192,7 @@ namespace xt * accumulate implementation * *****************************/ - /// @cond DOXYGEN_INCLUDE_NOEXCEPT + /// @cond DOXYGEN_INCLUDE_noexcept namespace detail { @@ -268,8 +260,8 @@ namespace xt ************************/ template - inline R apply(std::size_t index, F&& func, const std::tuple& s) - NOEXCEPT(noexcept(func(std::get<0>(s)))) + inline R + apply(std::size_t index, F&& func, const std::tuple& s) noexcept(noexcept(func(std::get<0>(s)))) { XTENSOR_ASSERT(sizeof...(S) > index); return std::apply( @@ -617,34 +609,6 @@ namespace xt template concept iterator_concept = is_iterator::value; - /******************************************** - * xtrivial_default_construct implemenation * - ********************************************/ - -#if defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE >= 7 -// has_trivial_default_constructor has not been available since libstdc++-7. -#define XTENSOR_GLIBCXX_USE_CXX11_ABI 1 -#else -#if defined(_GLIBCXX_USE_CXX11_ABI) -#if _GLIBCXX_USE_CXX11_ABI || (defined(_GLIBCXX_USE_DUAL_ABI) && !_GLIBCXX_USE_DUAL_ABI) -#define XTENSOR_GLIBCXX_USE_CXX11_ABI 1 -#endif -#endif -#endif - -#if !defined(__GNUG__) || defined(_LIBCPP_VERSION) || defined(XTENSOR_GLIBCXX_USE_CXX11_ABI) - - template - using xtrivially_default_constructible = std::is_trivially_default_constructible; - -#else - - template - using xtrivially_default_constructible = std::has_trivial_default_constructor; - -#endif -#undef XTENSOR_GLIBCXX_USE_CXX11_ABI - /************************* * conditional type cast * *************************/ diff --git a/include/xtensor/views/index_mapper.hpp b/include/xtensor/views/index_mapper.hpp new file mode 100644 index 000000000..d445ac1ec --- /dev/null +++ b/include/xtensor/views/index_mapper.hpp @@ -0,0 +1,551 @@ +/*************************************************************************** + * Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht * + * Copyright (c) QuantStack * + * * + * Distributed under the terms of the BSD 3-Clause License. * + * * + * The full license is in the file LICENSE, distributed with this software. * + ****************************************************************************/ + +#ifndef XTENSOR_INDEX_MAPPER_HPP +#define XTENSOR_INDEX_MAPPER_HPP + +#include + +#include "xview.hpp" + +namespace xt +{ + + template + struct index_mapper; + + /** + * @enum access_t + * @brief Defines the access policy for the underlying container. + */ + enum class access_t + { + SAFE, ///< Use .at() accessor (bounds checked). + UNSAFE ///< Use operator() accessor (no bounds checking). + }; + + /** + * @class index_mapper + * @brief A helper class for mapping indices between views and their underlying containers. + * + * The `index_mapper` class provides functionality to convert indices from a view's coordinate system + * to the corresponding indices in the underlying container. This is particularly useful for views + * that contain integral slices (fixed indices), as these slices reduce the dimensionality of the view. + * + * @tparam UndefinedView The primary template parameter, specialized for `xt::xview` types. + * + * @note This class is specialized for `xt::xview` types only. + * Other view types will trigger a compilation error. + * + * @example + * @code + * xt::xarray a = xt::arange(24).reshape({2, 3, 4}); + * auto view1 = xt::view(a, 1, xt::all(), xt::all()); // Fixed first dimension + * index_mapper mapper; + * + * // Map view indices (i,j) to container indices (1,i,j) + * double val = mapper.map(a, view1, 0, 0); // Returns a(1, 0, 0) + * double val2 = mapper.map(a, view1, 1, 2); // Returns a(1, 1, 2) + * @endcode + */ + template + class index_mapper> + { + public: + + /// @brief The view type this mapper works with + using view_type = xt::xview; + + /// @brief Reference type of the underlying view. + using reference = typename xt::xview::reference; + + /// @brief Const reference type of the underlying view. + using const_reference = typename xt::xview::const_reference; + + /// @brief Total number of explicitly passed slices in the view + static constexpr size_t n_slices = sizeof...(Slices); + + /// @brief Number of slices that are integral constants (fixed indices) + static constexpr size_t nb_integral_slices = (std::is_integral_v + ...); + + /// @brief Number of slices that are xt::newaxis (insert a dimension) + static constexpr size_t nb_new_axis_slices = (xt::detail::is_newaxis_v + ...); + + /** + * Compute how many indices are needed to address the underlying container + * when given N indices in the view. + */ + template + static constexpr size_t n_indices_full_v = size_t(sizeof...(Indices) + nb_integral_slices); + + /** + * @brief Map view indices to container reference using UNSAFE access. + * @param container The source container. + * @param view The view defining the mapping. + * @param indices The indices in view-space. + * @return Reference to the element in the container. + */ + template + reference map(UnderlyingContainer& container, const view_type& view, const Indices... indices) const; + + /** + * @brief Map view indices to container const_reference using UNSAFE access. + * @param container The source container. + * @param view The view defining the mapping. + * @param indices The indices in view-space. + * @return Reference to the element in the container. + */ + template + const_reference + cmap(const UnderlyingContainer& container, const view_type& view, const Indices... indices) const; + + /** + * @brief Map view indices to container reference using SAFE access. + * @param container The source container. + * @param view The view defining the mapping. + * @param indices The indices in view-space. + * @return Reference to the element in the container. + */ + template + reference map_at(UnderlyingContainer& container, const view_type& view, const Indices... indices) const; + + /** + * @brief Map view indices to container const_reference using SAFE access. + * @param container The source container. + * @param view The view defining the mapping. + * @param indices The indices in view-space. + * @return Reference to the element in the container. + */ + template + const_reference + cmap_at(const UnderlyingContainer& container, const view_type& view, const Indices... indices) const; + + /// @brief Return the dimensionality of the view + size_t dimension(const UnderlyingContainer& container) const; + + private: + + /// @brief Alias for selecting reference type based on const-correctness. + template + using conditional_reference = std::conditional_t; + + /// @brief Helper type alias for the I-th slice type + template + using slice_type = std::tuple_element_t>; + + /// @brief True if the I-th slice is an integral slice (fixed index) + template + static consteval bool is_slice_integral(); + + /// @brief True if the I-th slice is a newaxis slice + template + static consteval bool is_slice_new_axis(); + + /** + * Helper metafunction to build an index_sequence that skips + * newaxis slices. + * + * The resulting sequence contains only the indices that + * correspond to real container dimensions. + */ + template + struct indices_sequence_helper + { + // we add the current axis + using not_new_axis_type = typename indices_sequence_helper::type; + + // we skip the current axis + using new_axis_type = typename indices_sequence_helper::type; + + // NOTE: is_slice_new_axis works even if first >= sizeof...(Slices) + using type = std::conditional_t(), new_axis_type, not_new_axis_type>; + }; + + /// @brief Base case: recursion termination + template + struct indices_sequence_helper + { + using type = std::index_sequence; + }; + + ///< @brief Index sequence of non-newaxis slices + template + using indices_sequence = indices_sequence_helper<0, bound>::type; + + /** + * @brief Maps an index for a specific slice to the corresponding index in the underlying container. + * + * For integral slices (fixed indices), returns the fixed index value. + * For non-integral slices (like `xt::all()`), applies the slice transformation to the index. + * + * @tparam I The slice index to map. + * @tparam Index Type of the index (must be integral). + * @param view The view object containing slice information. + * @param i The index within the slice to map. + * @return size_t The mapped index in the underlying container. + * + * @throws Assertion failure if `i != 0` for integral slices. + * @throws Assertion failure if `i >= slice.size()` for non-integral slices. + */ + template + size_t map_ith_index(const view_type& view, const Index i) const; + + /** + * @brief Main recursion/logic handler for mapping operations. + * Handles dimension dropping if the provided index count exceeds view dimensionality. + * + * @tparam IS_CONST Boolean flag; true if the operation is on a const container. + * @tparam ACCESS The access policy (SAFE for .at(), UNSAFE for operator()). + * @param is_const Tag used for compile-time dispatching of const-correctness. + * @param container The underlying container (xarray, xtensor, etc.) being accessed. + * @param access Tag used for compile-time dispatching of the access method. + * @param view The xview instance that defines the coordinate transformation. + * @param firstIndice The current leading index in the coordinate pack. + * @param otherIndices The remaining indices in the coordinate pack. + */ + template + conditional_reference map_main( + std::bool_constant /* is_const */, + std::conditional_t container, + std::integral_constant /* access */, + const view_type& view, + const FirstIndice firstIndice, + const OtherIndices... otherIndices + ) const; + + /** + * @brief Base case for map_main recursion, where no indices is supplied and assumes (0, 0, ...). + * + * @tparam IS_CONST Boolean flag; true if the operation is on a const container. + * @tparam ACCESS The access policy (SAFE for .at(), UNSAFE for operator()). + * @param is_const Tag used for compile-time dispatching of const-correctness. + * @param container The underlying container (xarray, xtensor, etc.) being accessed. + * @param access Tag used for compile-time dispatching of the access method. + * @param view The xview instance that defines the coordinate transformation. + */ + template + conditional_reference map_main( + std::bool_constant /* is_const */, + std::conditional_t container, + std::integral_constant /* access */, + const view_type& view + ) const; + + /** + * @brief Maps all indices and accesses the container. + * + * @tparam IS_CONST Boolean flag for const-correctness. + * @tparam ACCESS The access policy (SAFE or UNSAFE). + * @tparam n_indices The size of the index array (calculated from view/container info). + * @tparam Is A pack of indices `0, 1, ..., n-1` used to unroll the mapping loop. + * @param is_const Tag for const-correctness dispatch. + * @param container The underlying container being accessed. + * @param access Tag for access method dispatch. + * @param view The xview instance providing the slice transformations. + * @param is_seq An index sequence used to drive the parameter pack expansion. + * @param indices An array containing the view-space indices to be mapped. + */ + template + conditional_reference map_all_indices( + std::bool_constant /* is_const */, + std::conditional_t container, + std::integral_constant /* access */, + const view_type& view, + std::index_sequence /* is_seq */, + const std::array& indices + ) const; + + /// @brief Expand view indices into a full index array, inserting dummy indices for integral slices + template + std::array> get_indices_full(const Indices... indices) const; + }; + + /******************************* + * index_mapper implementation * + *******************************/ + + template + template + consteval bool index_mapper>::is_slice_integral() + { + if constexpr (I < sizeof...(Slices)) + { + return std::is_integral_v>; + } + else + { + return false; + } + } + + template + template + consteval bool index_mapper>::is_slice_new_axis() + { + if constexpr (I < sizeof...(Slices)) + { + return xt::detail::is_newaxis_v>; + } + else + { + return false; + } + } + + template + template + auto + index_mapper>::get_indices_full(const Indices... indices) const + -> std::array> + { + constexpr size_t n_indices_full = n_indices_full_v; + + std::array args{size_t(indices)...}; + std::array args_full; + + const auto fill_args_full = [&args_full, &args](std::index_sequence) + { + auto it = std::cbegin(args); + + ((args_full[Is] = (is_slice_integral()) ? size_t(0) : *it++), ...); + }; + + fill_args_full(std::make_index_sequence{}); + + return args_full; + } + + template + template + auto index_mapper>::map( + UnderlyingContainer& container, + const view_type& view, + const Indices... indices + ) const -> reference + { + return map_main( + std::false_type{}, + container, + std::integral_constant{}, + view, + indices... + ); + } + + template + template + auto index_mapper>::cmap( + const UnderlyingContainer& container, + const view_type& view, + const Indices... indices + ) const -> const_reference + { + return map_main( + std::true_type{}, + container, + std::integral_constant{}, + view, + indices... + ); + } + + template + template + auto index_mapper>::map_at( + UnderlyingContainer& container, + const view_type& view, + const Indices... indices + ) const -> reference + { + return map_main( + std::false_type{}, + container, + std::integral_constant{}, + view, + indices... + ); + } + + template + template + auto index_mapper>::cmap_at( + const UnderlyingContainer& container, + const view_type& view, + const Indices... indices + ) const -> const_reference + { + return map_main( + std::true_type{}, + container, + std::integral_constant{}, + view, + indices... + ); + } + + template + template + auto index_mapper>::map_main( + std::bool_constant is_const, + std::conditional_t container, + std::integral_constant access, + const view_type& view, + const FirstIndice firstIndice, + const OtherIndices... otherIndices + ) const -> conditional_reference + { + constexpr size_t n_indices_full = n_indices_full_v; + + constexpr size_t underlying_n_dimensions = static_cast( + xt::static_dimension::shape_type>::value + ); + + // If there is too many indices, we need to drop the first ones. + // If the number of dimensions of the underlying container is known at compile time we can drop them + // at compile time Else a runtime-test is requires, which, breaks vectorization. + // I don't know if we can do it in another way. + + if constexpr (underlying_n_dimensions != size_t(-1)) + { + // the number of dimensions of the underlying container is known at compile time. + constexpr size_t n_dimensions = underlying_n_dimensions - nb_integral_slices + nb_new_axis_slices; + + // we can perform compile time checks + if constexpr (1 + sizeof...(OtherIndices) > n_dimensions) + { + return map_main(is_const, container, access, view, otherIndices...); + } + else + { + return map_all_indices( + is_const, + container, + access, + view, + indices_sequence{}, + get_indices_full(firstIndice, otherIndices...) + ); + } + } + else + { + // we need execution time checks + if (1 + sizeof...(OtherIndices) > dimension(container)) + { + return map_main(is_const, container, access, view, otherIndices...); + } + else + { + return map_all_indices( + is_const, + container, + access, + view, + indices_sequence{}, + get_indices_full(firstIndice, otherIndices...) + ); + } + } + } + + template + template + auto index_mapper>::map_main( + std::bool_constant is_const, + std::conditional_t container, + std::integral_constant access, + const view_type& view + ) const -> conditional_reference + { + // Work around compilers failing to deduce nb_integral_slices as a non-type template argument inline + // (error: use of variable template 'n_indices_full_v' requires template arguments) + constexpr size_t n_indices_full = nb_integral_slices; + + return map_all_indices( + is_const, + container, + access, + view, + indices_sequence{}, + get_indices_full() + ); + } + + template + template + auto index_mapper>::map_all_indices( + std::bool_constant /* is_const */, + std::conditional_t container, + std::integral_constant /* access */, + const view_type& view, + std::index_sequence /* is_seq */, + const std::array& indices + ) const -> conditional_reference + { + if constexpr (ACCESS == access_t::SAFE) + { + return container.at(map_ith_index(view, indices[Is])...); + } + else + { + return container(map_ith_index(view, indices[Is])...); + } + } + + template + template + auto + index_mapper>::map_ith_index(const view_type& view, const Index i) const + -> size_t + { + if constexpr (I < sizeof...(Slices)) + { + // if the slice is explicitly specified, use it + using current_slice = std::tuple_element_t>; + + static_assert(not xt::detail::is_newaxis_v); + + const auto& slice = std::get(view.slices()); + + if constexpr (std::is_integral_v) + { + assert(i == 0); + return size_t(slice); + } + else if constexpr (xt::detail::is_xall_slice>::value) + { + return size_t(i); + } + else + { + using slice_size_type = typename current_slice::size_type; + if constexpr (ACCESS == access_t::UNSAFE) + { + assert(static_cast(i) < slice.size()); + } + return size_t(slice(static_cast(i))); + } + } + else + { + // else assume xt::all + return i; + } + } + + template + auto index_mapper>::dimension(const UnderlyingContainer& container + ) const -> size_t + { + return container.dimension() - nb_integral_slices + nb_new_axis_slices; + } + +} // namespace xt + +#endif // XTENSOR_INDEX_MAPPER_HPP diff --git a/include/xtensor/views/xaxis_iterator.hpp b/include/xtensor/views/xaxis_iterator.hpp index 41da6822f..2b54748d4 100644 --- a/include/xtensor/views/xaxis_iterator.hpp +++ b/include/xtensor/views/xaxis_iterator.hpp @@ -235,7 +235,7 @@ namespace xt /** * Checks equality of the xaxis_slice_iterator and \c rhs. * - * @param + * @param rhs iterator to compare with * @return true if the iterators are equivalent, false otherwise */ template diff --git a/include/xtensor/views/xbroadcast.hpp b/include/xtensor/views/xbroadcast.hpp index 1626faaa3..68b11d443 100644 --- a/include/xtensor/views/xbroadcast.hpp +++ b/include/xtensor/views/xbroadcast.hpp @@ -13,8 +13,6 @@ #include #include #include -#include -#include #include #include @@ -98,25 +96,25 @@ namespace xt *****************************/ template - XTENSOR_CONSTEXPR_RETURN auto linear_begin(xbroadcast& c) noexcept + constexpr auto linear_begin(xbroadcast& c) noexcept { return linear_begin(c.expression()); } template - XTENSOR_CONSTEXPR_RETURN auto linear_end(xbroadcast& c) noexcept + constexpr auto linear_end(xbroadcast& c) noexcept { return linear_end(c.expression()); } template - XTENSOR_CONSTEXPR_RETURN auto linear_begin(const xbroadcast& c) noexcept + constexpr auto linear_begin(const xbroadcast& c) noexcept { return linear_begin(c.expression()); } template - XTENSOR_CONSTEXPR_RETURN auto linear_end(const xbroadcast& c) noexcept + constexpr auto linear_end(const xbroadcast& c) noexcept { return linear_end(c.expression()); } diff --git a/include/xtensor/views/xfunctor_view.hpp b/include/xtensor/views/xfunctor_view.hpp index 9f2daf712..1c76a91b5 100644 --- a/include/xtensor/views/xfunctor_view.hpp +++ b/include/xtensor/views/xfunctor_view.hpp @@ -29,7 +29,7 @@ namespace xt { /** - * @defgroup xt_xfunctor_view + * @defgroup xt_xfunctor_view Functor view * * Chunked array container. * Defined in ``xtensor/xfunctor_view.hpp`` diff --git a/include/xtensor/views/xindex_view.hpp b/include/xtensor/views/xindex_view.hpp index 8b911afb8..6701d3bf3 100644 --- a/include/xtensor/views/xindex_view.hpp +++ b/include/xtensor/views/xindex_view.hpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -505,7 +504,8 @@ namespace xt /** * Returns a reference to the element at the specified position in the xindex_view. * @param first iterator starting the sequence of indices - * The number of indices in the sequence should be equal to or greater 1. + * @param last iterator ending the sequence of indices (not used, only for compatibility with xexpression + * operator()) The number of indices in the sequence should be equal to or greater 1. */ template template @@ -517,7 +517,8 @@ namespace xt /** * Returns a reference to the element at the specified position in the xindex_view. * @param first iterator starting the sequence of indices - * The number of indices in the sequence should be equal to or greater 1. + * @param last iterator ending the sequence of indices (not used, only for compatibility with xexpression + * operator()) The number of indices in the sequence should be equal to or greater 1. */ template template diff --git a/include/xtensor/views/xslice.hpp b/include/xtensor/views/xslice.hpp index a7a4dae6e..d2142fb82 100644 --- a/include/xtensor/views/xslice.hpp +++ b/include/xtensor/views/xslice.hpp @@ -21,16 +21,6 @@ #include "../core/xtensor_config.hpp" #include "../utils/xutils.hpp" -#ifndef XTENSOR_CONSTEXPR -#if (defined(_MSC_VER) || __GNUC__ < 8) -#define XTENSOR_CONSTEXPR inline -#define XTENSOR_GLOBAL_CONSTEXPR static const -#else -#define XTENSOR_CONSTEXPR constexpr -#define XTENSOR_GLOBAL_CONSTEXPR constexpr -#endif -#endif - namespace xt { @@ -70,11 +60,11 @@ namespace xt * slice tags * **************/ -#define DEFINE_TAG_CONVERSION(NAME) \ - template \ - XTENSOR_CONSTEXPR NAME convert() const noexcept \ - { \ - return NAME(); \ +#define DEFINE_TAG_CONVERSION(NAME) \ + template \ + constexpr NAME convert() const noexcept \ + { \ + return NAME(); \ } struct xall_tag @@ -494,7 +484,7 @@ namespace xt template inline auto drop(T&& indices) { - if constexpr (xtl::is_integral::value) + if constexpr (xtl::is_integral>::value) { using slice_type = xdrop_slice; using container_type = typename slice_type::container_type; @@ -642,12 +632,12 @@ namespace xt std::ptrdiff_t rng[3]; // = { 0, 0, 0 }; }; - XTENSOR_CONSTEXPR xtuph get_tuph_or_val(std::ptrdiff_t /*val*/, std::true_type) + constexpr xtuph get_tuph_or_val(std::ptrdiff_t /*val*/, std::true_type) { return xtuph(); } - XTENSOR_CONSTEXPR std::ptrdiff_t get_tuph_or_val(std::ptrdiff_t val, std::false_type) + constexpr std::ptrdiff_t get_tuph_or_val(std::ptrdiff_t val, std::false_type) { return val; } @@ -655,7 +645,7 @@ namespace xt template struct rangemaker { - XTENSOR_CONSTEXPR operator xrange_adaptor() + constexpr operator xrange_adaptor() { return xrange_adaptor( {get_tuph_or_val(rng[0], std::is_same()), @@ -670,7 +660,7 @@ namespace xt template struct rangemaker { - XTENSOR_CONSTEXPR operator xrange_adaptor() + constexpr operator xrange_adaptor() { return xrange_adaptor( {get_tuph_or_val(rng[0], std::is_same()), @@ -683,7 +673,7 @@ namespace xt }; template - XTENSOR_CONSTEXPR auto operator|(const rangemaker& rng, const std::ptrdiff_t& t) + constexpr auto operator|(const rangemaker& rng, const std::ptrdiff_t& t) { auto nrng = rangemaker({rng.rng[0], rng.rng[1], rng.rng[2]}); nrng.rng[sizeof...(OA)] = t; @@ -691,17 +681,17 @@ namespace xt } template - XTENSOR_CONSTEXPR auto operator|(const rangemaker& rng, const xt::placeholders::xtuph& /*t*/) + constexpr auto operator|(const rangemaker& rng, const xt::placeholders::xtuph& /*t*/) { auto nrng = rangemaker({rng.rng[0], rng.rng[1], rng.rng[2]}); return nrng; } - XTENSOR_GLOBAL_CONSTEXPR xtuph _{}; - XTENSOR_GLOBAL_CONSTEXPR rangemaker<> _r = rangemaker<>({{0, 0, 0}}); - XTENSOR_GLOBAL_CONSTEXPR xall_tag _a{}; - XTENSOR_GLOBAL_CONSTEXPR xnewaxis_tag _n{}; - XTENSOR_GLOBAL_CONSTEXPR xellipsis_tag _e{}; + constexpr xtuph _{}; + constexpr rangemaker<> _r = rangemaker<>({{0, 0, 0}}); + constexpr xall_tag _a{}; + constexpr xnewaxis_tag _n{}; + constexpr xellipsis_tag _e{}; } inline auto xnone() @@ -718,7 +708,7 @@ namespace xt type operator()(T t) { - return (xtl::is_integral::value) ? static_cast(t) : t; + return static_cast(t); } }; @@ -780,7 +770,7 @@ namespace xt { if constexpr (is_xslice::value) { - return slice.size(); + return static_cast(slice.size()); } else { @@ -797,7 +787,7 @@ namespace xt { if constexpr (is_xslice::value) { - return slice.step_size(idx); + return static_cast(slice.step_size(idx)); } else { @@ -810,7 +800,7 @@ namespace xt { if constexpr (is_xslice::value) { - return slice.step_size(idx, n); + return static_cast(slice.step_size(idx, n)); } else { @@ -828,7 +818,7 @@ namespace xt if constexpr (is_xslice::value) { using ST = typename S::size_type; - return slice(static_cast(i)); + return static_cast(slice(static_cast(i))); } else { @@ -1598,6 +1588,4 @@ namespace xt } } -#undef XTENSOR_CONSTEXPR - #endif diff --git a/include/xtensor/views/xstrided_view.hpp b/include/xtensor/views/xstrided_view.hpp index f0fda4214..3faff70c8 100644 --- a/include/xtensor/views/xstrided_view.hpp +++ b/include/xtensor/views/xstrided_view.hpp @@ -12,7 +12,6 @@ #include #include -#include #include #include #include @@ -834,7 +833,7 @@ namespace xt if (iter != std::end(shape)) { const auto total = std::accumulate(shape.cbegin(), shape.cend(), -1, std::multiplies{}); - const auto missing_dimension = size / total; + const auto missing_dimension = size / static_cast(total); (*iter) = static_cast(missing_dimension); } } diff --git a/include/xtensor/views/xview.hpp b/include/xtensor/views/xview.hpp index ea546d4ff..025df0ae4 100644 --- a/include/xtensor/views/xview.hpp +++ b/include/xtensor/views/xview.hpp @@ -1645,8 +1645,9 @@ namespace xt { if constexpr (lesser_condition::value) { - return sliced_access(I) + newaxis_count_before(I + 1)>( - std::get(I + 1)>(m_slices), + constexpr size_type slice_index = newaxis_skip(I); + return sliced_access(slice_index)>( + std::get(m_slices), args... ); } diff --git a/include/xtensor/views/xview_utils.hpp b/include/xtensor/views/xview_utils.hpp index f74eb07e2..696fa8762 100644 --- a/include/xtensor/views/xview_utils.hpp +++ b/include/xtensor/views/xview_utils.hpp @@ -149,6 +149,9 @@ namespace xt { }; + template + constexpr bool is_newaxis_v = is_newaxis::value; + template struct newaxis_count_impl { diff --git a/test/test_xassign.cpp b/test/test_xassign.cpp index c6ca46305..3535b4886 100644 --- a/test/test_xassign.cpp +++ b/test/test_xassign.cpp @@ -15,6 +15,7 @@ #include "xtensor/containers/xtensor.hpp" #include "xtensor/core/xassign.hpp" #include "xtensor/core/xnoalias.hpp" +#include "xtensor/misc/xmanipulation.hpp" #include "test_common.hpp" #include "test_common_macros.hpp" @@ -167,4 +168,10 @@ namespace xt EXPECT_EQ(a.shape(1), 3); } } + + TEST(xassign, fixed_dimension_mismatch) + { + auto a = xt::xtensor::from_shape({2}); + XT_ASSERT_THROW(a = xt::expand_dims(a, 0), std::runtime_error); + } } diff --git a/test/test_xbuilder.cpp b/test/test_xbuilder.cpp index 73d952b6f..d35847a8e 100644 --- a/test/test_xbuilder.cpp +++ b/test/test_xbuilder.cpp @@ -424,6 +424,21 @@ namespace xt ASSERT_TRUE(arange(8) == w1); ASSERT_TRUE(w1 == w2); } + + TEST(xbuilder, vstack_fixed) + { + xtensor_fixed> a = {{1.f, 2.f}}; + xtensor_fixed> b = {{3.f, 4.f}, {5.f, 6.f}}; + + auto c = vstack(xtuple(a, b)); + + using expected_shape_t = fixed_shape<3, 2>; + ASSERT_EQ(expected_shape_t{}, c.shape()); + EXPECT_EQ(1.f, c(0, 0)); + EXPECT_EQ(2.f, c(0, 1)); + EXPECT_EQ(3.f, c(1, 0)); + EXPECT_EQ(6.f, c(2, 1)); + } #endif TEST(xbuilder, access) diff --git a/test/test_xcsv.cpp b/test/test_xcsv.cpp index 584d17345..00fcd4519 100644 --- a/test/test_xcsv.cpp +++ b/test/test_xcsv.cpp @@ -7,10 +7,8 @@ * The full license is in the file LICENSE, distributed with this software. * ****************************************************************************/ -#include #include -#include "xtensor/core/xmath.hpp" #include "xtensor/io/xcsv.hpp" #include "xtensor/io/xio.hpp" @@ -18,6 +16,19 @@ namespace xt { + TEST(xcsv, load_1D) + { + const std::string source = "1, 2, 3, 4"; + + std::stringstream source_stream(source); + + const xtensor res = load_csv(source_stream); + + const xtensor exp{{1, 2, 3, 4}}; + + ASSERT_TRUE(all(equal(res, exp))); + } + TEST(xcsv, load_double) { std::string source = "1.0, 2.0, 3.0, 4.0\n" @@ -32,6 +43,20 @@ namespace xt ASSERT_TRUE(all(equal(res, exp))); } + TEST(xcsv, load_binary_matrix) + { + const std::string source = "1,0,1\n" + "0,1,1"; + + std::stringstream source_stream(source); + + const xtensor res = load_csv(source_stream); + + const xtensor exp{{1, 0, 1}, {0, 1, 1}}; + + ASSERT_TRUE(all(equal(res, exp))); + } + TEST(xcsv, load_double_with_options) { std::string source = "A B C D\n" @@ -49,6 +74,66 @@ namespace xt ASSERT_TRUE(all(equal(res, exp))); } + TEST(xcsv, load_string_trims_cells) + { + const std::string source = " alpha , beta,gamma \n delta, epsilon , zeta "; + + std::stringstream source_stream(source); + + const xtensor res = load_csv(source_stream); + + ASSERT_EQ(res.shape()[0], std::size_t(2)); + ASSERT_EQ(res.shape()[1], std::size_t(3)); + ASSERT_EQ(res(0, 0), "alpha"); + ASSERT_EQ(res(0, 1), "beta"); + ASSERT_EQ(res(0, 2), "gamma"); + ASSERT_EQ(res(1, 0), "delta"); + ASSERT_EQ(res(1, 1), "epsilon"); + ASSERT_EQ(res(1, 2), "zeta"); + } + + TEST(xcsv, load_file_uses_config) + { + const std::string source = "metadata\n" + "//ignore this row\n" + "1;2;3\n" + "4;5;6\n" + "7;8;9"; + + std::stringstream source_stream(source); + xcsv_config config; + config.delimiter = ';'; + config.skip_rows = 1; + config.max_rows = 2; + config.comments = "//"; + + xtensor res = {{0, 0, 0}}; + load_file(source_stream, res, config); + + const xtensor exp{{1, 2, 3}, {4, 5, 6}}; + + ASSERT_TRUE(all(equal(res, exp))); + } + + TEST(xcsv, load_inconsistent_rows_throws) + { + const std::string source = "1,2,3\n4,5"; + + std::stringstream source_stream(source); + + XT_EXPECT_THROW(load_csv(source_stream), std::runtime_error); + } + + TEST(xcsv, dump_1D) + { + xtensor data{{1.0, 2.0, 3.0, 4.0}}; + + std::stringstream res; + + dump_csv(res, data); + ASSERT_EQ("1,2,3,4\n", res.str()); + } + TEST(xcsv, dump_double) { xtensor data{{1.0, 2.0, 3.0, 4.0}, {10.0, 12.0, 15.0, 18.0}}; @@ -58,4 +143,47 @@ namespace xt dump_csv(res, data); ASSERT_EQ("1,2,3,4\n10,12,15,18\n", res.str()); } + + TEST(xcsv, dump_file_matches_dump_csv) + { + xtensor data{{1, 2}, {3, 4}}; + std::stringstream res; + xcsv_config config; + + dump_file(res, data, config); + + ASSERT_EQ("1,2\n3,4\n", res.str()); + } + + TEST(xcsv, dump_higher_dimension_throws) + { + xtensor data{{{1.0, 2.0}, {3.0, 4.0}}}; + std::stringstream res; + + XT_EXPECT_THROW(dump_csv(res, data), std::runtime_error); + } + + TEST(xcsv, dump_with_config) + { + xtensor data{{1.0, 2.0, 3.0, 4.0}, {10.0, 12.0, 15.0, 18.0}}; + + std::stringstream res; + + xcsv_config config; + config.delimiter = ' '; + dump_csv(res, data, config); + ASSERT_EQ("1 2 3 4\n10 12 15 18\n", res.str()); + } + + TEST(xcsv, dump_file_with_config) + { + xtensor data{{1.0, 2.0, 3.0, 4.0}, {10.0, 12.0, 15.0, 18.0}}; + + std::stringstream res; + + xcsv_config config; + config.delimiter = ';'; + dump_file(res, data, config); + ASSERT_EQ("1;2;3;4\n10;12;15;18\n", res.str()); + } } diff --git a/test/test_xdatesupport.cpp b/test/test_xdatesupport.cpp index 6c5894be7..33cd7ee46 100644 --- a/test/test_xdatesupport.cpp +++ b/test/test_xdatesupport.cpp @@ -55,8 +55,6 @@ namespace xt EXPECT_EQ((result < result2), expected); } -// need to wait until the system clock on Windows catches up with Linux -#ifndef _MSC_VER TEST(xdate, date_arange) { xarray tarr = xt::arange( @@ -66,7 +64,6 @@ namespace xt ); EXPECT_TRUE(tarr.storage().back() > tarr.storage().front()); } -#endif TEST(xdate, xfunction) { diff --git a/test/test_xfixed.cpp b/test/test_xfixed.cpp index b4af22af2..3a6e8181b 100644 --- a/test/test_xfixed.cpp +++ b/test/test_xfixed.cpp @@ -22,6 +22,7 @@ #include "xtensor/core/xnoalias.hpp" #include "xtensor/io/xio.hpp" #include "xtensor/misc/xmanipulation.hpp" +#include "xtensor/views/xview.hpp" #include "test_common_macros.hpp" @@ -306,6 +307,13 @@ namespace xt using tiny_tensor = xtensor_fixed, layout_type::row_major, false>; EXPECT_GT(sizeof(fixed_tensor), sizeof(tiny_tensor)); } + + TEST(xtensor_fixed, iterators) + { + auto arr = xt::xtensor({5, 5, 5, 5, 5}); + auto fixed_arr = xt::xtensor_fixed>{1, 2, 3}; + xt::view(arr, xt::range(0, 3)) = fixed_arr + 1; + } } #endif diff --git a/test/test_xfunction.cpp b/test/test_xfunction.cpp index 05c869517..ddf08758c 100644 --- a/test/test_xfunction.cpp +++ b/test/test_xfunction.cpp @@ -10,6 +10,7 @@ #include "xtensor/containers/xarray.hpp" #include "xtensor/containers/xfixed.hpp" #include "xtensor/containers/xtensor.hpp" +#include "xtensor/core/xvectorize.hpp" #include "xtensor/generators/xrandom.hpp" #include "xtensor/views/xview.hpp" @@ -193,6 +194,21 @@ namespace xt } } + TEST(xfunction, access_dangling_reference) + { + auto values_original = xt::xtensor{1., 2., 3.}; + auto indexes = xt::arange(values_original.size()); + auto map = [&](const size_t& index) -> auto& + { + return values_original[index]; + }; + auto values_mapped = xt::vectorize(map)(indexes); + for (size_t i = 0; i < values_original.size(); ++i) + { + EXPECT_TRUE(&(values_mapped[i]) == &(values_original[i])); + } + } + TEST(xfunction, unchecked) { xfunction_features f; @@ -480,21 +496,13 @@ namespace xt auto f1 = 2.0 * x; auto f2 = x * 2.0 * x; iterator_tester(f1); -// For an unknown reason, MSVC cannot correctly generate -// linear_cbegin() for a function of function. Moreover, -// a simple SFINAE deduction like has_linear_iterator -// harcoded and tested here fails (while it builds fine in any -// empty project) -#ifndef _MSC_VER iterator_tester(f2); iterator_tester(x * y * 3.0); iterator_tester(5.0 + x * y * 3.0); iterator_tester(x * y * z); iterator_tester(x / y / z); -#endif } -#ifndef _MSC_VER TEST(xfunction, xfunction_in_xfunction) { using Point3 = xt::xtensor_fixed>; @@ -507,5 +515,4 @@ namespace xt xtensor res{r1, r2, r3}; EXPECT_EQ(c, res); } -#endif } diff --git a/test/test_xmasked_view.cpp b/test/test_xmasked_view.cpp index 14613d169..1c7f9cb4d 100644 --- a/test/test_xmasked_view.cpp +++ b/test/test_xmasked_view.cpp @@ -7,6 +7,8 @@ * The full license is in the file LICENSE, distributed with this software. * ****************************************************************************/ +#include + #include "xtensor/io/xio.hpp" #include "xtensor/optional/xoptional_assembly.hpp" #include "xtensor/views/xmasked_view.hpp" @@ -210,6 +212,21 @@ namespace xt EXPECT_EQ(data, expected2); } + TEST(xmasked_view, non_optional_data_stream) + { + const xarray data = {{1., 5., 3.}, {4., 5., 6.}}; + const xarray mask = {{true, false, false}, {false, true, false}}; + + const auto masked_data = masked_view(data, mask); + + std::stringstream out; + out << masked_data; + + const std::string expected = "{{ 1, masked, masked},\n" + " {masked, 5, masked}}"; + EXPECT_EQ(out.str(), expected); + } + TEST(xmasked_view, assign) { xarray data = {{1., -2., 3.}, {4., 5., -6.}, {7., 8., -9.}}; diff --git a/test/test_xmath.cpp b/test/test_xmath.cpp index 17bc57895..57771859b 100644 --- a/test/test_xmath.cpp +++ b/test/test_xmath.cpp @@ -222,6 +222,15 @@ namespace xt EXPECT_EQ(res1, clip(opt_a, 2.0, 4.0)); } + TEST(xmath, clip_amin_greater_than_amax) + { + // NumPy-compatible behavior: when a_min > a_max, all values + // are set to a_max (the hi bound). + const xarray arr = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + const xarray expected = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + EXPECT_EQ(expected, clip(arr, 8, 1)); + } + TEST(xmath, sign) { shape_type shape = {3, 2}; @@ -969,4 +978,51 @@ namespace xt EXPECT_TRUE(xt::allclose(expected, unwrapped)); } } + + // Test for GitHub issue #2871: Proper handling of intermediate results + // This test documents the correct way to use reducers with keep_dims + // when intermediate expressions are needed. + TEST(xmath, issue_2871_intermediate_result_handling) + { + // This test verifies the correct pattern for using reducers with + // intermediate results. Returning a lazy expression from a function can lead + // to dangling references — only the returned expression must be evaluated. + + // The CORRECT way: reducer results must be evaluated; element-wise lazy + // expressions are safe to leave as auto + auto logSoftmax_correct = [](const xt::xtensor& matrix) + { + xt::xtensor maxVals = xt::amax(matrix, {1}, xt::keep_dims); + auto shifted = matrix - maxVals; + auto expVals = xt::exp(shifted); + xt::xtensor sumExp = xt::sum(expVals, {1}, xt::keep_dims); + return xt::xtensor(shifted - xt::log(sumExp)); + }; + + // Alternative CORRECT way: use xt::eval for reducer results + auto logSoftmax_eval = [](const xt::xtensor& matrix) + { + auto maxVals = xt::eval(xt::amax(matrix, {1}, xt::keep_dims)); + auto shifted = matrix - maxVals; + auto expVals = xt::exp(shifted); + auto sumExp = xt::eval(xt::sum(expVals, {1}, xt::keep_dims)); + return xt::xtensor(shifted - xt::log(sumExp)); + }; + + // Test data + xt::xtensor input = {{1.0, 2.0, 3.0}, {4.0, 5.0, 6.0}}; + + // Both implementations should produce the same result + auto result1 = logSoftmax_correct(input); + auto result2 = logSoftmax_eval(input); + + EXPECT_TRUE(xt::allclose(result1, result2)); + + // Verify the result is a valid log-softmax (rows sum to 0 in log space) + // exp(log_softmax).sum(axis=1) should equal 1 + auto exp_result = xt::exp(result1); + auto row_sums = xt::sum(exp_result, {1}); + xt::xtensor expected_sums = {1.0, 1.0}; + EXPECT_TRUE(xt::allclose(row_sums, expected_sums)); + } } diff --git a/test/test_xoperation.cpp b/test/test_xoperation.cpp index fa97a0946..21a9c77bd 100644 --- a/test/test_xoperation.cpp +++ b/test/test_xoperation.cpp @@ -858,6 +858,29 @@ namespace xt EXPECT_EQ(expected1, res3); EXPECT_EQ(expected2, res4); } + + TEST_CASE("divide_4d") + { + using T4 = xt::xtensor; + using shape_type = typename T4::shape_type; + + shape_type shape = {2, 3, 2, 2}; + T4 a(shape, 4.5f); + T4 b(shape, 1.3f); + + EXPECT_EQ((a / b)(0, 0, 0, 0), a(0, 0, 0, 0) / b(0, 0, 0, 0)); + + float sb = 1.2f; + EXPECT_EQ((a / sb)(0, 0, 0, 0), a(0, 0, 0, 0) / sb); + + float sa = 4.6f; + EXPECT_EQ((sa / b)(0, 0, 0, 0), sa / b(0, 0, 0, 0)); + + // self-divide assignment: a = a / b (no zeros in b) + auto a_before = a(1, 2, 1, 1); + a = a / b; + EXPECT_EQ(a(1, 2, 1, 1), a_before / b(1, 2, 1, 1)); + } } #undef XOPERATION_TEST_TYPES diff --git a/test/test_xrandom.cpp b/test/test_xrandom.cpp index c732e2ab4..0d824a794 100644 --- a/test/test_xrandom.cpp +++ b/test/test_xrandom.cpp @@ -237,6 +237,20 @@ namespace xt #endif } + TEST(xrandom, poisson_geometric_default_type) + { + // poisson and geometric have no parameter from which T can be deduced, + // so they require a default T (= int) to be callable without explicit template arg. + auto p = random::poisson({3, 3}, 1.0); + static_assert(std::is_same::value, "poisson default T must be int"); + + auto g = random::geometric({3, 3}, 0.5); + static_assert(std::is_same::value, "geometric default T must be int"); + + auto p2 = random::poisson({3, 3}); + static_assert(std::is_same::value, "poisson default T must be int"); + } + TEST(xrandom, permutation) { xt::random::seed(123); diff --git a/test/test_xutils.cpp b/test/test_xutils.cpp index 62df3144e..e46a883c8 100644 --- a/test/test_xutils.cpp +++ b/test/test_xutils.cpp @@ -113,11 +113,7 @@ namespace xt return i; }; auto t = std::make_tuple(1, 2, 3); -#if (_MSC_VER >= 1910) - static_assert(!noexcept(apply(1, func_ne, t))); -#else static_assert(noexcept(apply(1, func_ne, t))); -#endif } TEST(utils, conditional_cast) diff --git a/test/test_xview.cpp b/test/test_xview.cpp index b2afa3c56..18449b742 100644 --- a/test/test_xview.cpp +++ b/test/test_xview.cpp @@ -28,6 +28,7 @@ #include "xtensor/generators/xbuilder.hpp" #include "xtensor/generators/xrandom.hpp" #include "xtensor/misc/xmanipulation.hpp" +#include "xtensor/views/index_mapper.hpp" #include "xtensor/views/xstrided_view.hpp" #include "xtensor/views/xview.hpp" @@ -143,6 +144,76 @@ namespace xt } } + TEST(xview_mapping, simple) + { + view_shape_type shape = {3, 4}; + xarray a(shape); + std::vector data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + std::copy(data.cbegin(), data.cend(), a.template begin()); + + auto view1 = view(a, 1, range(1, 4)); + + index_mapper mapper1; + + EXPECT_EQ(a(1, 1), mapper1.map(a, view1, 0)); + EXPECT_EQ(a(1, 2), mapper1.map(a, view1, 1)); + EXPECT_EQ(size_t(1), mapper1.dimension(a)); + XT_EXPECT_ANY_THROW(mapper1.map_at(a, view1, 10)); + + auto view0 = view(a, 0, range(0, 3)); + index_mapper mapper0; + + EXPECT_EQ(a(0, 0), mapper0.map(a, view0, 0)); + EXPECT_EQ(a(0, 1), mapper0.map(a, view0, 1)); + EXPECT_EQ(size_t(1), mapper0.dimension(a)); + + auto view2 = view(a, range(0, 2), 2); + index_mapper mapper2; + EXPECT_EQ(a(0, 2), mapper2.map(a, view2, 0)); + EXPECT_EQ(a(1, 2), mapper2.map(a, view2, 1)); + EXPECT_EQ(size_t(1), mapper2.dimension(a)); + + auto view4 = view(a, 1); + index_mapper mapper4; + EXPECT_EQ(size_t(1), mapper4.dimension(a)); + + auto view5 = view(view4, 1); + index_mapper mapper5; + EXPECT_EQ(size_t(0), mapper5.dimension(view4)); + + auto view6 = view(a, 1, all()); + index_mapper mapper6; + EXPECT_EQ(a(1, 0), mapper6.map(a, view6, 0)); + EXPECT_EQ(a(1, 1), mapper6.map(a, view6, 1)); + EXPECT_EQ(a(1, 2), mapper6.map(a, view6, 2)); + EXPECT_EQ(a(1, 3), mapper6.map(a, view6, 3)); + + auto view7 = view(a, all(), 2); + index_mapper mapper7; + EXPECT_EQ(a(0, 2), mapper7.map(a, view7, 0)); + EXPECT_EQ(a(1, 2), mapper7.map(a, view7, 1)); + EXPECT_EQ(a(2, 2), mapper7.map(a, view7, 2)); + } + + TEST(xview_mapping, indices) + { + xarray a = {{1., 2., 3.}, {4., 5., 6.}}; + + auto view1 = view(a, all(), all()); + index_mapper mapper1; + + EXPECT_EQ(a(0, 2), mapper1.map(a, view1, 0, 2)); + EXPECT_EQ(a(0, 2), mapper1.map(a, view1, 2)); + EXPECT_EQ(a(1, 2), mapper1.map(a, view1, 1, 1, 2)); + + auto view2 = view(a, all()); + index_mapper mapper2; + + EXPECT_EQ(a(0, 2), mapper2.map(a, view2, 0, 2)); + EXPECT_EQ(a(0, 2), mapper2.map(a, view2, 2)); + EXPECT_EQ(a(1, 2), mapper2.map(a, view2, 1, 1, 2)); + } + TEST(xview, negative_index) { view_shape_type shape = {3, 4}; @@ -269,6 +340,38 @@ namespace xt EXPECT_EQ(a(1, 1, 1), view1.element(idx.cbegin(), idx.cend())); } + TEST(xview_mapping, three_dimensional) + { + view_shape_type shape = {3, 4, 2}; + std::vector data = {1, 2, 3, 4, 5, 6, 7, 8, + + 9, 10, 11, 12, 21, 22, 23, 24, + + 25, 26, 27, 28, 29, 210, 211, 212}; + xarray a(shape); + std::copy(data.cbegin(), data.cend(), a.template begin()); + + auto view1 = view(a, 1, all(), all()); + index_mapper mapper1; + + EXPECT_EQ(size_t(2), mapper1.dimension(a)); + EXPECT_EQ(a(1, 0, 0), mapper1.map(a, view1, 0, 0)); + EXPECT_EQ(a(1, 0, 1), mapper1.map(a, view1, 0, 1)); + EXPECT_EQ(a(1, 1, 0), mapper1.map(a, view1, 1, 0)); + EXPECT_EQ(a(1, 1, 1), mapper1.map(a, view1, 1, 1)); + XT_EXPECT_ANY_THROW(mapper1.map_at(a, view1, 10, 10)); + + auto view2 = view(a, 1); + index_mapper mapper2; + + EXPECT_EQ(size_t(2), mapper2.dimension(a)); + EXPECT_EQ(a(1, 0, 0), mapper2.map(a, view2, 0, 0)); + EXPECT_EQ(a(1, 0, 1), mapper2.map(a, view2, 0, 1)); + EXPECT_EQ(a(1, 1, 0), mapper2.map(a, view2, 1, 0)); + EXPECT_EQ(a(1, 1, 1), mapper2.map(a, view2, 1, 1)); + XT_EXPECT_ANY_THROW(mapper2.map_at(a, view2, 10, 10)); + } + TEST(xview, integral_count) { size_t squeeze1 = integral_count>(); @@ -333,6 +436,32 @@ namespace xt EXPECT_EQ(v3(2, 3, 1, 1), arr(1, 2)); } + TEST(xview_mapping, access) + { + xt::xarray arr{{1.0, 2.0, 3.0}, {2.0, 5.0, 7.0}, {2.0, 5.0, 7.0}}; + + auto v1 = xt::view(arr, 1, xt::range(1, 3)); + index_mapper mapper1; + + EXPECT_EQ(mapper1.map(arr, v1), arr(0, 1)); + EXPECT_EQ(mapper1.map(arr, v1, 1), arr(1, 2)); + EXPECT_EQ(mapper1.map(arr, v1, 1, 1), arr(1, 2)); + + auto v2 = xt::view(arr, all(), newaxis(), all()); + index_mapper mapper2; + + // EXPECT_EQ(v2(1), arr(0, 1)); + EXPECT_EQ(mapper2.map(arr, v2, 1, 0, 2), arr(1, 2)); + EXPECT_EQ(mapper2.map(arr, v2, 2, 1, 0, 2), arr(1, 2)); + + auto v3 = xt::view(arr, xt::range(0, 2), xt::range(1, 3)); + index_mapper mapper3; + + // EXPECT_EQ(v3(1), arr(0, 2)); + EXPECT_EQ(mapper3.map(arr, v3, 1, 1), arr(1, 2)); + EXPECT_EQ(mapper3.map(arr, v3, 2, 3, 1, 1), arr(1, 2)); + } + TEST(xview, unchecked) { xt::xarray arr{{1.0, 2.0, 3.0}, {2.0, 5.0, 7.0}, {2.0, 5.0, 7.0}}; @@ -1462,6 +1591,45 @@ namespace xt EXPECT_EQ(a, b); } + TEST(xview, assign_through_multiple_leading_newaxis) + { + SUBCASE("updates the underlying tensor for every element") + { + xt::xtensor tensor = xt::zeros({4, 3}); + auto view = xt::view(tensor, xt::newaxis(), xt::newaxis(), xt::newaxis(), xt::all(), xt::all()); + + uint8_t value = 0; + for (std::size_t row = 0; row < 4; ++row) + { + for (std::size_t col = 0; col < 3; ++col) + { + view(std::size_t{0}, std::size_t{0}, std::size_t{0}, row, col) = value; + EXPECT_EQ(tensor(row, col), value); + ++value; + } + } + + EXPECT_EQ(tensor, xt::arange(12).reshape({4, 3})); + } + + SUBCASE("preserves bool assignment semantics") + { + xt::xtensor tensor = xt::zeros({4, 3}); + auto view = xt::view(tensor, xt::newaxis(), xt::newaxis(), xt::newaxis(), xt::all(), xt::all()); + + for (std::size_t row = 0; row < 4; ++row) + { + for (std::size_t col = 0; col < 3; ++col) + { + view(std::size_t{0}, std::size_t{0}, std::size_t{0}, row, col) = true; + EXPECT_TRUE(tensor(row, col)); + } + } + + EXPECT_EQ(tensor, xt::ones({4, 3})); + } + } + TEST(xview, in_bounds) { xt::xtensor a = {{0, 1, 2}, {3, 4, 5}}; @@ -1693,4 +1861,23 @@ namespace xt XT_ASSERT_THROW(const auto col = xt::col(arr, 0), std::invalid_argument); } + + TEST(xview, drop_on_1dim_array) + { + auto my_array = xt::xtensor({1, 2, 3}); + + // drop(1) creates a view excluding index 1 + auto v1 = xt::view(my_array, xt::drop(1)); + EXPECT_EQ(v1, (xt::xtensor{1, 3})); + + // Assign through the drop view + xt::view(my_array, xt::drop(1)) = 0.; + EXPECT_EQ(my_array, (xt::xtensor{0, 2, 0})); + + // Reset, then test drop with a variable (the original compilation issue) + my_array = xt::xtensor({1, 2, 3}); + const size_t index = 1; + auto v2 = xt::view(my_array, xt::drop(index)); + EXPECT_EQ(v2, (xt::xtensor{1, 3})); + } }