Skip to main content

pytorch-hexagdly — Hexagonal Convolutions for PyTorch

pytorch-hexagdly is a fork of HexagDLy that extends the original hexagonal convolution and pooling layers for PyTorch with ring-shared weights (share_neighbors), depth-axis same-padding (depth_padding="same" on Conv3d), hexagonal average pooling (AvgPool2d), an alternative kernel-size convention (kernel_size_unit) that reads kernel_size as a square-kernel width instead of a ring count, and torch.nn-compatible arguments, so the hex layers can replace nn.Conv2d / nn.MaxPool2d / nn.AvgPool2d in an existing model.

Getting Started

Pip Installation

pip install pytorch-hexagdly
import pytorch_hexagdly

To get the dependencies needed to run the provided unit tests and notebooks, add the dev option:

pip install pytorch-hexagdly[dev]

Manual Installation

Requires a working installation of PyTorch. Clone the repository and install in editable mode:

git clone https://github.com/YugnatD/pytorch-hexagdly
cd pytorch-hexagdly
pip install -e .

New Features

share_neighbors — weight sharing across kernel cells

Available on Conv2d and Conv3d. The share_neighbors parameter 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 total):

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° pairs share one weight.
  • "ring": the most aggressive reduction. All 6 direct neighbours share one weight, all 12 outer cells share another. Mirrors the TDSCAN triggering approach.
  • "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° 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 weights, sym=4 weights. For kernel_size=2 (19 cells): ring=3 weights, diag=10 weights, sym=10 weights. Compare to the default share_neighbors=None which gives 7 and 19 independent weights.

import torch
import pytorch_hexagdly

conv = pytorch_hexagdly.Conv2d(1, 8, kernel_size=2, stride=1, share_neighbors="ring")
x = torch.randn(1, 1, 21, 21)
print(conv(x).shape)

depth_padding="same" — temporal same-padding for Conv3d

When depth_padding="same", the depth/time axis is zero-padded symmetrically so the output depth equals the input depth. The default is "valid" (upstream behaviour).

conv3d = pytorch_hexagdly.Conv3d(1, 4, kernel_size=(3, 1), stride=1,
                                  depth_padding="same")
x = torch.randn(1, 1, 10, 21, 21)
print(conv3d(x).shape)  # depth dimension preserved

AvgPool2d — hexagonal average pooling

Each output cell is the mean over its hexagonal window (the same window as MaxPool2d). Cells past the grid border count in neither the sum nor the divisor, like nn.AvgPool2d(..., count_include_pad=False), so a constant image stays constant up to the border. Matches keras_hexagdly.AvgPool2d.

pool = pytorch_hexagdly.AvgPool2d(kernel_size=1, stride=2)
x = torch.randn(1, 4, 21, 21)
print(pool(x).shape)

Drop-in replacement for the torch.nn layers

Conv2d, MaxPool2d and AvgPool2d also accept padding, so a model can switch between the square and the hex layer classes and build both from the same keyword arguments:

Conv, Pool = (pytorch_hexagdly.Conv2d, pytorch_hexagdly.MaxPool2d) if hex_grid else (nn.Conv2d, nn.MaxPool2d)
x = Conv(4, 8, kernel_size=3, stride=1, padding=1)(x)
x = Pool(kernel_size=2, stride=2, padding=0)(x)

The hex grid is always padded so that every cell is a window centre, whatever the padding value: "same" (the default) and integer paddings are accepted and change nothing, and "valid" raises. Note that kernel_size counts hexagonal rings by default, not pixels: kernel_size=1 is a cell and its 6 neighbours -- see kernel_size_unit below for the alternative.

kernel_size_unit — reading kernel_size as a square-kernel width

kernel_size counts hexagonal rings by default (kernel_size_unit="rings"): kernel_size=1 is the 7-cell hexagon (a cell and its six neighbours), kernel_size=2 the 19-cell one. The same number therefore means a much larger kernel than on a square grid, where kernel_size=3 is 9 cells.

kernel_size_unit="pixels" reads kernel_size as a square-kernel width instead -- radius = kernel_size // 2 -- so the same architecture means a comparable receptive field on both grids, as in indexedconv's MaskedConv2d:

pytorch_hexagdly.Conv2d(4, 32, kernel_size=3, kernel_size_unit="pixels")  # the 7-cell hexagon
pytorch_hexagdly.Conv2d(4, 32, kernel_size=1, kernel_size_unit="pixels")  # a plain 1x1, no neighbours

kernel_size=0 (0 rings, a plain per-cell channel projection with no neighbours) is what kernel_size_unit="pixels" needs to express a true 1x1, and is accepted directly in "rings" mode too. A hexagonal window is centred on a cell, so it only exists for odd widths. An even width is rounded up to the next odd one (kernel_size=2 in "pixels" mode gives the 7-cell window of 3, not the single cell of 1, and 4 gives that of 5), and a UserWarning says so; use an odd width to silence it. Applies to MaxPool2d/AvgPool2d the same way; on Conv3d/MaxPool3d, only the hex element of the (depth, hex) size follows the unit, since depth is never a hex radius.

How to use pytorch-hexagdly

As pytorch-hexagdly is based on PyTorch, it is of advantage to be familiar with PyTorch's functionalities and concepts. Before applying it, ensure that the input data has the correct hexagonal layout. An example notebook illustrates the steps to get data into the correct format.

Basic example:

import torch
import pytorch_hexagdly

kernel_size, stride = 1, 4
in_channels, out_channels = 1, 3

hexconv = pytorch_hexagdly.Conv2d(in_channels, out_channels, kernel_size, stride)
input = torch.rand(1, 1, 21, 21)
output = hexconv(input)

HexagDLy uses an addressing scheme to map hexagonal grid data to a square tensor. The layout from top to bottom (along tensor index 2) must be of zig-zag-edge shape and from left to right (along tensor index 3) of armchair-edge shape.

Additional examples for basic use-cases are shown in the notebooks folder.

General Concept

As common deep learning frameworks process data on square grids, hexagonally sampled data must be mapped to a square tensor. This conversion is non-trivial due to the different symmetries of square (4-fold) vs hexagonal (6-fold) grids.

HexagDLy solves this by splitting each convolution kernel into sub-kernels that together cover the true neighbours of a data point in the hexagonal grid. A full hexagonal convolution with size 1 (next-neighbour kernel) decomposes into three sub-convolutions with two different sub-kernels applied to three differently padded versions of the input.

kerne size+stride

Please note: Operations are only performed where the center point of a kernel is located within the input tensor. This could result in output columns of different length; in such cases the output will be sliced according to the shortest column.

violating_symmetry

explicit_next_neighbour_conv

Disclaimer

pytorch-hexagdly is built as an easy-to-use prototyping tool to design convolutional neural networks for hexagonally sampled data. The implemented methods aim for flexibility rather than performance. Once a model is optimized, hard-coding kernel size, stride and input dimensions will make the implementation faster.

Authors

Fork (pytorch-hexagdly)

  • Tanguy Dietrich — HEPIA / SST-1M Collaboration

Original HexagDLy

  • Tim Lukas Holch
  • Constantin Steppa

See NOTICE.md for full attribution.

License

MIT license — see LICENSE.

Citation

If this work has helped your research, please cite the original HexagDLy paper:

@article{hexagdly_paper,
    title = "HexagDLy—Processing hexagonally sampled data with CNNs in PyTorch",
    author = "Constantin Steppa and Tim L. Holch",
    journal = "SoftwareX",
    volume = "9",
    pages = "193 - 198",
    year = "2019",
    issn = "2352-7110",
    doi = "https://doi.org/10.1016/j.softx.2019.02.010",
    url = "https://www.sciencedirect.com/science/article/pii/S2352711018302723",
    keywords = "Convolutional neural networks, Hexagonal grid, PyTorch, Astroparticle physics",
    abstract = "HexagDLy is a Python-library extending the PyTorch deep learning framework with convolution and pooling operations on hexagonal grids. It aims to ease the access to convolutional neural networks for applications that rely on hexagonally sampled data as, for example, commonly found in ground-based astroparticle physics experiments."
}

HexagDLy was developed as part of a research study in ground-based gamma-ray astronomy published in Astroparticle Physics.

Acknowledgments

The original HexagDLy project evolved by exploring new analysis techniques for Imaging Atmospheric Cherenkov Telescopes with H.E.S.S. The fork was developed in the context of the SST-1M Collaboration / HEPIA TDSCAN triggering project.

Metadata

Release files for pytorch-hexagdly 0.4.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytorch-hexagdly 0.4.1
File Size Uploaded Provenance
pytorch_hexagdly-0.4.1.tar.gz 4.5 MB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pytorch-hexagdly 0.4.1
File Interpreter ABI Platform
pytorch_hexagdly-0.4.1-py3-none-any.whl Python 3 none any Details

Total release size: 4.5 MB

Release files / pytorch_hexagdly-0.4.1.tar.gz

Download URL pytorch_hexagdly-0.4.1.tar.gz
Size 4.5 MB
Tags Source
SHA-256 checksum
How to use checksums
80fbd0486725a88c07e0ec3fec86f94e3c7247af7fad483b30f823e2a6ea1def
BLAKE2b-256 checksum
How to use checksums
0e718bdf7ae4416c688f3c2ff4c6a83e168d95ce528a0eca46380acd76aa1c44
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

Release files / pytorch_hexagdly-0.4.1-py3-none-any.whl

Download URL pytorch_hexagdly-0.4.1-py3-none-any.whl
Size 22.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f9d48eae90e8137f2bbf416c30d62d33362e07d69e80209821dd40f551b6b011
BLAKE2b-256 checksum
How to use checksums
1347ae868ef36531a306e2f62aab93a7db5a025156c6f6a9ee28d635c4925143
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

Security for pytorch-hexagdly 0.4.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for pytorch_hexagdly-0.4.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

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