measurable

A simple shallowly-embedded DSL for dealing with measures.
Log | Files | Refs | README | LICENSE

commit 5ac9d3d8812d3d8de878ab78d10d94a667045ca4
parent f1d3241a83c33fa50056af7eb628696a2f8d46d9
Author: Jared Tobin <jared@jtobin.ca>
Date:   Thu,  2 Apr 2015 18:48:58 +1000

Merge pull request #1 from jtobin/jt-cleanup

Misc cleanup.
Diffstat:
MREADME.md | 22+++++++++++++++++++++-
Acabal.config | 866+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aexamples/CRP.hs | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmeasurable.cabal | 62++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/Measurable/Core.hs | 360++++++++++++++++++++++++++++++++++---------------------------------------------
Asrc/Measurable/Measures.hs | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Measurable/Util.hs | 36++++++++++++++++++++++++++++++++++++
7 files changed, 1277 insertions(+), 216 deletions(-)

diff --git a/README.md b/README.md @@ -1,5 +1,25 @@ measurable ---------- -An experimental embedded DSL for creating, manipulating, and using measures. +*measurable* is a simple shallowly-embedded DSL for dealing with measures. + +It uses a `Measure` synonym for a standard continuation type with a restricted +output type and no `callCC` implementation. You can construct measures from +samples, mass/density functions, or even sampling functions. + +Construct image measures by `fmap`-ing measurable functions over them, or +create new measures from existing ones by measure convolution and friends +provided by a simple `Num` instance enabled by an `Applicative` instance. +Create measures from graphs of other measures using the `Monad` instance and +do-notation. + +Query measures by integrating meaurable functions against them. Extract +moments, cumulative density functions, or probabilities. + +Check out the module comments or **examples** folder for sample use. + +Caveat: while fun to play with, and rewarding to see how measures fit together, +measure operations as nested integrals are exponentially complex. Don't expect +them to scale very far! + diff --git a/cabal.config b/cabal.config @@ -0,0 +1,866 @@ +-- Stackage snapshot from: http://www.stackage.org/snapshot/lts-1.11 +-- Please place this file next to your .cabal file as cabal.config +-- To only use tested packages, uncomment the following line: +-- remote-repo: stackage-lts-1.11:http://www.stackage.org/snapshot/lts-1.11 +constraints: abstract-deque ==0.3, + abstract-par ==0.3.3, + accelerate ==0.15.0.0, + ace ==0.6, + action-permutations ==0.0.0.1, + active ==0.1.0.18, + AC-Vector ==2.3.2, + ad ==4.2.1.1, + adjunctions ==4.2, + aeson ==0.8.0.2, + aeson-pretty ==0.7.2, + aeson-qq ==0.7.4, + aeson-utils ==0.2.2.1, + alarmclock ==0.2.0.5, + alex ==3.1.4, + amqp ==0.10.1, + ansi-terminal ==0.6.2.1, + ansi-wl-pprint ==0.6.7.1, + appar ==0.1.4, + approximate ==0.2.1.1, + arbtt ==0.8.1.4, + arithmoi ==0.4.1.1, + array installed, + arrow-list ==0.6.1.5, + asn1-data ==0.7.1, + asn1-encoding ==0.9.0, + asn1-parse ==0.9.0, + asn1-types ==0.3.0, + async ==2.0.2, + atto-lisp ==0.2.2, + attoparsec ==0.12.1.3, + attoparsec-conduit ==1.1.0, + attoparsec-enumerator ==0.3.3, + attoparsec-expr ==0.1.1.1, + authenticate ==1.3.2.11, + auto-update ==0.1.2.1, + aws ==0.11.2, + bake ==0.2, + bank-holidays-england ==0.1.0.2, + barecheck ==0.2.0.6, + base installed, + base16-bytestring ==0.1.1.6, + base64-bytestring ==1.0.0.1, + base-compat ==0.5.0, + base-prelude ==0.1.16, + base-unicode-symbols ==0.2.2.4, + basic-prelude ==0.3.11.1, + bifunctors ==4.2.1, + binary installed, + binary-conduit ==1.2.3, + binary-list ==1.0.1.0, + bindings-DSL ==1.0.22, + bioace ==0.0.1, + bioalign ==0.0.5, + biocore ==0.3.1, + biofasta ==0.0.3, + biofastq ==0.1, + biophd ==0.0.5, + biopsl ==0.4, + biosff ==0.3.7.1, + bits ==0.4, + BlastHTTP ==1.0.1, + blastxml ==0.3.2, + blaze-builder ==0.3.3.4, + blaze-builder-enumerator ==0.2.0.6, + blaze-html ==0.7.1.0, + blaze-markup ==0.6.3.0, + blaze-svg ==0.3.4.1, + blaze-textual ==0.2.0.9, + BlogLiterately ==0.7.1.7, + BlogLiterately-diagrams ==0.1.4.3, + bloodhound ==0.5.0.1, + bmp ==1.2.5.2, + Boolean ==0.2.3, + bool-extras ==0.4.0, + bound ==1.0.4, + BoundedChan ==1.0.3.0, + broadcast-chan ==0.1.0, + bson ==0.3.1, + bumper ==0.6.0.3, + byteable ==0.1.1, + bytedump ==1.0, + byteorder ==1.0.4, + bytes ==0.14.1.3, + bytestring installed, + bytestring-builder ==0.10.4.1.2, + bytestring-lexing ==0.4.3.2, + bytestring-mmap ==0.2.2, + bytestring-progress ==1.0.3, + bytestring-show ==0.3.5.6, + bytestring-trie ==0.2.4, + bzlib ==0.5.0.5, + bzlib-conduit ==0.2.1.3, + c2hs ==0.20.1, + Cabal installed, + cabal-install ==1.18.0.8, + cabal-src ==0.2.5, + cairo ==0.13.1.0, + case-insensitive ==1.2.0.4, + cases ==0.1.2, + cassava ==0.4.2.1, + cautious-file ==1.0.2, + cereal ==0.4.1.1, + cereal-conduit ==0.7.2.3, + certificate ==1.3.9, + charset ==0.3.7, + Chart ==1.3.3, + Chart-diagrams ==1.3.3, + ChasingBottoms ==1.3.0.11, + check-email ==1.0, + checkers ==0.4.1, + chell ==0.4.0.1, + chell-quickcheck ==0.2.4, + chunked-data ==0.1.0.1, + cipher-aes ==0.2.10, + cipher-blowfish ==0.0.3, + cipher-camellia ==0.0.2, + cipher-des ==0.0.6, + cipher-rc4 ==0.1.4, + circle-packing ==0.1.0.4, + classy-prelude ==0.10.5, + classy-prelude-conduit ==0.10.5, + classy-prelude-yesod ==0.10.5, + clientsession ==0.9.1.1, + clock ==0.4.1.3, + cmdargs ==0.10.12, + code-builder ==0.1.3, + colour ==2.3.3, + comonad ==4.2.3, + comonads-fd ==4.0, + comonad-transformers ==4.0, + compdata ==0.9, + compensated ==0.6.1, + composition ==1.0.1.0, + compressed ==3.10, + concatenative ==1.0.1, + concurrent-extra ==0.7.0.9, + concurrent-supply ==0.1.7, + cond ==0.4.1.1, + conduit ==1.2.4, + conduit-combinators ==0.3.0.6, + conduit-extra ==1.1.7.0, + configurator ==0.3.0.0, + connection ==0.2.4, + constraints ==0.4.1.3, + containers installed, + containers-unicode-symbols ==0.3.1.1, + contravariant ==1.2.2, + control-monad-free ==0.5.3, + control-monad-loop ==0.1, + convertible ==1.1.0.0, + cookie ==0.4.1.4, + courier ==0.1.0.15, + cpphs ==1.18.9, + cprng-aes ==0.6.1, + cpu ==0.1.2, + criterion ==1.0.2.0, + crypto-api ==0.13.2, + cryptocipher ==0.6.2, + crypto-cipher-tests ==0.0.11, + crypto-cipher-types ==0.0.9, + cryptohash ==0.11.6, + cryptohash-conduit ==0.1.1, + cryptohash-cryptoapi ==0.1.3, + crypto-numbers ==0.2.7, + crypto-pubkey ==0.2.8, + crypto-pubkey-types ==0.4.3, + crypto-random ==0.0.8, + crypto-random-api ==0.2.0, + css-text ==0.1.2.1, + csv ==0.1.2, + csv-conduit ==0.6.3, + curl ==1.3.8, + data-accessor ==0.2.2.6, + data-accessor-mtl ==0.2.0.4, + data-binary-ieee754 ==0.4.4, + data-default ==0.5.3, + data-default-class ==0.0.1, + data-default-instances-base ==0.0.1, + data-default-instances-containers ==0.0.1, + data-default-instances-dlist ==0.0.1, + data-default-instances-old-locale ==0.0.1, + data-inttrie ==0.1.0, + data-lens-light ==0.1.2.1, + data-memocombinators ==0.5.1, + data-reify ==0.6, + DAV ==1.0.3, + Decimal ==0.4.2, + deepseq installed, + deepseq-generics ==0.1.1.2, + derive ==2.5.21, + diagrams ==1.2, + diagrams-builder ==0.6.0.3, + diagrams-cairo ==1.2.0.6, + diagrams-contrib ==1.1.2.5, + diagrams-core ==1.2.0.5, + diagrams-haddock ==0.2.2.13, + diagrams-lib ==1.2.0.8, + diagrams-postscript ==1.1.0.4, + diagrams-svg ==1.1.0.4, + Diff ==0.3.0, + digest ==0.0.1.2, + digestive-functors ==0.7.1.4, + dimensional ==0.13.0.1, + directory installed, + directory-tree ==0.12.0, + direct-sqlite ==2.3.15, + distributed-process ==0.5.3, + distributed-process-async ==0.2.1, + distributed-process-client-server ==0.1.2, + distributed-process-execution ==0.1.1, + distributed-process-extras ==0.2.0, + distributed-process-simplelocalnet ==0.2.2.0, + distributed-process-supervisor ==0.1.2, + distributed-process-task ==0.1.1, + distributed-static ==0.3.1.0, + distributive ==0.4.4, + djinn-ghc ==0.0.2.3, + djinn-lib ==0.0.1.2, + dlist ==0.7.1, + dlist-instances ==0.1, + doctest ==0.9.13, + double-conversion ==2.0.1.0, + dual-tree ==0.2.0.5, + easy-file ==0.2.0, + either ==4.3.3.2, + elm-build-lib ==0.14.0.0, + elm-compiler ==0.14.1, + elm-core-sources ==1.0.0, + elm-package ==0.2.2, + email-validate ==2.0.1, + enclosed-exceptions ==1.0.1, + entropy ==0.3.6, + enumerator ==0.4.20, + eq ==4.0.3, + erf ==2.0.0.0, + errorcall-eq-instance ==0.1.0, + errors ==1.4.7, + ersatz ==0.2.6.1, + esqueleto ==2.1.2.1, + exceptions ==0.6.1, + exception-transformers ==0.3.0.4, + executable-path ==0.0.3, + extensible-exceptions ==0.1.1.4, + extra ==1.0.1, + failure ==0.2.0.3, + fast-logger ==2.2.3, + fay ==0.21.2.1, + fay-base ==0.19.4.2, + fay-builder ==0.2.0.3, + fay-dom ==0.5.0.1, + fay-jquery ==0.6.0.3, + fay-text ==0.3.2.2, + fay-uri ==0.2.0.0, + fb ==1.0.8, + fb-persistent ==0.3.4, + fclabels ==2.0.2.2, + FenwickTree ==0.1.2, + fgl ==5.5.0.1, + file-embed ==0.0.8.2, + file-location ==0.4.6, + filemanip ==0.3.6.3, + filepath installed, + fingertree ==0.1.0.1, + fixed ==0.2.1.1, + fixed-list ==0.1.5, + flexible-defaults ==0.0.1.1, + focus ==0.1.3, + foldl ==1.0.7, + FontyFruity ==0.4.1, + force-layout ==0.3.0.9, + foreign-store ==0.1, + foreign-var ==0.0.0.1, + formatting ==6.0.0, + fpco-api ==1.2.0.5, + free ==4.10.0.1, + freenect ==1.2, + frisby ==0.2, + fsnotify ==0.1.0.3, + fuzzcheck ==0.1.1, + gd ==3000.7.3, + generic-aeson ==0.2.0.2, + generic-deriving ==1.6.3, + GenericPretty ==1.2.1, + generics-sop ==0.1.1, + ghc-heap-view ==0.5.3, + ghcid ==0.3.4, + ghc-mod ==5.2.1.2, + ghc-mtl ==1.2.1.0, + ghc-paths ==0.1.0.9, + ghc-prim installed, + ghc-syb-utils ==0.2.3, + gio ==0.13.0.4, + git-embed ==0.1.0, + gl ==0.6.3, + glib ==0.13.1.0, + Glob ==0.7.5, + GLURaw ==1.4.0.2, + GLUT ==2.5.1.1, + graph-core ==0.2.1.0, + graphs ==0.5.0.1, + gravatar ==0.6, + groundhog ==0.7.0.2, + groundhog-mysql ==0.7.0.1, + groundhog-postgresql ==0.7.0.2, + groundhog-sqlite ==0.7.0.1, + groundhog-th ==0.7.0, + groupoids ==4.0, + groups ==0.4.0.0, + gtk ==0.13.4, + gtk2hs-buildtools ==0.13.0.3, + haddock-api ==2.15.0.2, + haddock-library ==1.1.1, + half ==0.2.0.1, + HandsomeSoup ==0.3.5, + happstack-server ==7.3.9, + happy ==1.19.5, + hashable ==1.2.3.1, + hashable-extras ==0.2.0.1, + hashmap ==1.3.0.1, + hashtables ==1.2.0.2, + haskeline installed, + haskell2010 installed, + haskell98 installed, + haskell-lexer ==1.0, + haskell-names ==0.4.1, + haskell-packages ==0.2.4.4, + haskell-src ==1.0.2.0, + haskell-src-exts ==1.16.0.1, + haskell-src-meta ==0.6.0.8, + hasql ==0.7.2, + hasql-backend ==0.4.0, + hasql-postgres ==0.10.2, + hastache ==0.6.1, + HaTeX ==3.16.0.0, + HaXml ==1.25.3, + haxr ==3000.10.3.1, + HCodecs ==0.5, + hdaemonize ==0.5.0.0, + hdevtools ==0.1.0.6, + heaps ==0.3.1, + hebrew-time ==0.1.1, + heist ==0.14.1, + here ==1.2.6, + heredoc ==0.2.0.0, + hflags ==0.4, + highlighting-kate ==0.5.11.1, + hinotify ==0.3.7, + hint ==0.4.2.2, + histogram-fill ==0.8.4.1, + hit ==0.6.3, + hjsmin ==0.1.4.7, + hledger ==0.24, + hledger-lib ==0.24, + hlibgit2 ==0.18.0.14, + hlint ==1.9.16, + hmatrix ==0.16.1.4, + hmatrix-gsl ==0.16.0.3, + hoauth2 ==0.4.3, + holy-project ==0.1.1.1, + hoogle ==4.2.38, + hoopl installed, + hOpenPGP ==1.11, + hostname ==1.0, + hostname-validate ==1.0.0, + hourglass ==0.2.8, + hpc installed, + hPDB ==1.2.0.2, + hPDB-examples ==1.2.0.1, + hs-bibutils ==5.5, + hscolour ==1.20.3, + hse-cpp ==0.1, + hslogger ==1.2.8, + hslua ==0.3.13, + hspec ==2.1.4, + hspec2 ==0.6.1, + hspec-core ==2.1.4, + hspec-discover ==2.1.4, + hspec-expectations ==0.6.1.1, + hspec-meta ==2.0.0, + hspec-wai ==0.6.3, + hspec-wai-json ==0.6.0, + HStringTemplate ==0.7.3, + hsyslog ==2.0, + HTF ==0.12.2.3, + html ==1.0.1.2, + html-conduit ==1.1.1.1, + HTTP ==4000.2.19, + http-client ==0.4.8, + http-client-tls ==0.2.2, + http-conduit ==2.1.5, + http-date ==0.0.5, + http-reverse-proxy ==0.4.1.2, + http-types ==0.8.6, + HUnit ==1.2.5.2, + hweblib ==0.6.3, + hxt ==9.3.1.15, + hxt-charproperties ==9.2.0.1, + hxt-http ==9.1.5.2, + hxt-pickle-utils ==0.1.0.2, + hxt-regex-xmlschema ==9.2.0.2, + hxt-relaxng ==9.1.5.5, + hxt-unicode ==9.0.2.4, + hybrid-vectors ==0.1.2.1, + hyphenation ==0.4.2, + idna ==0.3.0, + ieee754 ==0.7.6, + IfElse ==0.85, + imagesize-conduit ==1.0.0.4, + immortal ==0.2, + incremental-parser ==0.2.3.4, + indents ==0.3.3, + ini ==0.3.1, + integer-gmp installed, + integration ==0.2.1, + interpolate ==0.1.0, + interpolatedstring-perl6 ==0.9.0, + intervals ==0.7.1, + io-choice ==0.0.5, + io-manager ==0.1.0.2, + io-memoize ==1.1.1.0, + iproute ==1.3.1, + iterable ==3.0, + ixset ==1.0.6, + js-flot ==0.8.3, + js-jquery ==1.11.2, + json-autotype ==0.2.5.4, + json-schema ==0.7.3.1, + JuicyPixels ==3.2.3, + JuicyPixels-repa ==0.7, + kan-extensions ==4.2.1, + kdt ==0.2.2, + keter ==1.3.8, + keys ==3.10.1, + kure ==2.16.6, + language-c ==0.4.7, + language-ecmascript ==0.16.2, + language-glsl ==0.1.1, + language-haskell-extract ==0.2.4, + language-java ==0.2.7, + language-javascript ==0.5.13.3, + lazy-csv ==0.5, + lca ==0.2.4, + lens ==4.6.0.1, + lens-aeson ==1.0.0.3, + lens-family-th ==0.4.1.0, + lhs2tex ==1.18.1, + libgit ==0.3.0, + libnotify ==0.1.1.0, + lifted-async ==0.2.0.2, + lifted-base ==0.2.3.6, + linear ==1.15.5, + linear-accelerate ==0.2, + list-t ==0.4.3, + loch-th ==0.2.1, + log-domain ==0.9.3, + logfloat ==0.12.1, + logict ==0.6.0.2, + loop ==0.2.0, + lucid ==2.5, + lzma-conduit ==1.1.3, + machines ==0.4.1, + mandrill ==0.1.1.0, + map-syntax ==0.2, + markdown ==0.1.13.1, + markdown-unlit ==0.2.0.1, + math-functions ==0.1.5.2, + matrix ==0.3.4.2, + MaybeT ==0.1.2, + MemoTrie ==0.6.2, + mersenne-random-pure64 ==0.2.0.4, + messagepack ==0.3.0, + messagepack-rpc ==0.1.0.3, + mime-mail ==0.4.8.1, + mime-mail-ses ==0.3.2.2, + mime-types ==0.1.0.5, + missing-foreign ==0.1.1, + MissingH ==1.3.0.1, + mmap ==0.5.9, + mmorph ==1.0.4, + MonadCatchIO-transformers ==0.3.1.3, + monad-control ==0.3.3.1, + monad-coroutine ==0.8.0.1, + monadcryptorandom ==0.6.1, + monad-extras ==0.5.9, + monadic-arrays ==0.2.1.3, + monad-journal ==0.6.0.2, + monad-logger ==0.3.13.1, + monad-loops ==0.4.2.1, + monad-par ==0.3.4.7, + monad-parallel ==0.7.1.4, + monad-par-extras ==0.3.3, + monad-primitive ==0.1, + monad-products ==4.0.0.1, + MonadPrompt ==1.0.0.5, + MonadRandom ==0.3.0.1, + monad-st ==0.2.4, + monads-tf ==0.1.0.2, + mongoDB ==2.0.3, + monoid-extras ==0.3.3.5, + monoid-subclasses ==0.3.6.2, + mono-traversable ==0.7.0, + mtl ==2.1.3.1, + mtlparse ==0.1.4.0, + mtl-prelude ==1.0.3, + multimap ==1.2.1, + multipart ==0.1.2, + MusicBrainz ==0.2.3, + mutable-containers ==0.2.1.2, + mwc-random ==0.13.3.0, + mysql ==0.1.1.7, + mysql-simple ==0.2.2.4, + nanospec ==0.2.0, + nats ==1, + neat-interpolation ==0.2.2, + nettle ==0.1.0, + network ==2.6.0.2, + network-conduit-tls ==1.1.0.2, + network-info ==0.2.0.5, + network-multicast ==0.0.11, + network-simple ==0.4.0.4, + network-transport ==0.4.1.0, + network-transport-tcp ==0.4.1, + network-transport-tests ==0.2.2.0, + network-uri ==2.6.0.1, + newtype ==0.2, + nsis ==0.2.4, + numbers ==3000.2.0.1, + numeric-extras ==0.0.3, + NumInstances ==1.4, + numtype ==1.1, + Octree ==0.5.4.2, + old-locale installed, + old-time installed, + OneTuple ==0.2.1, + opaleye ==0.3.1, + OpenGL ==2.9.2.0, + OpenGLRaw ==1.5.0.1, + openpgp-asciiarmor ==0.1, + operational ==0.2.3.2, + options ==1.2.1.1, + optparse-applicative ==0.11.0.2, + osdkeys ==0.0, + pandoc ==1.13.2, + pandoc-citeproc ==0.6, + pandoc-types ==1.12.4.1, + pango ==0.13.0.5, + parallel ==3.2.0.6, + parallel-io ==0.3.3, + parseargs ==0.1.5.2, + parsec ==3.1.8, + parsers ==0.12.1.1, + partial-handler ==0.1.0, + path-pieces ==0.1.5, + patience ==0.1.1, + pcre-light ==0.4.0.3, + pdfinfo ==1.5.2, + pem ==0.2.2, + persistent ==2.1.1.7, + persistent-mongoDB ==2.1.2.1, + persistent-mysql ==2.1.2.1, + persistent-postgresql ==2.1.2.2, + persistent-sqlite ==2.1.1.2, + persistent-template ==2.1.0.1, + phantom-state ==0.2.0.2, + pipes ==4.1.4, + pipes-concurrency ==2.0.3, + pipes-parse ==3.0.2, + placeholders ==0.1, + pointed ==4.2, + polyparse ==1.10, + pool-conduit ==0.1.2.3, + postgresql-binary ==0.5.1, + postgresql-libpq ==0.9.0.2, + postgresql-simple ==0.4.10.0, + pqueue ==1.2.1, + prefix-units ==0.1.0.2, + prelude-extras ==0.4, + present ==2.2, + pretty installed, + prettyclass ==1.0.0.0, + pretty-class ==1.0.1.1, + pretty-show ==1.6.8.2, + primes ==0.2.1.0, + primitive ==0.5.4.0, + process installed, + process-conduit ==1.2.0.1, + process-extras ==0.2.0, + product-profunctors ==0.6, + profunctor-extras ==4.0, + profunctors ==4.3.2, + project-template ==0.1.4.2, + publicsuffixlist ==0.1, + punycode ==2.0, + pure-io ==0.2.1, + pureMD5 ==2.1.2.1, + pwstore-fast ==2.4.4, + quandl-api ==0.2.0.0, + QuasiText ==0.1.2.5, + QuickCheck ==2.7.6, + quickcheck-assertions ==0.1.1, + quickcheck-instances ==0.3.10, + quickcheck-io ==0.1.1, + quickcheck-unicode ==1.0.0.0, + quickpull ==0.4.0.0, + rainbow ==0.20.0.4, + rainbow-tests ==0.20.0.4, + random ==1.0.1.1, + random-fu ==0.2.6.2, + random-shuffle ==0.0.4, + random-source ==0.3.0.6, + rank1dynamic ==0.2.0.1, + Rasterific ==0.4.2, + raw-strings-qq ==1.0.2, + ReadArgs ==1.2.2, + reducers ==3.10.3.1, + reflection ==1.5.1.1, + regex-applicative ==0.3.1, + regex-base ==0.93.2, + regex-compat ==0.95.1, + regex-pcre-builtin ==0.94.4.8.8.35, + regex-posix ==0.95.2, + regexpr ==0.5.4, + regex-tdfa ==1.2.0, + regex-tdfa-rc ==1.1.8.3, + regular ==0.3.4.4, + regular-xmlpickler ==0.2, + rematch ==0.2.0.0, + repa ==3.3.1.2, + repa-algorithms ==3.3.1.2, + repa-devil ==0.3.2.2, + repa-io ==3.3.1.2, + reroute ==0.2.2.1, + resource-pool ==0.2.3.2, + resourcet ==1.1.4.1, + rest-client ==0.4.0.5, + rest-core ==0.33.1.2, + rest-gen ==0.16.1.8, + rest-happstack ==0.2.10.4, + rest-snap ==0.1.17.14, + rest-stringmap ==0.2.0.3, + rest-types ==1.11.1.1, + rest-wai ==0.1.0.4, + rev-state ==0.1, + rfc5051 ==0.1.0.3, + runmemo ==1.0.0.1, + rvar ==0.2.0.2, + safe ==0.3.8, + safecopy ==0.8.4, + scientific ==0.3.3.7, + scotty ==0.9.0, + scrobble ==0.2.1.1, + securemem ==0.1.7, + semigroupoid-extras ==4.0, + semigroupoids ==4.2, + semigroups ==0.16.2.2, + sendfile ==0.7.9, + seqloc ==0.6.1.1, + setenv ==0.1.1.3, + SHA ==1.6.4.1, + shake ==0.14.3, + shake-language-c ==0.6.4, + shakespeare ==2.0.4.1, + shakespeare-i18n ==1.1.0, + shakespeare-text ==1.1.0, + shell-conduit ==4.5.2, + shelly ==1.5.7, + silently ==1.2.4.1, + simple-reflect ==0.3.2, + simple-sendfile ==0.2.18, + singletons ==1.0, + siphash ==1.0.3, + skein ==1.0.9.2, + slave-thread ==0.1.5, + smallcheck ==1.1.1, + smtLib ==1.0.7, + snap ==0.13.3.2, + snap-core ==0.9.6.4, + snaplet-fay ==0.3.3.10, + snap-server ==0.9.4.6, + socks ==0.5.4, + sodium ==0.11.0.3, + sourcemap ==0.1.3.0, + speculation ==1.5.0.1, + sphinx ==0.6.0.1, + split ==0.2.2, + Spock ==0.7.7.0, + Spock-digestive ==0.1.0.0, + Spock-worker ==0.2.1.3, + spoon ==0.3.1, + sqlite-simple ==0.4.8.0, + stackage ==0.3.1, + stateref ==0.3, + statestack ==0.2.0.3, + statistics ==0.13.2.1, + statistics-linreg ==0.3, + stm ==2.4.4, + stm-chans ==3.0.0.2, + stm-conduit ==2.5.4, + stm-containers ==0.2.8, + stm-stats ==0.2.0.0, + storable-complex ==0.2.2, + storable-endian ==0.2.5, + streaming-commons ==0.1.10.0, + streams ==3.2, + strict ==0.3.2, + stringable ==0.1.3, + stringbuilder ==0.5.0, + stringprep ==1.0.0, + stringsearch ==0.3.6.5, + stylish-haskell ==0.5.11.1, + SVGFonts ==1.4.0.3, + syb ==0.4.4, + syb-with-class ==0.6.1.5, + system-canonicalpath ==0.2.3.0, + system-fileio ==0.3.16.1, + system-filepath ==0.4.13.2, + system-posix-redirect ==1.1.0.1, + tabular ==0.2.2.7, + tagged ==0.7.3, + tagshare ==0.0, + tagsoup ==0.13.3, + tagstream-conduit ==0.5.5.3, + tar ==0.4.1.0, + tardis ==0.3.0.0, + tasty ==0.10.1, + tasty-ant-xml ==1.0.1, + tasty-golden ==2.2.2.4, + tasty-hunit ==0.9.1, + tasty-kat ==0.0.3, + tasty-quickcheck ==0.8.3.2, + tasty-smallcheck ==0.8.0.1, + tasty-th ==0.1.3, + template-haskell installed, + temporary ==1.2.0.3, + temporary-rc ==1.2.0.3, + terminal-progress-bar ==0.0.1.4, + terminal-size ==0.3.0, + terminfo installed, + test-framework ==0.8.1.1, + test-framework-hunit ==0.3.0.1, + test-framework-quickcheck2 ==0.3.0.3, + test-framework-th ==0.2.4, + testing-feat ==0.4.0.2, + testpack ==2.1.3.0, + texmath ==0.8.0.1, + text ==1.2.0.4, + text-binary ==0.1.0, + text-format ==0.3.1.1, + text-icu ==0.7.0.1, + tf-random ==0.5, + th-desugar ==1.4.2.1, + th-expand-syns ==0.3.0.5, + th-extras ==0.0.0.2, + th-lift ==0.7.2, + th-orphans ==0.8.3, + threads ==0.5.1.3, + th-reify-many ==0.1.3, + thyme ==0.3.5.5, + time installed, + time-compat ==0.1.0.3, + time-lens ==0.4.0.1, + timezone-olson ==0.1.6, + timezone-series ==0.1.4, + tls ==1.2.16, + tls-debug ==0.3.4, + tostring ==0.2.1.1, + transformers installed, + transformers-base ==0.4.4, + transformers-compat ==0.3.3.3, + traverse-with-class ==0.2.0.3, + tree-view ==0.4, + tuple ==0.3.0.2, + type-eq ==0.4.2, + type-list ==0.0.0.1, + udbus ==0.2.1, + unbounded-delays ==0.1.0.9, + union-find ==0.2, + uniplate ==1.6.12, + unix installed, + unix-compat ==0.4.1.4, + unix-time ==0.3.5, + unordered-containers ==0.2.5.1, + uri-encode ==1.5.0.4, + url ==2.1.3, + utf8-light ==0.4.2, + utf8-string ==0.3.8, + uuid ==1.3.8, + vault ==0.3.0.4, + vector ==0.10.12.2, + vector-algorithms ==0.6.0.3, + vector-binary-instances ==0.2.1.0, + vector-instances ==3.3.0.1, + vector-space ==0.8.7, + vector-space-points ==0.2.1, + vector-th-unbox ==0.2.1.2, + vhd ==0.2.2, + void ==0.7, + wai ==3.0.2.3, + wai-app-static ==3.0.0.6, + wai-conduit ==3.0.0.2, + wai-eventsource ==3.0.0, + wai-extra ==3.0.4.5, + wai-logger ==2.2.3, + wai-middleware-static ==0.6.0.1, + wai-websockets ==3.0.0.5, + warp ==3.0.9.3, + warp-tls ==3.0.2, + webdriver ==0.6.1, + web-fpco ==0.1.1.0, + websockets ==0.9.3.0, + wizards ==1.0.1, + wl-pprint ==1.1, + wl-pprint-extras ==3.5.0.4, + wl-pprint-terminfo ==3.7.1.3, + wl-pprint-text ==1.1.0.3, + word8 ==0.1.2, + wordpass ==1.0.0.2, + X11 ==1.6.1.2, + x509 ==1.5.0.1, + x509-store ==1.5.0, + x509-system ==1.5.0, + x509-validation ==1.5.1, + xenstore ==0.1.1, + xhtml installed, + xml ==1.3.14, + xml-conduit ==1.2.3.3, + xmlgen ==0.6.2.1, + xml-hamlet ==0.4.0.10, + xmlhtml ==0.2.3.4, + xml-types ==0.3.4, + xss-sanitize ==0.3.5.5, + yackage ==0.7.0.7, + yaml ==0.8.10.1, + Yampa ==0.9.6, + YampaSynth ==0.2, + yesod ==1.4.1.4, + yesod-auth ==1.4.3.1, + yesod-auth-deskcom ==1.4.0, + yesod-auth-fb ==1.6.6, + yesod-auth-hashdb ==1.4.1.2, + yesod-auth-oauth2 ==0.0.12, + yesod-bin ==1.4.4, + yesod-core ==1.4.8.1, + yesod-eventsource ==1.4.0.1, + yesod-fay ==0.7.1, + yesod-fb ==0.3.4, + yesod-form ==1.4.4, + yesod-gitrepo ==0.1.1.0, + yesod-newsfeed ==1.4.0.1, + yesod-persistent ==1.4.0.2, + yesod-sitemap ==1.4.0.1, + yesod-static ==1.4.0.4, + yesod-test ==1.4.3.1, + yesod-text-markdown ==0.1.7, + yesod-websockets ==0.2.1.1, + zeromq4-haskell ==0.6.3, + zip-archive ==0.2.3.7, + zlib ==0.5.4.2, + zlib-bindings ==0.1.1.5, + zlib-enum ==0.2.3.1, + zlib-lens ==0.1.1.2 diff --git a/examples/CRP.hs b/examples/CRP.hs @@ -0,0 +1,98 @@ +{-# LANGUAGE TemplateHaskell #-} + +-- | An example defining measures for various finite realizations of a Chinese +-- Restaurant Process. + +import Control.Applicative +import Control.Lens +import Control.Monad.Trans +import Data.IntMap.Strict (IntMap) +import qualified Data.IntMap.Strict as IntMap +import Measurable.Core + +data Table = Table { + _number :: {-# UNPACK #-} !Int + , _people :: {-# UNPACK #-} !Int + } deriving (Eq, Show) + +instance Ord Table where + t1 < t2 = _people t1 < _people t2 + t1 <= t2 = _people t1 <= _people t2 + +makeLenses ''Table + +-- | Mass function for a given table. It's dependent on the state of the +-- restaurant via @n@ and @newestTable@. +tableMass :: (Fractional a, Integral b) => b -> a -> Table -> Table -> a +tableMass n a newestTable table + | table^.number == newestTable^.number = a / (fromIntegral n + a) + | otherwise = fromIntegral (table^.people) / (fromIntegral n + a) + +-- | A measure defined over tables. +tableMeasure + :: (Integral b, Applicative m, Monad m, Traversable t) + => b + -> Double + -> Table + -> t Table + -> MeasureT m Table +tableMeasure n a newestTable = + fromDensityCountingT (tableMass n a newestTable) + +-- | A probability measure over restaurants, represented by IntMaps. +restaurantMeasure + :: (Monad m, Applicative m) + => Double + -> IntMap Table + -> MeasureT m (IntMap Table) +restaurantMeasure a restaurant = do + let numCustomers = sumOf (traverse.people) restaurant + numTables = lengthOf traverse restaurant + nextTableNum = succ numTables + possibleTable = Table nextTableNum 1 + possibleRestaurant = + IntMap.insert nextTableNum possibleTable restaurant + + table <- tableMeasure numCustomers a + possibleTable possibleRestaurant + + let newTable + | table^.number == possibleTable^.number = table + | otherwise = table&people %~ succ + + return (IntMap.insert (newTable^.number) newTable restaurant) + +-- | A measure for a finite realization of a CRP measure with a given number of +-- customers and concentration parameter. +chineseRestaurantProcess + :: (Enum a, Eq a, Monad m, Applicative m, Num a) + => a + -> Double + -> MeasureT m (IntMap Table) +chineseRestaurantProcess n a = go n IntMap.empty where + go 0 restaurant = return restaurant + go j restaurant = restaurantMeasure a restaurant >>= go (pred j) + +main :: IO () +main = do + let numTables = fromIntegral . lengthOf traverse + tinyRestaurant = chineseRestaurantProcess 2 1 + smallRestaurant = chineseRestaurantProcess 3 1 + bigRestaurant = chineseRestaurantProcess 9 1 + bigRestaurantAntisocial = chineseRestaurantProcess 9 3 + + meanTinyRestaurant <- integrate numTables tinyRestaurant + meanSmallRestaurant <- integrate numTables smallRestaurant + meanBigRestaurant <- integrate numTables bigRestaurant + meanBigRestaurantAntisocial <- integrate numTables bigRestaurantAntisocial + + let numCustomers = fromIntegral . sumOf (traverse.people) + + differentQuestion <- integrate numCustomers bigRestaurantAntisocial + + print meanTinyRestaurant + print meanSmallRestaurant + print meanBigRestaurant + print meanBigRestaurantAntisocial + print differentQuestion + diff --git a/measurable.cabal b/measurable.cabal @@ -2,7 +2,7 @@ name: measurable version: 1.0.0.0 license: BSD3 license-file: LICENSE -copyright: (c) Jared Tobin 2013 - 2014. +copyright: (c) Jared Tobin 2013 - 2015. author: Jared Tobin maintainer: jared@jtobin.ca stability: Experimental @@ -10,28 +10,70 @@ category: Math homepage: http://github.com/jtobin/measurable bug-reports: http://github.com/jtobin/measurable/issues build-type: Simple -cabal-version: >=1.10 -synopsis: Basic measure wrangling. +cabal-version: >=1.18 +synopsis: A shallowly-embedded DSL for basic measure wrangling. description: - Various types, instances, and combinators that make it easy to play with - measures. + @measurable@ is a simple shallowly-embedded DSL for dealing with measures. + + It adds a @Measure@ synonym for a standard continuation type with a + restricted output type and no @callCC@ implementation. + + You can construct measures from samples, mass/density functions, or even + sampling functions. + + Construct image measures by @fmap@-ing measurable functions over them, or + create new measures from existing ones by seamless measure arithmetic provided + by a simple @Num@ instance. Create measures from graphs of other measures + using the @Monad@ instance and do-notation. + + Query measures by integrating meaurable functions against them. Extract + moments, cumulative density functions, or probabilities. + + Caveat: while fun to play with, and rewarding to see how measures fit + together, measure operations as nested integrals are exponentially complex. + Don't expect them to scale very far! source-repository head type: git location: git://github.com/jtobin/measurable.git library - exposed-modules: Measurable.Core hs-source-dirs: src default-language: Haskell2010 + exposed-modules: + Measurable.Core + , Measurable.Measures + , Measurable.Util other-extensions: BangPatterns FlexibleInstances build-depends: - base >= 4.6 - , integration >= 0.2 - , mtl >= 2.1.2 - , transformers >= 0.3 + base >= 4.7 && < 4.8 + , foldl + , integration + , mtl + , statistics + , transformers + +Test-suite measurable-examples + type: exitcode-stdio-1.0 + hs-source-dirs: src, examples + main-is: CRP.hs + default-language: Haskell2010 + ghc-options: + -threaded -rtsopts + build-depends: + base >= 4.7 && < 4.8 + , containers + , foldl + , integration + , lens + , measurable + , mtl + , mwc-random + , primitive + , statistics + , transformers diff --git a/src/Measurable/Core.hs b/src/Measurable/Core.hs @@ -4,315 +4,265 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE FlexibleInstances #-} --- | --- --- The /measurable/ library provides basic facilities for defining, --- manipulating, and - where possible - evaluating measures. --- --- Measure theory is the foundation of formal probability. And while measures --- are useful for proofs and theoretical work, they're not a particularly --- efficient way to get anything done in the real world. That said, a solid --- mental model of probability in terms of abstract volumes and ratios is a --- valuable (and difficult) thing to achieve, and /measurable/ can help to --- achieve it. --- --- Measures are represented in their dual form as integrals. That is, a --- measure is represented as an /integration procedure/ that, when provided --- with a measurable function, evaluates an integral against a measure. --- Everything corresponds exactly to the more low-level definition of measures --- being set functions defined on sigma-algebras and all that, but in a fashion --- that is much more amenable to representation on a computer. Since they are --- /procedures/ that need a function to complete them, measures are naturally --- represented by continuations. --- --- As continuations, measures are instances of the @Functor@ class; fmapping a --- function over a measure transforms that measure's support while leaving its --- density structure unchanged. @fmap@ corresponds to the thing that's --- variably called a pushforward, distribution, or image measure; it adapts a --- measure from one measureable space to another. --- --- <image here> --- --- Measures are also instances of @Monad@. 'return' wraps a value up as a --- Dirac measure, and 'bind' is an integrating operator that marginalizes --- existing measures by blending them into others. --- --- Measures have to be created from something; /measurable/ offers four --- functions to build them: --- --- * 'fromPoints', for a discrete collection of points --- --- * 'fromDensityCounting', for a density with respect to counting measure --- (i.e. a mass function) --- --- * 'fromDensityLebesgue', for a density with espect to Lebesgue measure --- --- * 'fromSamplingFunction', for a sampling function that uniquely --- characterizes a measure. --- --- Addtionally, there are two important ways to query measures: --- --- * 'integrate' integrates a measurable function against a measure --- --- * 'expectation' integrates the identity function against a measure --- --- * 'cdf' returns a cumulative distribution function for a measure space --- --- Other queries (volume, variance, higher moments) are also available, but are --- moreso included as curiosities. --- --- Measures are not only implemented as stand-alone probabilistic objects, but --- as a monad transformers as well. This allows measure semantics to be --- layered on top of any existing monad. - module Measurable.Core where -import Control.Arrow import Control.Applicative import Control.Monad import Control.Monad.Trans import Data.Foldable (Foldable) import qualified Data.Foldable as Foldable import Data.Functor.Identity -import Data.Traversable hiding (mapM) +import Data.Traversable +import Measurable.Util import Numeric.Integration.TanhSinh --- | We don't want to allow nonlinear things like callCC, so we roll our own --- Continuation type that doesn't allow that sort of thing. +-- | A hand-rolled continuation type. Exactly like the standard one you'd find +-- in @Control.Monad.Trans.Cont@, but without the supporting functions like +-- @callCC@, etc. included in that module. newtype ContT r m a = ContT { runContT :: (a -> m r) -> m r } type Cont r = ContT r Identity runCont :: Cont r a -> (a -> r) -> r -runCont m k = runIdentity (runContT m (Identity . k)) +runCont m k = runIdentity $ runContT m (Identity . k) cont :: ((a -> r) -> r) -> Cont r a -cont f = ContT (\c -> Identity (f (runIdentity . c))) +cont f = ContT $ \c -> Identity $ f (runIdentity . c) + +-- | A measure can be represented by nothing more than a continuation with a +-- restricted output type corresponding to the reals. +-- +-- A @Functor@ instance implements pushforward or image measures - merely +-- @fmap@ a measurable function over a measure to create one. +-- +-- An @Applicative@ instance adds measure convolution, subtraction, and +-- multiplication by enabling a @Num@ instance via 'liftA2' and an implicit +-- marginalizing effect. A @Monad@ instance lumps the ability to create +-- measures from graphs of measures on top of that. +type Measure a = Cont Double a +type MeasureT m a = ContT Double m a instance Functor (ContT r m) where fmap f m = ContT $ \c -> runContT m (c . f) instance Applicative (ContT r m) where - pure x = ContT ($ x) - f <*> v = ContT $ \c -> runContT f $ \g -> runContT v (c . g) + pure x = ContT ($ x) + f <*> v = ContT $ \c -> + runContT f $ \g -> + runContT v (c . g) instance Monad (ContT r m) where - return x = ContT ($ x) - m >>= k = ContT $ \c -> runContT m (\x -> runContT (k x) c) + return x = ContT ($ x) + m >>= k = ContT $ \c -> + runContT m $ \x -> + runContT (k x) c instance MonadTrans (ContT r) where lift m = ContT (m >>=) --- | A measure is represented as a continuation with output restricted to the --- reals. Measures are *integration programs* that, when supplied with a --- measurable function, integrate that function against some measure. -type Measure a = Cont Double a -type MeasureT m a = ContT Double m a - --- | A more domain-specific alias for runCont. This follows the traditional --- mathematical form; integrate a function against a measure. +-- | The 'integrate' function is just 'runCont' with its arguments reversed +-- in order to resemble the conventional mathematical notation, in which one +-- integrates a measurable function against a measure. +-- +-- >>> let mu = fromSamples [-1, 0, 1] +-- >>> expectation mu +-- 0.0 +-- >>> expectation mu +-- 1.0 integrate :: (a -> Double) -> Measure a -> Double integrate = flip runCont -integrateT :: Monad m => (a -> Double) -> MeasureT m a -> m Double -integrateT f = (`runContT` (return . f)) +integrateT :: Applicative m => (a -> Double) -> MeasureT m a -> m Double +integrateT f = (`runContT` (pure . f)) --- | Things like convolution are trivially expressed by lifted arithmetic --- operators. Probability measures in particular - where things like --- \infty - \infty are not an issue - form a ring. --- --- Note that the complexity of integration over a sum, difference, or product --- of 'n' measures in this situation, each encoding 'm' elements, is O(m^n) --- in this implementation. Operations on independent measures can --- theoretically be implemented with drastically lower complexity, possibly --- by using some cleverness with Arrows. -instance (Monad m, Num a) => Num (ContT Double m a) where +instance (Applicative m, Num a) => Num (ContT Double m a) where (+) = liftA2 (+) (-) = liftA2 (-) (*) = liftA2 (*) - abs = id - signum = const 1 - fromInteger = return . fromInteger + abs = fmap id + signum = fmap signum + fromInteger = pure . fromInteger --- | Creates a measure from a density w/respect to counting measure. -fromDensityCounting - :: (Functor f, Foldable f) - => (a -> Double) - -> f a - -> Measure a -fromDensityCounting f support = cont $ \g -> - Foldable.sum $ (g /* f) <$> support +-- | Create a 'Measure' from a probability mass function and its support, +-- provided as a foldable container. +-- +-- The requirement to supply the entire support is restrictive but necessary; +-- for approximations, consider using 'fromSamples' or +-- 'fromSamplingFunction'. +-- +-- >>> let mu = fromMassFunction (binomialPmf 10 0.2) [0..10] +-- >>> integrate fromIntegral mu +-- 2.0 +fromMassFunction :: Foldable f => (a -> Double) -> f a -> Measure a +fromMassFunction f support = cont $ \g -> weightedAverage (g /* f) support -fromDensityCountingT - :: (Applicative m, Monad m, Traversable t) +fromMassFunctionT :: (Applicative m, Traversable t) => (a -> Double) -> t a -> MeasureT m a -fromDensityCountingT p support = ContT $ \f -> - fmap Foldable.sum . traverse (f //* pLifted) $ support - where - pLifted = return . p +fromMassFunctionT f support = ContT $ \g -> + fmap Foldable.sum . traverse (g //* (pure . f)) $ support --- | Create a measure from a density w/respect to Lebesgue measure. +-- | Create a 'Measure' from a probability density function. -- --- NOTE The quality of this implementation depends entirely on the underlying --- quadrature routine. This is included moreso for interest's sake and --- isn't particularly accurate. -fromDensityLebesgue :: (Double -> Double) -> Measure Double -fromDensityLebesgue d = cont $ \f -> quadratureTanhSinh $ f /* d where +-- Note that queries on measures constructed with @fromDensityFunction@ are +-- subject to numerical error due to the underlying dependency on quadrature! +-- +-- >>> let f x = 1 / (sqrt (2 * pi)) * exp (- (x ^ 2) / 2) +-- >>> let mu = fromDensityFunction f +-- >>> expectation mu +-- 0.0 +-- >>> variance mu +-- 1.0000000000000002 +fromDensityFunction :: (Double -> Double) -> Measure Double +fromDensityFunction d = cont $ \f -> quadratureTanhSinh $ f /* d where quadratureTanhSinh = result . last . everywhere trap --- | Create a measure from a fixed collection of points. -fromPoints :: (Functor f, Foldable f) => f a -> Measure a -fromPoints = cont . flip weightedAverage - -fromPointsT - :: (Applicative m, Monad m, Traversable f) +-- | Create a measure from a collection of observations. +-- +-- Useful for creating general purpose empirical measures. +-- +-- >>> let mu = fromSamples [(1, 2), (3, 4)] +-- >>> integrate (uncurry (+)) mu +-- 5.0 +fromSamples :: Foldable f => f a -> Measure a +fromSamples = cont . flip weightedAverage + +fromSamplesT + :: (Applicative m, Traversable f) => f a -> MeasureT m a -fromPointsT = ContT . flip weightedAverageM +fromSamplesT = ContT . flip weightedAverageM --- | Create a measure from a sampling function. Needs access to a random --- number supply monad, so only a monad transformer version is available. +-- | Create a measure from a sampling function. Runs the sampling function +-- the provided number of times and runs 'fromSamples' on the result. fromSamplingFunction :: (Monad m, Applicative m) => (t -> m b) -> Int -> t -> MeasureT m b -fromSamplingFunction f n g = (lift $ replicateM n (f g)) >>= fromPointsT +fromSamplingFunction f n g = (lift $ replicateM n (f g)) >>= fromSamplesT --- | Synonyms for fmap. +-- | A simple alias for @fmap@. push :: (a -> b) -> Measure a -> Measure b push = fmap pushT :: Monad m => (a -> b) -> MeasureT m a -> MeasureT m b pushT = fmap --- | Expectation is integration against the identity function. +-- | The expectation of a measure is typically understood to be its expected +-- value, which is found by integrating it against the identity function. expectation :: Measure Double -> Double -expectation = integrate id +expectation = integrate id -expectationT :: Monad m => MeasureT m Double -> m Double +expectationT :: Applicative m => MeasureT m Double -> m Double expectationT = integrateT id --- | Variance is obtained by the usual identity. +-- | The variance of a measure, as per the usual formula +-- @var X = E^2 X - EX^2@. variance :: Measure Double -> Double variance mu = integrate (^ 2) mu - expectation mu ^ 2 -varianceT :: Monad m => MeasureT m Double -> m Double -varianceT mu = liftM2 (-) (integrateT (^ 2) mu) (liftM (^ 2) (expectationT mu)) - --- | Convenience function for returning the expectation & variance as a pair. -meanVariance :: Measure Double -> (Double, Double) -meanVariance = expectation &&& variance +varianceT :: Applicative m => MeasureT m Double -> m Double +varianceT mu = liftA2 (-) (integrateT (^ 2) mu) ((^ 2) <$> expectationT mu) -meanVarianceT - :: (Applicative m, Monad m) - => MeasureT m Double - -> m (Double, Double) -meanVarianceT mu = (,) <$> expectationT mu <*> varianceT mu - --- | The nth raw moment of a measure. +-- | The @nth@ raw moment of a 'Measure'. rawMoment :: Int -> Measure Double -> Double rawMoment n = integrate (^ n) -rawMomentT :: Monad m => Int -> MeasureT m Double -> m Double +rawMomentT :: (Applicative m, Monad m) => Int -> MeasureT m Double -> m Double rawMomentT n = integrateT (^ n) --- | All raw moments of a measure. +-- | All raw moments of a 'Measure'. rawMoments :: Measure Double -> [Double] -rawMoments mu = map (`rawMoment` mu) [1..] +rawMoments mu = (`rawMoment` mu) <$> [1..] -rawMomentsT :: Monad m => MeasureT m Double -> Int -> m [Double] -rawMomentsT mu n = mapM (`rawMomentT` mu) (take n [1..]) +rawMomentsT :: (Applicative m, Monad m) => MeasureT m Double -> Int -> m [Double] +rawMomentsT mu n = traverse (`rawMomentT` mu) $ take n [1..] --- | The nth central moment of a measure. +-- | The @nth@ central moment of a 'Measure'. centralMoment :: Int -> Measure Double -> Double centralMoment n mu = integrate (\x -> (x - rm) ^ n) $ mu where rm = rawMoment 1 mu -centralMomentT :: Monad m => Int -> MeasureT m Double -> m Double +centralMomentT :: (Applicative m, Monad m) => Int -> MeasureT m Double -> m Double centralMomentT n mu = integrateT (^ n) $ do rm <- lift $ rawMomentT 1 mu - (\x -> x - rm) <$> mu + (subtract rm) <$> mu --- | All central moments. +-- | All central moments of a 'Measure'. centralMoments :: Measure Double -> [Double] -centralMoments mu = map (`centralMoment` mu) [1..] +centralMoments mu = (`centralMoment` mu) <$> [1..] -centralMomentsT :: Monad m => MeasureT m Double -> Int -> m [Double] -centralMomentsT mu n = mapM (`centralMomentT` mu) (take n [1..]) +centralMomentsT :: (Applicative m, Monad m) => MeasureT m Double -> Int -> m [Double] +centralMomentsT mu n = traverse (`centralMomentT` mu) $ take n [1..] --- | The moment generating function for a measure. +-- | The moment generating function corresponding to a 'Measure'. +-- +-- >>> let mu = fromSamples [1..10] +-- >>> let mgfMu = momentGeneratingFunction mu +-- >>> fmap mgfMu [0, 0.5, 1] +-- [1.0,37.4649671547254,3484.377384533132] momentGeneratingFunction :: Measure Double -> Double -> Double -momentGeneratingFunction mu t = integrate (exp . (* t) . id) mu +momentGeneratingFunction mu t = integrate (exp . (* t)) mu --- | The cumulant generating function for a measure. +-- | The cumulant generating function corresponding to a 'Measure'. +-- +-- >>> let mu = fromSamples [1..10] +-- >>> let cgfMu = cumulantGeneratingFunction mu +-- >>> fmap cgfMu [0, 0.5, 1] +-- [0.0,3.6234062871236543,8.156044651432666] cumulantGeneratingFunction :: Measure Double -> Double -> Double -cumulantGeneratingFunction mu = log . momentGeneratingFunction mu +cumulantGeneratingFunction mu = log . momentGeneratingFunction mu --- | Apply a measure to the underlying space. In particular, this is trivially --- 1 for any probability measure. +-- | Calculates the volume of a 'Measure' over its entire space. Trivially 1 +-- for any probability measure. +-- +-- >>> let mu = fromSamples [1..10] +-- >>> volume mu +-- 1.0 volume :: Measure a -> Double -volume = integrate (const 1) +volume = integrate $ const 1 -volumeT :: Monad m => MeasureT m a -> m Double -volumeT = integrateT (const 1) +volumeT :: Applicative m => MeasureT m a -> m Double +volumeT = integrateT $ const 1 --- | Cumulative distribution function. +-- | The cumulative distribution function corresponding to a 'Measure' +-- +-- >>> let mu = fromSamples [1..10] +-- >>> let cdfMu = cdf mu +-- >>> fmap cdfMu [0..10] +-- [0.0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0] cdf :: Measure Double -> Double -> Double cdf mu x = expectation $ negativeInfinity `to` x <$> mu -cdfT :: Monad m => MeasureT m Double -> Double -> m Double +cdfT :: Applicative m => MeasureT m Double -> Double -> m Double cdfT mu x = expectationT $ negativeInfinity `to` x <$> mu --- | Indicator function for the interval a <= x <= b. Useful for integrating --- from a to b. +-- | A helpful utility for calculating the volume of a region in a measure +-- space. +-- +-- >>> let mu = fromSamples [1..10] +-- >>> integrate (2 `to` 8) mu +-- 0.7 to :: (Num a, Ord a) => a -> a -> a -> a to a b x | x >= a && x <= b = 1 | otherwise = 0 --- | Integrate over a discrete, possibly unordered set. +-- | An analogue of 'to' for measures defined over non-ordered domains. +-- +-- >>> data Group = A | B | C deriving Eq +-- >>> let mu = fromSamples [A, A, A, B, A, B, C] +-- >>> integrate (containing [B]) mu +-- 0.2857142857142857 +-- >>> integrate (containing [A,C]) mu +-- 0.7142857142857143 +-- >>> integrate (containing [A,B,C]) mu +-- 1.0 containing :: (Num a, Eq b) => [b] -> b -> a -containing xs x +containing xs x | x `elem` xs = 1 | otherwise = 0 --- | End of the line. -negativeInfinity :: Fractional a => a -negativeInfinity = negate (1 / 0) - --- | Simple average. -average :: (Fractional a, Foldable f) => f a -> a -average xs = fst $ Foldable.foldl' - (\(!m, !n) x -> (m + (x - m) / fromIntegral (n + 1), n + 1)) (0, 0) xs -{-# INLINE average #-} - --- | Weighted average. -weightedAverage - :: (Functor f, Foldable f, Fractional c) - => (a -> c) - -> f a - -> c -weightedAverage f = average . fmap f -{-# INLINE weightedAverage #-} - --- | Monadic weighted average. -weightedAverageM - :: (Fractional c, Traversable f, Monad m, Applicative m) - => (a -> m c) - -> f a - -> m c -weightedAverageM f = liftM average . traverse f -{-# INLINE weightedAverageM #-} - --- | Lifted multiplication. -(/*) :: (Num c, Applicative f) => f c -> f c -> f c -(/*) = liftA2 (*) - --- | Doubly-lifted multiplication. -(//*) :: (Num c, Applicative f, Applicative g) => f (g c) -> f (g c) -> f (g c) -(//*) = liftA2 (/*) - diff --git a/src/Measurable/Measures.hs b/src/Measurable/Measures.hs @@ -0,0 +1,49 @@ + +module Measurable.Measures where + +import Measurable.Core +import Statistics.Distribution +import qualified Statistics.Distribution.Beta as Statistics +import qualified Statistics.Distribution.Binomial as Statistics +import qualified Statistics.Distribution.ChiSquared as Statistics +import qualified Statistics.Distribution.Gamma as Statistics +import qualified Statistics.Distribution.Exponential as Statistics +import qualified Statistics.Distribution.Normal as Statistics + +standard :: Measure Double +standard = fromDensityFunction pdf where + pdf = density Statistics.standard + +normal :: Double -> Double -> Measure Double +normal m s = fromDensityFunction pdf where + pdf = density $ Statistics.normalDistr m s + +logNormal :: Double -> Double -> Measure Double +logNormal m s = fmap exp (normal m s) + +exponential :: Double -> Measure Double +exponential r = fromDensityFunction pdf where + pdf = density $ Statistics.exponential r + +gamma :: Double -> Double -> Measure Double +gamma a b = fromDensityFunction pdf where + pdf = density $ Statistics.gammaDistr a b + +inverseGamma :: Double -> Double -> Measure Double +inverseGamma a b = fmap recip (gamma a b) + +chiSquare :: Int -> Measure Double +chiSquare k = fromDensityFunction pdf where + pdf = density $ Statistics.chiSquared k + +beta :: Double -> Double -> Measure Double +beta a b = fromDensityFunction pdf where + pdf = density $ Statistics.betaDistr a b + +binomial :: Int -> Double -> Measure Int +binomial n p = fromMassFunction pmf [0..n] where + pmf = probability $ Statistics.binomial n p + +bernoulli :: Double -> Measure Int +bernoulli = binomial 1 + diff --git a/src/Measurable/Util.hs b/src/Measurable/Util.hs @@ -0,0 +1,36 @@ + +module Measurable.Util where + +import Control.Applicative +import Control.Foldl +import qualified Control.Foldl as Foldl +import Data.Foldable (Foldable) +import Data.Traversable + +negativeInfinity :: Fractional a => a +negativeInfinity = negate $ 1 / 0 + +weightedAverage :: (Foldable f, Fractional r) => (a -> r) -> f a -> r +weightedAverage f = Foldl.fold (weightedAverageFold f) + +weightedAverageM + :: (Traversable t, Applicative f, Fractional r) + => (a -> f r) + -> t a + -> f r +weightedAverageM f = fmap (Foldl.fold averageFold) . traverse f + +weightedAverageFold :: Fractional r => (a -> r) -> Fold a r +weightedAverageFold f = Foldl.premap f averageFold + +averageFold :: Fractional a => Fold a a +averageFold = (/) <$> Foldl.sum <*> Foldl.genericLength + +-- | Lifted multiplication. +(/*) :: (Num c, Applicative f) => f c -> f c -> f c +(/*) = liftA2 (*) + +-- | Doubly-lifted multiplication. +(//*) :: (Num c, Applicative f, Applicative g) => f (g c) -> f (g c) -> f (g c) +(//*) = liftA2 (/*) +