xtensor icon indicating copy to clipboard operation
xtensor copied to clipboard

Runtime crash in xtensor

Open Sallee1 opened this issue 5 months ago • 2 comments

Environment:

  • xtensor: 0.25.0

  • xtl: 0.7.7

  • xsimd: 13.2.0

  • OS: Windows 11 24H2

  • Compiler: MSVC 19.44 (Visual Studio 2022)

  • C++ Standard: C++14

  • Compilation flags:

    /permissive- /ifcOutput "x64\Release\" /GS /GL /W3 /Gy /Zc:wchar_t 
    /I"xtensor-0.25.0\include" /I"xtl-0.7.7\include" /I"xsimd-13.2.0\include" 
    /Zi /Gm- /Od /Ob0 /sdl /Fd"vc143.pdb" /Zc:inline /fp:precise 
    /D "XTENSOR_USE_XSIMD" /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" 
    /errorReport:prompt /WX- /Zc:forScope /Gd /Oi /MD /std:c++17 /FC 
    /Fa"x64\Release\" /EHsc /nologo /Fo"x64\Release\" /Fp"demo.pch" 
    /diagnostics:column /utf-8
    
  • Linking flags:

    /MANIFEST /LTCG:incremental /NXCOMPAT /PDB:"demo.pdb" /DYNAMICBASE 
    "opencv_core4100.lib" ...(other libs)... /DEBUG /MACHINE:X64 /OPT:REF 
    /SUBSYSTEM:CONSOLE /LTCGOUT:"demo.iobj" /OPT:ICF /ERRORREPORT:PROMPT 
    /LIBPATH:"opencv_lib_path"
    

Description: It might be the following statement that caused the subsequent lazy evaluation to crash:

auto mfd_flow_weight = mfd_flow_weight_unnorm / mfd_flow_weight_sum;  // [H,W,8]

And the following statement triggers lazy evaluation, resulting in a segmentation fault:

xt::xtensor<double, 3>  result = mfd_flow_weight;

Minimal Reproducible Example:

#include <xtensor.hpp>
#include <opencv2/opencv.hpp>
using namespace xt::placeholders;
/**
 * @brief Sliding window view similar to numpy
 * @param img Input image
 * @param shape Window size
 * @return Sliding window view
*/
xt::xtensor<double, 4> sliding_window_view(const xt::xtensor<double, 2>& img, const std::array<size_t, 2>& shape, const std::array<size_t, 2>& stride = { 1,1 }) {
    assert((shape[0] & 1) == 1 && (shape[1] & 1) == 1);
    assert(stride[0] > 0 && stride[1] > 0);
    xt::svector<size_t, 4> new_shape{
        (img.shape(0) - shape[0]) / stride[0] + 1,
        (img.shape(1) - shape[1]) / stride[1] + 1,
        shape[0],
        shape[1] };
    xt::svector<std::ptrdiff_t, 4> new_strides{
        static_cast<std::ptrdiff_t>(stride[0] * img.strides()[0]),
        static_cast<std::ptrdiff_t>(stride[1] * img.strides()[1]),
        img.strides()[0],
        img.strides()[1]
    };
    auto sliding_window = xt::strided_view(img, new_shape, std::move(new_strides), 0);
    return sliding_window;
}
xt::xtensor<double, 3> mfd(xt::xtensor<double,2> dem, double dem_resolution) {
    // Sliding window view
    auto dem_pad = xt::pad(dem, 1, xt::pad_mode::constant, 0);            // [H+2,W+2]
    auto dem_slide_window = sliding_window_view(dem_pad, { 3,3 });         // [H,W,3,3]
    // Elevation differences
    auto diffs = xt::maximum(0,
        xt::view(dem_slide_window, xt::all(), xt::all(), xt::range(1, 2), xt::range(1, 2)) - dem_slide_window);   // [H,W,3,3]
    // Remove center
    auto diffs_without_center = xt::view(
        xt::reshape_view(diffs, { diffs.shape(0), diffs.shape(1), 9ULL }),
        xt::all(), xt::all(), xt::drop(4));            // [H,W,8]
    // Compute gradients
    const double diag_dist = dem_resolution * sqrt(2);
    xt::xtensor_fixed<double, xt::xshape<8>> slope_dist{
        diag_dist, dem_resolution, diag_dist,
        dem_resolution, dem_resolution,
        diag_dist, dem_resolution, diag_dist };        // [8]
    auto gradient8 = diffs_without_center / slope_dist;     // [H,W,8]
    auto max_gradient = xt::amax(gradient8, 2, xt::keep_dims);     // [H,W,1]
    std::cout << xt::adapt(max_gradient.shape()) << std::endl;
    // Compute flow concentration coefficient
    auto mfd_p = 8.9 * xt::minimum(max_gradient, 1.0) + 1.1;   // [H,W,1]
    std::cout << xt::adapt(mfd_p.shape()) << std::endl;
    // Compute unnormalized flow weights
    xt::xtensor_fixed<double, xt::xshape<8>> mfd_l{
        0.354,0.5,0.354,
        0.5,      0.5,
        0.354,0.5,0.354,
    };        // [8]
    auto mfd_flow_weight_unnorm = xt::pow(gradient8, mfd_p) * mfd_l;        // [H,W,8]
    auto mfd_flow_weight_sum = xt::sum(mfd_flow_weight_unnorm, 2, xt::keep_dims);        // [H,W,1]
    // Normalize the flow weights
    auto mfd_flow_weight = mfd_flow_weight_unnorm / mfd_flow_weight_sum;    // [H,W,8]
    xt::xtensor<double, 3>  result = mfd_flow_weight;
    std::cout << result << std::endl;
    return result;
}
int main() {
    cv::Mat dem_img = cv::imread("assets/dem.tif", cv::IMREAD_UNCHANGED);
    dem_img.convertTo(dem_img, CV_64FC1);
    xt::xtensor<double, 2> dem_tensor = xt::adapt(dem_img.data, dem_img.total(), xt::no_ownership(), 
        std::array<size_t, 2>{ static_cast<size_t>(dem_img.rows), static_cast<size_t>(dem_img.cols) });
    double resolution = 30.0;
    auto result = mfd(dem_tensor, resolution);
}

Attachments:

The call stack, executable program, pdb debug information, and minidump are provided in the following onedrive sharing

https://1drv.ms/u/c/4d2e14be0cb87b34/EWKRw2KHtwRDj8MKgIbLOy0Bc_LxLcAdvdhQXscQoLNX4g?e=gzj9ES

Sallee1 avatar Aug 18 '25 03:08 Sallee1

In addition, fake input below: dem.tif

Sallee1 avatar Aug 18 '25 04:08 Sallee1

When forced evaluation using eval() is applied to intermediate steps, the code runs successfully and produces expected results. This suggests the crash may be related to lazy evaluation in xtensor's expression templates.

like this

xt::xtensor<double, 3> mfd(xt::xtensor<double,2> dem, double dem_resolution) {
    // Sliding window view
    auto dem_pad = xt::pad(dem, 1, xt::pad_mode::constant, 0);            // [H+2,W+2]
    auto dem_slide_window = sliding_window_view(dem_pad, { 3,3 });         // [H,W,3,3]
    // Elevation differences
    auto diffs = xt::maximum(0,
        xt::view(dem_slide_window, xt::all(), xt::all(), xt::range(1, 2), xt::range(1, 2)) - dem_slide_window);   // [H,W,3,3]
    // Remove center
    auto diffs_without_center = xt::view(
        xt::reshape_view(diffs, { diffs.shape(0), diffs.shape(1), 9ULL }),
        xt::all(), xt::all(), xt::drop(4));            // [H,W,8]
    // Compute gradients
    const double diag_dist = dem_resolution * sqrt(2);
    xt::xtensor_fixed<double, xt::xshape<8>> slope_dist{
        diag_dist, dem_resolution, diag_dist,
        dem_resolution, dem_resolution,
        diag_dist, dem_resolution, diag_dist };        // [8]
	auto gradient8 = xt::eval(diffs_without_center / slope_dist);		//[H,W,8]
	auto max_gradient = xt::eval(xt::amax(gradient8, 2, xt::keep_dims));		//[H,W,1]
    // Compute flow concentration coefficient
    auto mfd_p = 8.9 * xt::minimum(max_gradient, 1.0) + 1.1;   // [H,W,1]
    // Compute unnormalized flow weights
	xt::xtensor_fixed<double, xt::xshape<8>> mfd_l{
		0.354,0.5,0.354,
		0.5,      0.5,
		0.354,0.5,0.354,
	};		//[8]
	auto mfd_flow_weight_unnorm = xt::pow(gradient8, mfd_p) * mfd_l;		//[H,W,8]
	auto mfd_flow_weight_sum = xt::eval(xt::sum(mfd_flow_weight_unnorm, 2, xt::keep_dims));		//[H,W,1]
	auto mfd_flow_weight = mfd_flow_weight_unnorm / mfd_flow_weight_sum;	//[H,W,8]
    xt::xtensor<double, 3>  result = mfd_flow_weight;
    std::cout << result << std::endl;
    return result;
}

Sallee1 avatar Aug 18 '25 04:08 Sallee1