qlat_utils.types — Element Type Descriptors and Buffer Protocol Support

Source: qlat-utils/qlat_utils/types.pyx

Note: Update this document when updating the source file.

Outline

  1. Overview

  2. Buffer Class

  3. ElemType Hierarchy

  4. Matrix Types

  5. Scalar Types

  6. Type Properties Reference

  7. Examples


Overview

types defines the Python-level type descriptors for the fundamental data types used in lattice QCD calculations. It provides:

  • Buffer — a Cython-level implementation of the Python buffer protocol (__getbuffer__ / __releasebuffer__) that allows C++ memory to be exposed as NumPy-compatible buffers without copying.

  • ElemType class hierarchy — a family of descriptor classes, each encoding the shape, itemsize, format string, and byte-size of a specific C++ lattice type (e.g., ColorMatrix, WilsonMatrix, ComplexD, RealD, etc.).

These types serve as metadata bridges between C++ field storage and Python’s buffer/NumPy ecosystem.

Python access:

import qlat_utils as q

Buffer Class

cdef class Buffer:
    def __cinit__(self, object obj=None, int ndim=1, int itemsize=1,
                  char* fmt=NULL, char* buf=NULL)

A Cython extension type that implements the Python buffer protocol. Instances are not typically created directly by users; they are used internally by field types to expose their data as buffer-compatible objects.

Constructor Parameters

Parameter

Type

Default

Description

obj

object

None

Parent object that owns the memory. Its release_buffer() method is called when the buffer is released.

ndim

int

1

Number of dimensions

itemsize

int

1

Size in bytes of a single element

fmt

char*

NULL

Format string for the buffer protocol (e.g. 'Zd' for complex double)

buf

char*

NULL

Pointer to the raw memory

Internal Methods

Method

Description

set_buffer(buffer, flags)

Populate a Py_buffer struct for the buffer protocol

set_dim_size(dim, size)

Set the size of dimension dim

update_strides_from_shape()

Compute C-contiguous strides from the current shape

get_len()

Return total byte length (itemsize * product of shape)

Buffer Protocol

Buffer implements __getbuffer__ and __releasebuffer__, making it compatible with memoryview, np.asarray(), and any Python API that accepts the buffer protocol. When the buffer is released, the parent object’s release_buffer() method is called.


ElemType Hierarchy

ElemType is the base class for all element type descriptors. Each subclass encodes the metadata for a specific C++ lattice data type.

class ElemType:
    name = ""

All subclasses provide the following class-level attributes:

Attribute

Type

Description

name

str

Human-readable type name

sizeof_m

int

Total byte size of the C++ type

All subclasses also provide the following Python-accessible static methods (wrappers around the internal cdef methods):

Method

Return Type

Description

py_format()

str

Buffer protocol format string (e.g., 'Zd' for complex double)

py_itemsize()

int

Byte size of one scalar element

py_ndim()

int

Number of dimensions

py_shape()

tuple

Dimension sizes

py_size()

int

Total byte size of the type


Matrix Types

These types represent 2D matrix structures used in lattice QCD calculations. All use ComplexD (complex double, 'Zd') as their scalar element type.

ElemTypeColorMatrix

3×3 complex color matrix. Used for SU(3) gauge links.

Property

Value

name

"ColorMatrix"

ndim

2

shape

(3, 3)

itemsize

sizeof(ComplexD) = 16 bytes

size

144 bytes

ElemTypeWilsonMatrix

12×12 complex Wilson (Dirac-spinor × color) matrix.

Property

Value

name

"WilsonMatrix"

ndim

2

shape

(12, 12)

itemsize

sizeof(ComplexD) = 16 bytes

size

2304 bytes

ElemTypeNonRelWilsonMatrix

6×6 complex non-relativistic Wilson matrix.

Property

Value

name

"NonRelWilsonMatrix"

ndim

2

shape

(6, 6)

itemsize

sizeof(ComplexD) = 16 bytes

size

576 bytes

ElemTypeIsospinMatrix

2×2 complex isospin matrix.

Property

Value

name

"IsospinMatrix"

ndim

2

shape

(2, 2)

itemsize

sizeof(ComplexD) = 16 bytes

size

64 bytes

ElemTypeSpinMatrix

4×4 complex Dirac spin matrix.

Property

Value

name

"SpinMatrix"

ndim

2

shape

(4, 4)

itemsize

sizeof(ComplexD) = 16 bytes

size

256 bytes

ElemTypeWilsonVector

12-component complex Wilson vector (Dirac spin × color).

Property

Value

name

"WilsonVector"

ndim

1

shape

(12,)

itemsize

sizeof(ComplexD) = 16 bytes

size

192 bytes


Scalar Types

These types represent scalar (0-dimensional) primitive types.

ElemTypeComplexD

Complex double precision (std::complex<double>).

Property

Value

name

"ComplexD"

ndim

0

shape

()

format

'Zd'

itemsize

16 bytes

ElemTypeComplexF

Complex single precision (std::complex<float>).

Property

Value

name

"ComplexF"

ndim

0

shape

()

format

'Zf'

itemsize

8 bytes

ElemTypeRealD

Double precision real (double).

Property

Value

name

"RealD"

ndim

0

shape

()

format

'd'

itemsize

8 bytes

ElemTypeRealF

Single precision real (float).

Property

Value

name

"RealF"

ndim

0

shape

()

format

'f'

itemsize

4 bytes

ElemTypeLong

64-bit signed integer (Long).

Property

Value

name

"Long"

ndim

0

shape

()

format

'q'

itemsize

8 bytes

ElemTypeInt

32-bit signed integer (int).

Property

Value

name

"Int"

ndim

0

shape

()

format

'i'

itemsize

4 bytes

ElemTypeChar

Signed character (char).

Property

Value

name

"Char"

ndim

0

shape

()

format

'b'

itemsize

1 byte

ElemTypeInt64t

64-bit signed integer (int64_t).

Property

Value

name

"Int64t"

ndim

0

shape

()

format

'q'

itemsize

8 bytes

ElemTypeInt32t

32-bit signed integer (int32_t).

Property

Value

name

"Int32t"

ndim

0

shape

()

format

'i'

itemsize

4 bytes

ElemTypeInt8t

8-bit signed integer (int8_t).

Property

Value

name

"Int8t"

ndim

0

shape

()

format

'b'

itemsize

1 byte


Type Properties Reference

Note: The tables below document the full type metadata. name and sizeof_m are class attributes. py_format(), py_itemsize(), py_ndim(), py_shape(), and py_size() are Python-accessible static methods (wrappers around internal cdef methods).

Matrix Types Summary

Type

Name

Shape

Item Format

Element Size

Total Size

ElemTypeColorMatrix

ColorMatrix

(3, 3)

Zd

16 B

144 B

ElemTypeWilsonMatrix

WilsonMatrix

(12, 12)

Zd

16 B

2304 B

ElemTypeNonRelWilsonMatrix

NonRelWilsonMatrix

(6, 6)

Zd

16 B

576 B

ElemTypeIsospinMatrix

IsospinMatrix

(2, 2)

Zd

16 B

64 B

ElemTypeSpinMatrix

SpinMatrix

(4, 4)

Zd

16 B

256 B

ElemTypeWilsonVector

WilsonVector

(12,)

Zd

16 B

192 B

Scalar Types Summary

Type

Name

Format

Size

ElemTypeComplexD

ComplexD

Zd

16 B

ElemTypeComplexF

ComplexF

Zf

8 B

ElemTypeRealD

RealD

d

8 B

ElemTypeRealF

RealF

f

4 B

ElemTypeLong

Long

q

8 B

ElemTypeInt

Int

i

4 B

ElemTypeChar

Char

b

1 B

ElemTypeInt64t

Int64t

q

8 B

ElemTypeInt32t

Int32t

i

4 B

ElemTypeInt8t

Int8t

b

1 B


Examples

Querying Type Metadata

import qlat_utils as q
from qlat_utils.types import ElemTypeColorMatrix, ElemTypeWilsonMatrix, ElemTypeRealD

# ColorMatrix: 3x3 complex double, total size 144 bytes
print(ElemTypeColorMatrix.name)       # "ColorMatrix"
print(ElemTypeColorMatrix.sizeof_m)   # 144

# WilsonMatrix: 12x12 complex double
print(ElemTypeWilsonMatrix.name)      # "WilsonMatrix"
print(ElemTypeWilsonMatrix.sizeof_m)  # 2304

# Scalar type
print(ElemTypeRealD.name)            # "RealD"
print(ElemTypeRealD.sizeof_m)        # 8

Buffer Protocol Usage

The Buffer class is used internally by field types. A typical pattern:

import numpy as np
import qlat_utils as q
from qlat_utils.types import Buffer, ElemTypeComplexD

# Buffer is typically created internally by field objects.
# When a field exposes its data via the buffer protocol,
# NumPy can access it without copying:
#   arr = np.asarray(field_buffer)
# The parent object controls memory lifetime via release_buffer().

Using ElemType for Array Construction

import numpy as np
from qlat_utils.types import ElemTypeColorMatrix

# Construct a zero-filled array matching the ColorMatrix layout
# ColorMatrix is 3x3 complex double (sizeof_m = 144 bytes)
shape = (3, 3)
dtype = np.complex128                 # matches 'Zd' format
mat = np.zeros(shape, dtype=dtype)
print(mat.nbytes)                     # 144, matches ElemTypeColorMatrix.sizeof_m

Querying Type Properties via Python-Accessible Methods

All ElemType subclasses expose py_format(), py_itemsize(), py_ndim(), py_shape(), and py_size() as static methods callable from Python:

from qlat_utils.types import ElemTypeColorMatrix, ElemTypeWilsonMatrix
from qlat_utils.types import ElemTypeComplexD, ElemTypeRealD, ElemTypeLong

# ColorMatrix: 3x3 complex double
assert ElemTypeColorMatrix.py_ndim() == 2
assert ElemTypeColorMatrix.py_shape() == (3, 3)
assert ElemTypeColorMatrix.py_itemsize() == 16
assert ElemTypeColorMatrix.py_size() == 144
assert ElemTypeColorMatrix.py_format() == 'Zd'

# WilsonMatrix: 12x12 complex double
assert ElemTypeWilsonMatrix.py_ndim() == 2
assert ElemTypeWilsonMatrix.py_shape() == (12, 12)
assert ElemTypeWilsonMatrix.py_itemsize() == 16
assert ElemTypeWilsonMatrix.py_size() == 2304

# Scalar types have ndim == 0 and empty shape
assert ElemTypeComplexD.py_ndim() == 0
assert ElemTypeComplexD.py_shape() == ()
assert ElemTypeComplexD.py_itemsize() == 16
assert ElemTypeComplexD.py_size() == 16

assert ElemTypeRealD.py_ndim() == 0
assert ElemTypeRealD.py_format() == 'd'
assert ElemTypeRealD.py_itemsize() == 8

assert ElemTypeLong.py_ndim() == 0
assert ElemTypeLong.py_format() == 'q'
assert ElemTypeLong.py_itemsize() == 8

Building a NumPy Array from Type Metadata

Use the Python-accessible methods to programmatically construct arrays whose layout matches a given ElemType, without hardcoding shape or dtype:

import numpy as np
from qlat_utils.types import ElemTypeSpinMatrix, ElemTypeWilsonVector

fmt_to_dtype = {
    'Zd': np.complex128,
    'Zf': np.complex64,
    'd': np.float64,
    'f': np.float32,
    'q': np.int64,
    'i': np.int32,
    'b': np.int8,
}

def make_array(elem_type, count):
    dtype = fmt_to_dtype[elem_type.py_format()]
    shape = (count,) + elem_type.py_shape()
    return np.zeros(shape, dtype=dtype)

# 10 SpinMatrix elements: shape (10, 4, 4), dtype complex128
arr = make_array(ElemTypeSpinMatrix, 10)
assert arr.shape == (10, 4, 4)
assert arr.dtype == np.complex128
assert arr.nbytes == 10 * ElemTypeSpinMatrix.py_size()

# 5 WilsonVector elements: shape (5, 12), dtype complex128
vec = make_array(ElemTypeWilsonVector, 5)
assert vec.shape == (5, 12)
assert vec.nbytes == 5 * ElemTypeWilsonVector.py_size()

Iterating Over All ElemType Subclasses

import numpy as np
from qlat_utils.types import (
    ElemTypeColorMatrix, ElemTypeWilsonMatrix, ElemTypeNonRelWilsonMatrix,
    ElemTypeIsospinMatrix, ElemTypeSpinMatrix, ElemTypeWilsonVector,
    ElemTypeComplexD, ElemTypeComplexF, ElemTypeRealD, ElemTypeRealF,
    ElemTypeLong, ElemTypeInt, ElemTypeChar, ElemTypeInt64t,
    ElemTypeInt32t, ElemTypeInt8t,
)

all_types = [
    ElemTypeColorMatrix, ElemTypeWilsonMatrix, ElemTypeNonRelWilsonMatrix,
    ElemTypeIsospinMatrix, ElemTypeSpinMatrix, ElemTypeWilsonVector,
    ElemTypeComplexD, ElemTypeComplexF, ElemTypeRealD, ElemTypeRealF,
    ElemTypeLong, ElemTypeInt, ElemTypeChar, ElemTypeInt64t,
    ElemTypeInt32t, ElemTypeInt8t,
]

for t in all_types:
    assert t.py_size() == t.sizeof_m, f"{t.name}: py_size()={t.py_size()} != sizeof_m={t.sizeof_m}"
    if t.py_ndim() > 0:
        expected = t.py_itemsize() * int(np.prod(t.py_shape()))
    else:
        expected = t.py_itemsize()
    assert t.py_size() == expected, f"{t.name}: py_size()={t.py_size()} != expected={expected}"

Verifying Consistency Between Properties

import numpy as np
from qlat_utils.types import (
    ElemTypeColorMatrix, ElemTypeWilsonMatrix, ElemTypeNonRelWilsonMatrix,
    ElemTypeIsospinMatrix, ElemTypeSpinMatrix, ElemTypeWilsonVector,
    ElemTypeComplexD, ElemTypeComplexF, ElemTypeRealD, ElemTypeRealF,
    ElemTypeLong, ElemTypeInt, ElemTypeChar, ElemTypeInt64t,
    ElemTypeInt32t, ElemTypeInt8t,
)

all_types = [
    ElemTypeColorMatrix, ElemTypeWilsonMatrix, ElemTypeNonRelWilsonMatrix,
    ElemTypeIsospinMatrix, ElemTypeSpinMatrix, ElemTypeWilsonVector,
    ElemTypeComplexD, ElemTypeComplexF, ElemTypeRealD, ElemTypeRealF,
    ElemTypeLong, ElemTypeInt, ElemTypeChar, ElemTypeInt64t,
    ElemTypeInt32t, ElemTypeInt8t,
]

for t in all_types:
    assert t.py_size() == t.sizeof_m, f"{t.name}: py_size()={t.py_size()} != sizeof_m={t.sizeof_m}"
    if t.py_ndim() > 0:
        expected = t.py_itemsize() * int(np.prod(t.py_shape()))
    else:
        expected = t.py_itemsize()
    assert t.py_size() == expected, f"{t.name}: py_size()={t.py_size()} != expected={expected}"
    assert isinstance(t.name, str) and len(t.name) > 0
    assert isinstance(t.py_ndim(), int) and t.py_ndim() >= 0
    assert isinstance(t.py_shape(), tuple)
    assert isinstance(t.py_itemsize(), int) and t.py_itemsize() > 0
    assert isinstance(t.py_size(), int) and t.py_size() > 0
    assert isinstance(t.py_format(), str) and len(t.py_format()) > 0