packages feed

mixed-strategies (empty) → 0.1.0.0

raw patch · 5 files changed

+345/−0 lines, 5 filesdep +arraydep +basedep +containerssetup-changed

Dependencies added: array, base, containers, mixed-strategies, simple-tabular

Files

+ COPYING view
@@ -0,0 +1,25 @@+Copyright © 2011 Bart Massey++[This program is licensed under the "MIT License"]++Permission is hereby granted, free of charge, to any person+obtaining a copy of this software and associated+documentation files (the "Software"), to deal in the+Software without restriction, including without limitation+the rights to use, copy, modify, merge, publish, distribute,+sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so,+subject to the following conditions:++The above copyright notice and this permission notice shall+be included in all copies or substantial portions of the+Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY+KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS+OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Data/MixedStrategies.hs view
@@ -0,0 +1,216 @@+-- Copyright © 2011 Bart Massey+-- [This program is licensed under the "MIT License"]+-- Please see the file COPYING in the source+-- distribution of this software for license terms.++-- | Calculate an optimal mixed strategy given the payoff+-- matrix of a two-player zero-sum single-round iterated+-- simultaneous game.  Follows the method of Chapter 6 of+-- J.D. Williams' \"The Compleat Strategyst\" (McGraw-Hill+-- 1954). Throughout, the player playing the rows (strategies+-- on the left) is assumed to be the \"maximizer\" and the+-- player playing the columns (strategies on top) is assumed+-- to be the \"minimizer\".++module Data.MixedStrategies (+  Schema(..), +  Edge(..),+  readSchema, +  pivot, +  Solution(..), +  extractSolution, +  solved,+  solve+) where++import Prelude hiding (Left, Right, lookup)+import Data.Array+import Data.Function (on)+import Data.List hiding (lookup)+import Data.Map (lookup, fromList)+import Data.Ord (comparing)+import Text.Printf+import Text.SimpleTabular++dim :: Array Int t -> Int+dim a = let (1, ix) = bounds a in ix++dims :: Array (Int, Int) t -> (Int, Int)+dims a = let ((1, 1), ixs) = bounds a in ixs++-- | An 'Edge' of a 'Schema' contains annotations of the Schema.+data Edge = Left | Top | Right | Bottom deriving (Ord, Eq, Ix)++-- | Schema describing a two-player hidden information game.+data Schema = Schema {+  -- | Score offset to make the game fair.+  offset :: Double,+  -- | \"Determinant\".+  d :: Double,+  -- | Strategy \"names\", given as labels along an edge of the+  -- payoff matrix.+  names :: Array Edge (Array Int (Maybe Int)),+  -- | Payoff matrix.+  payoffs :: Array (Int, Int) Double+}++-- | Show a 'Schema' in tabular format.+instance Show Schema where+  show s =+    let tab =       +          ["" : printNameVec Top] +++          map printRow [1 .. nr - 1] +++          ["" : printRowM nr] +++          ["" : printNameVec Bottom]+        in+    printf "O = %.2f, D = %.2f\n" (offset s) (d s) +++    tabular (replicate (nc + 1) 'r' ++ "l") tab+    where+      (nr, nc) = dims $ payoffs s+      printRowM i =+        printVec $ ixmap (1, nc) (\j -> (i, j)) (payoffs s)+        where+          printVec a =+            map (printf "%.2f" . (a !)) [1 .. dim a]+      printName e i =+        case names s ! e ! i of+          Just v -> printf "%d" v+          Nothing -> ""+      printNameVec e =+        map (printName e) [1 .. dim (names s ! e)]+      printRow i =+        [printName Left i] ++ +        printRowM i ++ +        [printName Right i]++-- | Read a 'Schema' (actually a payoff matrix) from standard input.+readSchema :: IO Schema+readSchema =+  construct `fmap` getContents+  where+    construct s = +      let o = minimum $ elems ps in+      let core = map (\(c, x) -> (c, x - o)) $ assocs ps+          augr = zip (zip (repeat (nr + 1)) [1..nc]) (repeat (-1))+          augc = zip (zip [1..nr] (repeat (nc + 1))) (repeat 1)+          augv = ((nr + 1, nc + 1), 0)+          bounds' = ((1, 1), (nr + 1, nc + 1)) in+      let ps' = array bounds' $ core ++ augr ++ augc ++ [augv] in+      Schema {+        offset = o,+        d = 1,+        names = mkNames,+        payoffs = ps' }+      where+        ps = mkPayoffs $ map (map read . words) $ lines s+        (nr, nc) = dims ps+        mkNames =+          array (Left, Bottom) [namesLeft, namesTop, namesRight, namesBottom]+          where+            namesLeft = (Left, listArray (1, nr) [Just n | n <- [1..nr]])+            namesTop = (Top, listArray (1, nc) [Just n | n <- [1..nc]])+            namesRight = (Right, listArray (1, nr) (replicate nr Nothing))+            namesBottom = (Bottom, listArray (1, nc) (replicate nc Nothing))+        mkPayoffs :: [[Double]] -> Array (Int, Int) Double+        mkPayoffs l@(e : es)+          | all ((== length e) . length) es =+              listArray ((1, 1), (length l, length e)) $ concat l+        mkPayoffs _ = error "bad payoff matrix format"++-- | Execute one pivot step in the 'Schema' reduction.+pivot :: Schema -> Schema+pivot s =+  Schema {+    offset = offset s,+    d = pv,+    names = updateNames,+    payoffs = updatePayoffs }+  where+    ps = payoffs s+    (nr, nc) = dims ps+    ((pr, pc), pv) = +        maximumBy pivotCompare $+        map (minimumBy pivotCompare) $+        groupBy ((==) `on` (snd . fst)) $ +        sortBy (comparing (snd . fst)) potPivots+        where+          potPivots = +            filter okPivot $ assocs ps+            where+              okPivot ((_, c), v) = v > 0 && (ps ! (nr, c)) < 0+          pivotCompare =+            comparing pivotCriteria+            where+              pivotCriteria ((r, c), p) =+                - (ps ! (nr, c)) * (ps ! (r, nc)) / p+    updateNames = +      array (Left, Bottom) [+        updateName Left Bottom pr pc,+        updateName Bottom Left pc pr,+        updateName Right Top pr pc,+        updateName Top Right pc pr ]+      where+        updateName e e' i i' =+          (e, (names s ! e) // [(i, names s ! e' ! i')])+    updatePayoffs = +      listArray (bounds ps) $ map updatePayoff $ assocs $ ps+      where+        updatePayoff ((r, c), n)+          | r == pr && c == pc = d s+          | r == pr = n+          | c == pc = -n+          | otherwise = (n * pv - (ps ! (pr, c)) * (ps ! (r, pc))) / d s++-- | A game solution, given as the value of the game and+-- an optimal mixed strategy for each player.+data Solution = Solution {+  value :: Double,+  leftStrategy, topStrategy :: [Double] }++instance Show Solution where+  show soln =+    printf "value = %.2f\n" (value soln) +++    printf "leftmax = %s\n" (showStrategy leftStrategy) +++    printf "topmin = %s" (showStrategy topStrategy)+    where+      showStrategy strat =+        unwords $ map (uncurry (printf "%d:%.2f")) $ +        zip [(1::Int)..] $ strat soln++-- | Given a solved 'Schema', extract the 'Solution'+-- implied by that 'Schema'.+extractSolution :: Schema -> Solution+extractSolution s | solved s = Solution {+  value = offset s + d s / (ps ! ds),+  leftStrategy = strat Bottom $ ixmap (1, nc - 1) (\j -> (nr, j)) ps,+  topStrategy = strat Right $ ixmap (1, nr - 1) (\j -> (j, nc)) ps }+  where+    ps = payoffs s+    ds@(nr, nc) = dims ps+    strat e odds =+      let strats = zip (elems (names s ! e)) (elems odds) in+      let mStrats = fromList [(nm, pr) | (Just nm, pr) <- strats] in+      let vStrats = map (zeroInactive mStrats) [1 .. dim odds] in+      [o / sum vStrats | o <- vStrats]+      where+        zeroInactive m nm =+          case lookup nm m of+            Just o -> o+            Nothing -> 0+extractSolution _ = error "refusing to extract solution from unsolved schema"++-- | Returns 'True' if the given 'Schema' is fully reduced (\"solved\");+-- 'False' otherwise.+solved :: Schema -> Bool+solved s =+  let ps = payoffs s in+  let ((1,1), (nr, nc)) = bounds ps in+  all (>= 0) [v | ((r, c), v) <- assocs ps, r == nr || c == nc]++-- | Keep reducing the given 'Schema' until a fixed+-- point is reached, and then return the corresponding+-- 'Solution'.+solve :: Schema -> Solution+solve s+  | solved s = extractSolution s+  | otherwise = solve (pivot s)
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ mixed-strategies.cabal view
@@ -0,0 +1,79 @@+-- Initial mixed-strategies.cabal generated by cabal init.  For further +-- documentation, see http://haskell.org/cabal/users-guide/++-- The name of the package.+name:                mixed-strategies++-- The package version.  See the Haskell package versioning policy (PVP) +-- for standards guiding when and how versions should be incremented.+-- http://www.haskell.org/haskellwiki/Package_versioning_policy+-- PVP summary:      +-+------- breaking API changes+--                   | | +----- non-breaking API additions+--                   | | | +--- code changes with no API change+version:             0.1.0.0++-- A short (one-line) description of the package.+synopsis:            Find optimal mixed strategies for two-player games++-- A longer description of the package.++description:         A Haskell implementation of the method of+                     Chapter 6 of The Compleat Strategyst+                     (J.D. Williams, McGraw-Hill 1955) for+                     finding optimal mixed strategies for+                     two-player hidden information games+                     given a payoff matrix.++-- URL for the project homepage or repository.+homepage:            http://wiki.cs.pdx.edu/bartforge/oms++-- The license under which the package is released.+license:             MIT++-- The file containing the license text.+license-file:        COPYING++-- The package author(s).+author:              Bart Massey++-- An email address to which users can send suggestions, bug reports, and +-- patches.+maintainer:          bart@cs.pdx.edu++-- A copyright notice.+-- copyright:           ++category:            Math++build-type:          Simple++-- Constraint on the version of Cabal needed to build this package.+cabal-version:       >=1.8++source-repository head+    type:            git+    location:        http://github.com/BartMassey/mixed-strategies.git++source-repository this+    type:            git+    location:        http://github.com/BartMassey/mixed-strategies.git+    tag:             v0.1++library+  -- Modules exported by the library.+  exposed-modules:     Data.MixedStrategies+  +  -- Modules included in this library but not exported.+  other-modules:       +  +  -- Other library packages from which modules are imported.+  build-depends:       base ==4.5.*, array ==0.4.*, containers ==0.5.*,+                       simple-tabular ==0.1.*++executable oms+  -- .hs or .lhs file containing the Main module.+  main-is:             oms.hs++  -- Other library packages from which modules are imported.+  build-depends:       base ==4.5.*, array ==0.4.*, containers ==0.5.*,+                       simple-tabular ==0.1.*, mixed-strategies
+ oms.hs view
@@ -0,0 +1,23 @@+-- Copyright © 2011 Bart Massey+-- [This program is licensed under the "MIT License"]+-- Please see the file COPYING in the source+-- distribution of this software for license terms.++import Data.MixedStrategies++untilM :: Monad m => (v -> Bool) -> (v -> m v) -> v -> m v+untilM p _ v | p v = return v+untilM p a v = a v >>= untilM p a++step :: Schema -> IO Schema+step s = do+  let s' = pivot s+  print s'+  return s'++main :: IO ()+main = do+  s0 <- readSchema+  print s0+  s <- untilM solved step s0+  print $ extractSolution s