{- |
Arrange resistors according to a tree of parallel and serial compositions.
Compare resistance of trees with the general graph resistance computation.
-}
module Tree where
import qualified Math.LinearCircuit as LinearCircuit
import qualified Test.QuickCheck as QC
import qualified Data.Graph.Comfort as Graph
import Data.Graph.Comfort (Graph)
import qualified Control.Monad.Trans.Class as MT
import qualified Control.Monad.Trans.State as MS
import Control.Monad (liftM, liftM2, replicateM)
import qualified Data.Map as Map; import Data.Map (Map)
import qualified Data.NonEmpty.Class as NonEmptyC
import qualified Data.NonEmpty as NonEmpty
import qualified Data.Foldable as Fold
import qualified Data.List.Match as Match
import Data.Functor.Classes
(Eq1, Ord1, Show1, liftEq, liftCompare, liftShowsPrec)
import Data.Monoid (mappend)
import Data.Ord.HT (comparing)
import Data.Eq.HT (equating)
data T =
Resistance Double
| Serial (NonEmpty.T [] T)
| Parallel (NonEmpty.T [] T)
deriving (Show)
instance QC.Arbitrary T where
arbitrary =
let res = liftM Resistance $ QC.choose (0,1)
go 0 = res
go size =
let subTree n =
let x = QC.resize (div size n) QC.arbitrary
in liftM2 NonEmpty.cons x (replicateM (n-1) x)
in QC.frequency $
(3, res) :
(1, liftM Serial (QC.choose (1,size) >>= subTree)) :
(1, liftM Parallel (QC.choose (1,size) >>= subTree)) :
[]
in QC.sized go
shrink tree =
case tree of
Resistance res ->
let simpleRess = [0,1]
in if elem res simpleRess
then []
else map Resistance simpleRess
Parallel xs -> NonEmpty.flatten xs ++ map Parallel (QC.shrink xs)
Serial xs -> NonEmpty.flatten xs ++ map Serial (QC.shrink xs)
parallel2 :: Double -> Double -> Double
parallel2 0 0 = 0
parallel2 x y = x*y / (x+y)
treeResistance :: T -> Double
treeResistance x =
case x of
Resistance res -> res
Serial xs -> Fold.foldl1 (+) $ fmap treeResistance xs
Parallel xs -> Fold.foldl1 parallel2 $ fmap treeResistance xs
newtype EdgeId = EdgeId Int
deriving (Eq, Ord, Show)
instance Enum EdgeId where
fromEnum (EdgeId n) = n
toEnum = EdgeId
newEdgeId :: (Monad m) => MS.StateT EdgeId m EdgeId
newEdgeId = do
n <- MS.get
MS.put $ succ n
return n
data Edge a =
Edge {
edgeId :: EdgeId,
edgeFrom, edgeTo :: a
}
deriving (Show)
instance Eq (Edge a) where (==) = equating edgeId
instance Ord (Edge a) where compare = comparing edgeId
instance Eq1 Edge where
liftEq _eq e0 e1 = edgeId e0 == edgeId e1
instance Ord1 Edge where
liftCompare _cmp e0 e1 = compare (edgeId e0) (edgeId e1)
instance Show1 Edge where
liftShowsPrec showsPrc _showList prec (Edge n from to) =
showParen (prec>10) $
showString "Edge"
. showChar ' ' . showsPrec 11 n
. showChar ' ' . showsPrc 11 from
. showChar ' ' . showsPrc 11 to
instance Fold.Foldable Edge where
foldMap f (Edge _ x y) = mappend (f x) (f y)
instance Graph.Edge Edge where
from (Edge _ n _) = n
to (Edge _ _ n) = n
instance Graph.Reverse Edge where
reverseEdge (Edge n from to) = Edge n to from
newtype Node = Node Int
deriving (Eq, Ord, Show)
instance Enum Node where
fromEnum (Node n) = n
toEnum = Node
newNode :: (Monad m) => MS.StateT Node m Node
newNode = do
n <- MS.get
MS.put $ succ n
return n
edgesFromTree ::
T -> (Node, Node) ->
MS.StateT EdgeId (MS.State Node) (Map (Edge Node) Double)
edgesFromTree tree (from, to) =
case tree of
Resistance res -> do
e <- newEdgeId
return $ Map.singleton (Edge e from to) res
Serial xs -> do
ns <- sequence $ Match.replicate (NonEmpty.tail xs) $ MT.lift newNode
fmap Map.unions $ sequence $
NonEmpty.flatten $
NonEmptyC.zipWith edgesFromTree xs $
NonEmpty.mapAdjacent (,) $
NonEmpty.cons from $ NonEmpty.snoc ns to
Parallel xs -> do
fmap Map.unions $ mapM (flip edgesFromTree (from,to)) $
NonEmpty.flatten xs
graphFromTree :: T -> (Graph Edge Node Double (), (Node, Node))
graphFromTree tree =
let ((edgeMap, globalEnds), lastNode) =
flip MS.runState (Node 0) $ flip MS.evalStateT (EdgeId 0) $ do
ends <- MT.lift $ liftM2 (,) newNode newNode
edges <- edgesFromTree tree ends
return (edges, ends)
in (Graph.fromMap
(Map.fromList $ map (flip (,) ()) [Node 0 .. pred lastNode])
edgeMap,
globalEnds)
graphResistance :: T -> Double
graphResistance =
uncurry (uncurry . LinearCircuit.resistance) . graphFromTree
data
FlippedGraph =
FlippedGraph (Graph Edge Node (Double, Bool) ()) (Node, Node)
deriving (Show)
instance QC.Arbitrary FlippedGraph where
arbitrary = do
(graph, ends) <- fmap graphFromTree QC.arbitrary
flpGraph <-
Graph.traverseEdge (\res -> liftM ((,) res) QC.arbitrary) graph
return $ FlippedGraph flpGraph ends
flippedResistances :: FlippedGraph -> (Double, Double)
flippedResistances (FlippedGraph graph ends) =
let flippedGraph =
Graph.fromMap
(Graph.nodeLabels graph)
(Map.fromList $
map
(\(e, (res, flp)) ->
(if flp then Graph.reverseEdge e else e, res)) $
Map.toList $ Graph.edgeLabels graph)
in (uncurry (LinearCircuit.resistance (Graph.mapEdge fst graph)) ends,
uncurry (LinearCircuit.resistance flippedGraph) ends)