Skip to main content

keras-hexagdly

Keras 3 port of HexagDLy: convolution and pooling methods for hexagonally sampled data, originally written for PyTorch by Tim Lukas Holch and Constantin Steppa (ai4iacts).

This port reproduces HexagDLy's hexagonal addressing scheme and sub-kernel decomposition exactly (bit-for-bit equivalent outputs, cross-checked against the upstream PyTorch package in hexagdly-oracle), but is built on Keras 3 so it runs on any backend (TensorFlow, JAX, PyTorch) and uses a channels-last (NHWC/NDHWC) tensor layout instead of PyTorch's channels-first.

It also adds three functionalities that do not exist in upstream HexagDLy:

  • share_neighbors (Conv2d, Conv3d): ties the weights of several cells of a hexagonal kernel together instead of giving every cell its own independent weight, which cuts the parameter count and imposes a geometric symmetry on the learned kernel. Three modes -- "ring", "diag", "sym" -- are illustrated below.
  • depth_padding="same" (Conv3d only): zero-pads the depth/time axis so the temporal kernel is centred on each time step and the output depth equals the input depth, instead of HexagDLy's "valid"-only behaviour (output depth shrinks by kernel - 1).
  • hls4ml export for FPGA synthesis, currently limited to io_stream and the 2D layers.

Weights trained with pytorch-hexagdly can be loaded into these layers -- see keras_hexagdly.torch_interop and notebooks/pytorch_to_keras_example.ipynb. No torch dependency is required: a plain .npz of the state dict works.

See NOTICE.md for attribution details and citation information.

Installation

pip install keras-hexagdly

For development (running the test suite, which also checks parity against upstream PyTorch HexagDLy, and the example notebooks):

pip install keras-hexagdly[dev]

Usage

import keras
import keras_hexagdly as hgly

hexconv = hgly.Conv2d(filters=3, kernel_size=1, strides=4)
x = keras.random.uniform((1, 21, 21, 1))  # channels-last: (N, H, W, C)
y = hexconv(x)

filters/strides/use_bias are the Keras-native names and the preferred form going forward; in_channels is never required -- it's inferred from the input on first call, like a standard Keras layer.

The original PyTorch-hexagdly-style names (in_channels, out_channels, stride, bias) still work exactly as before -- e.g. hgly.Conv2d(in_channels, out_channels, kernel_size, stride) -- and will keep working indefinitely for backward compatibility with existing code and previously-saved models; using them now emits a DeprecationWarning pointing at the new name (stride/bias only -- in_channels/ out_channels aren't deprecated, filters is just the additional Keras-idiomatic alias for out_channels).

New: share_neighbors -- weight sharing across kernel cells

Available on Conv2d and Conv3d. share_neighbors reduces the number of learnable parameters by grouping kernel cells that share a single weight. Three modes are available, illustrated below for kernel_size=2 (19 cells):

share_neighbors="ring" share_neighbors="diag" share_neighbors="sym"
ring diag sym
3 weights -- cells at the same hex distance from center share one weight (concentric rings). 10 weights -- visually opposite (antipodal) cells share one weight. 10 weights -- geometrically adjacent 60 degree pairs share one weight.
  • "ring": the most aggressive reduction. All 6 direct neighbours share one weight, all 12 outer cells share another. Enforces exact 6-fold rotational symmetry of the learned kernel.
  • "diag": antipodal symmetry -- each cell and its mirror image through the center share a weight. Useful when the kernel should be point-symmetric.
  • "sym": 60 degree adjacent pairs -- consecutive neighbours along the kernel boundary share a weight. Useful when the kernel should reflect local rotational symmetry.

For kernel_size=1 (7 cells): ring=2 weights, diag=4, sym=4. For kernel_size=2 (19 cells): ring=3 weights, diag=10, sym=10. Compare to the default share_neighbors=None, which gives 7 and 19 independent weights. "ring" works at any kernel size; "diag" and "sym" are defined for kernel_size 1 and 2 only, matching pytorch-hexagdly, which does not define them beyond n=2 either.

hexconv = hgly.Conv2d(filters=3, kernel_size=2, share_neighbors="ring")

share_neighbors=True is accepted as an alias for "ring".

New: same-padded temporal convolution (Conv3d)

conv3d = hgly.Conv3d(in_channels, out_channels, kernel_size=(depth_k, hex_k),
                      depth_padding="same")  # output depth == input depth

Before applying these layers, your data must already be arranged on the square-tensor layout HexagDLy expects (zig-zag columns); see notebooks/keras_hexagdly_addressing_scheme.ipynb for how to get there from raw detector coordinates, and notebooks/keras_hexagdly_2d_example.ipynb for a worked convolution/pooling example, including the new features above.

Notebooks

Ported from HexagDLy's own notebooks, one-to-one where the content is framework-specific, lightly adapted where it depends on a torch-specific dataloader/training loop:

FPGA export via hls4ml

The hex layers can be synthesised to HLS C++ through hls4ml. The layers are replaced by a fused line-buffer kernel that keeps only the resident rows of the frame in a shift register, rather than materialising the whole gathered tensor.

import hls4ml
from keras_hexagdly.hls4ml_handler import register_hex_gather_layers
from keras_hexagdly.hls4ml_ext import patch_model_for_hls, hex_reuse_config, check_hls_config

register_hex_gather_layers("Vitis")
hls_ready = patch_model_for_hls(model)                 # strategy="linebuffer"

config = hls4ml.utils.config_from_keras_model(hls_ready, granularity="name")
hex_reuse_config(config, hls_ready)                    # per-layer ReuseFactor
check_hls_config(config, hls_ready, io_type="io_stream")

hls_model = hls4ml.converters.convert_from_keras_model(
    hls_ready, hls_config=config, io_type="io_stream", backend="Vitis",
)

Supported scope

Only io_stream and the 2D layers (Conv2d, MaxPool2d) are supported for now. That is the combination that is covered by C-simulation and validated by RTL co-simulation.

io_stream io_parallel
Conv2d, MaxPool2d supported not supported
Conv3d, MaxPool3d not supported not supported

The unsupported paths are not silently wrong -- they raise. patch_model_for_hls raises NotImplementedError on a 3D layer, and check_hls_config raises on io_type="io_parallel". Both accept allow_unvalidated=True if you want to experiment with them anyway, but nothing about their numerics or resource usage is guaranteed.

hex_reuse_config matters more than it looks: hls4ml's config_from_keras_model(granularity="name") writes ReuseFactor=1 into every layer entry it recognises, which overrides the model-level value -- but custom layers get no entry and inherit the model-level one instead. Without an explicit per-layer setting, a single model ends up mixing two different reuse factors.

Testing

pip install -e .[dev] --no-build-isolation   # see note below
pytest tests/

(--no-build-isolation: only needed if your pip is old -- pip 22.0.2's isolated build environment was observed to pick up a setuptools version that mis-names the built wheel UNKNOWN. Verified clean with a modern pip (>=23) in a fresh venv: plain pip install . works with no workaround. Either way, pytest tests/ works without installing anything -- conftest.py puts src/ and tests/ on sys.path.)

Most of the suite no longer lives here. It has moved to hexagdly-oracle, the shared test repo for this library and pytorch-hexagdly: hand-verified layer outputs, share_neighbors weight-sharing oracles, depth_padding, mixed precision, serialization, edge cases, indexed-equivalence and the hls4ml export tests (including C-simulation). tests/ here keeps only what is genuinely local.

To run the full suite, check the oracle out as a sibling directory:

git clone https://github.com/YugnatD/hexagdly-oracle
PYTHONPATH=hexagdly-oracle/src pytest tests/ hexagdly-oracle/tests/

Verified to pass on all three Keras 3 backends (set KERAS_BACKEND=tensorflow|torch|jax before importing keras; tensorflow is the default if unset):

KERAS_BACKEND=tensorflow   # 891 passed,  7 skipped
KERAS_BACKEND=torch        # 881 passed, 17 skipped
KERAS_BACKEND=jax          # 806 passed, 92 skipped (slower: per-shape JIT compile)

A GitHub Actions workflow (.github/workflows/test.yml) runs this matrix (3 backends x 3 Python versions) plus ruff check/ruff format --check on every push and PR, checking out the oracle repo as part of the job.

Note for GPU users on the torch backend: the Keras torch backend runs on CUDA when a GPU is visible, and PyTorch defaults to cudnn.allow_tf32 = True, so convolutions are computed in TF32 (~1e-3 relative precision). That is enough to break equivalence assertions on hex kernels, which are wide by construction because dilation is done by zero insertion. The oracle's conftest.py pins the flag off for the test session; the library itself never touches global torch settings.

Disclaimer

Like upstream HexagDLy, this is a prototyping tool: it favors flexibility over performance. Once a model's architecture (kernel size, stride, input shape) is fixed, hard-coding those parameters would yield a faster implementation.

Performance

See benchmarks/ for a speed comparison against upstream PyTorch HexagDLy. Short version: run eagerly on CPU, this port is 1-7x slower than upstream for the same reason upstream itself is slow (the hex sub-kernel decomposition costs several op-dispatches per call -- a design choice, not a regression). Wrapped in a compiled call (jax.jit/ tf.function, which model.fit/model.predict do automatically) it is typically faster than upstream's eager PyTorch, sometimes by an order of magnitude. torch.compile support is currently unreliable for this layer (see the benchmarks README for why); eager execution on a GPU is the recommended way to get speed on the torch backend.

Changelog

See CHANGELOG.md.

License

MIT, see LICENSE. This is a derivative work of HexagDLy (Copyright (c) 2018 ai4iacts); see NOTICE.md.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

keras_hexagdly-0.3.1.tar.gz (2.6 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

keras_hexagdly-0.3.1-py3-none-any.whl (86.5 kB view details)

Uploaded Python 3

File details

Details for the file keras_hexagdly-0.3.1.tar.gz.

File metadata

  • Download URL: keras_hexagdly-0.3.1.tar.gz
  • Upload date:
  • Size: 2.6 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for keras_hexagdly-0.3.1.tar.gz
Algorithm Hash digest
SHA256 7f77f839635ae0afadb5062cbefe6e49368abdc0a1b4a3d0d91ab439033f8b07
MD5 37ca9f6a3f599becd29500cd2b599608
BLAKE2b-256 105791921b3480d1a474af59a34f747801ef3710b3f6118d318c239598cb986f

See more details on using hashes here.

Provenance

The following attestation bundles were made for keras_hexagdly-0.3.1.tar.gz:

Publisher: publish.yml on YugnatD/keras-hexagdly

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file keras_hexagdly-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: keras_hexagdly-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 86.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for keras_hexagdly-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5054c794dc883cafe95ad8153a5804a8adbd0310a6e2bf345002ee5304b9178a
MD5 c1c6e28d0c700ac3be8b7b6864c91837
BLAKE2b-256 e58303251aee50fdabe79abdd5d79773635f9588331f9fac1f98adf7d214716a

See more details on using hashes here.

Provenance

The following attestation bundles were made for keras_hexagdly-0.3.1-py3-none-any.whl:

Publisher: publish.yml on YugnatD/keras-hexagdly

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

This release

0.3.1 This release

2 files

0.3.0

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page