python
import numpy as np
from hypothesis import given, strategies as st, assume
from hypothesis.extra.numpy import arrays, array_shapes, dtypes
from math import isfinite
# Example normalize_vector implementation to test
def normalize_vector(x: np.ndarray) -> np.ndarray:
x = np.asarray(x)
norm = np.linalg.norm(x)
if norm == 0 or np.isnan(norm):
return np.zeros_like(x) if norm == 0 else x.copy()
return x / norm
# Strategy: numeric dtypes and 1D arrays of length 0..100
numeric_dtypes = dtypes(available_types=[np.float16, np.float32, np.float64, np.int32, np.int64])
vecs = lambda dt: arrays(dtype=dt, shape=array_shapes(min_dims=1, max_dims=1, min_side=0, max_side=50),
elements=st.floats(allow_nan=True, allow_infinity=True, width=None))
# 1) Non-zero vectors -> norm ≈ 1
@given(dt=numeric_dtypes, x=st.data())
def test_non_zero_normalizes(dt, x):
arr = x.draw(vecs(dt))
# cast ints to float for norm checking if needed
arr = np.asarray(arr, dtype=dt)
assume(np.linalg.norm(np.nan_to_num(arr, nan=0.0, posinf=0.0, neginf=0.0)) != 0)
out = normalize_vector(arr)
# tolerance: relative tolerance 1e-6 for float64, 1e-3 for float32, 1e-2 for float16
tol = {np.float64:1e-6, np.float32:1e-3, np.float16:1e-2}.get(out.dtype.type,1e-6)
if np.any(~np.isfinite(arr)):
# if input had NaN/inf, behavior documented separately
return
assert abs(np.linalg.norm(out) - 1.0) <= tol
# 2) Zero vector returns zero vector
@given(dt=numeric_dtypes, n=st.integers(min_value=0, max_value=50))
def test_zero_vector_preserved(dt, n):
z = np.zeros((n,), dtype=dt)
out = normalize_vector(z)
assert np.array_equal(out, np.zeros_like(z))
# 3) Dtype preserved for numeric types
@given(dt=numeric_dtypes, arr=vecs(dt))
def test_dtype_preserved(dt, arr):
arr = np.asarray(arr, dtype=dt)
out = normalize_vector(arr)
# for integer input, many implementations cast to float; choose policy: preserve dtype if float, else float64
if np.issubdtype(dt, np.floating):
assert out.dtype == dt
else:
assert np.issubdtype(out.dtype, np.floating)
# 4) NaN/inf handling policy: here we expect NaNs/inf to propagate (or documented behavior)
@given(arr=arrays(dtype=np.float64, shape=array_shapes(min_dims=1, max_dims=1, min_side=1, max_side=20),
elements=st.floats(allow_nan=True, allow_infinity=True)))
def test_nan_inf_policy(arr):
out = normalize_vector(arr)
# If any NaN present, ensure output contains NaN at same positions (propagation)
nan_mask = np.isnan(arr)
assert np.array_equal(np.isnan(out), nan_mask)
# For infinite entries, ensure not silently turned finite (policy: preserved or turned into NaN)
inf_mask = np.isinf(arr)
assert np.array_equal(np.isinf(out), inf_mask) or np.any(np.isnan(out[inf_mask]))