packages feed

graph-rewriting-lambdascope-0.5.2: 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.Applicative
import Data.Monoid
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]]