packages feed

penelope-core-0.1.0.0: src/Penelope/Core/Route.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}

{- |
Module      : Penelope.Core.Route
Description : Pure port routing, identity elision, and route peephole optimization
Copyright   : (c) 2026 The Penelope Authors
License     : EUPL-1.2 OR Apache-2.0
Maintainer  : nadia.yvette.chambers@ik.me
Stability   : experimental
Portability : non-portable (GHC extensions)

This module provides smart constructors and fusion routines for pure port wire
permutations over Polytropos 'RowMorph' structures.

See Note [Reified Row Morphisms and Gather-Scatter] [ref:note-row-morph-routing].
-}
module Penelope.Core.Route
  ( -- * Smart Constructors
    route
  , routeExplicit

    -- * Route Optimization & Fusion
  , fuseRoute
  , isIdentityRowMorph
  , isIdentityRoute
  ) where

import Data.Kind (Type)
import Data.Primitive.PrimArray (indexPrimArray, sizeofPrimArray)
import Penelope.Core.Syntax (Penelope (..), PolyEffect, PolyHEffect)
import Polytropos (RowOf)
import Polytropos.RowMorph
  ( KnownRowMorph (..)
  , RowMorph (..)
  , composeRowMorph
  )

{- |
Construct a pure wire permutation between input ports and output ports using
a compile-time synthesized 'RowMorph' solved by @Polytropos.Plugin@.

See Note [Reified Row Morphisms and Gather-Scatter] [ref:note-row-morph-routing].
-}
route ::
  forall {eh :: RowOf PolyHEffect} {ef :: RowOf PolyEffect} (ins :: RowOf Type) (outs :: RowOf Type).
  KnownRowMorph outs ins =>
  Penelope eh ef ins outs
{-# INLINE route #-}
route = Route (rowMorph @outs @ins)

-- | Construct a pure wire permutation with an explicit 'RowMorph'.
routeExplicit ::
  forall (eh :: RowOf PolyHEffect) (ef :: RowOf PolyEffect) (ins :: RowOf Type) (outs :: RowOf Type).
  RowMorph outs ins
  -> Penelope eh ef ins outs
{-# INLINE routeExplicit #-}
routeExplicit = Route

{- |
Fuse two consecutive pure route stages in \(O(|outs|)\) time with zero intermediate allocations.
Given a route from @a@ to @b@ ('RowMorph' @b@ @a@) and a route from @b@ to @c@ ('RowMorph' @c@ @b@),
computes the composite route from @a@ to @c@ ('RowMorph' @c@ @a@).
-}
fuseRoute ::
  RowMorph b a
  -> RowMorph c b
  -> RowMorph c a
{-# INLINE fuseRoute #-}
fuseRoute m1 m2 = composeRowMorph m2 m1

{- |
Check whether a 'RowMorph' represents the identity permutation (slot @i@ maps to @i@).
-}
isIdentityRowMorph :: RowMorph r1 r2 -> Bool
{-# INLINE isIdentityRowMorph #-}
isIdentityRowMorph (RowMorph arr) =
  let !n = sizeofPrimArray arr
      go !i
        | i >= n    = True
        | otherwise = indexPrimArray arr i == i && go (i + 1)
  in go 0

{- |
Check whether a 'Penelope' computation is an identity wire permutation.
-}
isIdentityRoute :: Penelope eh ef ins outs -> Bool
isIdentityRoute (Route m) = isIdentityRowMorph m
isIdentityRoute _         = False