Commit | Line | Data |
---|---|---|
967c39ef | 1 | module ProposalMatcher where |
d7d9561e MM |
2 | import Data.Array.IArray |
3 | import Data.Graph.Inductive.Graph | |
4 | import Data.Graph.Inductive.Tree | |
5 | import Data.List | |
578d7d98 | 6 | import Data.Either |
d7d9561e | 7 | |
d3852a83 | 8 | import ArrayStuff |
578d7d98 | 9 | import MonadStuff |
05a6f0ed | 10 | import PMInstance |
bc14b3b3 | 11 | import PMConfig |
8c5ee850 MM |
12 | |
13 | prefBoringness cfg p = if prefIsVeryBoring cfg p then 2 | |
14 | else if prefIsBoring cfg p then 1 else 0 | |
56b565b1 MM |
15 | expExpertness cfg x = if expIsExpert cfg x then 2 |
16 | else if expIsKnowledgeable cfg x then 1 else 0 | |
2e7d5426 | 17 | |
5a07db44 MM |
18 | data REdge = REdge { |
19 | reIdx :: Int, | |
20 | reCap :: Int, | |
21 | reCost :: Wt | |
22 | } | |
23 | ||
24 | instance Show REdge where | |
25 | show (REdge idx cap cost) = "#" ++ (show idx) ++ ": " | |
26 | ++ (show cap) ++ " @ " ++ (show cost) | |
27 | ||
28 | data ReductionResult = ReductionResult { | |
29 | rrGraph :: Gr () REdge, | |
30 | rrSource :: Node, | |
31 | rrSink :: Node, | |
32 | rrEIdxBounds :: (Int, Int), | |
33 | rrEDIdx :: (Int, Int) -> Int | |
34 | } | |
35 | ||
36 | -- Hack: show as much of the reduction result as we easily can | |
37 | data RR1 = RR1 (Gr () REdge) Node Node (Int, Int) deriving Show | |
38 | instance Show ReductionResult where | |
39 | show (ReductionResult g so si eib _) = show (RR1 g so si eib) | |
40 | ||
41 | indexEdges :: Int -> [(Int, Int, REdge)] -> (Int, [(Int, Int, REdge)]) | |
42 | indexEdges i [] = (i, []) | |
43 | indexEdges i ((v1, v2, re):es) = | |
44 | let (imax, ies) = indexEdges (i+1) es in | |
45 | (imax, (v1, v2, re{ reIdx = i }) : ies) | |
46 | ||
d3852a83 MM |
47 | implies :: Bool -> Bool -> Bool |
48 | x `implies` y = (not x) || y | |
49 | ||
05a6f0ed | 50 | doReduction :: PMConfig -> PMInstance -> ReductionResult |
578d7d98 | 51 | doReduction cfg (PMInstance numRvrs numProps rloadA prefA expA fixA pnrA) = |
d7d9561e | 52 | let |
d3852a83 MM |
53 | -- Need to figure out who is PC/ERC |
54 | isPC = (funcArray (0, numRvrs-1) (\i -> (rloadA ! i) == 1)) :: Array Int Bool | |
55 | isPCPaper = (funcArray (0, numProps-1) (\j -> all (\i -> (isPC ! i) `implies` (prefIsConflict cfg $ (prefA ! (i, j)))) [0 .. numRvrs - 1])) :: Array Int Bool | |
d7d9561e MM |
56 | source = 0 |
57 | sink = 1 | |
2e7d5426 | 58 | rvrNode i boringness = 2 + 3*i + boringness |
d3852a83 MM |
59 | -- We will waste a lot of nodes. Who cares, no one will visit them. |
60 | propNode j k = 2 + 3*numRvrs + 7*j + k | |
61 | numNodes = 2 + 3*numRvrs + 7*numProps | |
5a07db44 | 62 | edIdx (i, j) = i*numProps + j |
d7d9561e MM |
63 | in |
64 | let | |
578d7d98 | 65 | totalReviews = sum $ elems pnrA -- (reviewsEachProposal cfg) * numProps |
967c39ef | 66 | totalRelativeLoad = foldl (+) 0 (map (rloadA !) [0 .. numRvrs - 1]) |
d3852a83 MM |
67 | -- floor goes best with loadTolerance 2 |
68 | targetLoad i = floor (widenInteger totalReviews * (rloadA ! i) / totalRelativeLoad) - 1 | |
17e0995d | 69 | -- Edge groups A through H are indicated in the figure in the paper. |
2e7d5426 | 70 | edgesABC = do |
d7d9561e | 71 | i <- [0 .. numRvrs - 1] |
2e7d5426 | 72 | let tl = targetLoad i |
578d7d98 | 73 | let lt = if isPC ! i then loadTolerance cfg else ercLoadTolerance cfg |
5a07db44 MM |
74 | let freeEdgeA = (source, rvrNode i 0, REdge undefined tl 0) |
75 | let nonfreeEdgesA = do | |
578d7d98 MM |
76 | l <- [tl .. tl + lt - 1] |
77 | let costA = marginalLoadCost cfg ((widenInteger (l - tl) + 1/2) / widenInteger lt) | |
d3852a83 | 78 | return (source, rvrNode i 0, REdge undefined 1 costA) |
5a07db44 | 79 | let edgesBC = do |
578d7d98 | 80 | l <- [0 .. tl + lt - 1] |
96fe6497 | 81 | let costB = marginalBoringCost cfg ((widenInteger l + 1/2) / widenInteger tl) |
5a07db44 | 82 | let edgeB = (rvrNode i 0, rvrNode i 1, REdge undefined 1 costB) |
96fe6497 | 83 | let costC = marginalVeryBoringCost cfg ((widenInteger l + 1/2) / widenInteger tl) |
5a07db44 MM |
84 | let edgeC = (rvrNode i 1, rvrNode i 2, REdge undefined 1 costC) |
85 | [edgeB, edgeC] | |
86 | [freeEdgeA] ++ nonfreeEdgesA ++ edgesBC | |
578d7d98 | 87 | edgesDFix = do |
d7d9561e MM |
88 | i <- [0 .. numRvrs - 1] |
89 | j <- [0 .. numProps - 1] | |
967c39ef | 90 | let pref = prefA ! (i, j) |
56b565b1 | 91 | let xp = expA ! (i, j) |
fd0d2377 MM |
92 | -- We must generate an edge even if there is a conflict |
93 | -- of interest; otherwise we'll fail to read its flow | |
94 | -- value in doMatching. | |
d3852a83 | 95 | let xp_ = expExpertness cfg xp |
578d7d98 | 96 | let pn = propNode j $ if (isPC ! i) |
d3852a83 MM |
97 | then xp_ + 3 -- Can assume it is a PC paper, otherwise it would conflict anyway. |
98 | else xp_ | |
578d7d98 MM |
99 | let rn = rvrNode i (prefBoringness cfg pref) |
100 | if fixA ! (i, j) | |
101 | -- Max flow will emulate one unit of flow through the edge, | |
102 | -- at a cost of increasing the total flow value by 1. | |
103 | then [Right (rn, sink, REdge undefined 1 0), | |
104 | Right (source, pn, REdge undefined 1 0)] | |
105 | else [Left (rn, pn, REdge (edIdx (i, j)) | |
8c5ee850 MM |
106 | (if prefIsConflict cfg pref then 0 else 1) |
107 | (assignmentCost cfg pref))] | |
578d7d98 MM |
108 | edgesD = lefts edgesDFix |
109 | edgesFix = rights edgesDFix | |
5a07db44 | 110 | edgesEFGH = do |
d7d9561e | 111 | j <- [0 .. numProps - 1] |
d3852a83 | 112 | -- This is now different... |
578d7d98 | 113 | let numReviews = pnrA ! j |
d3852a83 MM |
114 | if isPCPaper ! j |
115 | then do -- Mostly traditional. | |
116 | -- Expert bonus | |
117 | let edgeFFirst = (propNode j 2, propNode j 1, REdge undefined 1 (-(expertBonus cfg))) | |
578d7d98 | 118 | let edgeFRest = (propNode j 2, propNode j 1, REdge undefined (numReviews - 1) 0) |
d3852a83 MM |
119 | -- Second kowledgeable bonus |
120 | let edgeGFirst = (propNode j 1, propNode j 0, REdge undefined 1 (-(knowledgeableBonus cfg))) | |
578d7d98 | 121 | let edgeGRest = (propNode j 1, propNode j 0, REdge undefined (numReviews - 2) 0) |
d3852a83 MM |
122 | -- Require one knowledgeable |
123 | let edgeH1 = (propNode j 1, sink, REdge undefined 1 0) | |
578d7d98 | 124 | let edgeH = (propNode j 0, sink, REdge undefined (numReviews - 1) 0) |
d3852a83 MM |
125 | [edgeFFirst, edgeFRest, edgeGFirst, edgeGRest, edgeH1, edgeH] |
126 | else do -- New gadget; man, a lot of edges | |
578d7d98 MM |
127 | let numPCReviews = pcReviewsEachProposal cfg |
128 | if numReviews < numPCReviews then fail "numReviews for paper < numPCReviews" else nop | |
d3852a83 | 129 | -- Structure to distribute knowledgeable PC members |
578d7d98 MM |
130 | let edgesP = [(propNode j k, propNode j 6, REdge undefined numPCReviews 0) | k <- [4 .. 5]] |
131 | let edgesR = [(propNode j k, propNode j (k - 3), REdge undefined (numReviews - numPCReviews) 0) | k <- [4 .. 5]] | |
d3852a83 | 132 | -- "Designated knowledgeable" with expert bonus |
578d7d98 MM |
133 | let edgeF = (propNode j 2, propNode j 1, REdge undefined (numReviews - numPCReviews) (-(expertBonus cfg))) |
134 | let edgeH1 = (propNode j 1, sink, REdge undefined (numReviews - numPCReviews) 0) | |
d3852a83 MM |
135 | -- "Designated PC" with knowledgeable bonus |
136 | let edgeGFirst = (propNode j 6, propNode j 3, REdge undefined 1 (-(knowledgeableBonus cfg))) | |
59c94dee MM |
137 | let edgeGRest = (propNode j 6, propNode j 3, REdge undefined (numPCReviews - 1) 0) |
138 | let edgeH = (propNode j 3, sink, REdge undefined (numPCReviews) 0) | |
d3852a83 | 139 | edgesP ++ edgesR ++ [edgeF, edgeH1, edgeGFirst, edgeGRest, edgeH] |
2e7d5426 | 140 | theNodes = [(x, ()) | x <- [0 .. numNodes - 1]] |
5a07db44 | 141 | -- Index the non-D edges |
578d7d98 | 142 | unindexedEdges = edgesABC ++ edgesFix ++ edgesEFGH |
5a07db44 MM |
143 | (imax, reindexedEdges) = indexEdges (numRvrs*numProps) unindexedEdges |
144 | theEdges = edgesD ++ reindexedEdges | |
d7d9561e | 145 | in |
5a07db44 | 146 | ReductionResult (mkGraph theNodes theEdges) source sink (0, imax-1) edIdx |
d7d9561e | 147 | |
d7d9561e | 148 | -- Returns a list of reviews as ordered pairs (reviewer#, proposal#). |
05a6f0ed | 149 | doMatching :: PMConfig -> PMInstance -> PMatching |
578d7d98 | 150 | doMatching cfg inst@(PMInstance numRvrs numProps _ _ _ fixA _) = |
8c5ee850 MM |
151 | let ReductionResult graph source sink idxBounds edIdx = doReduction cfg inst in |
152 | let flowArray = minCostFlow cfg idxBounds reIdx reCap reCost graph (source, sink) in | |
d7d9561e MM |
153 | let pairs = do |
154 | i <- [0 .. numRvrs - 1] | |
5a07db44 | 155 | j <- [0 .. numProps - 1] |
578d7d98 | 156 | if fixA ! (i, j) || flowArray ! edIdx (i, j) == 1 |
5a07db44 | 157 | then [(i, j)] |
2e7d5426 MM |
158 | else [] |
159 | in | |
56b565b1 | 160 | PMatching (sort pairs) -- for prettiness |