gdsnorm gives a GDS layout (or a gdsfactory Component) a stable content hash: the same value across runs and
machines, regardless of the order polygons were drawn, which vertex a polygon starts on, which way it winds, or
sub-grid float noise in the coordinates. Two layouts that draw the same shapes hash identically; a real change
does not. That makes the hash a reproducible regression key for CI, and a fast way to ask "are these two
layouts the same?" without a pixel diff.
- polygon order within a cell, and cell/label/reference order,
- the starting vertex of a polygon and its winding direction,
- coordinate noise smaller than the database grid.
pip install -r requirements.txt
numpy and gdstk are the dependencies. gdsfactory is optional and used only to read a Component; a GDS
file works without it.
From the command line, write a hash manifest for one layout, or compare two:
python cli.py layout.gds -o out/ # writes hashes.json and manifest.md
python cli.py a.gds b.gds # prints "canonically identical" or the first difference
The two-file form exits non-zero when the layouts differ, so it works as a CI gate.
From Python:
import load, cellhash, equivalence
a = load.from_gds("a.gds")
b = load.from_gds("b.gds")
print(cellhash.library_hashes(a)["library"]) # the stable library hash
print(equivalence.canonically_equal(a, b)) # (True, None) or (False, "cell 'x' differs")
# from a gdsfactory Component:
# cellhash.library_hashes(load.from_component(component))A runnable example is in examples/quickstart.py: it builds one layout two ways (polygons reordered and nudged
by less than a grid step) and shows the hashes match.
| Module | What it does |
|---|---|
load.py |
read a GDS / Component into a model that keeps the geometry |
canon.py |
snap to grid and put each polygon in a canonical vertex order and winding; sort everything |
cellhash.py |
a stable sha256 of the canonical form, per cell and per library |
equivalence.py |
compare two layouts and name the first differing cell |
report.py |
a hash manifest and a comparison verdict as JSON and Markdown |
cli.py |
python cli.py a.gds [b.gds] end to end |
python run_checks.py validate <name> # one module's self-check
python run_checks.py validate-all # every module's self-check
python run_checks.py suite # pytest tests/
The canonicaliser is validated by giving it the same square reordered, reverse-wound, and with sub-grid noise and confirming all forms collapse to one canonical tuple, while a real shift changes it. The hash is validated by adding the same two rectangles in either order and confirming the hash matches, then moving one and confirming it does not.
gdsnorm canonicalises and hashes geometry; it does not diff or render (for a human-readable structural diff see a layout review tool) and it is not a DRC. The canonicalisation is a plain Python pass per polygon: that is deliberate, since polygons are usually small and very many, and per-polygon numpy vectorisation measured about three times slower. The cost scales with polygon count (tens of thousands of polygons in well under a second), so the read of a large GDS can dominate.