- Implement CS2 min-cost-flow adaptor and generalize common min-cost-flow stuff
[match/match.git] / program / NaiveMinCostFlow.hs
... / ...
CommitLineData
1module NaiveMinCostFlow (minCostFlow) where
2import IMinCostFlow
3import BellmanFord
4import MonadStuff
5import Data.Array.IArray
6import Data.Array.ST
7import Control.Monad
8import Control.Monad.ST
9import Data.Graph.Inductive.Graph
10import Data.Graph.Inductive.Internal.RootPath
11import Data.List
12
13data MCFEdge i f c = MCFEdge {
14 edgeIdx :: i,
15 edgeCap :: f,
16 edgeCost :: c,
17 edgeIsRev :: Bool
18}
19data MCFState s gr a b i f c = MCFState {
20 mcfGraph :: gr a (MCFEdge i f c),
21 mcfSource :: Node,
22 mcfSink :: Node,
23 mcfFlow :: STArray s i f
24}
25
26edgeCapLeft :: (Graph gr, Ix i, Real f, Real c) => MCFState s gr a b i f c ->
27 MCFEdge i f c -> ST s f
28edgeCapLeft state (MCFEdge i cap _ isRev) = do
29 fwdFlow <- readArray (mcfFlow state) i
30 return (if isRev then fwdFlow else cap - fwdFlow)
31
32edgePush :: (Graph gr, Ix i, Real f, Real c) => MCFState s gr a b i f c ->
33 MCFEdge i f c -> f -> ST s ()
34edgePush state (MCFEdge i _ _ isRev) nf = do
35 oldFwdFlow <- readArray (mcfFlow state) i
36 let newFwdFlow = if isRev then oldFwdFlow - nf else oldFwdFlow + nf
37 writeArray (mcfFlow state) i newFwdFlow
38
39pathCapLeft :: (Graph gr, Ix i, Real f, Real c) => MCFState s gr a b i f c ->
40 (MCFEdge i f c, BFPath (MCFEdge i f c) c) -> ST s f
41pathCapLeft state (lastEdge, BFPath _ _ mFrom) = do
42 lastCL <- edgeCapLeft state lastEdge
43 case mFrom of
44 Nothing -> return lastCL
45 Just from -> do
46 fromCL <- pathCapLeft state from
47 return (min lastCL fromCL)
48
49augment :: (Graph gr, Ix i, Real f, Real c) => MCFState s gr a b i f c ->
50 f -> BFPath (MCFEdge i f c) c -> ST s ()
51augment state augAmt (BFPath _ _ mFrom) = case mFrom of
52 Nothing -> nop
53 Just (lastEdge, path1) -> do
54 edgePush state lastEdge augAmt
55 augment state augAmt path1
56
57doFlow :: forall s gr a b i f c. (Graph gr, Ix i, Real f, Real c) => MCFState s gr a b i f c ->
58 ST s ()
59doFlow state = do
60 filteredEdges <- filterM (\(_, _, l) -> do
61 ecl <- edgeCapLeft state l
62 return (ecl /= 0)
63 ) (labEdges (mcfGraph state))
64 let filteredGraph = mkGraph (labNodes (mcfGraph state)) filteredEdges :: gr a (MCFEdge i f c)
65 -- Why won't we get a negative cycle? The original graph from the
66 -- proposal matcher is acyclic (so no negative cycle), and if we
67 -- created a negative cycle, that would contradict the fact that we
68 -- always augment along the lowest-cost path.
69 let mAugPath = bellmanFord edgeCost (mcfSource state) filteredGraph
70 ! (mcfSink state)
71 case mAugPath of
72 Nothing -> nop -- Done.
73 -- source /= sink, so augPasth is not empty.
74 Just augPath@(BFPath _ _ (Just from)) -> do
75 augAmt <- pathCapLeft state from
76 augment state augAmt augPath
77 doFlow state
78
79-- We need to put the type parameters in scope for the mkGraph call.
80minCostFlow :: forall gr a b i f c. (Graph gr, Ix i, Real f, Real c) => MinCostFlowImpl1 gr a b i f c
81minCostFlow idxBounds edgeIdx edgeCap edgeCost theGraph (source, sink) = runSTArray (do
82 let ourFlipF isRev l =
83 MCFEdge (edgeIdx l) (edgeCap l)
84 (if isRev then -(edgeCost l) else edgeCost l)
85 isRev
86 let graph2 = mkGraph (labNodes theGraph) (concatMap
87 (\(n1, n2, l) -> [ -- Capacity of reverse edge is never used.
88 (n1, n2, MCFEdge (edgeIdx l) (edgeCap l) ( edgeCost l ) False),
89 (n2, n1, MCFEdge (edgeIdx l) undefined (-(edgeCost l)) True )
90 ]) $ labEdges theGraph) :: gr a (MCFEdge i f c)
91 -- Initialize only the slots of the flow array corresponding to
92 -- existing edges to zero to catch buggy callers that query
93 -- nonexistent edges.
94 flow <- newArray idxBounds undefined
95 sequence $ map (\(_, _, l) -> writeArray flow (edgeIdx l) 0)
96 (labEdges theGraph)
97 let state = MCFState graph2 source sink flow
98 doFlow state
99 return (mcfFlow state)
100 )