Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
d9f2597
Add channel autorouter
gpeairs Sep 15, 2025
42152ca
Add grid escape test and fix autorouter
gpeairs Mar 6, 2026
6bfffe9
Add actual shortest-distance pathfinding, fix issue with merge groups…
gpeairs Mar 9, 2026
105abbb
Don't assign tracks multiple times
gpeairs Mar 9, 2026
acbae38
Allow endpoint overlap (knock-knees) only with opposite tendency
gpeairs Apr 2, 2026
624126e
WIP AutoChannelRouting
gpeairs Apr 2, 2026
d59dda2
Fix track offset/ordering in autorouter
gpeairs Apr 2, 2026
70128d1
Add autoroute examples
gpeairs Apr 2, 2026
d5a4318
Fix examples
gpeairs Apr 3, 2026
230a6f0
Fix incorrect against_channel after track assignment
gpeairs Apr 3, 2026
2ccf7f0
Fix set mutation during iteration
gpeairs Apr 3, 2026
9a5b77a
Fix iterator mutation bug, improve performance
gpeairs Apr 3, 2026
328eb04
Find VCG shortest paths more efficiently
gpeairs Apr 5, 2026
be1bd4e
Cleanup
gpeairs Apr 15, 2026
9089792
Minor example and test changes
gpeairs Apr 15, 2026
f7f4236
Add autorouter internals tests
gpeairs Apr 15, 2026
76ea73c
Refactor for DL-independent channel routing core
gpeairs Apr 17, 2026
cf78054
Add routing summary diagnostic and verbose autoroute option
gpeairs Apr 17, 2026
03cf786
Route validation, rerouting convenience + docs, tests
gpeairs Apr 17, 2026
d09167e
Ignore epsilon self-intersections in test
gpeairs Apr 17, 2026
e1311db
Move autoroute examples to examples/ and add to docs
gpeairs Apr 20, 2026
4ade74c
Add schematic example
gpeairs Apr 21, 2026
96bbdd9
Fix tests and remove more dead code
gpeairs Apr 21, 2026
46c130d
Cleanup redundant channels field and fuzzy lookup
gpeairs Apr 23, 2026
129c615
Cleanup
gpeairs Apr 24, 2026
2a1442e
Run formatter
gpeairs Apr 24, 2026
f174eda
Autorouter concept docs
gpeairs Apr 27, 2026
f20bf76
Use bipartite shape for best matching instead of dense symmetric adja…
gpeairs Apr 27, 2026
277267d
Add guards for short discretization grids
gpeairs Apr 28, 2026
a85b015
Remove escape test, fix docs
gpeairs Apr 28, 2026
2f1d863
Cleanup and improve visualizations
gpeairs Apr 28, 2026
bef5ea5
Minor cleanup, demo tapered channel
gpeairs Apr 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ version = "1.13.0"

[deps]
Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697"
BipartiteMatching = "79040ab4-24c8-4c92-950c-d48b5991a0f6"
Cairo = "159f3aea-2a34-519c-b102-8c37f9878175"
Clipper = "c8f6d549-b3ab-5508-a0d1-48fe138e8cc1"
ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
Expand Down Expand Up @@ -31,6 +32,7 @@ PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
Preferences = "21216c6a-2e73-6563-6e65-726566657250"
QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
SpatialIndexing = "d4ead438-fe20-5cc5-a293-4fd39a41b74c"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
ThreadSafeDicts = "4239201d-c60e-5e0a-9702-85d713665ba7"
Expand All @@ -47,6 +49,7 @@ SchematicGraphMakieExt = "GraphMakie"
[compat]
Accessors = "0.1"
Aqua = "0.8"
BipartiteMatching = "0.1.1"
Cairo = "1.0"
Clipper = "0.6"
ColorSchemes = "3"
Expand Down
4 changes: 3 additions & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ makedocs(
"Texts" => "concepts/texts.md",
"Paths" => "concepts/paths.md",
"Routes" => "concepts/routes.md",
"Channel Autorouter" => "concepts/channel_autorouter.md",
"Rendering and Export" => "concepts/render.md",
"Solid Models (3D Geometry)" => "concepts/solidmodels.md",
"Schematic-Driven Design" => "concepts/schematic_driven_design.md",
Expand All @@ -71,7 +72,8 @@ makedocs(
"Examples" => [
"ExamplePDK" => "examples/examplepdk.md",
"Quantum Processor" => "examples/qpu17.md",
"Single-Transmon Simulation" => "examples/singletransmon.md"
"Single-Transmon Simulation" => "examples/singletransmon.md",
"Channel Autorouter" => "examples/autorouter.md"
],
"Reference" => [
"Overview" => "reference/index.md",
Expand Down
152 changes: 152 additions & 0 deletions docs/src/concepts/channel_autorouter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Channel Autorouter

The channel autorouter connects pairs of pins by choosing paths through a user-defined
network of [`Paths.RouteChannel`](@ref)s. It is the multi-net, multi-channel
counterpart to the [`Paths.SingleChannelRouting`](@ref) rule described in
[Routes](./routes.md#Channel-routing): rather than having the user assign each route a
channel and a track by hand, the autorouter decides which channels each wire passes
through and which track it occupies within each channel.

!!! warning

Autorouting solutions may change between minor DeviceLayout.jl versions. Only breaking
changes to the autorouter API, not its output, will force major version bumps.
Autorouting is under active development, so upcoming minor versions are especially
likely to see changes in solutions for fixed routing problems.

## Problem setup

A channel-routing problem is described by three pieces of data:

- **Channels**: a list of [`Paths.RouteChannel`](@ref)s. Channels can be straight,
curved, tapered, or composite — any `Path` that is valid as a `RouteChannel` is
valid as an autorouter channel. Channels intersect where their centerlines cross;
the autorouter precomputes these intersections and treats them as the graph along
which wires may travel.
- **Pins**: a list of `PointHook`s giving the location and direction of
each pin. A pin's ray along its direction must hit a channel; the first
channel it hits becomes the pin's entry or exit channel.
- **Nets**: pairs `(i, j)` of pin indices that should be connected.

The autorouter then attempts to connect each net by routing a wire from its source
pin, through a sequence of channels, to its destination pin.

## How routing works

Routing happens in two stages:

1. **Channel assignment** picks, for each net, the sequence of channels its wire will
traverse. This is a shortest-path search in a graph whose vertices are channels and
pins and whose edges are channel intersections, weighted by physical distance
between successive intersections along each channel. Nets are processed one at a
time. Channel assignment does **not** currently take into account crossings,
congestion, or channel capacity.
2. **Track assignment** proceeds channel by channel. Within a channel, every wire
segment occupies an interval of arclength between its entry and exit. Segments
whose intervals overlap must be placed on distinct tracks. The autorouter builds a
vertical-constraint graph (which segment must be "above" which, given the crossings
that occur entering or leaving the channel at the interval endpoints) and picks a
feasible track order that uses as few tracks as possible, merging non-overlapping
segments onto shared tracks when compatible.

The algorithms follow the classical channel-routing literature (Hashimoto & Stevens;
Yoshimura & Kuh), with a crossing-aware variant due to Condrat, Kalla, & Blair to
handle channels shared by nets with avoidable crossings. See [References](#References) below.

Once channels and tracks have been assigned, the autorouter builds a
[`Paths.Route`](@ref) for each net. The
`Route`'s rule is a `Paths.AutoChannelRouting` that uses a user-supplied
**transition rule** (for the short legs between pins and channels, and between
adjacent channels) and a **margin** (how much room to leave for bends at each
transition). The transition rule is typically [`Paths.StraightAnd90`](@ref),
[`Paths.StraightAnd45`](@ref), or [`Paths.BSplineRouting`](@ref).

## Geometry-level usage

At the geometry level, construct a `Paths.ChannelRouter` from nets, pins, and
channels, then call `Paths.autoroute!`:

```julia
using DeviceLayout, .PreferredUnits
import DeviceLayout.Paths:
ChannelRouter, RouteChannel, autoroute!, visualize_router_state

# Two horizontal channels and one vertical channel, with crossed nets
channels = RouteChannel.([
(p = Path(5.0, -1.0; α0 = 90°); straight!(p, 8.0, Paths.Trace(2.0)); p),
(p = Path(-1.0, 0.0); straight!(p, 10.0, Paths.Trace(2.0)); p),
(p = Path(-1.0, 6.0); straight!(p, 10.0, Paths.Trace(2.0)); p)
])

hooks = [
PointHook(Point(2.0, -0.5), 270°), # below h_bot
PointHook(Point(8.0, -0.5), 270°), # below h_bot
PointHook(Point(2.0, 6.5), 90°), # above h_top
PointHook(Point(8.0, 6.5), 90°) # above h_top
]
nets = [(1, 4), (2, 3)] # crossed

ar = ChannelRouter(nets, hooks, channels)
routes = autoroute!(ar, Paths.StraightAnd90(0.1), 0.1) # transition rule, margin

c = visualize_router_state(ar; wire_width = 0.05)
```

The returned `routes` are [`Paths.Route`](@ref) values; convert any of them to a drawn
path with `Path(route, style)`. `visualize_router_state` returns a `Cell` that overlays
the channels, tracks, pin labels, and route geometry — useful for debugging an
unexpected assignment. To check whether every route actually reaches its destination,
call `Paths.validate_routes`.

Worked examples for common topologies (parallel, crossing, fan-in/fan-out, grid,
angled, B-spline, dense, many-net fan-out) live in the
[Channel Autorouter examples page](@ref channel-autorouter-examples).

## Schematic-level usage

At the schematic level, the autorouter is exposed as a [`Paths.RouteRule`](@ref):
construct a `Paths.AutoChannelRouting` from a `ChannelRouter` (or directly from a
vector of channels) and use it as the rule in
[`route!`](@ref route!(::SchematicDrivenLayout.SchematicGraph, ::Paths.RouteRule, ::Pair{SchematicDrivenLayout.ComponentNode, Symbol}, ::Pair{SchematicDrivenLayout.ComponentNode, Symbol}, ::Any, ::Any)).
Every route that shares a channel set should use the **same** rule instance, so that
the underlying router sees all nets:

```julia
ar = ChannelRouter(channels)
rule = Paths.AutoChannelRouting(ar, Paths.StraightAnd90(0.1mm), 0.1mm)

route!(g, rule, node1 => :port_a, node2 => :port_b, Paths.Trace(5μm), meta)
route!(g, rule, node3 => :port_a, node4 => :port_b, Paths.Trace(5μm), meta)

sch = plan(g)
```

Pin positions and directions are pulled from the component hooks at each route's
endpoints during `plan`, and channel assignment + track assignment run once these have
been set for the last route that uses `rule`. Schematic-level autorouting does not allow the user to
pre-assign tracks—while [`Paths.SingleChannelRouting`](@ref) uses the `track` keyword in `route!`
(defaulting to incrementing by 1 for each new route), `AutoChannelRouting` makes all
track assignments automatically during `plan`.

Unlike with `SingleChannelRouting`, the autorouter does not look for channels in the
schematic to find their global coordinates. Channels must be provided to the autorouter in global coordinates. Channels may still be added to a schematic, but this has no effect on routing.

## Limitations

- A pin's outward ray must hit exactly one channel. If no channel is in the ray's
path, or the pin points the wrong way, graph construction fails.
- Channels are assumed not to self-intersect; self-intersections are ignored with an
`@info` message.
- Two channels may intersect at most once. Re-entering the same channel pair is not
supported.
- Every net in a single `AutoChannelRouting` rule must start at a distinct pin. Nets
that share a source pin should be split into separate rules (or merged upstream).
- Track assignment does not care about proximity of slightly misaligned tracks
entering from different channels or pins; only exact alignment of entry/exit segments
and topologically avoidable crossings constrain track assignment.

## References

- Hashimoto & Stevens, ["Wire routing by optimizing channel assignment within large apertures"](https://cs.baylor.edu/~maurer/CSI5346/originalCR.pdf), *DAC '71: Proceedings of the 8th Design Automation Workshop* (1971).
- Yoshimura & Kuh, ["Efficient Algorithms for Channel Routing"](https://my.ece.utah.edu/~kalla/phy_des/yk.pdf), *IEEE Transactions on Computer-Aided Design of Integrated Circuits and Systems* (1982).
- Condrat, Kalla, & Blair, ["Crossing-aware Channel Routing for Photonic Waveguides"](https://my.ece.utah.edu/~kalla/papers/condrat_crossing-aware_channel_routing_for_photonic_waveguides.pdf), 2013 IEEE 56th International Midwest Symposium on Circuits and Systems (MWSCAS) (2013).
1 change: 1 addition & 0 deletions docs/src/concepts/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ How DeviceLayout handles geometric objects and their metadata.
- [Texts](texts.md): Text elements as geometric entities
- [Paths](paths.md): Transmission lines and other path-based geometry
- [Routes](routes.md): Defining Paths implicitly based on routing rules
- [Channel Autorouter](channel_autorouter.md): Multi-net routing through a channel network
- [Rendering and File Export](render.md): How geometry becomes output data
- [Solid Models](solidmodels.md): 3D geometry and meshing

Expand Down
2 changes: 2 additions & 0 deletions docs/src/concepts/routes.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ nothing; # hide
<img src="../compound_channel.svg" style="width:6in;"/>
```

For multi-net routing through a network of channels, where the router decides which channels each wire passes through and which track it occupies, see [Concepts: Channel Autorouter](./channel_autorouter.md).

## Routing in Schematics

We can add `Route`s between components in a schematic using [`route!(g::SchematicGraph, ...)`](@ref route!(::SchematicDrivenLayout.SchematicGraph, ::Paths.RouteRule, ::Pair{SchematicDrivenLayout.ComponentNode, Symbol}, ::Pair{SchematicDrivenLayout.ComponentNode, Symbol}, ::Any, ::Any)), creating flexible connections that are only resolved after floorplanning has determined the positions of the components to be connected. (For more about the schematic workflow, see [Concepts: Schematic-Driven Design](./schematic_driven_design.md))
Expand Down
144 changes: 144 additions & 0 deletions docs/src/examples/autorouter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# [Channel Autorouter](@id channel-autorouter-examples)

The channel autorouter connects pairs of pins by routing wires through a user-defined network of channels. Routing proceeds in two steps: **channel assignment** (choosing which channels each net's wire passes through) and **track assignment** (assigning non-overlapping tracks within each channel). See [Concepts: Channel Autorouter](./../concepts/channel_autorouter.md) for a conceptual overview.

The full code for these examples can be found [in `examples/ChannelAutorouter/ChannelAutorouter.jl` in the DeviceLayout.jl repository](https://github.com/aws-cqc/DeviceLayout.jl/blob/main/examples/ChannelAutorouter/ChannelAutorouter.jl).

```@example autorouter
using DeviceLayout, FileIO
include("../../../examples/ChannelAutorouter/ChannelAutorouter.jl")
using .ChannelAutorouter
nothing # hide
```

## Simple

One horizontal channel, two pins, one net. The simplest possible autorouting scenario.

```@example autorouter
c, ar = ChannelAutorouter.example_simple()
save("autoroute_simple.png", c); nothing # hide
```

```@raw html
<img src="../autoroute_simple.png"/>
```

## Parallel

Three nets routed left-to-right at matching heights through a grid of two vertical and three horizontal channels. No crossings needed.

```@example autorouter
c, ar = ChannelAutorouter.example_parallel()
save("autoroute_parallel.png", c); nothing # hide
```

```@raw html
<img src="../autoroute_parallel.png"/>
```

## Crossing

Two nets that must cross in a shared vertical channel, forcing the router to assign multiple tracks.

```@example autorouter
c, ar = ChannelAutorouter.example_crossing()
save("autoroute_crossing.png", c); nothing # hide
```

```@raw html
<img src="../autoroute_crossing.png"/>
```

There is also a variant of this example using the schematic routing interface.

## Fan-in / fan-out

Clustered pins on one side, spread-out pins on the other, routed through a single shared horizontal channel. The router assigns multiple tracks to accommodate the asymmetric spacing.

```@example autorouter
c, ar = ChannelAutorouter.example_fanin_fanout()
save("autoroute_fanin_fanout.png", c); nothing # hide
```

```@raw html
<img src="../autoroute_fanin_fanout.png"/>
```

## Multichannel fan-out

Same topology as fan-in/fan-out, but with a dedicated horizontal channel per net. Each net uses exactly one track.

```@example autorouter
c, ar = ChannelAutorouter.example_multichannel_fanout()
save("autoroute_multichannel_fanout.png", c); nothing # hide
```

```@raw html
<img src="../autoroute_multichannel_fanout.png"/>
```

## Grid

A 4×4 grid of horizontal and vertical channels with pins on different edges. Nets take multi-hop paths through the grid.

```@example autorouter
c, ar = ChannelAutorouter.example_grid()
save("autoroute_grid.png", c); nothing # hide
```

```@raw html
<img src="../autoroute_grid.png"/>
```

## Angled channels

Non-Manhattan channels: two 45° diagonals crossing a horizontal channel. The router handles arbitrary channel geometry.

```@example autorouter
c, ar = ChannelAutorouter.example_angled()
save("autoroute_angled.png", c); nothing # hide
```

```@raw html
<img src="../autoroute_angled.png"/>
```

## Dense

Six nets sharing just two horizontal and two vertical channels. Forces three tracks per channel.

```@example autorouter
c, ar = ChannelAutorouter.example_dense()
save("autoroute_dense.png", c); nothing # hide
```

```@raw html
<img src="../autoroute_dense.png"/>
```

## B-spline channels

Curved channels using B-spline geometry with `BSplineRouting` transitions. Same fan-in/fan-out topology as above but with non-straight channels and a tapered channel.

```@example autorouter
c, ar = ChannelAutorouter.example_bspline()
save("autoroute_bspline.png", c); nothing # hide
```

```@raw html
<img src="../autoroute_bspline.png"/>
```

## 100-net fan-out

100 nets fan out through a single wide channel from an inner row of pins to an outer row with twice the spacing.

```@example autorouter
c, ar = ChannelAutorouter.example_fanout100()
save("autoroute_fanout100.svg", c; width=8DeviceLayout.Unitful.inch); nothing # hide
```

```@raw html
<img src="../autoroute_fanout100.svg"/>
```
2 changes: 2 additions & 0 deletions docs/src/reference/path_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@
Paths.CompoundRouteRule
Paths.SingleChannelRouting
Paths.RouteChannel
Paths.ChannelRouter
Paths.AutoChannelRouting
```

### Route drawing
Expand Down
Loading
Loading