qlat_utils.lat_data — Lattice Data Container

Source: qlat-utils/qlat_utils/lat_data.pyx.in

Note: Update this document when updating the source file.

Outline

  1. Overview

  2. LatData Types

  3. Construction

  4. Dimension Metadata

  5. Data Access

  6. I/O Operations

  7. MPI Operations

  8. Arithmetic

  9. NumPy Buffer Protocol

  10. Serialization

  11. Examples


Overview

lat_data is a template module (.pyx.in) that generates LatData container classes for multi-dimensional arrays with labeled dimensions and named indices. Each LatData instance stores a ranked array where each dimension has a name and optional string labels for its indices, making it suitable for momentum-projected correlator data, per-site observables, and similar structured lattice QCD datasets.

The generated classes support the NumPy buffer protocol for zero-copy array access, MPI broadcast and global sum operations, and a human-readable text-based file format.


LatData Types

Class

Element Type

Complex by Default

LatData

RealD (double)

Yes

LatDataRealF

RealF (float)

Yes

LatDataInt

Int (int)

No

LatDataLong

Long (long)

No

All four classes share the same API. The type is selected based on the precision and integer requirements of the data.


Construction

mk_lat_data(info_list, is_complex=True)

Factory function to create a LatData with the given dimension layout.

ld = q.mk_lat_data([["t", 8], ["p", 3]])

Parameter

Description

info_list

List of [dim_name, dim_size, dim_indices?] entries

is_complex

Whether the last (innermost) dimension is complex (re/im)

mk_lat_data_real_f(info_list, is_complex=True)

Same as mk_lat_data but creates a LatDataRealF (single-precision).

mk_lat_data_int(info_list)

Same as mk_lat_data but creates a LatDataInt (integer, non-complex).

mk_lat_data_long(info_list)

Same as mk_lat_data but creates a LatDataLong (long integer, non-complex).

LatData.set_info(info_list, is_complex=True)

Set the dimension layout on an existing instance.

ld = q.LatData()
ld.set_info([["t", 8], ["mom", 3, ["(0,0,0)", "(1,0,0)", "(1,1,0)"]]])

Dimension Metadata

Method

Description

ndim()

Number of dimensions (excluding the implicit re/im axis)

dim_name(dim)

Name of dimension dim

dim_size(dim)

Size of dimension dim

dim_names()

List of all dimension names

dim_sizes()

List of all dimension sizes

dim_indices(dim, is_filling_default=False)

String labels for dimension dim

dim_idx(dim, idx)

Look up the integer index of a string label

info(dim=None)

Full info for one dimension, or all dimensions if dim is None

is_complex()

Whether the data has a complex (re/im) dimension

set_dim_sizes(sizes, is_complex=True)

Resize (data is lost)

set_dim_name(dim, name, indices=None)

Set name and optional labels for dimension dim


Data Access

LatData supports standard Python indexing via __getitem__ and __setitem__, backed by np.asarray:

ld[0, 1] = 3.14
val = ld[0, 1]

Conversion

Method

Description

to_numpy()

Copy data to a NumPy array

from_numpy(arr, dim_names=None, is_complex=True)

Load from a NumPy array

to_list()

Flatten to a Python list

from_list(val, is_complex=True)

Load from a flat Python list

to_xarray()

Convert to an xarray.DataArray with labeled coordinates


I/O Operations

Method

Description

save(path)

Save from node 0

load(path)

Load from node 0 and broadcast

save_node(path)

Save on every node

load_node(path)

Load on every node (no broadcast)

save_str()

Serialize to a bytes object

load_str(content)

Deserialize from bytes

load_lat_data(path)

Factory function: load a LatData from file.

ld = q.load_lat_data("data/prop.lat")

load_lat_data_real_f(path)

Same but returns LatDataRealF.


MPI Operations

Method

Description

bcast(root=0)

Broadcast data from root to all nodes

glb_sum()

Return a new LatData with global-summed data

glb_sum_in_place()

Global-sum in place


Arithmetic

LatData (the double-precision variant) supports arithmetic operations:

Operation

Description

ld1 + ld2

Element-wise addition

ld1 - ld2

Element-wise subtraction

ld * scalar

Scalar multiplication (float or complex)

-ld

Negation

ld += ld2

In-place addition

ld -= ld2

In-place subtraction

ld *= scalar

In-place scalar multiplication

ld.qnorm()

Squared norm of the data vector

ld.set_zero()

Zero all elements

ld.is_match(ld2)

Check if dimensions are identical


NumPy Buffer Protocol

All LatData classes implement the buffer protocol, enabling zero-copy NumPy array access:

arr = np.asarray(ld)  # zero-copy view

The shape is (dim0_size, dim1_size, ...). For complex data, the dtype is complex128 (or complex64 for LatDataRealF) — the real/imaginary parts are encoded in the dtype, not as an extra dimension.


Serialization

LatData supports Python pickle via __getstate__ / __setstate__. The serialized form includes dimension metadata (names, sizes, indices) and the data array, enabling faithful round-tripping through pickle.dumps / pickle.loads.


Examples

Create and populate a LatData

import qlat_utils as q

ld = q.mk_lat_data([["t", 8], ["mom", 3]], is_complex=True)
ld.set_dim_name(1, "mom", ["(0,0,0)", "(1,0,0)", "(1,1,0)"])
ld[0, 0] = 1.0 + 0.5j
if q.get_id_node() == 0:
    print("shape:", ld.dim_sizes())
    print("value:", ld[0, 0])

Query dimension metadata

import qlat_utils as q

ld = q.mk_lat_data([["t", 4], ["mom", 3, ["p0", "p1", "p2"]]], is_complex=True)
if q.get_id_node() == 0:
    print("ndim:", ld.ndim())               # 2
    print("dim_names:", ld.dim_names())     # ['t', 'mom']
    print("dim_sizes:", ld.dim_sizes())     # [4, 3]
    print("is_complex:", ld.is_complex())   # True
    print("dim_indices(1):", ld.dim_indices(1))  # ['p0', 'p1', 'p2']
    print("dim_idx(1, 'p1'):", ld.dim_idx(1, "p1"))  # 1

Arithmetic operations

import qlat_utils as q

ld_a = q.mk_lat_data([["t", 4]])
ld_b = q.mk_lat_data([["t", 4]])
ld_a[0] = 1.0
ld_b[0] = 2.0

ld_sum = ld_a + ld_b         # element-wise addition
ld_diff = ld_a - ld_b        # element-wise subtraction
ld_scaled = ld_a * 3.0       # scalar multiplication
ld_neg = -ld_a               # negation

ld_a += ld_b                 # in-place addition
ld_a *= 0.5                  # in-place scalar multiplication
if q.get_id_node() == 0:
    print("qnorm:", ld_a.qnorm())  # squared norm

NumPy buffer protocol (zero-copy)

import qlat_utils as q
import numpy as np

ld = q.mk_lat_data([["t", 4], ["x", 3]])
ld[0, 0] = 5.0
arr = np.asarray(ld)    # zero-copy view
arr[1, 1] = 7.0         # writes through to LatData
if q.get_id_node() == 0:
    print("ld[1,1]:", ld[1, 1])  # 7.0

Pickle round-trip

import qlat_utils as q
import pickle

ld = q.mk_lat_data([["t", 4], ["mom", 2, ["p0", "p1"]]])
ld[0, 0] = 1.0 + 0.5j
data = pickle.dumps(ld)
ld_restored = pickle.loads(data)
if q.get_id_node() == 0:
    print("dim_names:", ld_restored.dim_names())
    print("dim_indices(1):", ld_restored.dim_indices(1))

save_str / load_str round-trip

import qlat_utils as q

ld = q.mk_lat_data([["t", 4], ["p", 2]])
ld[0, 0] = 1.5
content = ld.save_str()     # serialize to bytes
ld2 = q.LatData()
ld2.load_str(content)       # deserialize

Load from file and convert to NumPy

import qlat_utils as q
import numpy as np

ld = q.load_lat_data("data/pion_corr.lat")
arr = np.asarray(ld)
if q.get_id_node() == 0:
    print("NumPy shape:", arr.shape)

MPI global sum

import qlat_utils as q

size_node_list = [[1, 1, 1, 1]]
q.begin_with_mpi(size_node_list)
ld = q.mk_lat_data([["t", 8]])
ld.set_zero()
# ... accumulate local data ...
ld.glb_sum_in_place()  # sum across all nodes
q.end_with_mpi()