, 5 .
, unboxed vector ( Data.Vector.Unboxed
) 1,8 . , System.Random
.
random-mersenne-pure64
, 0,32 . 0,22 .
, , -, , "" 0,17 .
, unboxed ( , ) , . ( , .)
LCG. .
- extentBits
, , ( randomIndex
extentBits
, , extent
).
, True
dummy
, .
import Data.Bits ((.&.), shiftL)
import Data.Word
import qualified Data.Vector as V
type Spin = Bool
type Lattice = V.Vector Spin
-- Lattice extent is fixed to a square.
extent, extentBits, volume :: Int
extent = 30
extentBits = 5 -- no of bits s.t. 2**5 >= 30
volume = extent * extent
temperature :: Double
temperature = 0.0
-- Converts a `Spin` into `+1` or `-1`.
spin :: Spin -> Int
spin True = 1
spin False = (-1)
-- Wrap a coordinate for periodic boundary conditions.
wrap :: Int -> Int
wrap = flip mod $ extent
-- Converts an unbounded (x,y) index into a linearized index with periodic
-- boundary conditions.
index :: Int -> Int -> Int
index x y = wrap x + wrap y * extent
-- Retrieve a single element from the lattice, automatically performing
-- periodic boundary conditions.
get :: Lattice -> Int -> Int -> Spin
get l x y = l `V.unsafeIndex` index x y
-- Toggle the spin of an element
toggle :: Lattice -> Int -> Int -> Lattice
toggle l x y = l `V.unsafeUpd` [(i, not (l `V.unsafeIndex` i))] -- flip bit at index i
where i = index x y
-- Computes the sum of neighboring spins.
neighborSum :: Lattice -> Int -> Int -> Int
neighborSum l x y = sum $ map spin $ map (uncurry (get l)) neighbors
where
neighbors = [(x+1,y), (x-1,y), (x,y+1), (x,y-1)]
-- Computes the energy difference at a certain lattice site if it would be
-- flipped.
energy :: Lattice -> Int -> Int -> Int
energy l x y = 2 * neighborSum l x y * spin (get l x y)
-- Populates a lattice given a random seed.
initLattice :: Int -> (Lattice,MyGen)
initLattice s = (l, rng')
where
rng = newMyGen s
(allRandom, rng') = go [] rng volume
go out r 0 = (out, r)
go out r n = let (a,r') = randBool r
in go (a:out) r' (n-1)
l = V.fromList allRandom
-- Performs a single Metropolis update at the given lattice site.
update :: (Lattice, MyGen) -> Int -> Int -> (Lattice, MyGen)
update (l, rng) x y
| doUpdate = (toggle l x y, rng')
| otherwise = (l, rng')
where
doUpdate = (shift < 0) || (exp (- fromIntegral shift / temperature) > r)
shift = energy l x y
(r, rng') = randDouble rng
-- A full sweep through the lattice.
doSweep :: (Lattice, MyGen) -> (Lattice, MyGen)
doSweep (l, rng) = iterate updateRand (l, rng) !! (extent * extent)
updateRand :: (Lattice, MyGen) -> (Lattice, MyGen)
updateRand (l, rng)
= let (x, rng') = randIndex rng
(y, rng'') = randIndex rng'
in update (l, rng'') x y
-- Creates a random lattice and performs five sweeps.
main :: IO ()
main = do let lrngs = iterate doSweep (initLattice 2)
l = fst (lrngs !! 1000)
print $ V.length (V.filter id l) -- count the Trues
-- * Random number generation
data MyGen = MyGen Word32
newMyGen :: Int -> MyGen
newMyGen = MyGen . fromIntegral
-- | Get a (positive) integer with given number of bits.
randInt :: Int -> MyGen -> (Int, MyGen)
randInt bits (MyGen s) =
let s' = 1664525 * s + 1013904223
mask = (1 `shiftL` bits) - 1
in (fromIntegral (s' .&. mask), MyGen s')
-- | Random Bool value
randBool :: MyGen -> (Bool, MyGen)
randBool g = let (i, g') = randInt 1 g
in (if i==1 then True else False, g')
-- | Random index
randIndex :: MyGen -> (Int, MyGen)
randIndex g = let (i, g') = randInt extentBits g
in if i >= extent then randIndex g' else (i, g')
-- | Random [0,1]
randDouble :: MyGen -> (Double, MyGen)
randDouble rng = let (ri, rng') = randInt 32 rng
in (fromIntegral ri / (2**32), rng')
MT, , . , randInt
, 100%, 100% , , .
import Data.Bits ((.|.), shiftL, shiftR, xor)
import Data.Word
import qualified Data.Vector as V
import System.Random.Mersenne.Pure64
-- replace these definitions:
-- | Mersenne-Twister generator w/ pool of bits
data MyGen = MyGen PureMT !Int !Word64 !Int !Word64
newMyGen :: Int -> MyGen
newMyGen seed = MyGen (pureMT (fromIntegral seed)) 0 0 0 0
-- | Split w into bottom n bits and rest
splitBits :: Int -> Word64 -> (Word64, Word64)
splitBits n w =
let w2 = w `shiftR` n -- top 64-n bits
w1 = (w2 `shiftL` n) `xor` w -- bottom n bits
in (w1, w2)
-- | Get a (positive) integer with given number of bits.
randInt :: Int -> MyGen -> (Int, MyGen)
randInt bits (MyGen p lft1 w1 lft2 w2)
-- generate at least 64 bits
| let lft = lft1 + lft2, lft < 64
= let w1' = w1 .|. (w2 `shiftL` lft1)
(w2', p') = randomWord64 p
in randInt bits (MyGen p' lft w1' 64 w2')
| bits > 64 = error "randInt has max of 64 bits"
-- if not enough bits in first word, get needed bits from second
| bits > lft1
= let needed = bits - lft1
(bts, w2') = splitBits needed w2
out = (w1 `shiftL` needed) .|. bts
in (fromIntegral out, MyGen p (lft2 - needed) w2' 0 0)
-- otherwise, just take enough bits from first word
| otherwise
= let (out, w1') = splitBits bits w1
in (fromIntegral out, MyGen p (lft1 - bits) w1' lft2 w2)