Skip to content

Bug: F-contigous check in SimpleArray breaks #603

@ThreeMonth03

Description

@ThreeMonth03

Summary

When constructing a SimpleArray from a NumPy array, the current F-contiguous stride check is overly strict and incorrectly rejects arrays that NumPy itself considers both C and F contiguous — especially those with singleton dimensions such as (1, n) or (n, 1).

How to reproduce the error

Code:

import numpy as np
import modmesh as mm

# Test case 1: 1x2 array - fails with Fortran contiguous check
try:
    array1 = np.array([[1.0, 0.1]])
    print(f"Test 1: shape={array1.shape}, strides={array1.strides}")
    print(f"  C_CONTIGUOUS={array1.flags.c_contiguous}, F_CONTIGUOUS={array1.flags.f_contiguous}")
    error1 = mm.SimpleArrayFloat64(array=array1)
    print("  ✓ Success")
except Exception as e:
    print(f"  ✗ Failed: {e}")

# Test case 2: 2x1 array - fails with Fortran contiguous check
try:
    array2 = np.array([[1.0], [0.0]])
    print(f"\nTest 2: shape={array2.shape}, strides={array2.strides}")
    print(f"  C_CONTIGUOUS={array2.flags.c_contiguous}, F_CONTIGUOUS={array2.flags.f_contiguous}")
    error2 = mm.SimpleArrayFloat64(array=array2)
    print("  ✓ Success")
except Exception as e:
    print(f"  ✗ Failed: {e}")

# Working solution: 2 * 2 array
try:
    array_2d = np.array([[1.0, 0.1], [0.0, 1.0]])
    print(f"\nWorking solution: 1D array shape={array_2d.shape}, strides={array_2d.strides}")
    success = mm.SimpleArrayFloat64(array=array_2d)
    print("  ✓ 2D array works")
except Exception as e:
    print(f"  ✗ 2D array failed: {e}")

Console:

Test 1: shape=(1, 2), strides=(16, 8)
  C_CONTIGUOUS=True, F_CONTIGUOUS=True
  ✗ Failed: SimpleArray: Fortran contiguous stride must start with 1

Test 2: shape=(2, 1), strides=(8, 8)
  C_CONTIGUOUS=True, F_CONTIGUOUS=True
  ✗ Failed: SimpleArray: Fortran contiguous stride must match shape

Working solution: 1D array shape=(2, 2), strides=(16, 8)
  ✓ 2D array works

Root Cause

NumPy defines contiguity more flexibly:

The problematic code is located in SimpleArray.hpp, within the check_f_contiguous() function:

void check_f_contiguous(small_vector<size_t> const & shape,
                        small_vector<size_t> const & stride) const
{
    if (stride[0] != 1)
    {
        throw std::runtime_error("SimpleArray: Fortran contiguous stride must start with 1");
    }
    for (size_t it = 0; it < shape.size() - 1; ++it)
    {
        if (stride[it + 1] != shape[it] * stride[it])
        {
            throw std::runtime_error("SimpleArray: Fortran contiguous stride must match shape");
        }
    }
}

This logic assumes that:

  • The first dimension’s stride must always be 1 (in element units).
  • Each following stride must strictly equal the product of the previous stride and shape.
  • That assumption fails for singleton dimensions, where NumPy explicitly allows strides to take any value while the array remains contiguous.

Metadata

Metadata

Assignees

No one assigned

    Labels

    arrayMulti-dimensional array implementationgood first issueGood for newcomers

    Type

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions