Commit 5ce3af69 authored by Hans-Peter Deifel's avatar Hans-Peter Deifel 🐢

wta: Don't use floats for probability

This switches from floats to a sort of arbitrary precision base10 floating point
implementation where we can easily generate uniform random numbers.

The problem with floats is that they aren't distributed equally across the whole
range and thus a random number in [0, 1) is not uniform.
parent bb0d6c9b
...@@ -289,6 +289,7 @@ executable random-wta ...@@ -289,6 +289,7 @@ executable random-wta
other-modules: Types other-modules: Types
, Generator , Generator
, Output , Output
, Probability
default-language: Haskell2010 default-language: Haskell2010
default-extensions: OverloadedStrings default-extensions: OverloadedStrings
, LambdaCase , LambdaCase
......
...@@ -13,10 +13,11 @@ import Data.Maybe ...@@ -13,10 +13,11 @@ import Data.Maybe
import Data.Foldable import Data.Foldable
import Types hiding (spec) import Types hiding (spec)
import Probability
data GeneratorConfig m = GeneratorConfig data GeneratorConfig m = GeneratorConfig
{ spec :: WTASpec m { spec :: WTASpec m
, zeroFreq :: Double , zeroFreq :: Probability
} }
type Generator m = ReaderT (GeneratorConfig m) IO type Generator m = ReaderT (GeneratorConfig m) IO
...@@ -42,9 +43,7 @@ aritySummand arity = do ...@@ -42,9 +43,7 @@ aritySummand arity = do
decideZero :: Generator m Bool decideZero :: Generator m Bool
decideZero = do decideZero = do
freq <- asks zeroFreq asks zeroFreq >>= liftIO . decide
randomValue :: Double <- liftIO (randomRIO (0.0, 1.0))
return $ randomValue < freq
-- Generates Nothing, when it decides that a zero value would be in order -- Generates Nothing, when it decides that a zero value would be in order
genTransition :: Int -> Int -> [State] -> Generator m (Maybe (Transition m)) genTransition :: Int -> Int -> [State] -> Generator m (Maybe (Transition m))
......
...@@ -22,6 +22,7 @@ import System.IO ...@@ -22,6 +22,7 @@ import System.IO
import Types import Types
import Generator import Generator
import Output import Output
import Probability
data SomeMonoid = forall m. SomeMonoid (MonoidType m) data SomeMonoid = forall m. SomeMonoid (MonoidType m)
...@@ -29,7 +30,7 @@ data Opts = Opts ...@@ -29,7 +30,7 @@ data Opts = Opts
{ optMonoid :: SomeMonoid { optMonoid :: SomeMonoid
, optStates :: Int , optStates :: Int
, optSymbols :: SymbolSpec , optSymbols :: SymbolSpec
, optZeroFrequency :: Double , optZeroFrequency :: Probability
, optRandomState :: Maybe StdGen , optRandomState :: Maybe StdGen
} }
...@@ -82,10 +83,10 @@ parseOpts = ...@@ -82,10 +83,10 @@ parseOpts =
"Comma separated list of symbols per arity. E.g. 2,0,1 means two symbols with arity 0, non with arity 1 and one with arity two" "Comma separated list of symbols per arity. E.g. 2,0,1 means two symbols with arity 0, non with arity 1 and one with arity two"
) )
<*> Options.option <*> Options.option
Options.auto (Options.eitherReader readProbability)
( Options.long "zero-frequency" ( Options.long "zero-frequency"
<> Options.showDefault <> Options.showDefault
<> Options.value 0.7 <> Options.value (Probability 7 1)
<> Options.metavar "FREQ" <> Options.metavar "FREQ"
<> Options.help <> Options.help
"Frequency of edges with zero weight as number between 0 and 1." "Frequency of edges with zero weight as number between 0 and 1."
......
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Probability (Probability(..), readProbability, decide) where
import System.Random
import Text.Printf
data Probability = Probability Integer Int
readProbability :: String -> Either String Probability
readProbability input = case input of
"0" -> Right (Probability 0 0)
('0':'.':rest) -> case reads rest of
[(digits, "")] -> Right (Probability digits (length rest))
_ -> failure
"1" -> Right (Probability 1 0)
('1':'.':rest)
| all (=='0') rest -> Right (Probability 1 0)
| otherwise -> failure
_ -> failure
where failure = Left "Could not parse probability"
instance Show Probability where
show (Probability digits 0) = show digits
show (Probability digits exp) = "0." ++ printf "%0*d" exp digits
decide :: Probability -> IO Bool
decide (Probability digits exp) = do
randomNumber <- randomRIO (0, (10^exp)-1)
return $ randomNumber < digits
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment