feat: write complex numbers, incl. HDF5 2.0 native complex (class 11)
- Datatype::Complex serializes class 11 version 5 byte-identically to
libhdf5 2.2.0; containers holding it are written as version 5.
- DatasetBuilder::with_complex_f32/f64_data (h5py's {r, i} compound,
default) and with_native_complex_f32/f64_data (class 11, opt-in);
make_(native_)complex_f32/f64_type for attributes.
- Dataset::read_complex_f64/f32 read either form.
- Python create_dataset accepts complex64/complex128 (compound form).
- Parsing unchanged: class 11 still surfaces as {r, i}.
- Tests vs h5py 3.16 / libhdf5 2.0.0 and h5dump 2.2.0; docs.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -313,6 +313,9 @@ fn np_dtype_with_metadata<'py>(
|
||||
/// read as they are.
|
||||
pub(crate) fn fixed_dtype<'py>(py: Python<'py>, dt: &Datatype) -> PyResult<Bound<'py, PyAny>> {
|
||||
match dt {
|
||||
Datatype::Complex { size, base_type } => {
|
||||
fixed_dtype(py, &Datatype::complex_as_compound(*size, base_type))
|
||||
}
|
||||
Datatype::FixedPoint { .. } => np_dtype(py, int_format(dt)?),
|
||||
Datatype::FloatingPoint { .. } => np_dtype(py, float_format(dt)?),
|
||||
Datatype::String { size, charset, .. } => {
|
||||
|
||||
@@ -47,6 +47,7 @@ fn not_implemented(what: impl std::fmt::Display) -> PyErr {
|
||||
/// `edit_helpers._convert_array`), or why they cannot be written.
|
||||
pub(crate) fn category(dt: &Datatype) -> PyResult<&'static str> {
|
||||
match dt {
|
||||
Datatype::Complex { .. } => Ok("complex"),
|
||||
Datatype::FixedPoint { .. } => Ok("int"),
|
||||
Datatype::FloatingPoint { .. } if crate::convert::is_ieee_float(dt) => Ok("float"),
|
||||
// Read as a wider IEEE float; writing would need the reverse
|
||||
@@ -107,7 +108,9 @@ fn check_exact(dt: &Datatype) -> PyResult<()> {
|
||||
Datatype::Compound { members, .. } => {
|
||||
members.iter().try_for_each(|m| check_exact(&m.datatype))
|
||||
}
|
||||
Datatype::Array { base_type, .. } => check_exact(base_type),
|
||||
Datatype::Array { base_type, .. } | Datatype::Complex { base_type, .. } => {
|
||||
check_exact(base_type)
|
||||
}
|
||||
Datatype::String {
|
||||
padding: StringPadding::NullPad,
|
||||
..
|
||||
|
||||
@@ -161,6 +161,10 @@ pub(crate) enum DatasetData {
|
||||
I64(Vec<i64>),
|
||||
I32(Vec<i32>),
|
||||
U8(Vec<u8>),
|
||||
/// numpy `complex64`, `[re, im]` pairs, written as h5py does.
|
||||
C64(Vec<[f32; 2]>),
|
||||
/// numpy `complex128`, `[re, im]` pairs, written as h5py does.
|
||||
C128(Vec<[f64; 2]>),
|
||||
}
|
||||
|
||||
/// Specification for a dataset to be written.
|
||||
@@ -281,6 +285,14 @@ pub(crate) fn apply_dataset_spec(
|
||||
DatasetData::U8(v) => {
|
||||
db.with_u8_data(v);
|
||||
}
|
||||
// h5py's compound `{r, i}`, not HDF5 2.0's native complex type: it
|
||||
// is what h5py writes (3.16 included) and every libhdf5 can read it.
|
||||
DatasetData::C64(v) => {
|
||||
db.with_complex_f32_data(v);
|
||||
}
|
||||
DatasetData::C128(v) => {
|
||||
db.with_complex_f64_data(v);
|
||||
}
|
||||
}
|
||||
if !spec.shape.is_empty() {
|
||||
db.with_shape(&spec.shape);
|
||||
@@ -314,9 +326,19 @@ pub(crate) fn extract_numpy_data(
|
||||
"int64" => DatasetData::I64(flat.extract::<Vec<i64>>()?),
|
||||
"int32" => DatasetData::I32(flat.extract::<Vec<i32>>()?),
|
||||
"uint8" => DatasetData::U8(flat.extract::<Vec<u8>>()?),
|
||||
// A 1-D complex array viewed as floats is its (re, im) parts in order.
|
||||
"complex64" => {
|
||||
let parts: Vec<f32> = flat.call_method1("view", ("<f4",))?.extract()?;
|
||||
DatasetData::C64(parts.as_chunks::<2>().0.to_vec())
|
||||
}
|
||||
"complex128" => {
|
||||
let parts: Vec<f64> = flat.call_method1("view", ("<f8",))?.extract()?;
|
||||
DatasetData::C128(parts.as_chunks::<2>().0.to_vec())
|
||||
}
|
||||
_ => {
|
||||
return Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(format!(
|
||||
"unsupported numpy dtype: {dtype_str}; expected float64, float32, int64, int32, or uint8"
|
||||
"unsupported numpy dtype: {dtype_str}; expected float64, float32, int64, int32, \
|
||||
uint8, complex64 or complex128"
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user