packages feed

graph-rewriting-lambdascope-0.5.11: Rules.hs

{-# LANGUAGE UnicodeSyntax, FlexibleContexts, ScopedTypeVariables #-}
module Rules where

import Prelude.Unicode
import Graph
import GraphRewriting.Rule
import GraphRewriting.Pattern
import GraphRewriting.Pattern.InteractionNet
import GraphRewriting.Graph.Read
import GraphRewriting.Graph.Write
import Data.List (transpose, elemIndex, delete)
import Control.Monad

import Data.Maybe (fromJust)


compileShare ∷ (View [Port] n, View NodeLS n) ⇒ Rule n
compileShare = do
	Multiplexer {out = o, ins = is} ← node
	case is of
		[ ] → replace $ byNode Eraser {inp = o}
		[i] → rewire [[o,i]]
		ins → let (ins1, ins2) = splitAt (length ins `div` 2) ins in replace $ do
			(o1,o2) ← (,) <$> byEdge <*> byEdge
			byNode $ Duplicator {level = 0, inp = o, out1 = o1, out2 = o2}
			byNode $ Multiplexer {out = o1, ins = ins1}
			byNode $ Multiplexer {out = o2, ins = ins2}

withoutIdx ∷ [a] → Int → [a]
withoutIdx xs i = let (ys,zs) = splitAt i xs in ys ⧺ tail zs

insertIdx ∷ Int → a → [a] → [a]
insertIdx i x xs = let (l,r) = splitAt i xs in l ⧺ [x] ⧺ r

split ∷ Int → Int → [a] → [[a]]
split i n [] = replicate n []
split i n xs = let (x,xs') = splitAt i xs in x : split i n xs'

transpose' n [] = replicate n []
transpose' n xs = transpose xs

commute ∷ (View [Port] n, View NodeLS n) ⇒ Rule n
commute = do
	n1 :-: n2 ← activePair
	require (n1 ≢ n2) -- TODO: replace by linear
	let ports1 = inspect n1 ∷ [Port]
	let ports2 = inspect n2 ∷ [Port]
	let (pp1,pp1idx) = head [(p,i) | (p,i) ← ports1 `zip` [0..], p ≡ pp n1]
	let (pp2,pp2idx) = head [(p,i) | (p,i) ← ports2 `zip` [0..], p ≡ pp n2]
	let aux1 = pp1 `delete` inspect n1
	let aux2 = pp2 `delete` inspect n2
	let es1 = length aux1
	let es2 = length aux2
	replace $ do
		edges ← replicateM (es1 * es2) byEdge
		let edges1 = split es1 es2 edges
		let edges2 = transpose' es1 edges1
		mconcat [byNode $ updateLevel n2 $ update (insertIdx pp1idx pp1 auxs) n1 | (pp1,auxs) ← zip aux2 edges1]
		mconcat [byNode $ updateLevel n1 $ update (insertIdx pp2idx pp2 auxs) n2 | (pp2,auxs) ← zip aux1 edges2]
	where updateLevel you me = case me of
		Duplicator {} → maybeLevelUp
		Delimiter  {} → maybeLevelUp
		_ → me
		where maybeLevelUp = case you of
			Delimiter  {} → if level you ≤ level me then me {level = level me + 1} else me
			Abstractor {} → me {level = level me + 1}
			_ → me

annihilate ∷ (View [Port] n, View NodeLS n) ⇒ Rule n
annihilate = do
	n1 :-: n2 ← activePair
	require (n1 ≡ n2) -- TODO: ???
	let aux1 = pp n1 `delete` inspect n1
	let aux2 = pp n2 `delete` inspect n2
	rewire $ [[a1,a2] | (a1,a2) ← aux1 `zip` aux2]

annihilateDelimiters ∷ (View [Port] n, View NodeLS n) ⇒ Rule n
annihilateDelimiters = do
	rewrite ← annihilate
	Delimiter {} ← liftReader . inspectNode =<< previous
	return rewrite

-- This rule doesn't trigger for constants with arguments
eliminateDelimiterConstant ∷ (View [Port] n, View NodeLS n) ⇒ Rule n
eliminateDelimiterConstant = do
	c@Constant {args = as, name = n} :-: Delimiter {inp = iD} ← activePair
	require (inp c ≢ iD && as == [])
	replace $ byNode $ Constant {inp = iD, args = [], name = n}

eliminateDelimiterEraser ∷ (View [Port] n, View NodeLS n) ⇒ Rule n
eliminateDelimiterEraser = do
	c@Eraser {} :-: Delimiter {inp = iD} ← activePair
	require (inp c ≢ iD)
	replace $ byNode $ Eraser {inp = iD}

eliminateDuplicator ∷ (View [Port] n, View NodeLS n) ⇒ Rule n
eliminateDuplicator = do
	Eraser {inp = iE} ← node
	Duplicator {inp = iD, out1 = o1, out2 = o2} ← neighbour =<< previous
	require (iE ≡ o1 ∨ iE ≡ o2)
	if iE ≡ o1
		then rewire [[iD,o2]]
		else rewire [[iD,o1]]

eraser ∷ (View [Port] n, View NodeLS n) ⇒ Rule n
eraser = do
	rewrite ← commute
	Eraser {} ← liftReader . inspectNode =<< previous
	return rewrite

duplicate ∷ (View [Port] n, View NodeLS n) ⇒ Rule n
duplicate = do
	rewrite ← commute
	Duplicator {} ← liftReader . inspectNode =<< previous
	return rewrite

beta ∷ (View [Port] n, View NodeLS n) ⇒ Rule n
beta = do
	Applicator {inp = ai, func = f, arg = a} :-: Abstractor {body = b, var = v} ← activePair
	replace $ do
		byNode $ Delimiter {level = 0, inp = ai, out = b}
		byNode $ Delimiter {level = 0, inp = a, out = v}

commuteDelimiter ∷ (View [Port] n, View NodeLS n) ⇒ Rule n
commuteDelimiter = do
	rewrite ← commute
	Delimiter {} ← liftReader . inspectNode =<< previous
	return rewrite

applyConstant ∷ (View [Port] n, View NodeLS n) ⇒ Rule n
applyConstant = do
	Applicator {inp = i, arg = a} :-: Constant {name = n, args = as} ← activePair
	replace $ byNode $ Constant {inp = i, name = n, args = as ++ [a]}

applyOperator ∷ (View [Port] n, View NodeLS n) ⇒ Rule n
applyOperator = do
	Applicator {inp = i, arg = a} :-: Operator {ops = os, arity = ar, lmop = l, function = fn, name = n} ← activePair
	require (ar > length os)
	replace $ byNode $ Operator {inp = i, ops = os ++ [a], arity = ar, lmop = l, function = fn, name = n}

-- TODO: Require that the lmoPort is not on one of the unreduced ports yet
-- Do we only reduce operator args, if the operator has all args already?
reduceOperand ∷ (View [Port] n, View NodeLS n) ⇒ Rule n
reduceOperand = do
	o@(Operator {ops = os, lmop = lmo}) ← node
	opid ← previous
	let ports = inspect o ∷ [Port]
	let lmoport = ports !! lmo
	-- only change the lmo port if it is on top or if it is attached to a Constant
	require (lmo == 0) <|> do {Constant {} ← nodeWith lmoport; return ()}
	port ← branch os -- get a pattern that matches each port in os
	-- we require that at least one node attached to the operator is not a constant
	requireFailure $ do
		Constant {} ← nodeWith port
		return ()
	-- we need to add one, since the input port is not part of os, but is part of the port numbering
	let unreducedport = 1 + fromJust (elemIndex port os)
	return $ updateNode opid (o {lmop = unreducedport})

execOperator ∷ forall n. (View [Port] n, View NodeLS n) ⇒ Rule n
execOperator = do
	Operator {inp = i, ops = os, arity = ar, function = fn, name = n} ← node
	opid ← previous
	require (length os == ar)
	-- check that all args are constants
	argss ← forM os $ \o → do
			c@Constant {} ← adverse o opid
			return c
	case fn (map name argss) of
		Nothing → mempty
		Just n' → replace $ byNode $ Constant {inp = i, args = [], name = n'}

caseNode ∷ (View [Port] n, View NodeLS n) ⇒ Rule n
caseNode = do
	-- the order of constant and case here is important, otherwise strategies don't work
	Case {inp = i, alts = alts, names = names} :-: Constant {name = n, args = as} ← activePair
	let matchingport = alts !! (fromJust $ elemIndex n names)
	let nn = length alts
	let m = length as
	-- We generate m new applicator nodes with m+1 new edges connecting them
	if m > 0 then
		replace $ do
			es ← replicateM (m+1) byEdge
			byWire matchingport (es !! 0) -- We merge the first new edge with the matching port from the Case node
			byWire i (es !! m) -- We merge the last new edge with the input edge of the Case node
			mconcat [byNode $ Applicator {inp = es !! (i+1), func = es !! i, arg = as !! i} | i ← [0..m-1]]
			mconcat [byNode $ Eraser {inp = alts !! i} | i ← filter (/= fromJust (elemIndex n names)) [0..nn-1]]
			 else do
		replace $ do
			byWire i matchingport -- Attach the alternative directly to the input of the case node
			mconcat [byNode $ Eraser {inp = alts !! i} | i ← filter (/= fromJust (elemIndex n names)) [0..nn-1]]

-- | Not the readback semantics as defined in the paper. Just a non-semantics-preserving erasure of all
-- delimiters to make the graph more readable
readback ∷ (View [Port] n, View NodeLS n) ⇒ Rule n
readback = do
	Delimiter {inp = i, out = o} ← node
	rewire [[i,o]]