├── .circleci └── config.yml ├── .gitignore ├── .paket ├── Paket.Restore.targets └── paket.exe ├── ChangeLog.md ├── Directory.Build.props ├── FsRandom.nuspec ├── FsRandom.sln ├── LICENSE ├── README.md ├── RecycleBin.snk.pub ├── docs ├── csharp.md ├── documentation.fsx ├── images │ └── monte-carlo-pi.png ├── index.fsx ├── numerical-examples.fsx └── templates │ └── template.cshtml ├── examples ├── ApproximateBayesianComputation.fsx ├── Benchmark.fsx ├── FirstRandomNumber.fsx ├── GeneratorFunction.fsx ├── GibbsSampler.fsx ├── HamiltonianMonteCarlo.fsx ├── MonteCarlo.fsx ├── PRNG.fsx ├── Sequence.fsx ├── SystemRandom.fsx ├── Transform.fsx └── WeightedSample.fsx ├── paket.dependencies ├── paket.lock ├── src └── FsRandom │ ├── Array.fs │ ├── Array.fsi │ ├── Array2D.fs │ ├── Array2D.fsi │ ├── AssemblyInfo.fs │ ├── Collections.fs │ ├── FsRandom.fsproj │ ├── FsRandomExtensions.fs │ ├── FsRandomExtensions.fsi │ ├── List.fs │ ├── List.fsi │ ├── Math.fs │ ├── MersenneTwister.fs │ ├── MersenneTwister.fsi │ ├── Random.fs │ ├── Random.fsi │ ├── RandomNumberGenerator.fs │ ├── RandomNumberGenerator.fsi │ ├── RuntimeHelper.fs │ ├── Seq.fs │ ├── Seq.fsi │ ├── SimdOrientedFastMersenneTwister.fs │ ├── SimdOrientedFastMersenneTwister.fsi │ ├── Statistics.fs │ ├── Statistics.fsi │ ├── String.fs │ ├── String.fsi │ ├── Utility.fs │ ├── Utility.fsi │ └── paket.references ├── tests ├── FsRandom.Tests.CSharp │ ├── FsRandom.Tests.CSharp.csproj │ ├── TestRandomNumberGenerator.cs │ └── paket.references └── FsRandom.Tests │ ├── Array2DTest.fs │ ├── ArrayTest.fs │ ├── FsRandom.Tests.fsproj │ ├── Issues.fs │ ├── ListTest.fs │ ├── MersenneTwisterTest.fs │ ├── RandomBuilderTest.fs │ ├── RandomTest.fs │ ├── Resources │ ├── SFMT.11213.out.txt │ ├── SFMT.1279.out.txt │ ├── SFMT.132049.out.txt │ ├── SFMT.19937.out.txt │ ├── SFMT.216091.out.txt │ ├── SFMT.2281.out.txt │ ├── SFMT.4253.out.txt │ ├── SFMT.44497.out.txt │ ├── SFMT.607.out.txt │ ├── SFMT.86243.out.txt │ ├── mt19937-64.out.txt │ └── mt19937ar.out.txt │ ├── RuntimeHelper.fs │ ├── SimdOrientedFastMersenneTwisterTest.fs │ ├── StatisticsTest.fs │ ├── StringTest.fs │ ├── UtilityTest.fs │ └── paket.references └── tools ├── NuGetResolver.fsx ├── XBuildResolver.fsx ├── XmlEditor.fs └── build.fsx /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: microsoft/dotnet:2.0-sdk 6 | environment: 7 | FrameworkPathOverride: /usr/lib/mono/4.5/ 8 | steps: 9 | - checkout 10 | - run: 11 | name: Install mono 12 | command: | 13 | apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF 14 | echo "deb http://download.mono-project.com/repo/debian stable-stretch main" | tee /etc/apt/sources.list.d/mono-official-stable.list 15 | apt-get update -y 16 | apt-get install -y mono-devel fsharp 17 | environment: 18 | DEBIAN_FRONTEND: noninteractive 19 | - restore_cache: 20 | keys: 21 | - paket-cache-{{ .Branch }}-{{ checksum "paket.lock" }} 22 | - paket-cache-{{ .Branch }} 23 | - paket-cache 24 | - run: 25 | name: Restore dependencies 26 | command: dotnet restore 27 | - save_cache: 28 | key: paket-cache-{{ .Branch }}-{{ checksum "paket.lock" }} 29 | paths: 30 | - packages 31 | - paket-files 32 | - run: 33 | name: Build FsRandom 34 | command: dotnet build --no-restore 35 | 36 | - run: 37 | name: Test FsRandom 38 | command: dotnet test ./tests/FsRandom.Tests/FsRandom.Tests.fsproj --no-build 39 | - run: 40 | name: Test FsRandom C# extension 41 | command: dotnet test ./tests/FsRandom.Tests.CSharp/FsRandom.Tests.CSharp.csproj --no-build 42 | - run: 43 | name: Create deploy package 44 | command: fsharpi tools/build.fsx --clean-deploy --docs --deploy 45 | - store_artifacts: 46 | path: ./Deploy 47 | destination: artifacts 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | *.suo 4 | *.userprefs 5 | 6 | packages/*/ 7 | TestResults/ 8 | *.nupkg 9 | *.pdb 10 | *.xml 11 | 12 | Build/ 13 | Deploy/ 14 | 15 | packages 16 | paket-files 17 | .ionide/ 18 | -------------------------------------------------------------------------------- /.paket/paket.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/FsRandom/0c43ba5caa2ebd0b9e2e2d16cb99f88284136470/.paket/paket.exe -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | FsRandom ChangeLog 2 | ================== 3 | 4 | Version 1.4.0 5 | ------------- 6 | 7 | * Add support for .NET Standard (#81). 8 | * Drop support for .NET 4.0 (requires 4.5 or later). 9 | * Stop code signing. 10 | 11 | Version 1.3.3 12 | ------------- 13 | 14 | * Fix bugs (#79, #80). 15 | 16 | Version 1.3.2 17 | ------------- 18 | 19 | * Fix List module throws StackOverflowException (#77). 20 | * Add primitive random generators (#75). 21 | * Add negative binomial generator (#50). 22 | 23 | Version 1.3.1 24 | ------------- 25 | 26 | * Fix bug (#74). 27 | 28 | Version 1.3 29 | ----------- 30 | 31 | * C# support (#64). 32 | * Add Random.bind function. 33 | * Add sample functions that take one sample from array (#73). 34 | 35 | Version 1.2.3 36 | ------------- 37 | 38 | * Add utility random state (#61). 39 | * Add List module (#70). 40 | * Add or rename functions (#66, #68, #69). 41 | * Fix a bug (#65). 42 | 43 | Version 1.2.2 44 | ------------- 45 | 46 | * Add functions (multinormal, #46; wishart, #49; vonMises, #62). 47 | 48 | Version 1.2.1 49 | ------------- 50 | 51 | * Fix geometric generates random numbers incorrectly (#58), 52 | and support geometric on {0, 1, 2, ...} and {1, 2, 3, ...} (#59). 53 | Buggy geometric is removed and geometric0 and geometric1 on the respective supports are added. 54 | * Add Statistics.Standard module (#55). 55 | * sampleWithReplacement new throws an error before runtime (#45). 56 | * Add functions. 57 | 58 | Version 1.2 59 | ----------- 60 | 61 | * API change (#40). PrngState no longer requires generics. 62 | * Support use binding and error handling in random computation expression (#43). 63 | * Add String module (#23). 64 | * Add several useful functions. 65 | 66 | Version 1.1 67 | ----------- 68 | 69 | * API change (#35). 70 | * Add Array2D module (#38). 71 | * Add useful functions. 72 | 73 | Version 1.0.2 74 | ------------- 75 | 76 | * Add GeneratorFunction type abbreviation. 77 | * Add choose function (#34). 78 | 79 | Version 1.0.1 80 | ------------- 81 | 82 | * Enhance speed of Poisson generator. 83 | * Add rawBits generator function. 84 | 85 | Version 1.0.0.0 86 | --------------- 87 | 88 | * Rename project. 89 | * RandomBuilder.Run returns a function. 90 | * Added Seq module (#27). 91 | * High resolution of random numbers (#31). 92 | 93 | Version 1.2.2.0 (RecycleBin.Random) 94 | ----------------------------------- 95 | 96 | * Enhanced Array module (#22, #25, #26). 97 | 98 | Version 1.2.1.0 (RecycleBin.Random) 99 | ----------------------------------- 100 | 101 | * Bug fix. 102 | 103 | Version 1.2.0.0 (RecycleBin.Random) 104 | ----------------------------------- 105 | 106 | * Functions moved to separated modules (#15). 107 | * Added functions (#16, #18, #20). 108 | 109 | Version 1.1.4.0 (RecycleBin.Random) 110 | ----------------------------------- 111 | Tagged: 265f8ddfb416f2c8804e4a5af5465e21fbc31908 112 | 113 | * Added `coinFlip` function (#2). 114 | * Added `trialgular` function (#11). 115 | * Added `loguniform` function (#12). 116 | * Added `lognormal` function (#13). 117 | * Added `multinomial` function (#14). 118 | 119 | Version 1.1.3.0 (RecycleBin.Random) 120 | ----------------------------------- 121 | Tagged: 6d91d3df1ddc6aae4d52710fb56856c75f5125d6 122 | 123 | * Fixed SimdOrientedFastMersenneTwister implementation. 124 | 125 | Version 1.1.2.0 (RecycleBin.Random) 126 | ----------------------------------- 127 | Tagged: 1cc5c0474b9048862b849bededd00aa26f9ae1bd 128 | 129 | * Added Mersenne Twister implementation (#7). 130 | * Added SIMD-Oriented Fast Mersenne Twister implementation (#8). 131 | 132 | Version 1.1.1.0 (RecycleBin.Random) 133 | ----------------------------------- 134 | Tagged: 17920ee5ad02b48dd7e4d5888ce3c7343bb67069 135 | 136 | * Added `state` builder (#9). 137 | 138 | Version 1.1.0.0 (RecycleBin.Random) 139 | ----------------------------------- 140 | Tagged: 930858a0b1454057f4461ce5fd788a771d1b2a79 141 | 142 | * Supported loops (#4). 143 | * Random number conversion functions (#5). 144 | 145 | Version 1.0.0.0 (RecycleBin.Random) 146 | ----------------------------------- 147 | Tagged: db812be59c6fafc63a1169ae85887361cbb5bb14 148 | 149 | * Initial release. 150 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | /usr 6 | /Library/Frameworks/Mono.framework/Versions/Current 7 | $(MonoRoot)/lib/mono 8 | true 9 | $(MonoLibFolder)/4.5-api 10 | $(MonoLibFolder)/4.5.1-api 11 | $(MonoLibFolder)/4.5.2-api 12 | $(MonoLibFolder)/4.6-api 13 | $(MonoLibFolder)/4.6.1-api 14 | $(MonoLibFolder)/4.6.2-api 15 | $(MonoLibFolder)/4.7-api 16 | $(MonoLibFolder)/4.7.1-api 17 | $(MonoLibFolder)/4.7.2-api 18 | 19 | 20 | -------------------------------------------------------------------------------- /FsRandom.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FsRandom 5 | 1.4.0.1 6 | FsRandom 7 | RecycleBin 8 | kos59125 9 | https://github.com/fsprojects/FsRandom/blob/master/LICENSE 10 | http://fsprojects.github.io/FsRandom/ 11 | true 12 | 13 | This package offers users to generate random states using computation expression syntax (and also LINQ syntax for C# users). 14 | This provides a variety of random functions: generating random collections, random sampling, shufflig, and also statistical functions like normal, gamma, and Poisson. 15 | Xorshift algorithm is used for the default, but you can choose another algorithm to pick random numbers, e.g. Mersenne Twister and System.Random. 16 | 17 | Random number generating framework designed for F#. 18 | Copyright © 2013 RecycleBin 19 | F# random-number computation-expression monte-carlo statistics 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /FsRandom.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26430.15 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{F2FD43BB-E936-4250-A94E-53DE58DD605C}" 7 | ProjectSection(SolutionItems) = preProject 8 | tools\build.fsx = tools\build.fsx 9 | ChangeLog.md = ChangeLog.md 10 | FsRandom.nuspec = FsRandom.nuspec 11 | EndProjectSection 12 | EndProject 13 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{F9F25ACF-679E-48B4-82C6-7200C9245320}" 14 | ProjectSection(SolutionItems) = preProject 15 | examples\ApproximateBayesianComputation.fsx = examples\ApproximateBayesianComputation.fsx 16 | examples\Benchmark.fsx = examples\Benchmark.fsx 17 | examples\FirstRandomNumber.fsx = examples\FirstRandomNumber.fsx 18 | examples\GeneratorFunction.fsx = examples\GeneratorFunction.fsx 19 | examples\GibbsSampler.fsx = examples\GibbsSampler.fsx 20 | examples\HamiltonianMonteCarlo.fsx = examples\HamiltonianMonteCarlo.fsx 21 | examples\MonteCarlo.fsx = examples\MonteCarlo.fsx 22 | examples\PRNG.fsx = examples\PRNG.fsx 23 | examples\Sequence.fsx = examples\Sequence.fsx 24 | examples\SystemRandom.fsx = examples\SystemRandom.fsx 25 | examples\Transform.fsx = examples\Transform.fsx 26 | EndProjectSection 27 | EndProject 28 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{668880F2-5C7B-4188-8215-11DF13C3CE8C}" 29 | ProjectSection(SolutionItems) = preProject 30 | docs\csharp.md = docs\csharp.md 31 | docs\documentation.fsx = docs\documentation.fsx 32 | docs\index.fsx = docs\index.fsx 33 | docs\numerical-examples.fsx = docs\numerical-examples.fsx 34 | README.md = README.md 35 | EndProjectSection 36 | EndProject 37 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "templates", "templates", "{F54D831F-52EE-42FD-A8D8-B64693BFA78D}" 38 | ProjectSection(SolutionItems) = preProject 39 | docs\templates\template.cshtml = docs\templates\template.cshtml 40 | EndProjectSection 41 | EndProject 42 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FsRandom", "src\FsRandom\FsRandom.fsproj", "{761A3B20-4170-4007-8EC4-31C88C469295}" 43 | EndProject 44 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FsRandom.Tests", "tests\FsRandom.Tests\FsRandom.Tests.fsproj", "{FF3B2DE6-21ED-4A6C-9F2C-92DE0CD00C7B}" 45 | EndProject 46 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FsRandom.Tests.CSharp", "tests\FsRandom.Tests.CSharp\FsRandom.Tests.CSharp.csproj", "{C926651A-F1F4-4968-8EB6-45DB95C7871B}" 47 | EndProject 48 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4B703750-0405-41F2-8F76-8D6A13349A67}" 49 | EndProject 50 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{84145610-C339-4052-A4E7-2D45C84E1671}" 51 | EndProject 52 | Global 53 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 54 | Debug|Any CPU = Debug|Any CPU 55 | Release|Any CPU = Release|Any CPU 56 | EndGlobalSection 57 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 58 | {761A3B20-4170-4007-8EC4-31C88C469295}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 59 | {761A3B20-4170-4007-8EC4-31C88C469295}.Debug|Any CPU.Build.0 = Debug|Any CPU 60 | {761A3B20-4170-4007-8EC4-31C88C469295}.Release|Any CPU.ActiveCfg = Release|Any CPU 61 | {761A3B20-4170-4007-8EC4-31C88C469295}.Release|Any CPU.Build.0 = Release|Any CPU 62 | {FF3B2DE6-21ED-4A6C-9F2C-92DE0CD00C7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 63 | {FF3B2DE6-21ED-4A6C-9F2C-92DE0CD00C7B}.Debug|Any CPU.Build.0 = Debug|Any CPU 64 | {FF3B2DE6-21ED-4A6C-9F2C-92DE0CD00C7B}.Release|Any CPU.ActiveCfg = Release|Any CPU 65 | {FF3B2DE6-21ED-4A6C-9F2C-92DE0CD00C7B}.Release|Any CPU.Build.0 = Release|Any CPU 66 | {C926651A-F1F4-4968-8EB6-45DB95C7871B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 67 | {C926651A-F1F4-4968-8EB6-45DB95C7871B}.Debug|Any CPU.Build.0 = Debug|Any CPU 68 | {C926651A-F1F4-4968-8EB6-45DB95C7871B}.Release|Any CPU.ActiveCfg = Release|Any CPU 69 | {C926651A-F1F4-4968-8EB6-45DB95C7871B}.Release|Any CPU.Build.0 = Release|Any CPU 70 | EndGlobalSection 71 | GlobalSection(SolutionProperties) = preSolution 72 | HideSolutionNode = FALSE 73 | EndGlobalSection 74 | GlobalSection(NestedProjects) = preSolution 75 | {F54D831F-52EE-42FD-A8D8-B64693BFA78D} = {668880F2-5C7B-4188-8215-11DF13C3CE8C} 76 | {761A3B20-4170-4007-8EC4-31C88C469295} = {4B703750-0405-41F2-8F76-8D6A13349A67} 77 | {FF3B2DE6-21ED-4A6C-9F2C-92DE0CD00C7B} = {84145610-C339-4052-A4E7-2D45C84E1671} 78 | {C926651A-F1F4-4968-8EB6-45DB95C7871B} = {84145610-C339-4052-A4E7-2D45C84E1671} 79 | EndGlobalSection 80 | EndGlobal 81 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, RecycleBin 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the RecycleBin nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL RecycleBin BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CircleCI](https://circleci.com/gh/kos59125/FsRandom.svg?style=svg)](https://circleci.com/gh/kos59125/FsRandom) 2 | 3 | FsRandom 4 | ======== 5 | 6 | FsRandom is a purely-functional random number generator framework designed for F# language. 7 | It helps you to obtain a variety of random numbers to use more than ten predefined generators, 8 | and to define a new function to generate random numbers you want. 9 | 10 | See the [project home page](https://fsprojects.github.io/FsRandom/) for details. 11 | 12 | Install 13 | ------- 14 | 15 | FsRandom can be installed [from NuGet](https://www.nuget.org/packages/FsRandom/). 16 | 17 | License 18 | ------- 19 | 20 | [The 3-Clause BSD License](https://github.com/fsprojects/FsRandom/blob/master/LICENSE) 21 | 22 | Maintainer(s) 23 | ------------- 24 | 25 | - [@kos59125](https://github.com/kos59125) 26 | - [@pocketberserker](https://github.com/pocketberserker) 27 | 28 | The default maintainer account for projects under "fsprojects" is [@fsprojectsgit](https://github.com/fsprojectsgit) - F# Community Project Incubation Space (repo management) 29 | -------------------------------------------------------------------------------- /RecycleBin.snk.pub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/FsRandom/0c43ba5caa2ebd0b9e2e2d16cb99f88284136470/RecycleBin.snk.pub -------------------------------------------------------------------------------- /docs/csharp.md: -------------------------------------------------------------------------------- 1 | FsRandom From C# 2 | ================ 3 | 4 | From version 1.3, FsRandom supports C#'s query syntax. 5 | 6 | [lang=csharp] 7 | using FsRandom; 8 | using RNG = FsRandom.RandomNumberGenerator; 9 | 10 | var generator = from x in RNG.Standard // [0, 1) 11 | from y in RNG.Standard 12 | let z = x + y 13 | select z / 2; 14 | var result = RandomModule.Get(generator, UtilityModule.DefaultState); 15 | -------------------------------------------------------------------------------- /docs/documentation.fsx: -------------------------------------------------------------------------------- 1 | (*** hide ***) 2 | #I "../Build/lib/net45" 3 | #r "FsRandom.dll" 4 | open FsRandom 5 | 6 | (** 7 | FsRandom Documentation 8 | ====================== 9 | 10 | 11 | First Random Number 12 | ------------------- 13 | 14 | Let's try to get a first random number z ~ N(0, 1). 15 | It is easy to do with `normal` random number generator in the Statistics module. 16 | To give the specific parameter, say the mean of 0 and the variance of 1, do: 17 | *) 18 | 19 | let generator = Statistics.normal (0.0, 1.0) 20 | 21 | (** 22 | The generator function is only able to use with a pseudo-random number generator (PRNG). 23 | The PRNG constructs a computation expression to generate random numbers. 24 | The computation expression is a function which takes a random seed and returns a random number and a new seed for the next call. 25 | It is important to keep the new state because it is used when you generate a new random number. 26 | 27 | Here for example, you choose xorshift PRNG, which is implemented in FsRandom. 28 | You need to define an initial random seed first for xorshift algorithm 29 | (of course, another algorithm is available rather than xorshift, as described later). 30 | It is a tuple composed of four 32-bit unsigned integers. 31 | And then, you should combine the PRNG and the seed using `createState` function. 32 | *) 33 | 34 | let seed = 123456789u, 362436069u, 521288629u, 88675123u 35 | let state = createState xorshift seed 36 | 37 | (** 38 | FsRandom also provides a default random state and a method to create a random state randomly for ease of use: 39 | *) 40 | 41 | Utility.defaultState 42 | Utility.createRandomState () 43 | 44 | (** 45 | Now you can retrieve a random number using `Random.get` function. 46 | *) 47 | 48 | (*** define-output:z1 ***) 49 | let z1 = Random.get generator state 50 | printf "z1 = %f" z1 51 | (*** include-output:z1 ***) 52 | 53 | (** 54 | Since `Random.get` returns a stateless function, 55 | if you do the code above, the same thing occurs. 56 | *) 57 | 58 | (*** define-output:z2 ***) 59 | let z2 = Random.get generator state 60 | printf "z2 = %f" z2 61 | (*** include-output:z2 ***) 62 | 63 | (** 64 | To generate a new random number, 65 | you need to get next state using `Random.next` instead of `Random.get`: 66 | *) 67 | 68 | (*** define-output:z3 ***) 69 | let _, nextState = Random.next generator state 70 | let z3 = Random.get generator nextState 71 | printf "z3 = %f" z3 72 | (*** include-output:z3 ***) 73 | 74 | (** 75 | 76 | Transforming Random Numbers 77 | --------------------------- 78 | 79 | Transformation of random numbers is a regular work. 80 | FsRandom defines `Random.map` function for the purpose. 81 | The following code shows how to use it. 82 | *) 83 | 84 | let plusOne x = x + 1.0 85 | Random.map plusOne <| Statistics.uniform (0.0, 1.0) 86 | |> Random.get 87 | <| state 88 | 89 | (** 90 | `plusOne` is a function that takes an argument and adds one to it. 91 | `uniform` is a uniform random number generator between its two arguments. 92 | So `x` finally becomes a uniform random number between 1 and 2. 93 | 94 | The both following codes return the same results as above. 95 | *) 96 | 97 | Random.identity <| Statistics.uniform (0.0, 1.0) 98 | |> Random.get 99 | <| state 100 | |> plusOne 101 | 102 | (** *) 103 | 104 | Statistics.uniform (0.0, 1.0) 105 | |> Random.get 106 | <| state 107 | |> plusOne 108 | 109 | (** 110 | 111 | Random Number Sequence 112 | ---------------------- 113 | 114 | Usually, you use a lot of random numbers for our needs. 115 | The following code defines a function generating an infinite binary sequence 116 | using Bernoulli random number generator, 117 | and it illustrates how you can generate a number of random numbers. 118 | *) 119 | 120 | let rec binaries initialState = seq { 121 | let binary, nextState = Random.next (Statistics.bernoulli 0.5) initialState 122 | yield binary 123 | yield! binaries nextState // recursively generating binaries. 124 | } 125 | 126 | (** 127 | Or, more precisely like the following: 128 | *) 129 | 130 | let binaries2 state = Seq.ofRandom (Statistics.bernoulli 0.5) state 131 | 132 | (** 133 | 134 | Using System.Random 135 | ------------------- 136 | 137 | The examples above uses `xorshift` to generate random numbers. 138 | The familliar `System.Random` (and its subclasses) is available in the workflow. 139 | Just use `systemrandom` instead of `xorshift`. 140 | *) 141 | 142 | let r0 = System.Random () 143 | let s = createState systemrandom r0 144 | 145 | (** 146 | Because `System.Random` is a stateful object, 147 | unlike `xorshift`, you will get different result on each call. 148 | *) 149 | 150 | let u1 = Random.get generator s 151 | let u2 = Random.get generator s 152 | 153 | (** 154 | 155 | Generator function 156 | ------------------ 157 | 158 | This section explains how to construct generator functions such like `normal` and `uniform`. 159 | 160 | The type of generator function is `GeneratorFunction<'a>`, 161 | where `'a` is a type of random numbers the generator function returns. 162 | 163 | As an example of user-defined generator function, 164 | let's construct a random number generator to produce an *approximate* 165 | standard normal random number (approximately ~ N(0, 1)). 166 | Theorem says that the mean of 12 standard random numbers, 167 | namely, 12 random numbers between 0 and 1, approximates a normal random number 168 | with mean of 1/2 and variance of 1/12. 169 | Therefore, if you subtract 6 from the sum of 12 standard random numbers, the result 170 | approximates a standard normal random number. 171 | *) 172 | 173 | let approximatelyStandardNormal = random { 174 | let! values = Array.randomCreate 12 ``(0, 1)`` 175 | return Array.sum values - 6.0 176 | } 177 | 178 | (** 179 | The `approximatelyStandardNormal` can be used in the generating process as the following. 180 | *) 181 | 182 | Random.get approximatelyStandardNormal state 183 | 184 | (** 185 | Don't forget that FsRandom has a normal random number generator `normal`. 186 | 187 | 188 | Pseudo-random number generators 189 | ------------------------------- 190 | 191 | This section explains how to implement pseudo-random number generator (PRNG) algorithms such as `xorshift` and `systemrandom`. 192 | 193 | A PRNG is often defined as a simple series of numbers whose next number is determined by the current state. 194 | For example, the Xorshift algorithm has four 32-bit integers as a state. 195 | To describe such PRNGs, the type of PRNGs in FsRandom is defined as `type Prng<'s> = 's -> uint64 * 's`. 196 | Here `'s` is the type of random state of the PRNG. 197 | 198 | As an example of user-defined `Prng`, 199 | let's implement [linear congruential generator](http://en.wikipedia.org/wiki/Linear_congruential_generator). 200 | First, you make a function of `Prng`. 201 | *) 202 | 203 | // Coefficients are cited from Wikipedia 204 | let linear x = x, 6364136223846793005uL * x + 1442695040888963407uL 205 | 206 | (** 207 | The first returned value is a random number and the second returned value is a next state. 208 | Note that modulus is not defined because `Prng` is required to return random numbers 209 | in 64-bit resolution. 210 | 211 | Hereafter you can use the `linear` PRNG to generate random numbers. 212 | *) 213 | 214 | let linearState = createState linear 0x123456789ABCDEFuL 215 | Random.get generator linearState 216 | -------------------------------------------------------------------------------- /docs/images/monte-carlo-pi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/FsRandom/0c43ba5caa2ebd0b9e2e2d16cb99f88284136470/docs/images/monte-carlo-pi.png -------------------------------------------------------------------------------- /docs/index.fsx: -------------------------------------------------------------------------------- 1 | (*** hide ***) 2 | #I "../Build/lib/net45" 3 | #r "FsRandom.dll" 4 | 5 | (** 6 | FsRandom 7 | ======== 8 | 9 | FsRandom is a purely-functional random number generator framework designed for F# language. 10 | It helps you to obtain a variety of random numbers to use more than ten predefined generators, 11 | and to define a new function to generate random numbers you want. 12 | 13 | How to Install 14 | -------------- 15 | 16 | ### Install from NuGet 17 | 18 |
19 |
20 |
21 |
22 | FsRandom is available on the NuGet Gallery. 23 | Run in the Package Manager Console: 24 |
PM> Install-Package FsRandom
25 |
26 |
27 |
28 |
29 | 30 | ### Build from source code 31 | 32 |
33 |
34 |
35 |
36 | FAKE script is included in the source code. 37 | To make a debug build, run: 38 |
> fsi tools\build.fsx --debug
39 |
40 |
41 |
42 |
43 | 44 | Short Example 45 | ------------- 46 | *) 47 | 48 | (*** define-output:randomPoint ***) 49 | open FsRandom 50 | 51 | // Random state 52 | let state = createState xorshift (123456789u, 362436069u, 521288629u, 88675123u) 53 | 54 | // Random point generator 55 | let randomPointGenerator = random { 56 | let! x = ``[0, 1)`` // generates a random number between 0 and 1 57 | let! y = Statistics.normal (0.0, 1.0) // generates a normal random number 58 | return (x, y) 59 | } 60 | 61 | // Get a random point 62 | let randomPoint = Random.get randomPointGenerator state 63 | printf "(x, y) = (%f, %f)" <|| randomPoint 64 | 65 | (** 66 | The script yields: 67 | *) 68 | (*** include-output:randomPoint ***) 69 | 70 | (** 71 | Features 72 | -------- 73 | ### Random Functions 74 | 75 | FsRandom provides a variety of random number generator functions: 76 | 77 | * **RandomNumberGenerator module** provides standard random number generators: 78 | \`\`(0, 1)\`\`, \`\`[0, 1)\`\`, \`\`(0, 1]\`\`, and \`\`[0, 1]\`\`. 79 | * **Random module** manipulates random numbers. 80 | * **Statistics module** provides a variety of statistical distributions such like `uniform`, `normal` and `gamma`. 81 | * **Seq module** provides functions for generating random number sequences. 82 | * **Array module** and **Array2D module** provide functions for array operations like `createRandom`, `sample`, `sampleWithReplacement`, and `shuffle`. 83 | * **List module** provides functions for generating random lists. 84 | * **String module** provides functions for generating random strings. 85 | * **Utility module** provides utility functions. 86 | 87 | ### Pseudo-Random Number Generators 88 | 89 | You can choose an algorithm of pseudo-random numbger generator: 90 | 91 | * **xorshift** implements [Xorshift](http://en.wikipedia.org/wiki/Xorshift) algorithm. 92 | * **systemrandom** leverages System.Random and its subclasses as a random number generators. 93 | This PRNG is not purely functional because the state of the PRNG is controled in the classes. 94 | * **mersenne** in MersenneTwister module implements 64-bit version of [Mersenne Twister](http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html) algorithm. 95 | * **sfmt** in SimdOrientedFastMersenneTwister module implements [SFMT](http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html) algorithm. 96 | *) 97 | -------------------------------------------------------------------------------- /docs/numerical-examples.fsx: -------------------------------------------------------------------------------- 1 | (*** hide ***) 2 | #I "../Build/lib/net45" 3 | #r "FsRandom.dll" 4 | open FsRandom 5 | 6 | let state = createState xorshift (123456789u, 362436069u, 521288629u, 88675123u) 7 | 8 | (** 9 | 10 | Numerical Examples 11 | ================== 12 | 13 | This page illustrates how FsRandom is applicable to scientific computations. 14 | For statistics, the Statistics module is useful: 15 | *) 16 | 17 | open FsRandom.Statistics 18 | 19 | (** 20 | 21 | Estimating pi, the ratio of a circle's circumference to its diameter 22 | -------------------------------------------------------------------- 23 | 24 | Suppose there is a circle of radius 1 is inside a square with side length 2. 25 | The area of the circle is \\(\pi\\) and the area of the square is 4. 26 | If you put \\(N\\) random points on the square, roughly \\(\displaystyle\frac{\pi}{4}N\\) points are inside the circle. 27 | In other words, if you find \\(M\\) points out of \\(N\\) are inside the circle, 28 | \\(\displaystyle\frac{M}{N}\\) approximates \\(\displaystyle\frac{\pi}{4}\\). 29 | 30 |
31 | random points approximates pi 32 |
33 | 34 | Random points can be described simply and easily as follows: 35 | *) 36 | 37 | let randomPointGenerator = random { 38 | let! x = uniform (-1.0, 1.0) 39 | let! y = uniform (-1.0, 1.0) 40 | return (x, y) 41 | } 42 | 43 | (** 44 | To give each point weight 4 if the point is inside the circle and 0 otherwise 45 | adjusts the average of total score to become \\(\pi\\). 46 | *) 47 | 48 | let weight (x, y) = if x * x + y * y <= 1.0 then 4.0 else 0.0 49 | let randomScoreGenerator = Random.map weight randomPointGenerator 50 | 51 | (** 52 | The average of the random scores approximates \\(\pi\\). 53 | To generate 1,000,000 scores and to compute the approximation: 54 | *) 55 | 56 | (*** define-output:pi ***) 57 | Seq.ofRandom randomScoreGenerator state 58 | |> Seq.take 1000000 59 | |> Seq.average 60 | |> printf "%f" 61 | (*** include-output:pi ***) 62 | 63 | (** 64 | 65 | Generating bivariate normal random numbers using Gibbs sampler 66 | -------------------------------------------------------------- 67 | 68 | To sample from bivariate normal distribution 69 | \\(\displaystyle N\_{2}\left( 70 | \left[\begin{array}{c} 71 | \mu\_{X} \\\\ 72 | \mu\_{Y} 73 | \end{array}\right], 74 | \left[\begin{array}{cc} 75 | \sigma\_{X}^{2} & \sigma\_{XY} \\\\ 76 | \sigma\_{XY} & \sigma\_{Y}^{2} 77 | \end{array}\right] 78 | \right) \\), 79 | you will construct a Gibbs sampler. 80 | Let \\(f\_{2}(x, y)\\) be the density function of \\(N\_{2}\\), 81 | and let \\(x'\\) and \\(y'\\) be \\(x-\mu\_{X}\\) and \\(y-\mu\_{Y}\\) respectively. 82 | Then, 83 | $$ 84 | \begin{eqnarray} 85 | f\_{2}(x, y) & \propto & \exp\left( 86 | -\frac{1}{2}\left[\begin{array}{c} 87 | x' \\\\ 88 | y' 89 | \end{array}\right]^{T} 90 | \left[\begin{array}{cc} 91 | \sigma\_{X}^{2} & \sigma\_{XY} \\\\ 92 | \sigma\_{XY} & \sigma\_{Y}^{2} 93 | \end{array}\right]^{-1} 94 | \left[\begin{array}{c} 95 | x' \\\\ 96 | y' 97 | \end{array}\right] 98 | \right) \\\\ 99 | & \propto & \exp\left( 100 | -\frac{\left(x'-\sigma\_{XY}y'/\sigma\_{Y}^{2}\right)^{2}}{2\left(\sigma\_{X}^{2}-\sigma\_{XY}^{2}/\sigma\_{Y}^{2}\right)} 101 | \right) 102 | \end{eqnarray} 103 | $$ 104 | This means the conditional probability of \\(x\\) given \\(y\\) is distributed normally, 105 | and its mean is \\(\displaystyle \mu\_{X}+\frac{\sigma\_{XY}}{\sigma\_{Y}^{2}}\left(y-\mu\_{Y}\right)\\) 106 | and its variance is \\(\displaystyle \sigma\_{X}^{2}-\frac{\sigma\_{XY}^{2}}{\sigma\_{Y}^{2}}\\). 107 | Therefore, the Gibbs sampler for bivariate normal distribution consists of iterating as the following: 108 | 109 | 1. Draw \\(\displaystyle x\_{t+1}\sim N\left(\mu\_{X}+\frac{\sigma\_{XY}}{\sigma\_{Y}^{2}}(y\_{t}-\mu\_{Y}), \sigma\_{X}^{2}-\frac{\sigma\_{XY}^{2}}{\sigma\_{Y}^{2}}\right)\\) 110 | 2. Draw \\(\displaystyle y\_{t+1}\sim N\left(\mu\_{Y}+\frac{\sigma\_{XY}}{\sigma\_{X}^{2}}(x\_{t+1}-\mu\_{Y}), \sigma\_{Y}^{2}-\frac{\sigma\_{XY}^{2}}{\sigma\_{X}^{2}}\right)\\) 111 | 112 | And it can be naturally translated into F# code as the following. 113 | *) 114 | 115 | let gibbsBinormal (meanX, meanY, varX, varY, cov) = 116 | let sdx = sqrt <| varX - cov ** 2.0 / varY 117 | let sdy = sqrt <| varY - cov ** 2.0 / varX 118 | fun (_, y) -> random { 119 | let! x' = normal (meanX + cov * (y - meanY) / varY, sdx) 120 | let! y' = normal (meanY + cov * (x' - meanX) / varX, sdy) 121 | return (x', y') 122 | } 123 | let binormal parameter = Seq.markovChain (gibbsBinormal parameter) 124 | 125 | (** 126 | Note that the generating bivariate normal random number sequence is [autocorrelated](http://en.wikipedia.org/wiki/Autocorrelation). 127 | 128 | 129 | Hamiltonian Monte Carlo 130 | ----------------------- 131 | 132 | Gibbs sampler sometimes produces strongly autocorrelated traces. 133 | Hamiltonian Monte Carlo (also known as [hybrid Monte Carlo](http://en.wikipedia.org/wiki/Hybrid_Monte_Carlo)) 134 | could be efficient in such situations. 135 | Hamiltonian Monte Carlo algorithm is available 136 | if you know about the density of the taget distribution without normalizing constant. 137 | *) 138 | 139 | let inline updateWith f (ys:float []) (xs:float []) = 140 | for index = 0 to Array.length xs - 1 do 141 | xs.[index] <- f xs.[index] ys.[index] 142 | 143 | /// Hamiltonian Monte Carlo 144 | let hmc minusLogDensity gradMinusLogDensity epsilon step = 145 | /// Leapfrog integration 146 | let leapfrog q p = 147 | updateWith (fun x y -> x + 0.5 * epsilon * y) (gradMinusLogDensity q) p 148 | for i = 1 to step - 1 do 149 | updateWith (fun x y -> x + epsilon * y) p q 150 | updateWith (fun x y -> x - epsilon * y) (gradMinusLogDensity q) p 151 | updateWith (fun x y -> x + epsilon * y) p q 152 | updateWith (fun x y -> -x + 0.5 * epsilon * y) (gradMinusLogDensity q) p 153 | /// Hamiltonian 154 | let hamiltonian q p = 155 | let potential = minusLogDensity q 156 | let kinetic = Array.fold (fun acc x -> acc + x * x) 0.0 p / 2.0 157 | potential + kinetic 158 | /// resampling of particles 159 | let resampleK n = Array.randomCreate n Standard.normal 160 | fun currentQ -> random { 161 | let q = Array.copy currentQ 162 | // resampling of particles 163 | let! currentP = resampleK (Array.length currentQ) 164 | let p = Array.copy currentP 165 | leapfrog q p 166 | let currentH = hamiltonian currentQ currentP 167 | let proposedH = hamiltonian q p 168 | let! r = ``[0, 1)`` 169 | return if r < exp (currentH - proposedH) then q else currentQ 170 | } 171 | 172 | (** 173 | 174 | Approximate Bayesian Computation 175 | -------------------------------- 176 | 177 | [Approximate Bayesian computation](http://en.wikipedia.org/wiki/Approximate_Bayesian_computation) 178 | is known as a likelihood-free method of parameter estimation. 179 | This section follows the example in the Wikipedia article. 180 | 181 | The initial state of the system is not described whether it is determined or inferred. 182 | Here it is assumed as 'A' for convenience. 183 | Then, the model is described as follows: 184 | *) 185 | 186 | type HiddenSystem = A | B 187 | let switch = function A -> B | B -> A 188 | let observe correctly = function 189 | | A -> if correctly then 'A' else 'B' 190 | | B -> if correctly then 'B' else 'A' 191 | let model (theta, gamma, length) = random { 192 | let state = ref A 193 | let builder = System.Text.StringBuilder () 194 | for index = 1 to length do 195 | let! switchState = Utility.flipCoin theta 196 | if switchState then state := switch !state 197 | let! correctly = Utility.flipCoin gamma 198 | builder.Append (observe correctly !state) |> ignore 199 | return builder.ToString () 200 | } 201 | 202 | (** 203 | Step 1: the observed data is `AAAABAABBAAAAAABAAAA`. 204 | *) 205 | 206 | let observed = "AAAABAABBAAAAAABAAAA" 207 | 208 | (** 209 | Step 2: the prior of theta is a uniform [0, 1] and gamma is known. 210 | *) 211 | 212 | let prior = uniform (0.0, 1.0) 213 | let gamma = 0.8 214 | 215 | (** 216 | Step 3: the summary statistic is the frequency of switches between two states (A and B). 217 | Note that the summary statistic is bad to estimate theta (see the article). 218 | *) 219 | 220 | let w (data:string) = 221 | Seq.windowed 2 data 222 | |> Seq.filter (fun c -> c.[0] <> c.[1]) // switch 223 | |> Seq.length 224 | 225 | (** 226 | Step 4: the distance between the observed and simulated is the difference between the summary statistics. 227 | *) 228 | 229 | let rho simulated = abs (w observed - w simulated) 230 | 231 | (** 232 | Step 5: do the simulation with tolerance epsilon. 233 | *) 234 | 235 | type SimulationResult = 236 | | Accept of float 237 | | Reject 238 | let simulate epsilon = random { 239 | let! theta = prior 240 | let! simulated = model (theta, gamma, String.length observed) 241 | return if rho simulated <= epsilon then Accept (theta) else Reject 242 | } 243 | -------------------------------------------------------------------------------- /docs/templates/template.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | @Title 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
22 |

@{ 23 | if (System.Uri.IsWellFormedUriString(Root, System.UriKind.Absolute)) 24 | { 25 | @System.String.Format("{1}", Root, Properties["project-name"]); 26 | } 27 | else 28 | { 29 | @Properties["project-name"]; 30 | } 31 | }

32 |
33 |
34 |
35 |
36 | @RenderBody() 37 |
38 | 66 |
67 |
68 | Fork me on GitHub 69 | 70 | 71 | -------------------------------------------------------------------------------- /examples/ApproximateBayesianComputation.fsx: -------------------------------------------------------------------------------- 1 | #if INTERACTIVE 2 | #I "../Build" 3 | #r "FsRandom.dll" 4 | #endif 5 | 6 | open FsRandom 7 | open FsRandom.Statistics 8 | 9 | let seed = 123456789u, 362436069u, 521288629u, 88675123u 10 | let state = createState xorshift seed 11 | 12 | type HiddenSystem = A | B 13 | let switch = function A -> B | B -> A 14 | let observe correctly = function 15 | | A -> if correctly then 'A' else 'B' 16 | | B -> if correctly then 'B' else 'A' 17 | let model (theta, gamma, length) = random { 18 | let state = ref A 19 | let builder = System.Text.StringBuilder () 20 | for index = 1 to length do 21 | let! switchState = Utility.flipCoin theta 22 | if switchState then state := switch !state 23 | let! correctly = Utility.flipCoin gamma 24 | builder.Append (observe correctly !state) |> ignore 25 | return builder.ToString () 26 | } 27 | 28 | let observed = "AAAABAABBAAAAAABAAAA" 29 | let prior = uniform (0.0, 1.0) // try `beta (6.0, 13.0)` for better acceptance ratio 30 | let gamma = 0.8 31 | 32 | let w (data:string) = 33 | Seq.windowed 2 data 34 | |> Seq.filter (fun c -> c.[0] <> c.[1]) // switch 35 | |> Seq.length 36 | let rho simulated = abs (w observed - w simulated) 37 | 38 | /// Datail to output 39 | type SimulationDetail = { 40 | Theta : float 41 | SimulatedData : string 42 | SummaryStatistic : int 43 | Distance : int 44 | Accepted : bool 45 | } 46 | let simulate epsilon = random { 47 | let! theta = prior 48 | let! simulated = model (theta, gamma, String.length observed) 49 | let distance = rho simulated 50 | return { 51 | Theta = theta 52 | SimulatedData = simulated 53 | SummaryStatistic = w simulated 54 | Distance = distance 55 | Accepted = distance <= epsilon 56 | } 57 | } 58 | 59 | let n = 25 60 | let epsilon = 2 61 | printfn "epsilon = %d" epsilon 62 | printfn "index theta simulated data summary rho outcome" 63 | Seq.ofRandom (simulate epsilon) state 64 | |> Seq.take n 65 | |> Seq.iteri (fun index detail -> 66 | printfn "%5d %.3f %s %7d %3d %7s" 67 | <| index + 1 68 | <| detail.Theta 69 | <| detail.SimulatedData 70 | <| detail.SummaryStatistic 71 | <| detail.Distance 72 | <| if detail.Accepted then "accepted" else "rejected" 73 | ) 74 | -------------------------------------------------------------------------------- /examples/Benchmark.fsx: -------------------------------------------------------------------------------- 1 | #if INTERACTIVE 2 | #I "../Build" 3 | #r "FsRandom.dll" 4 | #endif 5 | 6 | open System 7 | open FsRandom 8 | 9 | type BenchmarkOption = { 10 | Iterate : int 11 | Size : int 12 | Round : int 13 | Trim : float 14 | } 15 | let option = 16 | #if INTERACTIVE 17 | // recursion iteration with fsharpi (Mac/Linux) is very slow. 18 | // Too large Iterate does not return so long. 19 | let defaultOption = { Iterate = 100000; Size = 100000; Round = 20; Trim = 0.2 } 20 | let args = fsi.CommandLineArgs 21 | #else 22 | let defaultOption = { Iterate = 5000000; Size = 100000; Round = 20; Trim = 0.2 } 23 | let args = Environment.GetCommandLineArgs () 24 | #endif 25 | let rec loop acc = function 26 | | [] -> acc 27 | | "--iterate" :: n :: rest -> loop { acc with Iterate = int n } rest 28 | | "--size" :: n :: rest -> loop { acc with Size = int n } rest 29 | | "--round" :: r :: rest -> loop { acc with Round = int r } rest 30 | | "--trim" :: t :: rest -> loop { acc with Trim = float t } rest 31 | | s :: _ -> 32 | eprintfn "unknown option: %s" s 33 | let programName = System.IO.Path.GetFileName (args.[0]) 34 | eprintfn "usage: %s [--iterate int] [--size int] [--round int] [--trim p]" programName 35 | exit 1 36 | let args = (List.ofArray args).Tail 37 | loop defaultOption args 38 | 39 | let time f x = 40 | let stopwatch = System.Diagnostics.Stopwatch () 41 | stopwatch.Start () 42 | f x |> ignore 43 | stopwatch.Stop () 44 | stopwatch.Elapsed 45 | let trimmedMean p (s:seq) = 46 | let mutable n = 0 47 | let mutable all = [] 48 | use e = s.GetEnumerator () 49 | while e.MoveNext () do 50 | n <- n + 1 51 | all <- e.Current :: all 52 | let init xs = 53 | let rec loop = function 54 | | _, [] -> [] 55 | | head, y :: ys -> head :: loop (y, ys) 56 | match xs with 57 | | [] -> failwith "empty list" 58 | | y :: ys -> loop (y, ys) 59 | let rec trim = function 60 | | 0, acc -> acc 61 | | _, [] -> failwith "empty list" 62 | | c, xs -> trim (c - 1, (List.tail >> init) xs) 63 | let total = trim (int (float n * p), List.sort all) |> List.fold (+) TimeSpan.Zero 64 | TimeSpan.FromTicks (int64 (float total.Ticks / float n)) 65 | 66 | let r = Random () 67 | let xorshiftState = createState xorshift (123456789u, 362436069u, 521288629u, 88675123u) 68 | let systemrandomState = createState systemrandom r 69 | 70 | [] 71 | let ``1 / 2^52`` = 2.22044604925031308084726333618e-16 72 | let inline generate53bit () = 73 | let u1 = r.Next () 74 | let u2 = r.Next () 75 | let r = (uint64 u1 <<< 26) ||| (uint64 (u2 &&& 0b00000011111111111111111111111111)) 76 | (float r + 0.5) * ``1 / 2^52`` 77 | 78 | let fsx = Seq.ofRandom ``[0, 1)`` xorshiftState 79 | let fss = Seq.ofRandom ``[0, 1)`` systemrandomState 80 | let ds1 = 81 | let rec loop () = seq { 82 | yield r.NextDouble () 83 | yield! loop () 84 | } 85 | loop () 86 | let ds2 = seq { while true do yield r.NextDouble () } 87 | let ds3 = 88 | let rec loop () = seq { 89 | yield generate53bit () 90 | yield! loop () 91 | } 92 | loop () 93 | let ds4 = seq { while true do yield generate53bit () } 94 | 95 | let fsxArrayCreate n = Random.get (Array.randomCreate n ``[0, 1)``) xorshiftState 96 | let fssArrayCreate n = Random.get (Array.randomCreate n ``[0, 1)``) systemrandomState 97 | let fsxArrayInit n = Random.get (Array.randomInit n (fun _ -> ``[0, 1)``)) xorshiftState 98 | let fssArrayInit n = Random.get (Array.randomInit n (fun _ -> ``[0, 1)``)) systemrandomState 99 | let dsArrayInit n = Array.init n (fun _ -> r.NextDouble ()) 100 | let dsArrayInit53 n = Array.init n (fun _ -> generate53bit ()) 101 | 102 | let iterate = option.Iterate 103 | let size = option.Size 104 | let round = option.Round 105 | let trim = option.Trim 106 | let benchmarkSeq name s = 107 | GC.Collect () 108 | printf "%s" name 109 | Seq.init round (fun _ -> s) 110 | |> Seq.map (time (Seq.take iterate >> Seq.length)) 111 | |> Seq.map (fun s -> printf "."; s) 112 | |> trimmedMean trim 113 | |> printfn "\t%A" 114 | let benchmarkArray name s = 115 | GC.Collect () 116 | printf "%s" name 117 | Seq.init round (fun _ -> s) 118 | |> Seq.map (fun s -> time s size) 119 | |> Seq.map (fun s -> printf "."; s) 120 | |> trimmedMean trim 121 | |> printfn "\t%A" 122 | 123 | printfn "---" 124 | printfn "Iterates %d random numbers %d times" iterate round 125 | printfn "---" 126 | benchmarkSeq "*Seq.ofRandom (xorshift)" fsx 127 | benchmarkSeq "*Seq.ofRandom (systemrandom)" fss 128 | benchmarkSeq "^Recursion" ds1 129 | benchmarkSeq "^Imperative" ds2 130 | benchmarkSeq "*Recursion" ds3 131 | benchmarkSeq "*Imperative" ds4 132 | printfn "---" 133 | printfn "Creates %d random arrays of size %d" round size 134 | printfn "---" 135 | benchmarkArray "*Array.randomCreate (xorshift)" fsxArrayCreate 136 | benchmarkArray "*Array.randomCreate (systemrandom)" fssArrayCreate 137 | benchmarkArray "*Array.randomInit (xorshift)" fsxArrayInit 138 | benchmarkArray "*Array.randomInit (systemrandom)" fssArrayInit 139 | benchmarkArray "^Array.init" dsArrayInit 140 | benchmarkArray "*Array.init" dsArrayInit53 141 | printfn "---" 142 | printfn "%d%% trimmed mean" (int <| 100.0 * trim) 143 | printfn "*, 53-bit resolution; ^, 31-bit resolution" 144 | -------------------------------------------------------------------------------- /examples/FirstRandomNumber.fsx: -------------------------------------------------------------------------------- 1 | #if INTERACTIVE 2 | #I "../Build" 3 | #r "FsRandom.dll" 4 | #endif 5 | 6 | open FsRandom 7 | 8 | let seed = 123456789u, 362436069u, 521288629u, 88675123u 9 | let state = createState xorshift seed 10 | 11 | let generator = Statistics.normal (0.0, 1.0) 12 | 13 | let z = Random.get generator state 14 | printfn "%f" z 15 | 16 | let z1, nextState = Random.next generator state 17 | let z2 = Random.get generator nextState 18 | printfn "%f\n%f" z1 z2 19 | -------------------------------------------------------------------------------- /examples/GeneratorFunction.fsx: -------------------------------------------------------------------------------- 1 | #if INTERACTIVE 2 | #I "../Build" 3 | #r "FsRandom.dll" 4 | #endif 5 | 6 | open FsRandom 7 | 8 | let seed = 123456789u, 362436069u, 521288629u, 88675123u 9 | let state = createState xorshift seed 10 | 11 | let approximatelyStandardNormal = random { 12 | let! values = Array.randomCreate 12 ``(0, 1)`` // ``(0, 1)`` is a standard random number generator in (0, 1) 13 | return Array.sum values - 6.0 14 | } 15 | 16 | let z = Random.get approximatelyStandardNormal state 17 | printfn "%f" z 18 | -------------------------------------------------------------------------------- /examples/GibbsSampler.fsx: -------------------------------------------------------------------------------- 1 | #if INTERACTIVE 2 | #I "../Build" 3 | #r "FsRandom.dll" 4 | #endif 5 | 6 | open FsRandom 7 | open FsRandom.Statistics 8 | 9 | let seed = 123456789u, 362436069u, 521288629u, 88675123u 10 | let state = createState xorshift seed 11 | 12 | let gibbsBinormal (meanX, meanY, varX, varY, cov) (_ : float, y : float) = random { 13 | let! x' = normal (meanX + cov * (y - meanY) / varY, sqrt <| varX - cov ** 2.0 / varY) 14 | let! y' = normal (meanY + cov * (x' - meanX) / varX, sqrt <| varY - cov ** 2.0 / varX) 15 | return (x', y') 16 | } 17 | let binormal parameter = Seq.markovChain (gibbsBinormal parameter) 18 | 19 | module Seq = 20 | let takeBy n (source:seq<'a>) = seq { 21 | use e = source.GetEnumerator () 22 | while e.MoveNext () do 23 | yield e.Current 24 | let skip = ref (n - 1) 25 | while !skip > 0 && e.MoveNext () do 26 | decr skip 27 | } 28 | 29 | let parameter = (0.0, 0.0, 1.0, 1.0, 0.7) 30 | let initialPoint = (0.0, 0.0) 31 | let sampler = 32 | binormal parameter initialPoint state 33 | |> Seq.skip 100 // burn-in 34 | |> Seq.takeBy 20 // to avoid autocorrelation 35 | 36 | sampler 37 | |> Seq.take 50 38 | |> Seq.iter (fun (x, y) -> printfn "%6.3f\t%6.3f" x y) 39 | -------------------------------------------------------------------------------- /examples/HamiltonianMonteCarlo.fsx: -------------------------------------------------------------------------------- 1 | #if INTERACTIVE 2 | #I "../Build" 3 | #r "FsRandom.dll" 4 | #endif 5 | 6 | open FsRandom 7 | open FsRandom.Statistics 8 | 9 | let seed = 123456789u, 362436069u, 521288629u, 88675123u 10 | let state = createState xorshift seed 11 | 12 | let inline updateWith f (ys:float []) (xs:float []) = 13 | for index = 0 to Array.length xs - 1 do 14 | xs.[index] <- f xs.[index] ys.[index] 15 | 16 | /// Hamiltonian Monte Carlo 17 | let hmc minusLogDensity gradMinusLogDensity epsilon step = 18 | /// Leapfrog integration 19 | let leapfrog q p = 20 | updateWith (fun x y -> x + 0.5 * epsilon * y) (gradMinusLogDensity q) p 21 | for i = 1 to step - 1 do 22 | updateWith (fun x y -> x + epsilon * y) p q 23 | updateWith (fun x y -> x - epsilon * y) (gradMinusLogDensity q) p 24 | updateWith (fun x y -> x + epsilon * y) p q 25 | updateWith (fun x y -> -x + 0.5 * epsilon * y) (gradMinusLogDensity q) p 26 | /// Hamiltonian 27 | let hamiltonian q p = 28 | let potential = minusLogDensity q 29 | let kinetic = Array.fold (fun acc x -> acc + x * x) 0.0 p / 2.0 30 | potential + kinetic 31 | /// resampling of particles 32 | let resampleK n = Array.randomCreate n Standard.normal 33 | fun currentQ -> random { 34 | let q = Array.copy currentQ 35 | // resampling of particles 36 | let! currentP = resampleK (Array.length currentQ) 37 | let p = Array.copy currentP 38 | leapfrog q p 39 | let currentH = hamiltonian currentQ currentP 40 | let proposedH = hamiltonian q p 41 | let! r = ``[0, 1)`` 42 | return if r < exp (currentH - proposedH) then q else currentQ 43 | } 44 | 45 | // Sampling from N2 with correlation coefficient of 0.95. 46 | let r = 0.95 47 | let detSigma = 1.0 * 1.0 - r * r // determinant of variance-covariance matrix 48 | let initialPoint = [|0.0; 0.0|] 49 | let minusLogF2 (xy:float []) = 50 | let x = xy.[0] 51 | let y = xy.[1] 52 | (x * x - 2.0 * r * x * y + y * y) / (2.0 * detSigma) 53 | let gradMinusLogF2 (xy:float []) = 54 | let x = xy.[0] 55 | let y = xy.[1] 56 | [|(x - r * y) / detSigma; (-r * x + y) / detSigma|] 57 | let sampler = Seq.markovChain (hmc minusLogF2 gradMinusLogF2 1.0e-2 2000) initialPoint state 58 | 59 | sampler 60 | |> Seq.take 1000 61 | |> Seq.iter (fun xy -> printfn "%6.3f\t%6.3f" xy.[0] xy.[1]) 62 | -------------------------------------------------------------------------------- /examples/MonteCarlo.fsx: -------------------------------------------------------------------------------- 1 | #if INTERACTIVE 2 | #I "../Build" 3 | #r "FsRandom.dll" 4 | #endif 5 | 6 | open FsRandom 7 | 8 | let seed = 123456789u, 362436069u, 521288629u, 88675123u 9 | let state = createState xorshift seed 10 | 11 | // Generates random points on [-1, 1] x [-1, 1]. 12 | let randomPointGenerator = random { 13 | let! x = Statistics.uniform (-1.0, 1.0) 14 | let! y = Statistics.uniform (-1.0, 1.0) 15 | return (x, y) 16 | } 17 | // Weight of a point 18 | // If the distance from (0, 0) is equal to or less than 1 (in the unit circle), 19 | // the weight is 4 (because random points are distributed on [-1, 1] x [-1, 1]). 20 | let weight (x, y) = if x * x + y * y <= 1.0 then 4.0 else 0.0 21 | // Function to generate a sequence 22 | let values = Seq.ofRandom (Random.map weight randomPointGenerator) 23 | 24 | // Monte Carlo integration 25 | // Generates 1,000,000 random values and the average becomes estimator of pi 26 | values state 27 | |> Seq.take 1000000 28 | |> Seq.average 29 | |> printfn "%f" 30 | -------------------------------------------------------------------------------- /examples/PRNG.fsx: -------------------------------------------------------------------------------- 1 | #if INTERACTIVE 2 | #I "../Build" 3 | #r "FsRandom.dll" 4 | #endif 5 | 6 | open FsRandom 7 | 8 | // Coefficients are cited from Wikipedi 9 | let linear x = x, 6364136223846793005uL * x + 1442695040888963407uL 10 | let linearState = createState linear 0x123456789ABCDEFuL 11 | let y = Random.get ``[0, 1)`` linearState 12 | printfn "%f" y 13 | -------------------------------------------------------------------------------- /examples/Sequence.fsx: -------------------------------------------------------------------------------- 1 | #if INTERACTIVE 2 | #I "../Build" 3 | #r "FsRandom.dll" 4 | #endif 5 | 6 | open FsRandom 7 | 8 | let seed = 123456789u, 362436069u, 521288629u, 88675123u 9 | let state = createState xorshift seed 10 | 11 | let rec binaries initialState = seq { 12 | let binary, nextState = Random.next (Statistics.bernoulli 0.5) initialState 13 | yield binary 14 | yield! binaries nextState // recursively generating binaries. 15 | } 16 | binaries state |> Seq.take 20 |> Seq.iter (printf "%d") 17 | printfn "" 18 | 19 | let binaries2 = Seq.ofRandom (Statistics.bernoulli 0.5) 20 | binaries2 state |> Seq.take 20 |> Seq.iter (printf "%d") 21 | printfn "" 22 | -------------------------------------------------------------------------------- /examples/SystemRandom.fsx: -------------------------------------------------------------------------------- 1 | #if INTERACTIVE 2 | #I "../Build" 3 | #r "FsRandom.dll" 4 | #endif 5 | 6 | open FsRandom 7 | 8 | let r0 = System.Random () 9 | let state = createState systemrandom r0 10 | 11 | // systemrandom offers a statefun workflow. 12 | // The result should be different. 13 | printfn "%f" <| Random.get ``[0, 1)`` state 14 | printfn "%f" <| Random.get ``[0, 1)`` state 15 | -------------------------------------------------------------------------------- /examples/Transform.fsx: -------------------------------------------------------------------------------- 1 | #if INTERACTIVE 2 | #I "../Build" 3 | #r "FsRandom.dll" 4 | #endif 5 | 6 | open FsRandom 7 | 8 | let seed = 123456789u, 362436069u, 521288629u, 88675123u 9 | let state = createState xorshift seed 10 | 11 | let plusOne x = x + 1.0 12 | Random.map plusOne <| Statistics.uniform (0.0, 1.0) 13 | |> Random.get 14 | <| state 15 | |> printfn "%f" 16 | 17 | Random.identity <| Statistics.uniform (0.0, 1.0) 18 | |> Random.get 19 | <| state 20 | |> plusOne 21 | |> printfn "%f" 22 | 23 | Statistics.uniform (0.0, 1.0) 24 | |> Random.get 25 | <| state 26 | |> plusOne 27 | |> printfn "%f" 28 | -------------------------------------------------------------------------------- /examples/WeightedSample.fsx: -------------------------------------------------------------------------------- 1 | #if INTERACTIVE 2 | #I "../Build/lib/net45" 3 | #r "FsRandom.dll" 4 | #endif 5 | 6 | open FsRandom 7 | // from https://www.scb.se/hitta-statistik/statistik-efter-amne/befolkning/amnesovergripande-statistik/namnstatistik/pong/tabell-och-diagram/samtliga-folkbokforda--efternamn-topplistor/efternamn-topp-100/ 8 | let be0001namntab40_2019=[ 9 | "Andersson",228_264 10 | "Johansson",227_104 11 | "Karlsson",202_331 12 | "Nilsson",155_686 13 | "Eriksson",136_533 14 | "Larsson",114_698 15 | "Olsson",103_689 16 | "Persson",98_019 17 | "Svensson",91_741 18 | "Gustafsson",89_534 19 | "Pettersson",87_169 20 | "Jonsson",68_358 21 | "Jansson",45_844 22 | "Hansson",40_517 23 | "Bengtsson",31_649 24 | "Jönsson",28_874 25 | "Lindberg",27_119 26 | "Jakobsson",25_669 27 | "Magnusson",25_109 28 | "Olofsson",24_493 29 | "Lindström",24_321 30 | "Lindqvist",22_461 31 | "Lindgren",22_250 32 | "Axelsson",21_671 33 | "Berg",21_525 34 | "Bergström",20_739 35 | "Lundberg",20_665 36 | "Lind",20_305 37 | "Lundgren",19_995 38 | "Lundqvist",19_617 39 | "Mattsson",18_926 40 | "Berglund",18_706 41 | "Fredriksson",17_691 42 | "Sandberg",17_406 43 | "Henriksson",16_908 44 | "Forsberg",16_335 45 | "Sjöberg",16_187 46 | "Wallin",15_831 47 | "Ali",15_473 48 | "Engström",15_320 49 | "Mohamed",15_253 50 | "Eklund",15_097 51 | "Danielsson",14_898 52 | "Lundin",14_755 53 | "Håkansson",14_545 54 | "Björk",14_200 55 | "Bergman",14_067 56 | "Gunnarsson",14_017 57 | "Holm",13_897 58 | "Wikström",13_738 59 | "Samuelsson",13_643 60 | "Isaksson",13_474 61 | "Fransson",13_432 62 | "Bergqvist",13_254 63 | "Nyström",13_051 64 | "Holmberg",12_892 65 | "Arvidsson",12_862 66 | "Löfgren",12_655 67 | "Söderberg",12_435 68 | "Nyberg",12_368 69 | "Blomqvist",12_226 70 | "Claesson",12_067 71 | "Nordström",11_969 72 | "Mårtensson",11_717 73 | "Lundström",11_527 74 | "Ahmed",11_431 75 | "Viklund",11_287 76 | "Björklund",11_187 77 | "Eliasson",11_187 78 | "Pålsson",11_112 79 | "Hassan",11_061 80 | "Berggren",11_016 81 | "Sandström",10_676 82 | "Lund",10_526 83 | "Nordin",10_514 84 | "Ström",10_299 85 | "Åberg",10_283 86 | "Hermansson",10_157 87 | "Ekström",10_136 88 | "Falk",10_054 89 | "Holmgren",9_966 90 | "Dahlberg",9_805 91 | "Hellström",9_784 92 | "Hedlund",9_749 93 | "Sundberg",9_696 94 | "Sjögren",9_628 95 | "Ek",9_473 96 | "Blom",9_413 97 | "Abrahamsson",9_310 98 | "Martinsson",9_270 99 | "Öberg",9_254 100 | "Andreasson",9_024 101 | "Strömberg",8_930 102 | "Månsson",8_896 103 | "Åkesson",8_745 104 | "Hansen",8_673 105 | "Norberg",8_587 106 | "Lindholm",8_578 107 | "Dahl",8_563 108 | "Jonasson",8_520 109 | ] 110 | let weights = List.map snd be0001namntab40_2019 |> List.map float |> List.toArray 111 | let names = List.map fst be0001namntab40_2019 |> List.toArray 112 | let n = Array.length weights 113 | let randomNames = Array.weightedSample n weights names 114 | 115 | let seed = 123456789u, 362436069u, 521288629u, 88675123u 116 | let state = createState xorshift seed 117 | Random.get randomNames state |> Seq.take 20 |> Seq.iter (printfn "%s") 118 | -------------------------------------------------------------------------------- /paket.dependencies: -------------------------------------------------------------------------------- 1 | source https://api.nuget.org/v3/index.json 2 | 3 | storage: none 4 | 5 | nuget FSharp.Core 6 | nuget FsUnit 7 | nuget MathNet.Numerics 8 | nuget Microsoft.NET.Test.Sdk 9 | nuget NUnit 10 | nuget NUnit3TestAdapter 11 | 12 | group Build 13 | source https://api.nuget.org/v3/index.json 14 | 15 | nuget FAKE 16 | nuget FSharp.Formatting 17 | nuget Nuget.CommandLine 18 | -------------------------------------------------------------------------------- /src/FsRandom/Array.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module FsRandom.Array 3 | 4 | open System 5 | 6 | let swap i j (array:'a []) = 7 | let temp = array.[i] 8 | array.[i] <- array.[j] 9 | array.[j] <- temp 10 | 11 | [] 12 | let randomCreate count generator = 13 | if count < 0 then 14 | outOfRange "count" "`count' must not be negative." 15 | else 16 | GeneratorFunction (fun s0 -> 17 | let result = Array.zeroCreate count 18 | let mutable s0 = s0 19 | for index = 0 to count - 1 do 20 | let r, s' = Random.next generator s0 21 | result.[index] <- r 22 | s0 <- s' 23 | result, s0 24 | ) 25 | 26 | [] 27 | let randomInit count initializer = 28 | if count < 0 then 29 | outOfRange "count" "`count' must not be negative." 30 | else 31 | GeneratorFunction (fun s0 -> 32 | let result = Array.zeroCreate count 33 | let mutable s0 = s0 34 | for index = 0 to count - 1 do 35 | let r, s' = Random.next (initializer index) s0 36 | result.[index] <- r 37 | s0 <- s' 38 | result, s0 39 | ) 40 | 41 | [] 42 | let randomFill (array:'a []) targetIndex count generator = 43 | if count < 0 then 44 | outOfRange "count" "`count' must not be negative." 45 | else 46 | GeneratorFunction (fun s0 -> 47 | let mutable s0 = s0 48 | for index = targetIndex to targetIndex + count - 1 do 49 | let r, s' = Random.next generator s0 50 | array.[index] <- r 51 | s0 <- s' 52 | (), s0 53 | ) 54 | 55 | [] 56 | let shuffleInPlace array = 57 | GeneratorFunction (fun s0 -> 58 | let mutable s0 = s0 59 | for index = Array.length array - 1 downto 1 do 60 | let u, s' = Random.next ``[0, 1)`` s0 61 | s0 <- s' 62 | let randomIndex = int <| u * float (index + 1) 63 | swap index randomIndex array 64 | (), s0 65 | ) 66 | 67 | [] 68 | let shuffle array = 69 | GeneratorFunction (fun s0 -> 70 | let copiedArray = Array.copy array 71 | let _, s' = Random.next (shuffleInPlace copiedArray) s0 72 | copiedArray, s' 73 | ) 74 | 75 | [] 76 | let sample n source = 77 | let size = Array.length source 78 | if n < 0 || size < n then 79 | outOfRange "n" "`n' must be between 0 and the number of elements in `source`." 80 | else 81 | GeneratorFunction (fun s0 -> 82 | let result = Array.zeroCreate n 83 | let mutable p = size 84 | let mutable s0 = s0 85 | for index = n - 1 downto 0 do 86 | let mutable probability = 1.0 87 | let u, s' = Random.next ``[0, 1)`` s0 88 | s0 <- s' 89 | while u < probability do 90 | probability <- probability * float (p - index - 1) / float p 91 | p <- p - 1 92 | result.[index] <- source.[size - p - 1] 93 | result, s0 94 | ) 95 | 96 | [] 97 | let sampleOne source = 98 | let size = float <| Array.length source 99 | GeneratorFunction (fun s0 -> 100 | let u, s' = Random.next ``[0, 1)`` s0 101 | let index = int <| size * u 102 | source.[index], s' 103 | ) 104 | 105 | [] 106 | let weightedSample n weight source = 107 | let size = Array.length source 108 | if n < 0 || size < n then 109 | outOfRange "n" "`n' must be between 0 and the number of elements in `source`." 110 | elif Array.length weight <> size then 111 | invalidArg "weight" "`weight' must have the same length of `source'." 112 | else 113 | // Efraimidis and Spirakis (2006) Weighted random sampling with a reservoir (DOI: 10.1016/j.ipl.2005.11.003) 114 | GeneratorFunction (fun s0 -> 115 | let s = ref s0 116 | let result = ref BinarySearchTree.empty 117 | for index = 0 to n - 1 do 118 | let u, s' = Random.next ``[0, 1)`` !s 119 | s := s' 120 | let key = u ** (1.0 / weight.[index]) 121 | result := BinarySearchTree.insert key source.[index] !result 122 | let index = ref n 123 | while !index < size do 124 | let threshold = BinarySearchTree.min !result |> fst 125 | let u, s' = Random.next ``[0, 1)`` !s 126 | s := s' 127 | let x = log u / log threshold 128 | let weightSum = ref 0.0 129 | while !index < size && !weightSum < x do 130 | weightSum := !weightSum + weight.[!index] 131 | incr index 132 | if !weightSum >= x then 133 | let index = !index - 1 134 | let w = weight.[index] 135 | let u, s' = Random.next ``[0, 1)`` !s 136 | s := s' 137 | let r = let t = threshold ** w in t + u * (1.0 - t) 138 | let key = r ** (1.0 / w) 139 | result := BinarySearchTree.removeMinimum !result |> BinarySearchTree.insert key source.[index] 140 | !result |> (BinarySearchTree.toList >> List.map snd >> List.toArray), !s 141 | ) 142 | 143 | [] 144 | let weightedSampleOne weight source = 145 | let size = Array.length source 146 | if size <> Array.length weight then 147 | invalidArg "source" "different size of array" 148 | else 149 | let cdf = 150 | let s = Array.sum weight 151 | Array.scan (+) 0.0 weight |> Array.map (fun p -> p / s) 152 | let binarySearch u = 153 | let rec loop left right = 154 | let n = right - left 155 | if n <= 1 then 156 | left 157 | else 158 | let middle = (left + right) / 2 159 | let v = cdf.[middle] 160 | if u < v then 161 | loop left middle 162 | else 163 | loop middle right 164 | loop 0 size 165 | GeneratorFunction (fun s0 -> 166 | let u, s' = Random.next ``[0, 1)`` s0 167 | let index = binarySearch u 168 | source.[index], s' 169 | ) 170 | 171 | [] 172 | let sampleWithReplacement n source = 173 | let size = Array.length source 174 | if n < 0 then 175 | outOfRange "n" "`n' must not be negative." 176 | elif size = 0 then 177 | invalidArg "source" "empty array." 178 | else 179 | GeneratorFunction (fun s0 -> 180 | let result = Array.zeroCreate n 181 | let size = float <| Array.length source 182 | let mutable s0 = s0 183 | for index = 0 to n - 1 do 184 | let u, s' = Random.next ``[0, 1)`` s0 185 | s0 <- s' 186 | result.[index] <- source.[int (u * size)] 187 | result, s0 188 | ) 189 | 190 | [] 191 | let weightedSampleWithReplacement n weight source = 192 | let size = Array.length source 193 | if n < 0 then 194 | outOfRange "n" "`n' must not be negative." 195 | elif size = 0 then 196 | invalidArg "source" "empty array." 197 | elif Array.length weight <> size then 198 | invalidArg "weight" "`weight' must have the same length of `source'." 199 | else 200 | GeneratorFunction (fun s0 -> 201 | let result = Array.zeroCreate n 202 | let cdf = Array.accumulate (+) weight 203 | let sum = cdf.[size - 1] 204 | let mutable s0 = s0 205 | for index = 0 to n - 1 do 206 | let u, s' = Random.next ``[0, 1)`` s0 207 | s0 <- s' 208 | let p = sum * u 209 | let resultIndex = Array.findIndex (fun x -> p < x) cdf 210 | result.[index] <- source.[resultIndex] 211 | result, s0 212 | ) 213 | -------------------------------------------------------------------------------- /src/FsRandom/Array.fsi: -------------------------------------------------------------------------------- 1 | /// 2 | /// Provides basic operations on arrays. 3 | /// 4 | [] 5 | [] 6 | module FsRandom.Array 7 | 8 | /// 9 | /// Creates an array whose elements are randomly generated. 10 | /// 11 | /// The length of the array to create. 12 | /// The generator function. 13 | [] 14 | val randomCreate : count:int -> generator:GeneratorFunction<'a> -> GeneratorFunction<'a []> 15 | 16 | /// 17 | /// Creates an array whose elements are randomly generated. 18 | /// 19 | /// The length of the array to create. 20 | /// The function to take an index and produce a random number generating function. 21 | [] 22 | val randomInit : count:int -> initializer:(int -> GeneratorFunction<'a>) -> GeneratorFunction<'a []> 23 | 24 | /// 25 | /// Fills an array whose elements are randomly generated. 26 | /// 27 | /// The length of the array to create. 28 | /// The generator function. 29 | [] 30 | val randomFill : array:'a [] -> targetIndex:int -> count:int -> generator:GeneratorFunction<'a> -> GeneratorFunction 31 | 32 | /// 33 | /// Creates a new array whose elements are random set of the elements of the specified array. 34 | /// 35 | /// The array to shuffle. 36 | /// 37 | [] 38 | val shuffle : array:'a [] -> GeneratorFunction<'a []> 39 | 40 | /// 41 | /// Shuffles the elements of the specified array by mutating it in-place. 42 | /// 43 | /// The array to shuffle. 44 | /// 45 | [] 46 | val shuffleInPlace : array:'a [] -> GeneratorFunction 47 | 48 | /// 49 | /// Picks up random samples without replacement in the specified array. 50 | /// 51 | /// The number of samples to pick up. 52 | /// The source array. 53 | /// 54 | [] 55 | val sample : n:int -> source:'a [] -> GeneratorFunction<'a []> 56 | 57 | /// 58 | /// Picks up a random sample in the specified array. 59 | /// 60 | /// The source array. 61 | [] 62 | val sampleOne : source:'a [] -> GeneratorFunction<'a> 63 | 64 | /// 65 | /// Picks up weighted random samples without replacement in the specified array. 66 | /// 67 | /// 68 | /// Implements Efraimidis & Spirakis's A-ExpJ algorithm (Efraimidis & Spirakis 2006). 69 | /// 70 | /// The number of samples to pick up. 71 | /// The sampling weight for each sample. 72 | /// The source array. 73 | [] 74 | val weightedSample : n:int -> weight:float [] -> source:'a [] -> GeneratorFunction<'a []> 75 | 76 | /// 77 | /// Picks up a random sample in the specified array. 78 | /// 79 | /// The sampling weight for each sample. 80 | /// The source array. 81 | [] 82 | val weightedSampleOne : weight:float [] -> source:'a [] -> GeneratorFunction<'a> 83 | 84 | /// 85 | /// Picks up random samples with replacement in the specified array. 86 | /// 87 | /// The number of samples to pick up. 88 | /// The source array. 89 | /// 90 | [] 91 | val sampleWithReplacement : n:int -> source:'a [] -> GeneratorFunction<'a []> 92 | 93 | /// 94 | /// Picks up weighted random samples with replacement in the specified array. 95 | /// 96 | /// The number of samples to pick up. 97 | /// The source array. 98 | /// The sampling weight for each sample. 99 | [] 100 | val weightedSampleWithReplacement : n:int -> weight:float [] -> source:'a [] -> GeneratorFunction<'a []> 101 | -------------------------------------------------------------------------------- /src/FsRandom/Array2D.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module FsRandom.Array2D 3 | 4 | open System 5 | 6 | [] 7 | let randomCreate rowCount columnCount generator = 8 | if rowCount < 0 then 9 | outOfRange "rowCount" "`rowCount' must not be negative." 10 | elif columnCount < 0 then 11 | outOfRange "columnCount" "`columnCount' must not be negative." 12 | else 13 | GeneratorFunction (fun s0 -> 14 | let result = Array2D.zeroCreate rowCount columnCount 15 | let mutable s0 = s0 16 | for i = 0 to rowCount - 1 do 17 | for j = 0 to columnCount - 1 do 18 | let r, s' = Random.next generator s0 19 | result.[i, j] <- r 20 | s0 <- s' 21 | result, s0 22 | ) 23 | 24 | [] 25 | let randomCreateBased rowBase columnBase rowCount columnCount generator = 26 | if rowCount < 0 then 27 | outOfRange "rowCount" "`rowCount' must not be negative." 28 | elif columnCount < 0 then 29 | outOfRange "columnCount" "`columnCount' must not be negative." 30 | else 31 | GeneratorFunction (fun s0 -> 32 | let result = Array2D.zeroCreateBased rowBase columnBase rowCount columnCount 33 | let mutable s0 = s0 34 | for i = rowBase to rowBase + rowCount - 1 do 35 | for j = columnBase to columnBase + columnCount - 1 do 36 | let r, s' = Random.next generator s0 37 | result.[i, j] <- r 38 | s0 <- s' 39 | result, s0 40 | ) 41 | 42 | [] 43 | let randomInit rowCount columnCount initializer = 44 | if rowCount < 0 then 45 | outOfRange "rowCount" "`rowCount' must not be negative." 46 | elif columnCount < 0 then 47 | outOfRange "columnCount" "`columnCount' must not be negative." 48 | else 49 | GeneratorFunction (fun s0 -> 50 | let result = Array2D.zeroCreate rowCount columnCount 51 | let mutable s0 = s0 52 | for i = 0 to rowCount - 1 do 53 | let init = initializer i 54 | for j = 0 to columnCount - 1 do 55 | let r, s' = Random.next (init j) s0 56 | result.[i, j] <- r 57 | s0 <- s' 58 | result, s0 59 | ) 60 | 61 | [] 62 | let randomInitBased rowBase columnBase rowCount columnCount initializer = 63 | if rowCount < 0 then 64 | outOfRange "rowCount" "`rowCount' must not be negative." 65 | elif columnCount < 0 then 66 | outOfRange "columnCount" "`columnCount' must not be negative." 67 | else 68 | GeneratorFunction (fun s0 -> 69 | let result = Array2D.zeroCreateBased rowBase columnBase rowCount columnCount 70 | let mutable s0 = s0 71 | for i = rowBase to rowBase + rowCount - 1 do 72 | let init = initializer i 73 | for j = columnBase to columnBase + columnCount - 1 do 74 | let r, s' = Random.next (init j) s0 75 | result.[i, j] <- r 76 | s0 <- s' 77 | result, s0 78 | ) 79 | -------------------------------------------------------------------------------- /src/FsRandom/Array2D.fsi: -------------------------------------------------------------------------------- 1 | /// 2 | /// Provides basic operations on 2-dimensional arrays. 3 | /// 4 | [] 5 | [] 6 | module FsRandom.Array2D 7 | 8 | /// 9 | /// Creates an array whose elements are randomly generated. 10 | /// 11 | /// The length of the first dimension of the array. 12 | /// The length of the second dimension of the array. 13 | /// The generator function. 14 | [] 15 | val randomCreate : rowCount:int -> columnCount:int -> generator:GeneratorFunction<'a> -> GeneratorFunction<'a [,]> 16 | 17 | /// 18 | /// Creates a based array whose elements are randomly generated. 19 | /// 20 | /// The base of the first dimension of the array. 21 | /// The base of the second dimension of the array. 22 | /// The length of the first dimension of the array. 23 | /// The length of the second dimension of the array. 24 | /// The generator function. 25 | [] 26 | val randomCreateBased : rowBase:int -> columnBase:int -> rowCount:int -> columnCount:int -> generator:GeneratorFunction<'a> -> GeneratorFunction<'a [,]> 27 | 28 | /// 29 | /// Creates an array whose elements are randomly generated. 30 | /// 31 | /// The length of the first dimension of the array. 32 | /// The length of the second dimension of the array. 33 | /// The function to take an index and produce a random number generating function. 34 | [] 35 | val randomInit : rowCount:int -> columnCount:int -> initializer:(int -> int -> GeneratorFunction<'a>) -> GeneratorFunction<'a [,]> 36 | 37 | /// 38 | /// Creates a based array whose elements are randomly generated. 39 | /// 40 | /// The base of the first dimension of the array. 41 | /// The base of the second dimension of the array. 42 | /// The length of the first dimension of the array. 43 | /// The length of the second dimension of the array. 44 | /// The generator function. 45 | [] 46 | val randomInitBased : rowBase:int -> columnBase:int -> rowCount:int -> columnCount:int -> initializer:(int -> int -> GeneratorFunction<'a>) -> GeneratorFunction<'a [,]> 47 | -------------------------------------------------------------------------------- /src/FsRandom/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | namespace global 2 | 3 | open System.Reflection 4 | open System.Runtime.CompilerServices 5 | open System.Runtime.InteropServices 6 | 7 | [] 8 | [] 9 | [] 10 | [] 11 | [] 12 | [] 13 | [] 14 | [] 15 | 16 | [] 17 | [] 18 | 19 | [] 20 | 21 | () 22 | -------------------------------------------------------------------------------- /src/FsRandom/Collections.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module internal FsRandom.Collections 3 | 4 | [] 5 | module List = 6 | let accumulate accumulation = function 7 | | [] -> invalidArg "list" "Empty list." 8 | | x :: xs -> List.scan accumulation x xs 9 | 10 | [] 11 | module Array = 12 | let accumulate accumulation array = 13 | if Array.length array = 0 then 14 | invalidArg "array" "Empty array." 15 | else 16 | let size = Array.length array 17 | let result = Array.zeroCreate size 18 | result.[0] <- array.[0] 19 | for index = 1 to size - 1 do 20 | result.[index] <- accumulation result.[index - 1] array.[index] 21 | result 22 | 23 | type Tree<'a> = 24 | | Empty 25 | | Node of 'a * Tree<'a> * Tree<'a> 26 | 27 | [] 28 | module BinarySearchTree = 29 | let empty = Tree.Empty 30 | let singleton key value = Tree.Node ((key, value), empty, empty) 31 | let rec insert key value = function 32 | | Node ((key', _) as y, left, right) when key < key' -> Node (y, insert key value left, right) 33 | | Node ((key', _) as y, left, right) -> Node (y, left, insert key value right) 34 | | Empty -> singleton key value 35 | let rec removeMinimum = function 36 | | Node (_, Empty, right) -> right 37 | | Node (x, left, right) -> Node (x, removeMinimum left, right) 38 | | Empty -> Empty 39 | let rec min = function 40 | | Node (x, Empty, _) -> x 41 | | Node (_, left, _) -> min left 42 | | Empty -> failwith "Empty." 43 | let toList tree = 44 | let rec loop acc = function 45 | | Node (x, left, right) -> loop (x :: loop acc right) left 46 | | Empty -> acc 47 | loop [] tree 48 | -------------------------------------------------------------------------------- /src/FsRandom/FsRandom.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net45;netstandard1.6 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/FsRandom/FsRandomExtensions.fs: -------------------------------------------------------------------------------- 1 | namespace FsRandom 2 | 3 | open System 4 | open System.Runtime.CompilerServices 5 | 6 | [] 7 | module FsRandomExtensions = 8 | [] 9 | let Select x (f:Func<_, _>) = 10 | Random.map f.Invoke x 11 | [] 12 | let SelectMany x (f:Func<_, _>) (selector:Func<_, _, _>) = random { 13 | let! u = x 14 | let! v = f.Invoke u 15 | return selector.Invoke (u, v) 16 | } 17 | -------------------------------------------------------------------------------- /src/FsRandom/FsRandomExtensions.fsi: -------------------------------------------------------------------------------- 1 | namespace FsRandom 2 | 3 | open System 4 | open System.ComponentModel 5 | open System.Runtime.CompilerServices 6 | 7 | /// 8 | /// Provides extension methods that allow languages to use LINQ expressions. 9 | /// 10 | [] 11 | [] 12 | module FsRandomExtensions = 13 | [] 14 | val Select : GeneratorFunction<'a> -> Func<'a, 'b> -> GeneratorFunction<'b> 15 | [] 16 | val SelectMany : GeneratorFunction<'a> -> Func<'a, GeneratorFunction<'b>> -> Func<'a, 'b, 'c> -> GeneratorFunction<'c> 17 | -------------------------------------------------------------------------------- /src/FsRandom/List.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module FsRandom.List 3 | 4 | [] 5 | let randomCreate count generator = 6 | if count < 0 then 7 | outOfRange "count" "`count' must not be negative." 8 | else 9 | let rec loop s n cont = 10 | match n with 11 | | 0 -> ([], s) |> cont 12 | | n -> 13 | Random.next generator s 14 | |> (fun (r, s1) -> 15 | loop s1 (n - 1) (fun (acc, s2) -> (r :: acc, s2) |> cont) 16 | ) 17 | GeneratorFunction (fun s0 -> loop s0 count id) 18 | 19 | [] 20 | let randomInit count initializer = 21 | if count < 0 then 22 | outOfRange "count" "`count' must not be negative." 23 | else 24 | let rec loop s n cont = 25 | match n with 26 | | 0 -> ([], s) |> cont 27 | | n -> 28 | Random.next (initializer (count - n)) s 29 | |> (fun (r, s1) -> 30 | loop s1 (n - 1) (fun (acc, s2) -> (r :: acc, s2) |> cont) 31 | ) 32 | GeneratorFunction (fun s0 -> loop s0 count id) 33 | -------------------------------------------------------------------------------- /src/FsRandom/List.fsi: -------------------------------------------------------------------------------- 1 | /// 2 | /// Provides basic operations on lists. 3 | /// 4 | [] 5 | [] 6 | module FsRandom.List 7 | 8 | /// 9 | /// Creates a list whose elements are randomly generated. 10 | /// 11 | /// The length of the array to create. 12 | /// The generator function. 13 | [] 14 | val randomCreate : count:int -> generator:GeneratorFunction<'a> -> GeneratorFunction<'a list> 15 | 16 | /// 17 | /// Creates a list whose elements are randomly generated. 18 | /// 19 | /// The length of the array to create. 20 | /// The function to take an index and produce a random number generating function. 21 | [] 22 | val randomInit : count:int -> initializer:(int -> GeneratorFunction<'a>) -> GeneratorFunction<'a list> 23 | -------------------------------------------------------------------------------- /src/FsRandom/Math.fs: -------------------------------------------------------------------------------- 1 | [] 2 | [] 3 | module internal FsRandom.Math 4 | 5 | open Microsoft.FSharp.Core.LanguagePrimitives 6 | 7 | [] 8 | let pi = 3.1415926535897932384626433832795 9 | [] 10 | let ``2pi`` = 6.283185307179586476925286766559 11 | [] 12 | let log2pi = 1.8378770664093454835606594728112 13 | /// The minimum number greater than 1 (= 2^(-52)). 14 | [] 15 | let epsilon = 2.22044604925031308084726333618e-16 16 | 17 | let inline polynomial (coefficient : float list) = fun x -> List.reduceBack (fun c acc -> c + x * acc) coefficient 18 | 19 | // Coefficients for the loggamma function. 20 | [] 21 | let private a0 = 0.08333333333333333333333333333333 22 | [] 23 | let private a1 = -0.00277777777777777777777777777778 24 | [] 25 | let private a2 = 7.9365079365079365079365079365079e-4 26 | [] 27 | let private a3 = -5.952380952380952380952380952381e-4 28 | [] 29 | let private a4 = 8.4175084175084175084175084175084e-4 30 | [] 31 | let private a5 = -0.00191752691752691752691752691753 32 | [] 33 | let private a6 = 0.00641025641025641025641025641026 34 | [] 35 | let private a7 = -0.02955065359477124183006535947712 36 | [] 37 | let private N = 8.0 38 | 39 | let loggamma x = 40 | let mutable v = 1.0 41 | let mutable x = x 42 | while x < N do 43 | v <- v * x 44 | x <- x + 1.0 45 | let s = polynomial [a0; a1; a2; a3; a4; a5; a6; a7] (1.0 / (x * x)) 46 | s / x + 0.5 * log2pi - log v - x + (x - 0.5) * log x 47 | 48 | let gamma x = 49 | if x < 0.0 then 50 | pi / (sin (pi * x) * exp (loggamma (1.0 - x))) 51 | else 52 | exp (loggamma x) 53 | 54 | let cdf (p:float list) = 55 | let sum = List.sum p 56 | List.accumulate (+) p |> List.map (fun w -> w / sum) 57 | 58 | let sqrtsumsq x y = 59 | if abs x > abs y then 60 | let r = y / x 61 | in abs x * sqrt (1.0 + r * r) 62 | elif y <> 0.0 then 63 | let r = x / y 64 | in abs y * sqrt (1.0 + r * r) 65 | else 66 | 0.0 67 | 68 | [] 69 | module Vector = 70 | let inline normalize vector = 71 | let d = Array.fold sqrtsumsq 0.0 vector 72 | Array.map (fun x -> x / d) vector 73 | let inline add a b = 74 | let n = Array.length a 75 | Array.init n (fun i -> Array.get a i + Array.get b i) 76 | let inline cross vector = 77 | Array.reduce (fun x -> x * x) vector 78 | let inline transposeCross vector = 79 | let n = Array.length vector 80 | Array2D.init n n (fun i j -> vector.[i] * vector.[j]) 81 | 82 | [] 83 | module Matrix = 84 | let transpose matrix = 85 | Array2D.init (Array2D.length2 matrix) (Array2D.length1 matrix) (fun i j -> matrix.[j, i]) 86 | 87 | let inline diag n = Array2D.init n n (fun i j -> if i = j then GenericOne else GenericZero) 88 | let inline diagByVector vector = 89 | let n = Array.length vector 90 | Array2D.init n n (fun i j -> if i = j then vector.[i] else GenericZero) 91 | 92 | let private diagSize matrix = min (Array2D.length1 matrix) (Array2D.length2 matrix) 93 | let getDiag matrix = Array.init (diagSize matrix) (fun i -> matrix.[i, i]) 94 | let forallDiag f matrix = seq { 0 .. diagSize matrix - 1 } |> Seq.forall (fun i -> f matrix.[i, i]) 95 | let existsDiag f matrix = seq { 0 .. diagSize matrix - 1 } |> Seq.exists (fun i -> f matrix.[i, i]) 96 | 97 | let inline add a b = 98 | let m = Array2D.length1 a 99 | let n = Array2D.length2 b 100 | Array2D.init m n (fun i j -> a.[i, j] + b.[i, j]) 101 | 102 | let inline multiply a b = 103 | let m = Array2D.length1 a 104 | let k = Array2D.length2 a 105 | let n = Array2D.length2 b 106 | let p = Array2D.zeroCreate m n 107 | for i = 0 to m - 1 do 108 | for j = 0 to n - 1 do 109 | let mutable sum = 0.0 110 | for t = 0 to k - 1 do 111 | sum <- sum + a.[i, t] * b.[t, j] 112 | p.[i, j] <- sum 113 | p 114 | 115 | let inline multiplyVector a x = 116 | let m = Array2D.length1 a 117 | let n = Array.length x 118 | let p = Array.zeroCreate m 119 | for i = 0 to m - 1 do 120 | let mutable sum = 0.0 121 | for t = 0 to n - 1 do 122 | sum <- sum + a.[i, t] * x.[t] 123 | p.[i] <- sum 124 | p 125 | 126 | let inline isSymmetric matrix = 127 | let m = Array2D.length1 matrix 128 | let n = Array2D.length2 matrix 129 | m = n && Seq.forall (fun i -> Seq.forall (fun j -> matrix.[i, j] = matrix.[j, i]) <| seq { 0 .. i - 1 }) <| seq { 0 .. n - 1 } 130 | 131 | /// Computes eigenvalues and eigenvectors of symmetric matrix. 132 | /// 133 | let jacobi matrix = 134 | let n = Array2D.length1 matrix 135 | let m = n - 1 136 | let eigenvalues = Array2D.copy matrix 137 | let eigenvectors = diag n 138 | let findMax () = 139 | seq { 140 | for i = 0 to m do 141 | for j = 0 to m do 142 | if i <> j then 143 | yield (i, j), abs eigenvalues.[i, j] 144 | } 145 | |> Seq.maxBy snd 146 | let loop = ref true 147 | while !loop do 148 | let (p, q), max = findMax () 149 | if max < epsilon then 150 | loop := false 151 | else 152 | let app = eigenvalues.[p, p] 153 | let aqq = eigenvalues.[q, q] 154 | let apq = eigenvalues.[p, q] 155 | let t = 0.5 * (app - aqq) 156 | let ss = 0.5 * (1.0 - abs t / sqrtsumsq apq t) // sin^2 157 | let cc = 1.0 - ss // cos^2 158 | let s = if apq * t > 0.0 then -sqrt ss else sqrt ss // sin 159 | let c = sqrt cc // cos 160 | let sc = s * c // sin * cos 161 | for i = 0 to m do 162 | let api = eigenvalues.[p, i] 163 | let aqi = eigenvalues.[q, i] 164 | eigenvalues.[p, i] <- api * c - aqi * s 165 | eigenvalues.[q, i] <- api * s + aqi * c 166 | for i = 0 to m do 167 | eigenvalues.[i, p] <- eigenvalues.[p, i] 168 | eigenvalues.[i, q] <- eigenvalues.[q, i] 169 | eigenvalues.[p, p] <- app * cc - 2.0 * apq * sc + aqq * ss 170 | eigenvalues.[q, q] <- aqq * cc + 2.0 * apq * sc + app * ss 171 | eigenvalues.[p, q] <- 0.0 172 | eigenvalues.[q, p] <- 0.0 173 | for i = 0 to m do 174 | let aip = eigenvectors.[i, p] 175 | let aiq = eigenvectors.[i, q] 176 | eigenvectors.[i, p] <- aip * c - aiq * s 177 | eigenvectors.[i, q] <- aip * s + aiq * c 178 | getDiag eigenvalues, eigenvectors 179 | -------------------------------------------------------------------------------- /src/FsRandom/MersenneTwister.fs: -------------------------------------------------------------------------------- 1 | module FsRandom.MersenneTwister 2 | 3 | [] 4 | let N = 312 5 | [] 6 | let M = 156 7 | [] 8 | let MatrixA = 0xB5026F5AA96619E9uL 9 | [] 10 | let UpperMask = 0xFFFFFFFF80000000uL 11 | [] 12 | let LowerMask = 0x7FFFFFFFuL 13 | 14 | let initialize seed = 15 | let vector = Array.zeroCreate N 16 | vector.[0] <- seed 17 | for index = 1 to N - 1 do 18 | let previous = vector.[index - 1] 19 | vector.[index] <- 6364136223846793005uL * (previous ^^^ (previous >>> 62)) + uint64 index 20 | vector 21 | 22 | type StateVector (index : int, vector : uint64 []) = 23 | static member Initialize (seed : uint64) = 24 | StateVector (N, initialize seed) 25 | static member Initialize (seed : uint64 []) = 26 | let vector = initialize 19650218uL 27 | let mutable i = 1 28 | let mutable j = 0 29 | for k = max N seed.Length downto 1 do 30 | vector.[i] <- (vector.[i] ^^^ ((vector.[i - 1] ^^^ (vector.[i - 1] >>> 62)) * 3935559000370003845uL)) + seed.[j] + uint64 j 31 | i <- i + 1 32 | j <- j + 1 33 | if i >= N then 34 | vector.[0] <- vector.[N - 1] 35 | i <- 1 36 | if j >= seed.Length then 37 | j <- 0 38 | () 39 | for k = N - 1 downto 1 do 40 | vector.[i] <- (vector.[i] ^^^ ((vector.[i - 1] ^^^ (vector.[i - 1] >>> 62)) * 2862933555777941757uL)) - uint64 i 41 | i <- i + 1 42 | if i >= N then 43 | vector.[0] <- vector.[N - 1] 44 | i <- 1 45 | vector.[0] <- 1uL <<< 63 46 | StateVector (N, vector) 47 | member val Index = index with get 48 | member this.Item (index) = vector.[index] 49 | member this.Vector = vector 50 | 51 | let inline twist u l v = 52 | let y = (u &&& UpperMask) ||| (l &&& LowerMask) 53 | let mag = if y &&& 1uL = 0uL then 0uL else MatrixA // mag01[y & 0x1] 54 | v ^^^ (y >>> 1) ^^^ mag 55 | let refresh (state : StateVector) = 56 | let vector = Array.copy state.Vector 57 | for kk = 0 to N - M - 1 do 58 | vector.[kk] <- twist vector.[kk] vector.[kk + 1] vector.[kk + M] 59 | for kk = N - M to N - 2 do 60 | vector.[kk] <- twist vector.[kk] vector.[kk + 1] vector.[kk + (M - N)] 61 | vector.[N - 1] <- twist vector.[N - 1] vector.[0] vector.[M - 1] 62 | StateVector (0, vector) 63 | 64 | [] 65 | let mersenne (state : StateVector) = 66 | let state = if state.Index >= N then refresh state else state 67 | let index = state.Index 68 | let mutable y = state.[index] 69 | y <- y ^^^ ((y >>> 29) &&& 0x5555555555555555uL) 70 | y <- y ^^^ ((y <<< 17) &&& 0x71D67FFFEDA60000uL) 71 | y <- y ^^^ ((y <<< 37) &&& 0xFFF7EEE000000000uL) 72 | y <- y ^^^ (y >>> 43) 73 | // Creates a new instance of StateVector, but the internal vector refers to the same array to avoid cost of copying. 74 | y, StateVector(index + 1, state.Vector) 75 | -------------------------------------------------------------------------------- /src/FsRandom/MersenneTwister.fsi: -------------------------------------------------------------------------------- 1 | /// 2 | /// Implements Mersenne Twister 64-bit version. 3 | /// See http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html for details. 4 | /// 5 | module FsRandom.MersenneTwister 6 | 7 | /// 8 | /// Keeps a random state used in the Mersenne Twister process. 9 | /// 10 | [] 11 | type StateVector = 12 | /// 13 | /// Initializes a new instance with an integer. 14 | /// 15 | /// A random seed integer. 16 | static member Initialize : seed:uint64 -> StateVector 17 | /// 18 | /// Initializes a new instance with integers. 19 | /// 20 | /// A random seed array. 21 | static member Initialize : seed:uint64 [] -> StateVector 22 | 23 | /// 24 | /// Random number generator using Mersenne Twister algorithm (Matsumoto & Nishimura 1998). 25 | /// 26 | [] 27 | val mersenne : Prng 28 | -------------------------------------------------------------------------------- /src/FsRandom/Random.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module FsRandom.Random 3 | 4 | [] 5 | let bind f m = bindRandom m f 6 | [] 7 | let next generator s = runRandom generator s 8 | [] 9 | let get generator s = evaluateRandom generator s 10 | 11 | [] 12 | let singleton x = returnRandom x 13 | [] 14 | let identity (generator:GeneratorFunction<_>) = generator 15 | [] 16 | let map f generator = 17 | GeneratorFunction (fun s0 -> let r, s' = next generator s0 in f r, s') 18 | [] 19 | let map2 f g1 g2 = 20 | GeneratorFunction (fun s0 -> 21 | let r1, s1 = next g1 s0 22 | let r2, s2 = next g2 s1 23 | f r1 r2, s2 24 | ) 25 | [] 26 | let map3 f g1 g2 g3 = 27 | GeneratorFunction (fun s0 -> 28 | let r1, s1 = next g1 s0 29 | let r2, s2 = next g2 s1 30 | let r3, s3 = next g3 s2 31 | f r1 r2 r3, s3 32 | ) 33 | [] 34 | let transformBy f generator = map f generator 35 | [] 36 | let transformBy2 f g1 g2 = map2 f g1 g2 37 | [] 38 | let transformBy3 f g1 g2 g3 = map3 f g1 g2 g3 39 | 40 | [] 41 | let zip g1 g2 = map2 tuple g1 g2 42 | [] 43 | let zip3 g1 g2 g3 = map3 tuple3 g1 g2 g3 44 | [] 45 | let merge gs = List.foldBack (map2 cons) gs (returnRandom []) 46 | [] 47 | let mergeWith f gs = merge gs |> map f 48 | -------------------------------------------------------------------------------- /src/FsRandom/Random.fsi: -------------------------------------------------------------------------------- 1 | /// 2 | /// Provides basic operations on generator functions. 3 | /// 4 | [] 5 | [] 6 | module FsRandom.Random 7 | 8 | /// 9 | /// Invokes a function on a random function. 10 | /// 11 | [] 12 | val bind : binder:('a -> GeneratorFunction<'b>) -> generator:GeneratorFunction<'a> -> GeneratorFunction<'b> 13 | /// 14 | /// Generates a random number with the next random state. 15 | /// 16 | [] 17 | val next : generator:GeneratorFunction<'a> -> PrngState -> 'a * PrngState 18 | /// 19 | /// Generates a random number. 20 | /// 21 | [] 22 | val get : generator:GeneratorFunction<'a> -> PrngState -> 'a 23 | 24 | /// 25 | /// Always returns the specified value. 26 | /// 27 | /// The value. 28 | [] 29 | val singleton : x:'a -> GeneratorFunction<'a> 30 | /// 31 | /// Generates a random number by and returns the value. 32 | /// 33 | /// The random number generator. 34 | [] 35 | val identity : generator:GeneratorFunction<'a> -> GeneratorFunction<'a> 36 | /// 37 | /// Generates a random number by and returns a transformed value by function. 38 | /// 39 | /// The function to transform a random value. 40 | /// The random number generator. 41 | [] 42 | val map : transformation:('a -> 'b) -> generator:GeneratorFunction<'a> -> GeneratorFunction<'b> 43 | /// 44 | /// Generates a random number by using two random numbers. 45 | /// 46 | /// The function to transform two random values into one. 47 | /// The first random number generator. 48 | /// The second random number generator. 49 | [] 50 | val map2 : transformation:('a1 -> 'a2 -> 'b) -> generator1:GeneratorFunction<'a1> -> generator2:GeneratorFunction<'a2> -> GeneratorFunction<'b> 51 | /// 52 | /// Generates a random number by using three random numbers. 53 | /// 54 | /// The function to transform two random values into one. 55 | /// The first random number generator. 56 | /// The second random number generator. 57 | /// The third random number generator. 58 | [] 59 | val map3 : transformation:('a1 -> 'a2 -> 'a3 -> 'b) -> generator1:GeneratorFunction<'a1> -> generator2:GeneratorFunction<'a2> -> generator2:GeneratorFunction<'a3> -> GeneratorFunction<'b> 60 | /// 61 | /// Generates a random number by and returns a transformed value by function. 62 | /// 63 | /// 64 | /// This function is a synonym for . 65 | /// 66 | /// The function to transform a random value. 67 | /// The random number generator. 68 | [] 69 | val transformBy : transformation:('a -> 'b) -> generator:GeneratorFunction<'a> -> GeneratorFunction<'b> 70 | /// 71 | /// Generates a random number by using two random numbers. 72 | /// 73 | /// 74 | /// This function is a synonym for . 75 | /// 76 | /// The function to transform two random values into one. 77 | /// The first random number generator. 78 | /// The second random number generator. 79 | [] 80 | val transformBy2 : transformation:('a1 -> 'a2 -> 'b) -> generator1:GeneratorFunction<'a1> -> generator2:GeneratorFunction<'a2> -> GeneratorFunction<'b> 81 | /// 82 | /// Generates a random number by using three random numbers. 83 | /// 84 | /// 85 | /// This function is a synonym for . 86 | /// 87 | /// The function to transform two random values into one. 88 | /// The first random number generator. 89 | /// The second random number generator. 90 | /// The third random number generator. 91 | [] 92 | val transformBy3 : transformation:('a1 -> 'a2 -> 'a3 -> 'b) -> generator1:GeneratorFunction<'a1> -> generator2:GeneratorFunction<'a2> -> generator2:GeneratorFunction<'a3> -> GeneratorFunction<'b> 93 | 94 | /// 95 | /// Merges two random streams into one. 96 | /// 97 | [] 98 | val zip : generator1:GeneratorFunction<'a1> -> generator2:GeneratorFunction<'a2> -> GeneratorFunction<'a1 * 'a2> 99 | /// 100 | /// Merges three random streams into one. 101 | /// 102 | [] 103 | val zip3 : generator1:GeneratorFunction<'a1> -> generator2:GeneratorFunction<'a2> -> generator3:GeneratorFunction<'a3> -> GeneratorFunction<'a1 * 'a2 * 'a3> 104 | /// 105 | /// Merges random stream list into one. 106 | /// 107 | [] 108 | val merge : generators:GeneratorFunction<'a> list -> GeneratorFunction<'a list> 109 | /// 110 | /// Merges random stream list into one and then apply the specified function. 111 | /// 112 | [] 113 | val mergeWith : f:('a list -> 'b) -> (GeneratorFunction<'a> list -> GeneratorFunction<'b>) 114 | -------------------------------------------------------------------------------- /src/FsRandom/RandomNumberGenerator.fs: -------------------------------------------------------------------------------- 1 | module FsRandom.RandomNumberGenerator 2 | 3 | open System 4 | 5 | type Prng<'s> = 's -> uint64 * 's 6 | type PrngState = 7 | abstract Next64Bits : unit -> uint64 * PrngState 8 | type GeneratorFunction<'a> = GeneratorFunction of (PrngState -> 'a * PrngState) 9 | 10 | [] 11 | let rec createState (prng:Prng<'s>) (seed:'s) = { 12 | new PrngState with 13 | member __.Next64Bits () = 14 | let r, next = prng seed 15 | r, createState prng next 16 | } 17 | 18 | let bindRandom (GeneratorFunction m) f = 19 | GeneratorFunction (fun s0 -> let v, s' = m s0 in match f v with GeneratorFunction (g) -> g s') 20 | let returnRandom x = GeneratorFunction (fun s -> x, s) 21 | let runRandom (GeneratorFunction m) x = m x 22 | let evaluateRandom (GeneratorFunction m) x = m x |> fst 23 | let executeRandom (GeneratorFunction m) x = m x |> snd 24 | 25 | let inline (|>>) m f = bindRandom m f 26 | let inline (&>>) m b = bindRandom m (fun _ -> b) 27 | 28 | type RandomBuilder () = 29 | member this.Bind (m, f) = m |>> f 30 | member this.Combine (a, b) = a &>> b 31 | member this.Return (x) = returnRandom x 32 | member this.ReturnFrom (m : GeneratorFunction<_>) = m 33 | member this.Zero () = GeneratorFunction (fun s -> Unchecked.defaultof<_>, s) 34 | member this.Delay (f) = returnRandom () |>> f 35 | member this.While (condition, m:GeneratorFunction<'a>) : GeneratorFunction<'a> = 36 | if condition () then 37 | m |>> (fun _ -> this.While (condition, m)) 38 | else 39 | this.Zero () 40 | member this.For (source : seq<'a>, f) = 41 | use e = source.GetEnumerator () 42 | this.While (e.MoveNext, this.Delay (fun () -> f e.Current)) 43 | member this.TryFinally (GeneratorFunction g, finalizer) = 44 | GeneratorFunction (fun s -> try g s finally finalizer ()) 45 | member this.TryWith (GeneratorFunction g, handler) = 46 | GeneratorFunction (fun s -> try g s with ex -> let (GeneratorFunction h) = handler ex in h s) 47 | member this.Using (x:#IDisposable, f) = 48 | this.TryFinally (f x, fun () -> using x ignore) 49 | let random = RandomBuilder () 50 | 51 | [] 52 | let systemrandom (random : Random) = 53 | let lower = (uint64 (random.Next ()) ) &&& 0b0000000000000000000000000000000000000000000011111111111111111111uL 54 | let middle = (uint64 (random.Next ()) <<< 20) &&& 0b0000000000000000000000111111111111111111111100000000000000000000uL 55 | let upper = (uint64 (random.Next ()) <<< 42) &&& 0b1111111111111111111111000000000000000000000000000000000000000000uL 56 | lower ||| middle ||| upper, random 57 | 58 | [] 59 | let xorshift (x:uint32, y:uint32, z:uint32, w:uint32) = 60 | let s = x ^^^ (x <<< 11) 61 | let t = y ^^^ (y <<< 11) 62 | let u = (w ^^^ (w >>> 19)) ^^^ (s ^^^ (s >>> 8)) 63 | let v = (u ^^^ (u >>> 19)) ^^^ (t ^^^ (t >>> 8)) 64 | to64bit u v, (z, w, u, v) 65 | 66 | [] 67 | let rawBits = GeneratorFunction (fun s -> s.Next64Bits ()) 68 | [] 69 | let ``1 / 2^52`` = 2.22044604925031308084726333618e-16 70 | [] 71 | let ``1 / 2^53`` = 1.11022302462515654042363166809e-16 72 | [] 73 | let ``1 / (2^53 - 1)`` = 1.1102230246251566636831481e-16 74 | [] 75 | let ``(0, 1)`` = GeneratorFunction (fun s0 -> let r, s' = s0.Next64Bits () in (float (r >>> 12) + 0.5) * ``1 / 2^52``, s') 76 | [] 77 | let ``[0, 1)`` = GeneratorFunction (fun s0 -> let r, s' = s0.Next64Bits () in float (r >>> 11) * ``1 / 2^53``, s') 78 | [] 79 | let ``(0, 1]`` = GeneratorFunction (fun s0 -> let r, s' = s0.Next64Bits () in (float (r >>> 12) + 1.0) * ``1 / 2^52``, s') 80 | [] 81 | let ``[0, 1]`` = GeneratorFunction (fun s0 -> let r, s' = s0.Next64Bits () in float (r >>> 11) * ``1 / (2^53 - 1)``, s') 82 | 83 | [] 84 | let rint8 = GeneratorFunction (fun s0 -> let r, s' = s0.Next64Bits () in int8 r, s') 85 | [] 86 | let rint16 = GeneratorFunction (fun s0 -> let r, s' = s0.Next64Bits () in int16 r, s') 87 | [] 88 | let rint32 = GeneratorFunction (fun s0 -> let r, s' = s0.Next64Bits () in int32 r, s') 89 | [] 90 | let rint64 = GeneratorFunction (fun s0 -> let r, s' = s0.Next64Bits () in int64 r, s') 91 | [] 92 | let ruint8 = GeneratorFunction (fun s0 -> let r, s' = s0.Next64Bits () in uint8 r, s') 93 | [] 94 | let ruint16 = GeneratorFunction (fun s0 -> let r, s' = s0.Next64Bits () in uint16 r, s') 95 | [] 96 | let ruint32 = GeneratorFunction (fun s0 -> let r, s' = s0.Next64Bits () in uint32 r, s') 97 | [] 98 | let ruint64 = rawBits 99 | -------------------------------------------------------------------------------- /src/FsRandom/RandomNumberGenerator.fsi: -------------------------------------------------------------------------------- 1 | /// 2 | /// Provides the core random classes and the primitive random number generators. 3 | /// 4 | [] 5 | module FsRandom.RandomNumberGenerator 6 | 7 | open System 8 | 9 | /// 10 | /// Represents a pseudorandom number generator that supports 64-bit resolution. 11 | /// 12 | type Prng<'s> = 's -> uint64 * 's 13 | /// 14 | /// Represents a random state. 15 | /// 16 | type PrngState = 17 | abstract Next64Bits : unit -> uint64 * PrngState 18 | /// 19 | /// Generates random numbers. 20 | /// 21 | type GeneratorFunction<'a> = GeneratorFunction of (PrngState -> 'a * PrngState) 22 | 23 | /// 24 | /// Constructs a random state. 25 | /// 26 | /// The PRNG. 27 | /// The random seed. 28 | [] 29 | val createState : prng:Prng<'s> -> seed:'s -> PrngState 30 | 31 | val inline internal ( |>> ) : m:GeneratorFunction<'a> -> f:('a -> GeneratorFunction<'b>) -> GeneratorFunction<'b> 32 | val inline internal ( &>> ) : m:GeneratorFunction<'a> -> b:GeneratorFunction<'b> -> GeneratorFunction<'b> 33 | val internal bindRandom : m:GeneratorFunction<'a> -> f:('a -> GeneratorFunction<'b>) -> GeneratorFunction<'b> 34 | val internal returnRandom : a:'a -> GeneratorFunction<'a> 35 | val internal runRandom : GeneratorFunction<'a> -> PrngState -> 'a * PrngState 36 | val internal evaluateRandom : GeneratorFunction<'a> -> PrngState -> 'a 37 | val internal executeRandom : GeneratorFunction<'a> -> PrngState -> PrngState 38 | 39 | [] 40 | type RandomBuilder = 41 | member Bind : m:GeneratorFunction<'a> * f:('a -> GeneratorFunction<'b>) -> GeneratorFunction<'b> 42 | member Combine : a:GeneratorFunction<'a> * b:GeneratorFunction<'b> -> GeneratorFunction<'b> 43 | member Return : a:'a -> GeneratorFunction<'a> 44 | member ReturnFrom : m:GeneratorFunction<'a> -> GeneratorFunction<'a> 45 | member Zero : unit -> GeneratorFunction<'a> 46 | member Delay : (unit -> GeneratorFunction<'a>) -> GeneratorFunction<'a> 47 | member While : condition:(unit -> bool) * m:GeneratorFunction<'a> -> GeneratorFunction<'a> 48 | member For : source:seq<'a> * f:('a -> GeneratorFunction<'b>) -> GeneratorFunction<'b> 49 | member TryFinally : m:GeneratorFunction<'a> * finalizer:(unit -> unit) -> GeneratorFunction<'a> 50 | member TryWith : m:GeneratorFunction<'a> * handler:(exn -> GeneratorFunction<'a>) -> GeneratorFunction<'a> 51 | member Using : a:'a * f:('a -> GeneratorFunction<'b>) -> GeneratorFunction<'b> when 'a :> IDisposable 52 | /// 53 | /// Constructs a random number function. 54 | /// 55 | val random : RandomBuilder 56 | 57 | /// 58 | /// Random number generator using . 59 | /// 60 | /// 61 | /// You will get different result on each call because an instance of has state by itself. 62 | /// 63 | [] 64 | val systemrandom : Prng 65 | /// 66 | /// Random number generator using Xorshift algorithm (Marsaglia 2003). 67 | /// 68 | [] 69 | val xorshift : Prng 70 | 71 | /// 72 | /// Returns a random 64-bit number. 73 | /// 74 | [] 75 | val rawBits : GeneratorFunction 76 | /// 77 | /// Returns a random number in the range of (0, 1). 78 | /// 79 | [] 80 | val ``(0, 1)`` : GeneratorFunction 81 | /// 82 | /// Returns a random number in the range of [0, 1). 83 | /// 84 | [] 85 | val ``[0, 1)`` : GeneratorFunction 86 | /// 87 | /// Returns a random number in the range of (0, 1]. 88 | /// 89 | [] 90 | val ``(0, 1]`` : GeneratorFunction 91 | /// 92 | /// Returns a random number in the range of [0, 1]. 93 | /// 94 | [] 95 | val ``[0, 1]`` : GeneratorFunction 96 | 97 | /// 98 | /// Returns a random 8-bit signed integer. 99 | /// 100 | [] 101 | val rint8 : GeneratorFunction 102 | /// 103 | /// Returns a random 16-bit signed integer. 104 | /// 105 | [] 106 | val rint16 : GeneratorFunction 107 | /// 108 | /// Returns a random 32-bit signed integer. 109 | /// 110 | [] 111 | val rint32 : GeneratorFunction 112 | /// 113 | /// Returns a random 64-bit signed integer. 114 | /// 115 | [] 116 | val rint64 : GeneratorFunction 117 | /// 118 | /// Returns a random 8-bit unsigned integer. 119 | /// 120 | [] 121 | val ruint8 : GeneratorFunction 122 | /// 123 | /// Returns a random 16-bit unsigned integer. 124 | /// 125 | [] 126 | val ruint16 : GeneratorFunction 127 | /// 128 | /// Returns a random 32-bit unsigned integer. 129 | /// 130 | [] 131 | val ruint32 : GeneratorFunction 132 | /// 133 | /// Returns a random 64-bit unsigned integer. 134 | /// This function is an alias for . 135 | /// 136 | [] 137 | val ruint64 : GeneratorFunction 138 | -------------------------------------------------------------------------------- /src/FsRandom/RuntimeHelper.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module internal FsRandom.RuntimeHelper 3 | 4 | open System 5 | open Microsoft.FSharp.Core.LanguagePrimitives 6 | 7 | let inline curry f x y = f (x, y) 8 | let inline uncurry f (x, y) = f x y 9 | let inline flip f x y = f y x 10 | let inline cons x xs = x :: xs 11 | let inline tuple x y = x, y 12 | let inline tuple3 x y z = x, y, z 13 | 14 | let inline outOfRange (paramName:string) (message:string) = 15 | ArgumentOutOfRangeException (paramName, message) |> raise 16 | 17 | let inline isNaN (value : ^a when ^a : (static member IsNaN : ^a -> bool)) = 18 | (^a : (static member IsNaN : ^a -> bool) value) 19 | 20 | let inline isInfinity (value : ^a when ^a : (static member IsInfinity : ^a -> bool)) = 21 | (^a : (static member IsInfinity : ^a -> bool) value) 22 | 23 | let inline isInt x = x % GenericOne = GenericZero 24 | 25 | let inline ensuresFiniteValue argument argumentName = 26 | if isNaN argument || isInfinity argument then 27 | invalidArg argumentName (sprintf "`%s' must be a finite number." argumentName) |> raise 28 | 29 | let inline to64bit (lower:uint32) (upper:uint32) = (uint64 upper <<< 32) ||| uint64 lower 30 | -------------------------------------------------------------------------------- /src/FsRandom/Seq.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module FsRandom.Seq 3 | 4 | [] 5 | let ofRandom generator = 6 | let f = Random.next generator 7 | fun s0 -> seq { 8 | let s = ref s0 9 | while true do 10 | let r, s' = f !s 11 | yield r 12 | s := s' 13 | } 14 | -------------------------------------------------------------------------------- /src/FsRandom/Seq.fsi: -------------------------------------------------------------------------------- 1 | /// 2 | /// Generates sequences for generator functions. 3 | /// 4 | [] 5 | [] 6 | module FsRandom.Seq 7 | 8 | /// 9 | /// Makes infinite sequence of random numbers. 10 | /// 11 | /// A random function. 12 | /// 13 | /// A function which takes a seed and returns infinite sequence of random numbers. 14 | /// 15 | [] 16 | val ofRandom : generator:GeneratorFunction<'a> -> (PrngState -> seq<'a>) 17 | -------------------------------------------------------------------------------- /src/FsRandom/SimdOrientedFastMersenneTwister.fs: -------------------------------------------------------------------------------- 1 | module FsRandom.SimdOrientedFastMersenneTwister 2 | 3 | #nowarn "9" 4 | open System.Runtime.InteropServices 5 | 6 | module W128 = 7 | [] 8 | let Size = 4 9 | [] 10 | type W128_T = 11 | [] 12 | val u : uint32 [] 13 | new (u0, u1, u2, u3) = { u = [|u0; u1; u2; u3|] } 14 | let inline rshift (input : W128_T) shift = 15 | let th = (uint64 input.u.[3] <<< 32) ||| (uint64 input.u.[2]) 16 | let tl = (uint64 input.u.[1] <<< 32) ||| (uint64 input.u.[0]) 17 | let s = shift * 8 18 | let oh = th >>> s 19 | let ol = let ol = tl >>> s in ol ||| (th <<< (64 - s)) 20 | let u1 = uint32 (ol >>> 32) 21 | let u0 = uint32 ol 22 | let u3 = uint32 (oh >>> 32) 23 | let u2 = uint32 oh 24 | W128_T (u0, u1, u2, u3) 25 | let inline lshift (input : W128_T) shift = 26 | let th = (uint64 input.u.[3] <<< 32) ||| (uint64 input.u.[2]) 27 | let tl = (uint64 input.u.[1] <<< 32) ||| (uint64 input.u.[0]) 28 | let s = shift * 8 29 | let oh = let oh = th <<< s in oh ||| (tl >>> (64 - s)) 30 | let ol = tl <<< s 31 | let u1 = uint32 (ol >>> 32) 32 | let u0 = uint32 ol 33 | let u3 = uint32 (oh >>> 32) 34 | let u2 = uint32 oh 35 | W128_T (u0, u1, u2, u3) 36 | module Array = 37 | let get index (vector : W128_T []) = vector.[index / Size].u.[index % Size] 38 | let set index value (vector : W128_T []) = vector.[index / Size].u.[index % Size] <- value 39 | let update index f (vector : W128_T []) = set index (f (get index vector)) vector 40 | let zeroCreate count = 41 | Array.init count (fun _ -> W128_T (0u, 0u, 0u, 0u)) 42 | let init count initializer = 43 | Array.init count (fun index -> let u0, u1, u2, u3 = initializer index in W128_T (u0, u1, u2, u3)) 44 | let copy (vector : W128_T []) = 45 | Array.init (Array.length vector) (fun index -> let u = vector.[index].u in W128_T (u.[0], u.[1], u.[2], u.[3])) 46 | 47 | type SfmtParams (mexp, pos1, sl1, sl2, sr1, sr2, mask1, mask2, mask3, mask4, parity1, parity2, parity3, parity4) = 48 | static member Params607 = SfmtParams (607, 2, 15, 3, 13, 3, 0xFDFF37FFu, 0xEF7F3F7Du, 0xFF777B7Du, 0x7FF7FB2Fu, 0x00000001u, 0x00000000u, 0x00000000u, 0x5986F054u) 49 | static member Params1279 = SfmtParams (1279, 7, 14, 3, 5, 1, 0xF7FEFFFDu, 0x7FEFCFFFu, 0xAFF3EF3Fu, 0xB5FFFF7Fu, 0x00000001u, 0x00000000u, 0x00000000u, 0x20000000u) 50 | static member Params2281 = SfmtParams (2281, 12, 19, 1, 5, 1, 0xBFF7FFBFu, 0xFDFFFFFEu, 0xF7FFEF7Fu, 0xF2F7CBBFu, 0x00000001u, 0x00000000u, 0x00000000u, 0x41DFA600u) 51 | static member Params4253 = SfmtParams (4253, 17, 20, 1, 7, 1, 0x9F7BFFFFu, 0x9FFFFF5Fu, 0x3EFFFFFBu, 0xFFFFF7BBu, 0xA8000001u, 0xAF5390A3u, 0xB740B3F8u, 0x6C11486Du) 52 | static member Params11213 = SfmtParams (11213, 68, 14, 3, 7, 3, 0xEFFFF7FBu, 0xFFFFFFEFu, 0xDFDFBFFFu, 0x7FFFDBFDu, 0x00000001u, 0x00000000u, 0xE8148000u, 0xD0C7AFA3u) 53 | static member Params19937 = SfmtParams (19937, 122, 18, 1, 11, 1, 0xDFFFFFEFu, 0xDDFECB7Fu, 0xBFFAFFFFu, 0xBFFFFFF6u, 0x00000001u, 0x00000000u, 0x00000000u, 0x13C9E684u) 54 | static member Params44497 = SfmtParams (44497, 330, 5, 3, 9, 3, 0XEFFFFFFBu, 0xDFBEBFFFu, 0xBFBF7BEFu, 0x9FFD7BFFu, 0x00000001u, 0x00000000u, 0xA3AC4000u, 0xECC1327Au) 55 | static member Params86243 = SfmtParams (86243, 366, 6, 7, 19, 1, 0xFDBFFBFFu, 0xBFF7FF3Fu, 0xFD77EFFFu, 0xBF9FF3FFu, 0x00000001u, 0x00000000u, 0x00000000u, 0xE9528D85u) 56 | static member Params132049 = SfmtParams (132049, 110, 19, 1, 21, 1, 0xFFFFBB5Fu, 0xFB6EBF95u, 0xFFFEFFFAu, 0xCFF77FFFu, 0x00000001u, 0x00000000u, 0xCB520000u, 0xC7E91C7Du) 57 | static member Params216091 = SfmtParams (216091, 627, 11, 3, 10, 1, 0xBFF7BFF7u, 0xBFFFFFFFu, 0xBFFFFA7Fu, 0xFFDDFBFBu, 0xF8000001u, 0x89E80709u, 0x3BD2B64Bu, 0x0C64B1E4u) 58 | member val Period : int = mexp 59 | member val N = mexp / 128 + 1 60 | member val N32 = W128.Size * (mexp / 128 + 1) 61 | member val Pos1 : int = pos1 62 | member val SL1 : int = sl1 63 | member val SL2 : int = sl2 64 | member val SR1 : int = sr1 65 | member val SR2 : int = sr2 66 | member val Mask1 : uint32 = mask1 67 | member val Mask2 : uint32 = mask2 68 | member val Mask3 : uint32 = mask3 69 | member val Mask4 : uint32 = mask4 70 | member val Parity1 : uint32 = parity1 71 | member val Parity2 : uint32 = parity2 72 | member val Parity3 : uint32 = parity3 73 | member val Parity4 : uint32 = parity4 74 | 75 | let certificatePeriod (parameter : SfmtParams) vector = 76 | let parity = [|parameter.Parity1; parameter.Parity2; parameter.Parity3; parameter.Parity4|] 77 | let mutable inner = 0u 78 | for index = 0 to 3 do 79 | inner <- inner ^^^ ((W128.Array.get index vector) &&& parity.[index]) 80 | for i in [16; 8; 4; 2; 1] do 81 | inner <- inner ^^^ (inner >>> i) 82 | inner <- inner &&& 1u 83 | if inner <> 1u then 84 | let incomplete = ref true 85 | let mutable index = 0 86 | while !incomplete && index < Array.length parity do 87 | let mutable j = 0 88 | let work = ref 1u 89 | while !incomplete && j < 32 do 90 | if !work &&& parity.[index] <> 0u then 91 | W128.Array.update index (fun value -> value ^^^ !work) vector 92 | incomplete := false 93 | j <- j + 1 94 | work := !work <<< 1 95 | index <- index + 1 96 | vector 97 | 98 | let initialize (parameter : SfmtParams) seed = 99 | let vector = W128.Array.zeroCreate parameter.N32 100 | W128.Array.set 0 seed vector 101 | let mutable value = seed 102 | for index = 1 to parameter.N32 - 1 do 103 | value <- 1812433253u * (value ^^^ (value >>> 30)) + uint32 index 104 | W128.Array.set index value vector 105 | certificatePeriod parameter vector 106 | let inline func1 x = (x ^^^ (x >>> 27)) * 1664525u 107 | let inline func2 x = (x ^^^ (x >>> 27)) * 1566083941u 108 | let xor i j k vector = W128.Array.get i vector ^^^ W128.Array.get j vector ^^^ W128.Array.get k vector 109 | let plus i j k vector = W128.Array.get i vector + W128.Array.get j vector + W128.Array.get k vector 110 | let initializeByArray (parameter : SfmtParams) seed = 111 | let vector = W128.Array.init parameter.N32 (fun _ -> 0x8B8B8B8Bu, 0x8B8B8B8Bu, 0x8B8B8B8Bu, 0x8B8B8B8Bu) 112 | let size = parameter.N32 113 | let lag = if size >= 623 then 11 elif size >= 68 then 7 elif size >= 39 then 5 else 3 114 | let mid = (size - lag) / 2 115 | let mutable r = func1 <| xor 0 mid (size - 1) vector 116 | W128.Array.update mid ((+) r) vector 117 | r <- r + uint32 (Array.length seed) 118 | W128.Array.update (mid + lag) ((+) r) vector 119 | W128.Array.set 0 r vector 120 | let count = max (Array.length seed + 1) size - 1 121 | let mutable i = 1 122 | let mutable j = 0 123 | while j < count && j < Array.length seed do 124 | r <- func1 <| xor i ((i + mid) % size) ((i + size - 1) % size) vector 125 | W128.Array.update ((i + mid) % size) ((+) r) vector 126 | r <- r + seed.[j] + uint32 i 127 | W128.Array.update ((i + mid + lag) % size) ((+) r) vector 128 | W128.Array.set i r vector 129 | i <- (i + 1) % size 130 | j <- j + 1 131 | while j < count do 132 | r <- func1 <| xor i ((i + mid) % size) ((i + size - 1) % size) vector 133 | W128.Array.update ((i + mid) % size) ((+) r) vector 134 | r <- r + uint32 i 135 | W128.Array.update ((i + mid + lag) % size) ((+) r) vector 136 | W128.Array.set i r vector 137 | i <- (i + 1) % size 138 | j <- j + 1 139 | for j = 0 to size - 1 do 140 | r <- func2 <| plus i ((i + mid) % size) ((i + size - 1) % size) vector 141 | W128.Array.update ((i + mid) % size) ((^^^) r) vector 142 | r <- r - uint32 i 143 | W128.Array.update ((i + mid + lag) % size) ((^^^) r) vector 144 | W128.Array.set i r vector 145 | i <- (i + 1) % size 146 | certificatePeriod parameter vector 147 | 148 | type StateVector (parameter : SfmtParams, index : int, vector : W128.W128_T []) = 149 | static member Initialize (parameter : SfmtParams, seed : uint32) = 150 | StateVector (parameter, parameter.N32, initialize parameter seed) 151 | static member Initialize (parameter : SfmtParams, seed : uint32 []) = 152 | StateVector (parameter, parameter.N32, initializeByArray parameter seed) 153 | member val Parameter = parameter 154 | member val Index = index 155 | member this.Item (index) = vector.[index] 156 | member this.Vector = vector 157 | 158 | let doRecursion index (parameter : SfmtParams) a b c d (vector : W128.W128_T []) = 159 | let va = vector.[a] 160 | let vb = vector.[b] 161 | let vc = vector.[c] 162 | let vd = vector.[d] 163 | let x = W128.lshift va parameter.SL2 164 | let y = W128.rshift vc parameter.SR2 165 | let u0 = va.u.[0] ^^^ x.u.[0] ^^^ ((vb.u.[0] >>> parameter.SR1) &&& parameter.Mask1) ^^^ y.u.[0] ^^^ (vd.u.[0] <<< parameter.SL1) 166 | let u1 = va.u.[1] ^^^ x.u.[1] ^^^ ((vb.u.[1] >>> parameter.SR1) &&& parameter.Mask2) ^^^ y.u.[1] ^^^ (vd.u.[1] <<< parameter.SL1) 167 | let u2 = va.u.[2] ^^^ x.u.[2] ^^^ ((vb.u.[2] >>> parameter.SR1) &&& parameter.Mask3) ^^^ y.u.[2] ^^^ (vd.u.[2] <<< parameter.SL1) 168 | let u3 = va.u.[3] ^^^ x.u.[3] ^^^ ((vb.u.[3] >>> parameter.SR1) &&& parameter.Mask4) ^^^ y.u.[3] ^^^ (vd.u.[3] <<< parameter.SL1) 169 | vector.[index] <- W128.W128_T (u0, u1, u2, u3) 170 | let refresh (state : StateVector) = 171 | let vector = W128.Array.copy state.Vector 172 | let parameter = state.Parameter 173 | let n = parameter.N 174 | let pos1 = parameter.Pos1 175 | let mutable r1 = n - 2 176 | let mutable r2 = n - 1 177 | for index = 0 to n - pos1 - 1 do 178 | doRecursion index parameter index (index + pos1) r1 r2 vector 179 | r1 <- r2 180 | r2 <- index 181 | for index = n - pos1 to n - 1 do 182 | doRecursion index parameter index (index + pos1 - n) r1 r2 vector 183 | r1 <- r2 184 | r2 <- index 185 | StateVector (parameter, 0, vector) 186 | 187 | let sfmtImpl (state : StateVector) = 188 | let state = if state.Index >= state.Parameter.N32 then refresh state else state 189 | let index = state.Index 190 | let vector = state.Vector 191 | let r = W128.Array.get index vector 192 | // Creates a new instance of StateVector, but the parameter and the internal vector 193 | // refers to the same array to avoid cost of copying. 194 | r, StateVector(state.Parameter, index + 1, vector) 195 | [] 196 | let sfmt (s : StateVector) = 197 | let lower, s = sfmtImpl s 198 | let upper, s = sfmtImpl s 199 | to64bit lower upper, s 200 | -------------------------------------------------------------------------------- /src/FsRandom/SimdOrientedFastMersenneTwister.fsi: -------------------------------------------------------------------------------- 1 | /// 2 | /// Implements SIMD-Oriented Fast Mersenne Twister. 3 | /// See http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html for details. 4 | /// 5 | /// 6 | /// Basically, SIMD is not used in this module because of the lack of SIMD support in .NET Framework. 7 | /// 8 | module FsRandom.SimdOrientedFastMersenneTwister 9 | 10 | /// 11 | /// Defines parameters to produce a random cycle of SIMD-Oriented Fast Mersenne Twister. 12 | /// 13 | [] 14 | type SfmtParams = 15 | /// 16 | /// Parameter for period 2^607 - 1. 17 | /// 18 | static member Params607 : SfmtParams 19 | /// 20 | /// Parameter for period 2^1279 - 1. 21 | /// 22 | static member Params1279 : SfmtParams 23 | /// 24 | /// Parameter for period 2^2281 - 1. 25 | /// 26 | static member Params2281 : SfmtParams 27 | /// 28 | /// Parameter for period 2^4253 - 1. 29 | /// 30 | static member Params4253 : SfmtParams 31 | /// 32 | /// Parameter for period 2^11213 - 1. 33 | /// 34 | static member Params11213 : SfmtParams 35 | /// 36 | /// Parameter for period 2^19937 - 1. 37 | /// 38 | static member Params19937 : SfmtParams 39 | /// 40 | /// Parameter for period 2^44497 - 1. 41 | /// 42 | static member Params44497 : SfmtParams 43 | /// 44 | /// Parameter for period 2^86243 - 1. 45 | /// 46 | static member Params86243 : SfmtParams 47 | /// 48 | /// Parameter for period 2^132049 - 1. 49 | /// 50 | static member Params132049 : SfmtParams 51 | /// 52 | /// Parameter for period 2^216091 - 1. 53 | /// 54 | static member Params216091 : SfmtParams 55 | /// 56 | /// The approximate random period in log 2 scale. 57 | /// 2^ - 1 is the exact length. 58 | /// 59 | member Period : int with get 60 | 61 | /// 62 | /// Keeps a random state used in the SIMD-Oriented Fast Mersenne Twister process. 63 | /// 64 | [] 65 | type StateVector = 66 | /// 67 | /// Initializes a new instance with an integer. 68 | /// 69 | /// A parameter to determine the period of the random sequence. 70 | /// A random seed integer. 71 | static member Initialize : parameter:SfmtParams * seed:uint32 -> StateVector 72 | /// 73 | /// Initializes a new instance with integers. 74 | /// 75 | /// A parameter to determine the period of the random sequence. 76 | /// A random seed array. 77 | static member Initialize : parameter:SfmtParams * seed:uint32 [] -> StateVector 78 | 79 | /// 80 | /// Random number generator using SIMD-Oriented Fast Mersenne Twister algorithm (Saito & Matsumoto 2006). 81 | /// 82 | /// 83 | /// SIMD is not supported. 84 | /// 85 | [] 86 | val sfmt : Prng 87 | -------------------------------------------------------------------------------- /src/FsRandom/Statistics.fsi: -------------------------------------------------------------------------------- 1 | /// 2 | /// Provides generator functions related to statistical random distributions. 3 | /// 4 | [] 5 | module FsRandom.Statistics 6 | 7 | /// 8 | /// Returns a random number distributed uniformly in the range of [, ]. 9 | /// 10 | /// The inclusive lower limit. 11 | /// The inclusive upper limit. 12 | [] 13 | val uniform : min:float * max:float -> GeneratorFunction 14 | /// 15 | /// Returns a random number distributed log-uniformly in the range of [, ]. 16 | /// 17 | /// The inclusive lower limit. 18 | /// The inclusive upper limit. 19 | [] 20 | val loguniform : min:float * max:float -> GeneratorFunction 21 | /// 22 | /// Returns a random number distributed triangular. 23 | /// 24 | /// The inclusive lower limit. 25 | /// The inclusive upper limit. 26 | /// The mode. 27 | [] 28 | val triangular : min:float * max:float * mode:float -> GeneratorFunction 29 | /// 30 | /// Returns a random number distributed normally. 31 | /// 32 | /// The mean. 33 | /// The standard deviation. 34 | [] 35 | val normal : mean:float * standardDeviation:float -> GeneratorFunction 36 | /// 37 | /// Returns a random number distributed log-normally. 38 | /// 39 | /// The mu parameter. 40 | /// The sigma parameter. 41 | [] 42 | val lognormal : mu:float * sigma:float -> GeneratorFunction 43 | /// 44 | /// Returns a random number distributed gamma. 45 | /// 46 | /// The shape parameter. 47 | /// The scale parameter. 48 | [] 49 | val gamma : shape:float * scale:float -> GeneratorFunction 50 | /// 51 | /// Returns a random number distributed beta. 52 | /// 53 | /// The first parameter. 54 | /// The second parameter. 55 | [] 56 | val beta : alpha:float * beta:float -> GeneratorFunction 57 | /// 58 | /// Returns a random number distributed exponentially. 59 | /// 60 | /// The rate parameter (equals to its mean^(-1)). 61 | [] 62 | val exponential : rate:float -> GeneratorFunction 63 | /// 64 | /// Returns a random number distributed Weibull. 65 | /// 66 | /// The shape parameter. 67 | /// The scale parameter. 68 | [] 69 | val weibull : shape:float * scale:float -> GeneratorFunction 70 | /// 71 | /// Returns a random number distributed Gumbel. 72 | /// 73 | /// The location parameter. 74 | /// The scale parameter. 75 | [] 76 | val gumbel : location:float * scale:float -> GeneratorFunction 77 | /// 78 | /// Returns a random number distributed Cauchy. 79 | /// 80 | /// The location parameter. 81 | /// The scale parameter. 82 | [] 83 | val cauchy : location:float * scale:float -> GeneratorFunction 84 | /// 85 | /// Returns a random number distributed chi-square. 86 | /// 87 | /// The degree of freedom. 88 | [] 89 | val chisquare : degreeOfFreedom:int -> GeneratorFunction 90 | /// 91 | /// Returns a random number distributed Student-t. 92 | /// 93 | /// The degree of freedom. 94 | [] 95 | val studentT : degreeOfFreedom:int -> GeneratorFunction 96 | /// 97 | /// Returns a random number distributed Student-t. 98 | /// 99 | /// 100 | /// This is a synonym for 101 | /// 102 | /// The degree of freedom. 103 | [] 104 | [] 105 | val t : degreeOfFreedom:int -> GeneratorFunction 106 | /// 107 | /// Returns a random number distributed von Mises. 108 | /// 109 | /// The direction parameter. 110 | /// The concentration parameter. 111 | [] 112 | val vonMises : direction:float * concentration:float -> GeneratorFunction 113 | /// 114 | /// Returns a random number distributed uniformly. 115 | /// 116 | /// The inclusive lower bound. 117 | /// The inclusive upper bound. 118 | [] 119 | val uniformDiscrete : min:int * max:int -> GeneratorFunction 120 | /// 121 | /// Returns a random number distributed Poisson. 122 | /// 123 | /// The lambda parameter (equals to its mean). 124 | [] 125 | val poisson : lambda:float -> GeneratorFunction 126 | /// 127 | /// Returns a random number distributed geometcally on {0, 1, 2, ...}. 128 | /// 129 | /// The probability to success a trial. 130 | /// 131 | [] 132 | val geometric0 : probability:float -> GeneratorFunction 133 | /// 134 | /// Returns a random number distributed geometcally on {1, 2, 3, ...}. 135 | /// 136 | /// The probability to success a trial. 137 | /// 138 | [] 139 | val geometric1 : probability:float -> GeneratorFunction 140 | /// 141 | /// Returns a random number distributed Bernoulli. 142 | /// 143 | /// The probability of success. 144 | /// 145 | [] 146 | val bernoulli : probability:float -> GeneratorFunction 147 | /// 148 | /// Returns a random number distributed binomially. 149 | /// 150 | /// The number of trials. 151 | /// The probability to success a trial. 152 | [] 153 | val binomial : n:int * probability:float -> GeneratorFunction 154 | /// 155 | /// Returns a random number distributed negative binomially. 156 | /// 157 | /// The number of failures until the experiment is stopped. 158 | /// The probability to success a trial. 159 | [] 160 | val negativeBinomial : r:float * probability:float -> GeneratorFunction 161 | /// 162 | /// Returns a random number distributed Dirichlet. 163 | /// 164 | /// The alpha parameter. 165 | [] 166 | val dirichlet : alpha:float list -> GeneratorFunction 167 | /// 168 | /// Returns a random number distributed multinomially. 169 | /// 170 | /// The number of trials. 171 | /// The list of probability. 172 | /// Each item is normalized in the function so that the sum of values can be less or greater than 1. 173 | [] 174 | val multinomial : n:int * weight:float list -> GeneratorFunction 175 | /// 176 | /// Returns a random vector distributed multinormally. 177 | /// 178 | /// The mean vector. 179 | /// The covariance matrix. 180 | [] 181 | val multinormal : mu:float [] * sigma:float [,] -> GeneratorFunction 182 | /// 183 | /// Returns a random matrix distributed Wishart. 184 | /// 185 | /// The degree of freedom. 186 | /// The covariance matrix. 187 | [] 188 | val wishart : degreeOfFreedom:int * sigma:float [,] -> GeneratorFunction 189 | /// 190 | /// Returns a mixted distribution. 191 | /// 192 | /// The mixed model. 193 | [] 194 | val mix : distributions:(GeneratorFunction<'a> * float) list -> GeneratorFunction<'a> 195 | 196 | [] 197 | [] 198 | module Standard = 199 | /// 200 | /// Returns a standard uniform random number. 201 | /// 202 | [] 203 | val uniform : GeneratorFunction 204 | /// 205 | /// Returns a standard normal random number. 206 | /// 207 | [] 208 | val normal : GeneratorFunction 209 | /// 210 | /// Returns a standard gamma random number. 211 | /// 212 | /// The shape parameter. 213 | [] 214 | val gamma : shape:float -> GeneratorFunction 215 | /// 216 | /// Returns a standard exponential random number. 217 | /// 218 | [] 219 | val exponential : GeneratorFunction 220 | /// 221 | /// Returns a standard Weibull random number. 222 | /// 223 | /// The shape parameter. 224 | [] 225 | val weibull : shape:float -> GeneratorFunction 226 | /// 227 | /// Returns a standard Gumbel random number. 228 | /// 229 | [] 230 | val gumbel : GeneratorFunction 231 | /// 232 | /// Returns a standard Cauchy random number. 233 | /// 234 | [] 235 | val cauchy : GeneratorFunction 236 | 237 | [] 238 | [] 239 | module Seq = 240 | /// 241 | /// Makes infinite Markov chain. 242 | /// 243 | /// A random function. 244 | /// 245 | /// A Markov chain. 246 | /// 247 | [] 248 | val markovChain : generator:('a -> GeneratorFunction<'a>) -> ('a -> PrngState -> seq<'a>) 249 | -------------------------------------------------------------------------------- /src/FsRandom/String.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module FsRandom.String 3 | 4 | open System.Globalization 5 | 6 | let ascii = [|'!' .. '~'|] 7 | let digit = [|'0' .. '9'|] 8 | let upper = [|'A' .. 'Z'|] 9 | let lower = [|'a' .. 'z'|] 10 | let alphabet = Array.concat [upper; lower] 11 | let alphanumeric = Array.concat [digit; alphabet] 12 | 13 | let inline makeString (array:char []) = System.String (array) 14 | let inline randomStringByCharArray array length = 15 | if length = 0 then 16 | Random.singleton "" 17 | else 18 | Random.map makeString (Array.sampleWithReplacement length array) 19 | let getCharacters s = 20 | let e = StringInfo.GetTextElementEnumerator (s) 21 | seq { while e.MoveNext () do yield string e.Current } |> Seq.toArray 22 | let inline randomStringByStringArray array length = 23 | if length = 0 then 24 | Random.singleton "" 25 | else 26 | Random.map (String.concat "") (Array.sampleWithReplacement length array) 27 | 28 | [] 29 | let randomByString (s:string) length = randomStringByStringArray (getCharacters s) length 30 | [] 31 | let randomAscii length = randomStringByCharArray ascii length 32 | [] 33 | let randomNumeric length = randomStringByCharArray digit length 34 | [] 35 | let randomAlphabet length = randomStringByCharArray alphabet length 36 | [] 37 | let randomAlphanumeric length = randomStringByCharArray alphanumeric length 38 | [] 39 | let randomConcat separator randomStringGenerators = Random.mergeWith (String.concat separator) randomStringGenerators 40 | -------------------------------------------------------------------------------- /src/FsRandom/String.fsi: -------------------------------------------------------------------------------- 1 | /// 2 | /// Provides basic operations on strings. 3 | /// 4 | [] 5 | [] 6 | module FsRandom.String 7 | 8 | /// 9 | /// Returns a random string which is composed of characters in the given string. 10 | /// 11 | [] 12 | val randomByString : (string -> int -> GeneratorFunction) 13 | /// 14 | /// Returns a random string which is composed of non-control non-space ASCII characters. 15 | /// 16 | [] 17 | val randomAscii : (int -> GeneratorFunction) 18 | /// 19 | /// Returns a random string which is composed of digits. 20 | /// 21 | [] 22 | val randomNumeric : (int -> GeneratorFunction) 23 | /// 24 | /// Returns a random string which is composed of alphabets. 25 | /// 26 | [] 27 | val randomAlphabet : (int -> GeneratorFunction) 28 | /// 29 | /// Returns a random string which is composed of alphabets or digits. 30 | /// 31 | [] 32 | val randomAlphanumeric : (int -> GeneratorFunction) 33 | /// 34 | /// Concatenates random strings into one random string. 35 | /// 36 | [] 37 | val randomConcat : (string -> GeneratorFunction list -> GeneratorFunction) 38 | -------------------------------------------------------------------------------- /src/FsRandom/Utility.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module FsRandom.Utility 3 | 4 | open System 5 | open Microsoft.FSharp.Core.LanguagePrimitives 6 | 7 | [] 8 | let defaultState = createState xorshift (123456789u, 362436069u, 521288629u, 88675123u) 9 | [] 10 | let createRandomState () = 11 | let guid = Guid.NewGuid () 12 | let bytes = guid.ToByteArray () 13 | let seed = Array.init 4 (fun i -> BitConverter.ToUInt32 (bytes, i * 4)) 14 | createState xorshift (seed.[0], seed.[1], seed.[2], seed.[3]) 15 | 16 | [] 17 | let inline randomSign () = 18 | let inline g s0 = 19 | let r, s' = Random.next rawBits s0 20 | let sign = if r &&& 1uL = 0uL then GenericOne else -GenericOne 21 | sign, s' 22 | GeneratorFunction (g) 23 | 24 | [] 25 | let flipCoin probability = 26 | ensuresFiniteValue probability "probability" 27 | if probability < 0.0 || 1.0 < probability then 28 | outOfRange "probability" "`probability' must be in the range of [0, 1]." 29 | else 30 | let transform u = u < probability 31 | Random.map transform ``[0, 1)`` 32 | 33 | [] 34 | let choose m n = 35 | if m <= 0 then 36 | outOfRange "size" "`size' must be positive." 37 | elif n < 0 || m < n then 38 | outOfRange "count" "`count' must be in the range of [0, size]." 39 | else 40 | GeneratorFunction (fun s0 -> 41 | let mutable acc = [] 42 | let mutable p = m 43 | let mutable index = n - 1 44 | let mutable s = s0 45 | while index >= 0 do 46 | let mutable probability = 1.0 47 | let u, s' = Random.next ``[0, 1)`` s 48 | s <- s' 49 | while u < probability do 50 | probability <- probability * float (p - index - 1) / float p 51 | p <- p - 1 52 | acc <- m - p - 1 :: acc 53 | index <- index - 1 54 | List.rev acc, s 55 | ) 56 | 57 | [] 58 | let chooseOne n = 59 | if n <= 0 then 60 | outOfRange "upper" "`upper' must be positive." 61 | else 62 | let n = float n 63 | Random.map (fun u -> int (u * n)) ``[0, 1)`` 64 | -------------------------------------------------------------------------------- /src/FsRandom/Utility.fsi: -------------------------------------------------------------------------------- 1 | /// 2 | /// Provides utility functions. 3 | /// 4 | [] 5 | module FsRandom.Utility 6 | 7 | /// 8 | /// Provides a default random state. 9 | /// 10 | [] 11 | val defaultState : PrngState 12 | /// 13 | /// Creates a random random state. 14 | /// 15 | [] 16 | val createRandomState : unit -> PrngState 17 | 18 | /// 19 | /// Returns +1 or -1 randomly. 20 | /// 21 | //val randomSign : GeneratorFunction 22 | [] 23 | val inline randomSign : unit -> GeneratorFunction<(^a)> 24 | when ^a : (static member One : ^a) 25 | and ^a : (static member (~-) : ^a -> ^a) 26 | 27 | /// 28 | /// Returns a random Boolean value with the specified probability. 29 | /// 30 | /// The probability of success. 31 | /// 32 | [] 33 | val flipCoin : probability:float -> GeneratorFunction 34 | 35 | /// 36 | /// Returns random indices of collections. 37 | /// 38 | /// The size of collections. 39 | /// The number of indices to take. 40 | [] 41 | val choose : size:int -> count:int -> GeneratorFunction 42 | 43 | /// 44 | /// Returns a random number less than the specified value. 45 | /// 46 | /// The exclusive upper bound. 47 | [] 48 | val chooseOne : upper:int -> GeneratorFunction 49 | -------------------------------------------------------------------------------- /src/FsRandom/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core -------------------------------------------------------------------------------- /tests/FsRandom.Tests.CSharp/FsRandom.Tests.CSharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net46;netcoreapp2.0 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/FsRandom.Tests.CSharp/TestRandomNumberGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using RNG = FsRandom.RandomNumberGenerator; 4 | 5 | namespace FsRandom 6 | { 7 | [TestFixture] 8 | public class TestRandomNumberGenerator 9 | { 10 | [Test] 11 | public void CanGenerateRandom() 12 | { 13 | var seed = Tuple.Create(123456789u, 362436069u, 521288629u, 88675123u); 14 | var state = RNG.CreateState(RNG.XorshiftPrng, seed); 15 | var r1 = RandomModule.Next(RNG.RawBits, state); 16 | var r2 = RandomModule.Next(RNG.RawBits, r1.Item2); 17 | Assert.That(r2.Item1, Is.Not.EqualTo(r1.Item1)); 18 | } 19 | 20 | [Test] 21 | public void CanUseSingleSelectQueryExpression() 22 | { 23 | var g = from x in RNG.RawBits 24 | select x + 1uL; 25 | var actual = RandomModule.Get(g, UtilityModule.DefaultState); 26 | var expected = RandomModule.Get(RNG.RawBits, UtilityModule.DefaultState) + 1uL; 27 | Assert.That(actual, Is.EqualTo(expected)); 28 | } 29 | 30 | [Test] 31 | public void CanUseSelectManyQueryExpression() 32 | { 33 | var g = from x in RNG.RawBits 34 | from y in RNG.RawBits 35 | let z = x ^ y 36 | select z + 1uL; 37 | var actual = RandomModule.Get(g, UtilityModule.DefaultState); 38 | var expectedX = RandomModule.Next(RNG.RawBits, UtilityModule.DefaultState); 39 | var expectedY = RandomModule.Next(RNG.RawBits, expectedX.Item2); 40 | Assert.That(actual, Is.EqualTo((expectedX.Item1 ^ expectedY.Item1) + 1uL)); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/FsRandom.Tests.CSharp/paket.references: -------------------------------------------------------------------------------- 1 | Microsoft.NET.Test.Sdk 2 | NUnit 3 | NUnit3TestAdapter -------------------------------------------------------------------------------- /tests/FsRandom.Tests/Array2DTest.fs: -------------------------------------------------------------------------------- 1 | module FsRandom.Array2DTest 2 | 3 | open FsUnit 4 | open NUnit.Framework 5 | 6 | [] 7 | let ``Validates Array2D.randomCreate`` () = 8 | let tester = Utility.defaultState 9 | let expected = 10 | Random.get 11 | <| random { 12 | let r = Array2D.zeroCreate 3 2 13 | for i = 0 to 2 do 14 | for j = 0 to 1 do 15 | let! u = ``[0, 1)`` 16 | r.[i, j] <- u 17 | return r 18 | } 19 | <| tester 20 | let actual = Random.get (Array2D.randomCreate 3 2 ``[0, 1)``) tester 21 | actual.GetLength (0) |> should equal 3 22 | actual.GetLength (1) |> should equal 2 23 | actual |> should equal expected 24 | 25 | [] 26 | let ``Validates Array2D.randomCreateBased`` () = 27 | let tester = Utility.defaultState 28 | let expected = 29 | Random.get 30 | <| random { 31 | let r = Array2D.zeroCreateBased 4 1 3 2 32 | for i = 4 to 6 do 33 | for j = 1 to 2 do 34 | let! u = ``[0, 1)`` 35 | r.[i, j] <- u 36 | return r 37 | } 38 | <| tester 39 | let actual = Random.get (Array2D.randomCreateBased 4 1 3 2 ``[0, 1)``) tester 40 | actual.GetLength (0) |> should equal 3 41 | actual.GetLength (1) |> should equal 2 42 | actual |> should equal expected 43 | 44 | [] 45 | let ``Validates Array2D.randomInit`` () = 46 | let tester = Utility.defaultState 47 | let f i j u = float i + float j * u 48 | let expected = 49 | Random.get 50 | <| random { 51 | let r = Array2D.zeroCreate 3 2 52 | for i = 0 to 2 do 53 | for j = 0 to 1 do 54 | let! u = ``[0, 1)`` 55 | r.[i, j] <- f i j u 56 | return r 57 | } 58 | <| tester 59 | let actual = 60 | Random.get 61 | <| Array2D.randomInit 3 2 (fun i j -> Random.map (f i j) ``[0, 1)``) 62 | <| tester 63 | actual.GetLength (0) |> should equal 3 64 | actual.GetLength (1) |> should equal 2 65 | actual |> should equal expected 66 | 67 | [] 68 | let ``Validates Array2D.randomInitBased`` () = 69 | let tester = Utility.defaultState 70 | let f i j u = float i + float j * u 71 | let expected = 72 | Random.get 73 | <| random { 74 | let r = Array2D.zeroCreateBased 4 1 3 2 75 | for i = 4 to 6 do 76 | for j = 1 to 2 do 77 | let! u = ``[0, 1)`` 78 | r.[i, j] <- f i j u 79 | return r 80 | } 81 | <| tester 82 | let actual = 83 | Random.get 84 | <| Array2D.randomInitBased 4 1 3 2 (fun i j -> Random.map (f i j) ``[0, 1)``) 85 | <| tester 86 | actual.GetLength (0) |> should equal 3 87 | actual.GetLength (1) |> should equal 2 88 | actual |> should equal expected 89 | -------------------------------------------------------------------------------- /tests/FsRandom.Tests/ArrayTest.fs: -------------------------------------------------------------------------------- 1 | module FsRandom.ArrayTest 2 | 3 | open FsUnit 4 | open NUnit.Framework 5 | 6 | [] 7 | let ``Validates Array.randomCreate`` () = 8 | let tester = Utility.defaultState 9 | let expected = 10 | Random.get 11 | <| random { 12 | let r = Array.zeroCreate 3 13 | for index = 0 to 2 do 14 | let! u = ``[0, 1)`` 15 | r.[index] <- u 16 | return r 17 | } 18 | <| tester 19 | let actual = Random.get (Array.randomCreate 3 ``[0, 1)``) tester 20 | Array.length actual |> should equal 3 21 | actual |> should equal expected 22 | 23 | [] 24 | let ``Validates Array.randomInit`` () = 25 | let tester = Utility.defaultState 26 | let expected = 27 | Random.get 28 | <| random { 29 | let r = Array.zeroCreate 3 30 | for index = 0 to 2 do 31 | let! u = ``[0, 1)`` 32 | r.[index] <- float index + u 33 | return r 34 | } 35 | <| tester 36 | let actual = Random.get (Array.randomInit 3 (fun i -> Random.map (fun u -> float i + u) ``[0, 1)``)) tester 37 | Array.length actual |> should equal 3 38 | actual |> should equal expected 39 | 40 | [] 41 | let ``Validates Array.randomFill`` () = 42 | let tester = Utility.defaultState 43 | let expected = 44 | Random.get 45 | <| random { 46 | let r = Array.zeroCreate 5 47 | for index = 2 to 3 do 48 | let! u = ``[0, 1)`` 49 | r.[index] <- u 50 | return r 51 | } 52 | <| tester 53 | let actual = Array.zeroCreate 5 54 | Random.get (Array.randomFill actual 2 2 ``[0, 1)``) tester 55 | actual |> should equal expected 56 | 57 | [] 58 | let ``Validates Array.sample`` () = 59 | let array = Array.init 10 id 60 | let tester = Utility.defaultState 61 | let result, next = Random.next (Array.sample 8 array) tester 62 | Assert.That (next, Is.Not.EqualTo(tester)) 63 | Assert.That (Array.length result, Is.EqualTo(8)) 64 | Assert.That (Array.forall (fun x -> Array.exists ((=) x) array) result, Is.True) 65 | Assert.That (Seq.length (Seq.distinct result), Is.EqualTo(8)) 66 | 67 | [] 68 | let ``Validates Array.sampleOne`` () = 69 | let array = Array.init 1000 id 70 | let tester = Utility.defaultState 71 | let r1, next = Random.next (Array.sampleOne array) tester 72 | let r2 = Random.get (Array.sampleOne array) next 73 | Assert.That (r1, Is.Not.EqualTo(r2)) 74 | 75 | [] 76 | let ``Validates Array.weightedSample`` () = 77 | let array = Array.init 10 id 78 | let weight = Array.init (Array.length array) (id >> float >> ((+) 1.0)) 79 | let tester = Utility.defaultState 80 | let result, next = Random.next (Array.weightedSample 8 weight array) tester 81 | Assert.That (next, Is.Not.EqualTo(tester)) 82 | Assert.That (Array.length result, Is.EqualTo(8)) 83 | Assert.That (Array.forall (fun x -> Array.exists ((=) x) array) result, Is.True) 84 | Assert.That (Seq.length (Seq.distinct result), Is.EqualTo(8)) 85 | 86 | [] 87 | let ``Validates Array.weightedSampleOne`` () = 88 | let weight = [|1.0; 3.0; 6.0|] 89 | let array = Array.init (Array.length weight) id 90 | let counts = 91 | Seq.ofRandom (Array.weightedSampleOne weight array) Utility.defaultState 92 | |> Seq.take 10000 93 | |> Seq.countBy id 94 | |> Map.ofSeq 95 | Assert.That (counts.[0], Is.GreaterThanOrEqualTo(950).Or.LessThanOrEqualTo(1050)) 96 | Assert.That (counts.[1], Is.GreaterThanOrEqualTo(2900).Or.LessThanOrEqualTo(3100)) 97 | Assert.That (counts.[2], Is.GreaterThanOrEqualTo(5800).Or.LessThanOrEqualTo(6200)) 98 | 99 | [] 100 | let ``Validates Array.sampleWithReplacement`` () = 101 | let array = Array.init 5 id 102 | let tester = Utility.defaultState 103 | let result, next = Random.next (Array.sampleWithReplacement 8 array) tester 104 | Assert.That (next, Is.Not.EqualTo(tester)) 105 | Assert.That (Array.length result, Is.EqualTo(8)) 106 | Assert.That (Array.forall (fun x -> Array.exists ((=) x) array) result, Is.True) 107 | Assert.That (Seq.length (Seq.distinct result), Is.Not.EqualTo(1)) 108 | 109 | [] 110 | let ``Validates Array.weightedSampleWithReplacement`` () = 111 | let array = Array.init 5 id 112 | let weight = Array.init (Array.length array) (id >> float >> ((+) 1.0)) 113 | let tester = Utility.defaultState 114 | let result, next = Random.next (Array.weightedSampleWithReplacement 8 weight array) tester 115 | Assert.That (next, Is.Not.EqualTo(tester)) 116 | Assert.That (Array.length result, Is.EqualTo(8)) 117 | Assert.That (Array.forall (fun x -> Array.exists ((=) x) array) result, Is.True) 118 | Assert.That (Seq.length (Seq.distinct result), Is.Not.EqualTo(1)) 119 | 120 | [] 121 | let ``Validates Array.shuffle`` () = 122 | let tester = Utility.defaultState 123 | let array = Array.init 8 id 124 | let result, next = Random.next (Array.shuffle array) tester 125 | Assert.That (next, Is.Not.EqualTo(tester)) 126 | Assert.That (System.Object.ReferenceEquals (result, array), Is.False) 127 | Assert.That (Array.length result, Is.EqualTo(Array.length array)) 128 | Assert.That (Array.zip array result |> Array.forall (fun (x, y) -> x = y), Is.False) 129 | Assert.That (Array.sort result, Is.EquivalentTo(array)) 130 | 131 | [] 132 | let ``Validates Array.shuffleInPlace`` () = 133 | let tester = Utility.defaultState 134 | let array = Array.init 8 id 135 | let copied = Array.copy array 136 | let _, next = Random.next (Array.shuffleInPlace array) tester 137 | Assert.That (Array.zip copied array |> Array.forall (fun (x, y) -> x = y), Is.False) 138 | Assert.That (Array.sort array, Is.EquivalentTo(copied)) 139 | -------------------------------------------------------------------------------- /tests/FsRandom.Tests/FsRandom.Tests.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net46;netcoreapp2.0 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | PreserveNewest 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /tests/FsRandom.Tests/Issues.fs: -------------------------------------------------------------------------------- 1 | module FsRandom.Issues 2 | 3 | open System 4 | open System.Globalization 5 | open FsRandom.Statistics 6 | open FsRandom.Utility 7 | open NUnit.Framework 8 | open FsUnit 9 | 10 | [] 11 | [] 12 | let ``flipCoin can accept 0`` () = 13 | let tester = Utility.defaultState 14 | Random.get (flipCoin 0.0) tester |> ignore 15 | 16 | [] 17 | [] 18 | let ``flipCoin can accept 1`` () = 19 | let tester = Utility.defaultState 20 | Random.get (flipCoin 1.0) tester |> ignore 21 | 22 | [] 23 | [] 24 | let ``sampleWithReplacement fails before run`` () = 25 | (fun () -> Array.sampleWithReplacement 0 Array.empty |> ignore) |> should throw typeof 26 | 27 | [] 28 | [] 29 | let ``weightedSampleWithReplacement fails before run`` () = 30 | (fun () -> Array.weightedSampleWithReplacement 0 Array.empty Array.empty |> ignore) |> should throw typeof 31 | 32 | [] 33 | [] 34 | let ``multinormal (mu, _) is affected by modification of mu`` () = 35 | let mu = [|0.0; 0.0|] 36 | let sigma = Array2D.init 2 2 (fun i j -> if i = j then 1.0 else 0.7) 37 | let m = multinormal (mu, sigma) 38 | let sample = Random.get m Utility.defaultState 39 | sample.[0] |> should be (lessThan 50.0) 40 | mu.[0] <- 100.0 41 | let sample = Random.get m Utility.defaultState 42 | sample.[0] |> should be (lessThan 50.0) 43 | 44 | [] 45 | [] 46 | let ``String.randomByString breaks surrogate pairs`` () = 47 | let s = "𠮷野家" // the first character is a surrogate pair character 48 | let tester = Utility.defaultState 49 | let actual = 50 | let r = Random.get (String.randomByString s 100) tester 51 | StringInfo (r) 52 | actual.LengthInTextElements |> should equal 100 53 | let actual = seq { 0 .. 99 } |> Seq.map (fun i -> actual.SubstringByTextElements (i, 1)) |> Seq.toArray 54 | actual |> should contain "𠮷" 55 | actual |> should contain "野" 56 | actual |> should contain "家" 57 | 58 | [] 59 | [] 60 | let ``List.randomCreate doesn't throw StackOverflowException`` () = 61 | List.randomCreate 10000 rawBits 62 | |> Random.get <| Utility.defaultState 63 | |> ignore 64 | 65 | [] 66 | [] 67 | let ``List.randomInit doesn't throw StackOverflowException`` () = 68 | List.randomInit 10000 (fun _ -> rawBits) 69 | |> Random.get <| Utility.defaultState 70 | |> ignore 71 | -------------------------------------------------------------------------------- /tests/FsRandom.Tests/ListTest.fs: -------------------------------------------------------------------------------- 1 | module FsRandom.ListTest 2 | 3 | open FsUnit 4 | open NUnit.Framework 5 | 6 | [] 7 | let ``Validates List.randomCreate`` () = 8 | let tester = Utility.defaultState 9 | let expected = 10 | Random.get 11 | <| random { 12 | let! r1 = rawBits 13 | let! r2 = rawBits 14 | let! r3 = rawBits 15 | return [r1; r2; r3] 16 | } 17 | <| tester 18 | let actual = Random.get (List.randomCreate 3 rawBits) tester 19 | List.length actual |> should equal 3 20 | actual |> should equal expected 21 | 22 | [] 23 | let ``Validates List.randomInit`` () = 24 | let tester = Utility.defaultState 25 | let expected = 26 | Random.get 27 | <| random { 28 | let! r1 = ``[0, 1)`` 29 | let! r2 = ``[0, 1)`` 30 | let! r3 = ``[0, 1)`` 31 | return [r1 + 1.0; r2 + 2.0; r3 + 3.0] 32 | } 33 | <| tester 34 | let actual = Random.get (List.randomInit 3 (fun i -> Random.map (fun u -> u + float (i + 1)) ``[0, 1)``)) tester 35 | List.length actual |> should equal 3 36 | actual |> should equal expected 37 | -------------------------------------------------------------------------------- /tests/FsRandom.Tests/MersenneTwisterTest.fs: -------------------------------------------------------------------------------- 1 | module FsRandom.MersenneTwisterTest 2 | 3 | open System 4 | open System.IO 5 | open FsRandom.MersenneTwister 6 | open NUnit.Framework 7 | 8 | [] 9 | let ``Checks first 2,000 output`` () = 10 | let expected = 11 | use reader = new StringReader (KnownRandomSequence.mt19937_64_out) 12 | reader.ReadLine () |> ignore 13 | let raws = Array.zeroCreate 1000 14 | for i = 0 to 199 do 15 | let line = reader.ReadLine () 16 | let values = line.Split ([|' '|], StringSplitOptions.RemoveEmptyEntries) 17 | for j = 0 to Array.length values - 1 do 18 | raws.[5 * i + j] <- values.[j] 19 | reader.ReadLine () |> ignore 20 | reader.ReadLine () |> ignore 21 | let standards = Array.zeroCreate 1000 22 | for i = 0 to 199 do 23 | let line = reader.ReadLine () 24 | let values = line.Split ([|' '|], StringSplitOptions.RemoveEmptyEntries) 25 | for j = 0 to Array.length values - 1 do 26 | standards.[5 * i + j] <- values.[j] 27 | raws, standards 28 | let actual = 29 | let seed = StateVector.Initialize [|0x12345uL; 0x23456uL; 0x34567uL; 0x45678uL|] 30 | Random.get 31 | <| random { 32 | let raws = Array.zeroCreate 1000 33 | for index = 0 to Array.length raws - 1 do 34 | let! u = rawBits 35 | raws.[index] <- (sprintf "%20u" u).Trim () 36 | let standards = Array.zeroCreate 1000 37 | for index = 0 to Array.length standards - 1 do 38 | let! u = ``[0, 1)`` 39 | standards.[index] <- (sprintf "%10.8f" u).Trim () 40 | return raws, standards 41 | } 42 | <| createState mersenne seed 43 | Assert.That (fst actual, Is.EquivalentTo(fst expected)) 44 | Assert.That (snd actual, Is.EquivalentTo(snd expected)) 45 | -------------------------------------------------------------------------------- /tests/FsRandom.Tests/RandomBuilderTest.fs: -------------------------------------------------------------------------------- 1 | module FsRandom.RandomBuilderTest 2 | 3 | open FsUnit 4 | open NUnit.Framework 5 | 6 | [] 7 | let ``Satisfies monad law 1 (left identity)`` () = 8 | let tester = Utility.defaultState 9 | let a = 1.0 10 | let f x = Random.map ((+) x) ``[0, 1)`` 11 | let l = Random.bind f (random.Return (a)) |> Random.get <| tester 12 | let r = f a |> Random.get <| tester 13 | r |> should equal l 14 | 15 | [] 16 | let ``Satisfies monad law 2 (right identity)`` () = 17 | let tester = Utility.defaultState 18 | let m = ``[0, 1)`` 19 | let l = Random.bind random.Return m |> Random.get <| tester 20 | let r = m |> Random.get <| tester 21 | r |> should equal l 22 | 23 | [] 24 | let ``Satisfies monad law 3 (associativity)`` () = 25 | let tester = Utility.defaultState 26 | let m = ``[0, 1)`` 27 | let f x = Random.map ((+) x) ``[0, 1)`` 28 | let g x = Random.map (fun t -> t - x) ``[0, 1)`` 29 | let l = Random.bind g (Random.bind f m) |> Random.get <| tester 30 | let r = Random.bind (fun y -> Random.bind g (f y)) m |> Random.get <| tester 31 | r |> should equal l 32 | 33 | [] 34 | let ``Can use if-else expression in random computation expression (true)`` () = 35 | let actual = 36 | Random.get 37 | <| random { 38 | let u = ref 0uL 39 | let! x = rawBits 40 | if true then 41 | u := x 42 | else 43 | u := x >>> 1 44 | return !u 45 | } 46 | <| Utility.defaultState 47 | let expected = Random.get rawBits Utility.defaultState 48 | actual |> should equal expected 49 | 50 | [] 51 | let ``Can use if-else expression in random computation expression (false)`` () = 52 | let actual = 53 | Random.get 54 | <| random { 55 | let u = ref 0uL 56 | let! x = rawBits 57 | if false then 58 | u := x 59 | else 60 | u := x >>> 1 61 | return !u 62 | } 63 | <| Utility.defaultState 64 | let expected = Random.get <| Random.map (fun u -> u >>> 1) rawBits <| Utility.defaultState 65 | actual |> should equal expected 66 | 67 | [] 68 | let ``Can use if without else expression in random computation expression`` () = 69 | let actual = 70 | Random.get 71 | <| random { 72 | let u = ref -1.0 73 | if false then 74 | let! x = ``[0, 1)`` 75 | u := x 76 | return !u 77 | } 78 | <| Utility.defaultState 79 | actual |> should equal -1.0 80 | 81 | [] 82 | let ``Can use while-do expression in random computation expression`` () = 83 | let actual = 84 | Random.get 85 | <| random { 86 | let r = ref [] 87 | let i = ref 0 88 | while !i < 3 do 89 | let! x = rawBits 90 | r := x :: !r 91 | incr i 92 | return !r 93 | } 94 | <| Utility.defaultState 95 | let expected = 96 | Random.get 97 | <| random { 98 | let! x = rawBits 99 | let! y = rawBits 100 | let! z = rawBits 101 | return [z; y; x] 102 | } 103 | <| Utility.defaultState 104 | actual |> should equal expected 105 | 106 | [] 107 | let ``Can use for-do expression in random computation expression`` () = 108 | let actual = 109 | Random.get 110 | <| random { 111 | let r = ref [] 112 | for i = 0 to 2 do 113 | let! x = rawBits 114 | r := x :: !r 115 | return !r 116 | } 117 | <| Utility.defaultState 118 | let expected = 119 | Random.get 120 | <| random { 121 | let! x = rawBits 122 | let! y = rawBits 123 | let! z = rawBits 124 | return [z; y; x] 125 | } 126 | <| Utility.defaultState 127 | actual |> should equal expected 128 | 129 | [] 130 | let ``Can use try-with expression in random computation expression`` () = 131 | Random.get 132 | <| random { 133 | try 134 | invalidOp "" 135 | return false 136 | with 137 | | _ -> return true 138 | } 139 | <| Utility.defaultState 140 | |> should be True 141 | 142 | [] 143 | let ``Can use try-finally in random computation expression`` () = 144 | let isFinallyRun = ref false 145 | Random.get 146 | <| random { 147 | try 148 | return true 149 | finally 150 | isFinallyRun := true 151 | } 152 | <| Utility.defaultState 153 | |> should be True 154 | !isFinallyRun |> should be True 155 | 156 | type Resource () = 157 | member val Closed = false with get, set 158 | interface System.IDisposable with 159 | member this.Dispose () = this.Closed <- true 160 | [] 161 | let ``Can use use binding in random computation expression`` () = 162 | let r = new Resource () 163 | Random.get 164 | <| random { 165 | use r2 = r 166 | return r2.Closed 167 | } 168 | <| Utility.defaultState 169 | |> should be False 170 | r.Closed |> should be True 171 | -------------------------------------------------------------------------------- /tests/FsRandom.Tests/RandomTest.fs: -------------------------------------------------------------------------------- 1 | module FsRandom.RandomTest 2 | 3 | open FsUnit 4 | open NUnit.Framework 5 | 6 | [] 7 | let ``Validates singleton`` () = 8 | let tester = Utility.defaultState 9 | Random.get (Random.singleton 42) tester |> should equal 42 10 | Random.get (Random.singleton "foo") tester |> should equal "foo" 11 | 12 | [] 13 | let ``Validates identity`` () = 14 | let tester = Utility.defaultState 15 | let expected = Random.get ``[0, 1)`` tester 16 | let actual = Random.get (Random.identity ``[0, 1)``) tester 17 | actual |> should equal expected 18 | 19 | [] 20 | let ``Validates transformBy`` () = 21 | let tester = Utility.defaultState 22 | let f = (+) 1.0 23 | let expected = Random.get ``[0, 1)`` tester |> f 24 | let actual = Random.get (Random.map f ``[0, 1)``) tester 25 | actual |> should equal expected 26 | 27 | [] 28 | let ``Validates transformBy2`` () = 29 | let tester = Utility.defaultState 30 | let f x y = 2.0 * x - y 31 | let expected = 32 | Random.get 33 | <| random { 34 | let! u1 = ``[0, 1)`` 35 | let! u2 = ``[0, 1)`` 36 | return f u1 u2 37 | } 38 | <| tester 39 | let actual = Random.get (Random.map2 f ``[0, 1)`` ``[0, 1)``) tester 40 | actual |> should equal expected 41 | 42 | [] 43 | let ``Validates zip`` () = 44 | let tester = Utility.defaultState 45 | let expected = 46 | Random.get 47 | <| random { 48 | let! u1 = ``[0, 1)`` 49 | let! u2 = ``[0, 1)`` 50 | return u1, u2 51 | } 52 | <| tester 53 | let actual = Random.get (Random.zip ``[0, 1)`` ``[0, 1)``) tester 54 | actual |> should equal expected 55 | 56 | [] 57 | let ``Validates zip3`` () = 58 | let tester = Utility.defaultState 59 | let expected = 60 | Random.get 61 | <| random { 62 | let! u1 = ``[0, 1)`` 63 | let! u2 = ``[0, 1)`` 64 | let! u3 = ``[0, 1)`` 65 | return u1, u2, u3 66 | } 67 | <| tester 68 | let actual = Random.get (Random.zip3 ``[0, 1)`` ``[0, 1)`` ``[0, 1)``) tester 69 | actual |> should equal expected 70 | 71 | [] 72 | let ``Validates merge`` () = 73 | let tester = Utility.defaultState 74 | let expected = 75 | Random.get 76 | <| random { 77 | let! u1 = ``[0, 1)`` 78 | let! u2 = ``[0, 1)`` 79 | let! u3 = ``[0, 1)`` 80 | return [u1; u2; u3] 81 | } 82 | <| tester 83 | let actual = Random.get (Random.merge <| List.init 3 (fun _ -> ``[0, 1)``)) tester 84 | actual |> should equal expected 85 | 86 | [] 87 | let ``Validates mergeWith`` () = 88 | let tester = Utility.defaultState 89 | let f = List.reduce (+) 90 | let expected = 91 | Random.get 92 | <| random { 93 | let! u1 = ``[0, 1)`` 94 | let! u2 = ``[0, 1)`` 95 | let! u3 = ``[0, 1)`` 96 | return f [u1; u2; u3] 97 | } 98 | <| tester 99 | let actual = Random.get (Random.mergeWith f <| List.init 3 (fun _ -> ``[0, 1)``)) tester 100 | actual |> should equal expected 101 | -------------------------------------------------------------------------------- /tests/FsRandom.Tests/RuntimeHelper.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module internal FsRandom.RuntimeHelper 3 | 4 | open System.IO 5 | open MathNet.Numerics 6 | open NUnit.Framework 7 | 8 | let curry f x y = f (x, y) 9 | let uncurry f (x, y) = f x y 10 | 11 | // Kolmogorov-Smirnov test 12 | let ksTest distribution samples = 13 | let samples = Array.sort samples 14 | let n = Array.length samples 15 | let f = distribution 16 | let Dn = 17 | samples 18 | |> Array.mapi (fun index x -> abs (f x - float (index + 1) / float n)) 19 | |> Array.max 20 | let k = 1.628 // K-S distribution critical value (99%) 21 | Dn < k 22 | 23 | // Chi-square goodness of fit test 24 | let chisqTest distribution samples = 25 | let n = Array.length samples 26 | let f = distribution 27 | let samples = Seq.countBy id samples |> Seq.toArray |> Array.sortBy fst 28 | let chi2 = 29 | let sumP = samples |> Array.sumBy (fst >> f) 30 | let residual = 1.0 - sumP 31 | let sampled = 32 | samples 33 | |> Array.map (fun (k, obs) -> 34 | let npi = float n * f k 35 | pown (float obs - npi) 2 / npi 36 | ) 37 | |> Array.sum 38 | if residual > 1.0e-6 then 39 | sampled + float n * residual 40 | else 41 | sampled 42 | chi2 < SpecialFunctions.GammaLowerRegularizedInv (float (n - 1) / 2.0, 0.99) 43 | 44 | module KnownRandomSequence = 45 | let private getResourceString fileName = 46 | let path = Path.Combine(TestContext.CurrentContext.TestDirectory, "Resources", fileName) 47 | File.ReadAllText(path) 48 | 49 | let SFMT_11213_out = getResourceString "SFMT.11213.out.txt" 50 | let SFMT_1279_out = getResourceString "SFMT.1279.out.txt" 51 | let SFMT_132049_out = getResourceString "SFMT.132049.out.txt" 52 | let SFMT_19937_out = getResourceString "SFMT.19937.out.txt" 53 | let SFMT_216091_out = getResourceString "SFMT.216091.out.txt" 54 | let SFMT_2281_out = getResourceString "SFMT.2281.out.txt" 55 | let SFMT_4253_out = getResourceString "SFMT.4253.out.txt" 56 | let SFMT_44497_out = getResourceString "SFMT.44497.out.txt" 57 | let SFMT_607_out = getResourceString "SFMT.607.out.txt" 58 | let SFMT_86243_out = getResourceString "SFMT.86243.out.txt" 59 | let mt19937_64_out = getResourceString "mt19937-64.out.txt" 60 | let mt19937ar_out = getResourceString "mt19937ar.out.txt" 61 | -------------------------------------------------------------------------------- /tests/FsRandom.Tests/SimdOrientedFastMersenneTwisterTest.fs: -------------------------------------------------------------------------------- 1 | module FsRandom.SimdOrientedFastMersenneTwisterTest 2 | 3 | open System 4 | open System.IO 5 | open FsRandom.SimdOrientedFastMersenneTwister 6 | open NUnit.Framework 7 | 8 | let test parameter resource = 9 | let expected = 10 | use reader = new StringReader (resource) 11 | reader.ReadLine () |> ignore 12 | reader.ReadLine () |> ignore 13 | reader.ReadLine () |> ignore 14 | let raws = Array.zeroCreate 1000 15 | for i = 0 to 199 do 16 | let line = reader.ReadLine () 17 | let values = line.Split ([|' '|], StringSplitOptions.RemoveEmptyEntries) 18 | for j = 0 to Array.length values - 1 do 19 | raws.[5 * i + j] <- values.[j] 20 | reader.ReadLine () |> ignore 21 | reader.ReadLine () |> ignore 22 | let standards = Array.zeroCreate 1000 23 | for i = 0 to 199 do 24 | let line = reader.ReadLine () 25 | let values = line.Split ([|' '|], StringSplitOptions.RemoveEmptyEntries) 26 | for j = 0 to Array.length values - 1 do 27 | standards.[5 * i + j] <- values.[j] 28 | raws, standards 29 | let actual = 30 | let seedByInt = StateVector.Initialize (parameter, 1234u) 31 | let randomByInt = 32 | Random.get 33 | <| random { 34 | let raws = Array.zeroCreate 1000 35 | for index = 0 to Array.length raws / 2 - 1 do 36 | let! u = rawBits 37 | raws.[2 * index] <- (sprintf "%10u" (uint32 (u &&& 0xFFFFFFFFFFFFFFFFuL))).Trim () 38 | raws.[2 * index + 1] <- (sprintf "%10u" (uint32 ((u >>> 32) &&& 0xFFFFFFFFFFFFFFFFuL))).Trim () 39 | return raws 40 | } 41 | <| createState sfmt seedByInt 42 | let seedByArray = StateVector.Initialize (parameter, [|0x1234u; 0x5678u; 0x9ABCu; 0xDEF0u|]) 43 | let randomByArray = 44 | Random.get 45 | <| random { 46 | let raws = Array.zeroCreate 1000 47 | for index = 0 to Array.length raws / 2 - 1 do 48 | let! u = rawBits 49 | raws.[2 * index] <- (sprintf "%10u" (uint32 (u &&& 0xFFFFFFFFFFFFFFFFuL))).Trim () 50 | raws.[2 * index + 1] <- (sprintf "%10u" (uint32 ((u >>> 32) &&& 0xFFFFFFFFFFFFFFFFuL))).Trim () 51 | return raws 52 | } 53 | <| createState sfmt seedByArray 54 | randomByInt, randomByArray 55 | Assert.That (fst actual, Is.EquivalentTo(fst expected)) 56 | Assert.That (snd actual, Is.EquivalentTo(snd expected)) 57 | 58 | [] 59 | let ``Checks params 607 output`` () = 60 | test SfmtParams.Params607 KnownRandomSequence.SFMT_607_out 61 | 62 | [] 63 | let ``Checks params 1279 output`` () = 64 | test SfmtParams.Params1279 KnownRandomSequence.SFMT_1279_out 65 | 66 | [] 67 | let ``Checks params 2281 output`` () = 68 | test SfmtParams.Params2281 KnownRandomSequence.SFMT_2281_out 69 | 70 | [] 71 | let ``Checks params 4253 output`` () = 72 | test SfmtParams.Params4253 KnownRandomSequence.SFMT_4253_out 73 | 74 | [] 75 | let ``Checks params 11213 output`` () = 76 | test SfmtParams.Params11213 KnownRandomSequence.SFMT_11213_out 77 | 78 | [] 79 | let ``Checks params 19937 output`` () = 80 | test SfmtParams.Params19937 KnownRandomSequence.SFMT_19937_out 81 | 82 | [] 83 | let ``Checks params 44497 output`` () = 84 | test SfmtParams.Params44497 KnownRandomSequence.SFMT_44497_out 85 | 86 | [] 87 | let ``Checks params 86243 output`` () = 88 | test SfmtParams.Params86243 KnownRandomSequence.SFMT_86243_out 89 | 90 | [] 91 | let ``Checks params 132049 output`` () = 92 | test SfmtParams.Params132049 KnownRandomSequence.SFMT_132049_out 93 | 94 | [] 95 | let ``Checks params 216091 output`` () = 96 | test SfmtParams.Params216091 KnownRandomSequence.SFMT_216091_out 97 | -------------------------------------------------------------------------------- /tests/FsRandom.Tests/StatisticsTest.fs: -------------------------------------------------------------------------------- 1 | module FsRandom.StatisticsTest 2 | 3 | open FsRandom.Statistics 4 | open FsUnit 5 | open MathNet.Numerics.Distributions 6 | open NUnit.Framework 7 | 8 | let n = 5000 9 | let getSamples g = Seq.ofRandom g Utility.defaultState |> Seq.take n |> Seq.toArray 10 | 11 | [] 12 | let tearDown () = 13 | () 14 | 15 | [] 16 | let ``Validates uniform`` () = 17 | let distribution = ContinuousUniform (-10.0, 10.0) 18 | getSamples (uniform (-10.0, 10.0)) 19 | |> ksTest distribution.CumulativeDistribution 20 | |> should be True 21 | 22 | [] 23 | let ``Validates loguniform`` () = 24 | let cdf (a, b) x = 1.0 / (log b - log a) * log x 25 | getSamples (loguniform (1.0, 100.0)) 26 | |> ksTest (cdf (1.0, 100.0)) 27 | |> should be True 28 | 29 | [] 30 | let ``Validates triangular`` () = 31 | let distribution = Triangular (-3.3, 10.7, 2.1) 32 | getSamples (triangular (-3.3, 10.7, 2.1)) 33 | |> ksTest distribution.CumulativeDistribution 34 | |> should be True 35 | 36 | [] 37 | let ``Validates normal`` () = 38 | let distribution = Normal (-5.0, 3.0) 39 | getSamples (normal (-5.0, 3.0)) 40 | |> ksTest distribution.CumulativeDistribution 41 | |> should be True 42 | 43 | [] 44 | let ``Validates lognormal`` () = 45 | let distribution = LogNormal (3.1, 7.2) 46 | getSamples (lognormal (3.1, 7.2)) 47 | |> ksTest distribution.CumulativeDistribution 48 | |> should be True 49 | 50 | [] 51 | let ``Validates gamma (shape < 1)`` () = 52 | let distribution = Gamma (0.3, 1.0 / 2.0) 53 | getSamples (gamma (0.3, 2.0)) 54 | |> ksTest distribution.CumulativeDistribution 55 | |> should be True 56 | 57 | [] 58 | let ``Validates gamma (shape > 1)`` () = 59 | let distribution = Gamma (5.6, 1.0 / 0.4) 60 | getSamples (gamma (5.6, 0.4)) 61 | |> ksTest distribution.CumulativeDistribution 62 | |> should be True 63 | 64 | [] 65 | let ``Validates gamma (shape is integer)`` () = 66 | let distribution = Gamma (3.0, 1.0 / 7.9) 67 | getSamples (gamma (3.0, 7.9)) 68 | |> ksTest distribution.CumulativeDistribution 69 | |> should be True 70 | 71 | [] 72 | let ``Validates exponential`` () = 73 | let distribution = Exponential (1.5) 74 | getSamples (exponential (1.5)) 75 | |> ksTest distribution.CumulativeDistribution 76 | |> should be True 77 | 78 | [] 79 | let ``Validates weibull`` () = 80 | let distribution = Weibull (6.1, 1.4) 81 | getSamples (weibull (6.1, 1.4)) 82 | |> ksTest distribution.CumulativeDistribution 83 | |> should be True 84 | 85 | [] 86 | let ``Validates gumbel`` () = 87 | let cdf (mu, beta) x = exp <| -exp (-(x - mu) / beta) 88 | getSamples (gumbel (6.1, 1.4)) 89 | |> ksTest (cdf (6.1, 1.4)) 90 | |> should be True 91 | 92 | [] 93 | let ``Validates beta`` () = 94 | let distribution = Beta (1.5, 0.4) 95 | getSamples (beta (1.5, 0.4)) 96 | |> ksTest distribution.CumulativeDistribution 97 | |> should be True 98 | 99 | [] 100 | let ``Validates cauchy`` () = 101 | let distribution = Cauchy (-1.5, 0.1) 102 | getSamples (cauchy (-1.5, 0.1)) 103 | |> ksTest distribution.CumulativeDistribution 104 | |> should be True 105 | 106 | [] 107 | let ``Validates chisquare`` () = 108 | let distribution = ChiSquared (10.0) 109 | getSamples (chisquare (10)) 110 | |> ksTest distribution.CumulativeDistribution 111 | |> should be True 112 | 113 | [] 114 | let ``Validates studentT`` () = 115 | let distribution = StudentT (0.0, 1.0, 3.0) 116 | getSamples (studentT (3)) 117 | |> ksTest distribution.CumulativeDistribution 118 | |> should be True 119 | 120 | // CDF is unknown 121 | //[] 122 | //let ``Validates vonMises`` () = 123 | // () 124 | 125 | [] 126 | let ``Validates uniformDiscrete`` () = 127 | let distribution = DiscreteUniform (-10, 10) 128 | getSamples (uniformDiscrete (-10, 10)) 129 | |> chisqTest distribution.Probability 130 | |> should be True 131 | 132 | [] 133 | let ``Validates poisson`` () = 134 | let distribution = Poisson (5.2) 135 | getSamples (poisson (5.2)) 136 | |> chisqTest distribution.Probability 137 | |> should be True 138 | 139 | [] 140 | let ``Validates geometric0`` () = 141 | let distribution = Geometric (0.2) 142 | getSamples (geometric0 (0.2)) 143 | |> chisqTest (fun x -> distribution.Probability (x + 1)) // Math.NET Numerics' Geometric supports [1, 2, ...] 144 | |> should be True 145 | 146 | [] 147 | let ``Validates geometric1`` () = 148 | let distribution = Geometric (0.7) 149 | getSamples (geometric1 (0.7)) 150 | |> chisqTest distribution.Probability 151 | |> should be True 152 | 153 | [] 154 | let ``Validates bernoulli`` () = 155 | let distribution = Bernoulli (0.4) 156 | getSamples (bernoulli (0.4)) 157 | |> chisqTest distribution.Probability 158 | |> should be True 159 | 160 | [] 161 | let ``Validates binomial`` () = 162 | let distribution = Binomial (0.3, 20) 163 | getSamples (binomial (20, 0.3)) 164 | |> chisqTest distribution.Probability 165 | |> should be True 166 | 167 | [] 168 | let ``Validates negativeBinomial`` () = 169 | let distribution = NegativeBinomial (15.5, 0.8) 170 | getSamples (negativeBinomial (15.5, 0.8)) 171 | |> chisqTest distribution.Probability 172 | |> should be True 173 | 174 | // TODO: implement 175 | //[] 176 | //let ``Validates dirichlet`` () = 177 | // testDirichlet Utility.defaultState [1.0; 2.0; 2.5; 0.5] 178 | // 179 | // TODO: implement 180 | //[] 181 | //let ``Validates multinomial`` () = 182 | // testMultinomial Utility.defaultState [1.0; 2.0; 2.5; 0.5] 183 | 184 | [] 185 | let ``wishart returns positive and positive semidefinite matrices`` () = 186 | let sigma = Array2D.init 3 3 (fun i j -> 187 | match i, j with 188 | | 0, 0 -> 1.0 189 | | 1, 1 -> 1.0 190 | | 2, 2 -> 4.0 191 | | 0, 1 | 1, 0 -> 0.7 192 | | 0, 2 | 2, 0 -> -1.0 193 | | 1, 2 | 2, 1 -> 0.0 194 | | _ -> failwith "never" 195 | ) 196 | let samples = 197 | Seq.ofRandom (wishart (4, sigma)) Utility.defaultState 198 | |> Seq.take 1000 199 | |> Seq.toList 200 | samples 201 | |> List.forall (fun m -> Seq.forall2 (fun i j -> m.[i, j] > 0.0) [0..2] [0..2]) 202 | |> should be True 203 | samples 204 | |> List.map (Matrix.jacobi >> fst) 205 | |> List.forall (Array.forall (fun x -> x >= 0.0)) 206 | |> should be True 207 | 208 | [] 209 | let ``Validates mix (float)`` () = 210 | let cdf x = 211 | let gamma = Gamma (3.0, 1.0 / 2.0) 212 | let normal = Normal (-2.0, 1.0) 213 | if x <= 0.0 then 214 | 0.75 * normal.CumulativeDistribution (x) 215 | else 216 | 0.25 * gamma.CumulativeDistribution (x) + 0.75 * normal.CumulativeDistribution (x) 217 | getSamples (mix [gamma (3.0, 2.0), 1.0; normal (-2.0, 1.0), 3.0]) 218 | |> ksTest cdf 219 | |> should be True 220 | 221 | [] 222 | let ``uniformDiscrete on full-range of int generates both positive and negative values`` () = 223 | let g = uniformDiscrete (System.Int32.MinValue, System.Int32.MaxValue) 224 | let values = Random.get <| List.randomCreate 10 g <| Utility.defaultState 225 | List.exists (fun x -> x > 0) values |> should be True 226 | List.exists (fun x -> x < 0) values |> should be True 227 | -------------------------------------------------------------------------------- /tests/FsRandom.Tests/StringTest.fs: -------------------------------------------------------------------------------- 1 | module FsRandom.StringTest 2 | 3 | open System 4 | open FsUnit 5 | open NUnit.Framework 6 | 7 | [] 8 | let ``Validates randomByString`` () = 9 | let tester = Utility.defaultState 10 | let actual = Random.get (String.randomByString "FsRandom" 1000) tester 11 | String.length actual |> should equal 1000 12 | let actual = List.ofSeq actual 13 | actual |> should contain 'F' 14 | actual |> should contain 's' 15 | actual |> should contain 'R' 16 | actual |> should contain 'a' 17 | actual |> should contain 'n' 18 | actual |> should contain 'd' 19 | actual |> should contain 'o' 20 | actual |> should contain 'm' 21 | actual |> should not' (contain 'r') 22 | actual |> should not' (contain '#') 23 | 24 | [] 25 | let ``randomByString _ 0 generates empty string`` () = 26 | let tester = Utility.defaultState 27 | let actual = Random.get (String.randomByString "" 0) tester 28 | actual |> should be EmptyString 29 | 30 | [] 31 | let ``Validates randomAscii`` () = 32 | let tester = Utility.defaultState 33 | let actual = Random.get (String.randomAscii 1000) tester 34 | String.length actual |> should equal 1000 35 | actual |> String.forall (fun c -> int c < 128) |> should be True 36 | actual |> String.exists (fun c -> Char.IsControl (c)) |> should be False 37 | actual |> String.exists (fun c -> Char.IsWhiteSpace (c)) |> should be False 38 | 39 | [] 40 | let ``Validates randomNumeric`` () = 41 | let tester = Utility.defaultState 42 | let actual = Random.get (String.randomNumeric 1000) tester 43 | String.length actual |> should equal 1000 44 | actual 45 | |> String.forall (fun c -> '0' <= c && c <= '9') 46 | |> should be True 47 | 48 | [] 49 | let ``Validates randomAlphabet`` () = 50 | let tester = Utility.defaultState 51 | let actual = Random.get (String.randomAlphabet 1000) tester 52 | String.length actual |> should equal 1000 53 | actual 54 | |> String.forall (fun c -> ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) 55 | |> should be True 56 | 57 | [] 58 | let ``Validates randomAlphanumeric`` () = 59 | let tester = Utility.defaultState 60 | let actual = Random.get (String.randomAlphanumeric 1000) tester 61 | String.length actual |> should equal 1000 62 | actual 63 | |> String.forall (fun c -> '0' <= c && c <= '9' || ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) 64 | |> should be True 65 | 66 | [] 67 | let ``Validates randomConcat`` () = 68 | let tester = Utility.defaultState 69 | let suffix = ".png" 70 | let generators = [String.randomByString "AB" 3; String.randomNumeric 5; Random.singleton suffix] 71 | let actual = Random.get (String.randomConcat "x" generators) tester 72 | String.length actual |> should equal (3 + 5 + String.length suffix + 2) 73 | actual.[3] |> should equal 'x' 74 | actual.[9] |> should equal 'x' 75 | actual 76 | |> fun s -> s.Substring (0, 3) 77 | |> String.forall (fun c -> c = 'A' || c = 'B') 78 | |> should be True 79 | actual 80 | |> fun s -> s.Substring (4, 5) 81 | |> String.forall (fun c -> '0' <= c && c <= '9') 82 | |> should be True 83 | actual 84 | |> fun s -> s.Substring (10) 85 | |> should equal ".png" 86 | -------------------------------------------------------------------------------- /tests/FsRandom.Tests/UtilityTest.fs: -------------------------------------------------------------------------------- 1 | module FsRandom.UtilityTest 2 | 3 | open FsRandom.Utility 4 | open FsUnit 5 | open NUnit.Framework 6 | 7 | [] 8 | let ``Validates randomSign (int)`` () = 9 | let g = Random.map (fun sign -> sign * 1) (randomSign ()) 10 | g.GetType () |> should equal typeof> 11 | 12 | [] 13 | let ``Validates randomSign (float)`` () = 14 | let g = Random.map (fun sign -> sign * 1.0) (randomSign ()) 15 | g.GetType () |> should equal typeof> 16 | 17 | [] 18 | let ``Validates choose`` () = 19 | let n = 4 20 | let tester = Utility.defaultState 21 | let result = Random.get (Utility.choose 10 n) tester 22 | Assert.That (List.length result, Is.EqualTo(n)) 23 | Assert.That (List.forall (fun x -> List.exists ((=) x) [0..9]) result, Is.True) 24 | Assert.That (Seq.length (Seq.distinct result), Is.EqualTo(n)) 25 | 26 | [] 27 | let ``Validates chooseOne`` () = 28 | let tester = Utility.defaultState 29 | let result = Random.get (Utility.chooseOne 10) tester 30 | Assert.That (0 <= result && result < 10, Is.True) 31 | -------------------------------------------------------------------------------- /tests/FsRandom.Tests/paket.references: -------------------------------------------------------------------------------- 1 | MathNet.Numerics 2 | FSharp.Core 3 | NUnit 4 | NUnit3TestAdapter 5 | FsUnit 6 | Microsoft.NET.Test.Sdk -------------------------------------------------------------------------------- /tools/NuGetResolver.fsx: -------------------------------------------------------------------------------- 1 | #load "XmlEditor.fs" 2 | #r "System.Xml.Linq.dll" 3 | 4 | open XmlEditor 5 | open System.Xml.Linq 6 | 7 | let nugetXmlns = "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd" 8 | let editProject rawProjectXml = 9 | let project = XDocument.Parse rawProjectXml 10 | query { 11 | for file in project.Descendants (XName.Get ("file", nugetXmlns)) do 12 | for attribute in file.Attributes () do 13 | select attribute 14 | } 15 | |> Seq.iter (fun attribute -> attribute.Value <- attribute.Value.Replace ('\\', '/')) 16 | project 17 | 18 | main editProject 19 | -------------------------------------------------------------------------------- /tools/XBuildResolver.fsx: -------------------------------------------------------------------------------- 1 | #load "XmlEditor.fs" 2 | #r "System.Xml.Linq.dll" 3 | 4 | open XmlEditor 5 | open System.Xml.Linq 6 | 7 | let editProject rawProjectXml = 8 | let project = XDocument.Parse rawProjectXml 9 | query { 10 | for import in project.Descendants (XName.Get ("Import", "http://schemas.microsoft.com/developer/msbuild/2003")) do 11 | let project = import.Attribute (XName.Get ("Project")) 12 | where (project.Value.EndsWith ("Microsoft.FSharp.Targets")) 13 | select (import.Attribute (XName.Get ("Condition"))) 14 | } 15 | |> Seq.iter (fun attribute -> attribute.Remove ()) 16 | project 17 | 18 | main editProject 19 | -------------------------------------------------------------------------------- /tools/XmlEditor.fs: -------------------------------------------------------------------------------- 1 | // Modification of https://gist.github.com/3564010 2 | 3 | module XmlEditor 4 | 5 | open System 6 | open System.IO 7 | open Microsoft.FSharp.Core.LanguagePrimitives 8 | 9 | type Trial<'a, 'b> = 10 | | Success of 'a 11 | | Failure of 'b 12 | 13 | type Error = 14 | | None = 0 15 | | IO = 1 16 | | InvalidArgument = 2 17 | | InvalidInput = 3 18 | 19 | let exit = EnumToValue >> Environment.Exit 20 | 21 | let doEdit edit = 22 | function 23 | | Success input -> 24 | try 25 | Success (edit input) 26 | with 27 | | ex -> Failure (ex, Error.InvalidInput) 28 | | Failure (ex, error) -> 29 | Failure (ex, error) 30 | 31 | let getUserInput () = 32 | match Environment.GetCommandLineArgs () with 33 | | [| _; _ |] -> 34 | try 35 | Success <| Console.In.ReadToEnd () 36 | with 37 | | ex -> Failure (ex, Error.IO) 38 | | [| _; _; path |] -> 39 | try 40 | Success <| File.ReadAllText (path) 41 | with 42 | | ex -> Failure (ex, Error.IO) 43 | | args -> 44 | let filename = Path.GetFileName (args.[0]) 45 | let message = sprintf "Usage: Fsi.exe %s [project.fsproj]" filename 46 | let ex = ApplicationException (message) :> exn 47 | Failure (ex, Error.InvalidArgument) 48 | 49 | let main edit = 50 | getUserInput () 51 | |> doEdit edit 52 | |> function 53 | | Success result -> 54 | printfn "%A" result 55 | Error.None 56 | | Failure (ex, error) -> 57 | eprintfn "%s" ex.Message 58 | error 59 | |> exit 60 | -------------------------------------------------------------------------------- /tools/build.fsx: -------------------------------------------------------------------------------- 1 | #I @"../packages/build/FSharp.Formatting/lib/net40" 2 | #I @"../packages/build/FSharp.Compiler.Service/lib/net40" 3 | #I @"../packages/build/FSharpVSPowerTools.Core/lib/net45" 4 | #r @"../packages/build/FAKE/tools/FakeLib.dll" 5 | #r "FSharp.Literate.dll" 6 | #r "FSharp.CodeFormat.dll" 7 | #r "FSharp.MetadataFormat.dll" 8 | #r "FSharp.Markdown.dll" 9 | #r "RazorEngine.dll" 10 | 11 | open System 12 | open System.IO 13 | open System.Reflection 14 | open Fake 15 | open Fake.FileHelper 16 | open FSharp.Literate 17 | open FSharp.MetadataFormat 18 | 19 | let baseDir = Path.GetDirectoryName (__SOURCE_DIRECTORY__) 20 | let inline (~%) name = Path.Combine (baseDir, name) 21 | let inline (%) dir name = Path.Combine (dir, name) 22 | 23 | let nugetToolPath = % @"packages/build/NuGet.CommandLine/tools/NuGet.exe" 24 | let buildDir = % "Build" 25 | let deployDir = % "Deploy" 26 | let libDir = buildDir % "lib" 27 | let docsDir = buildDir % "docs" 28 | 29 | let mainSolution = % "src" % "FsRandom" % "FsRandom.fsproj" 30 | let targetFrameworks = ["net45"; "netstandard1.6"] 31 | let projectName = "FsRandom" 32 | let zipName = deployDir % "FsRandom.zip" 33 | 34 | type BuildParameter = { 35 | Help : bool 36 | Documentation : bool 37 | DocumentationRoot : string 38 | Debug : bool 39 | Deploy : bool 40 | CleanDeploy : bool 41 | NoZip : bool 42 | NoNuGet : bool 43 | Key : string option 44 | } 45 | let buildParams = 46 | let isOption (s:string) = s.StartsWith ("-") 47 | let rec loop acc = function 48 | | [] -> acc 49 | | "-h" :: _ | "--help" :: _ -> { acc with Help = true } // don't care other arguments 50 | | "--docs" :: args -> loop { acc with Documentation = true } args 51 | | "--docs-root" :: path :: args -> loop { acc with DocumentationRoot = path } args 52 | | "--debug" :: args -> loop { acc with Debug = true } args 53 | | "--deploy" :: args -> loop { acc with Deploy = true } args 54 | | "--clean-deploy" :: args -> loop { acc with CleanDeploy = true } args 55 | | "--no-zip" :: args -> loop { acc with NoZip = true } args 56 | | "--no-nuget" :: args -> loop { acc with NoNuGet = true } args 57 | | "--key" :: path :: args -> loop { acc with Key = Some (path) } args 58 | | arg :: _ -> 59 | eprintfn "Unknown parameter: %s" arg 60 | exit 1 61 | let defaultBuildParam = { 62 | Help = false 63 | Documentation = false 64 | DocumentationRoot = "." 65 | Debug = false 66 | Deploy = false 67 | CleanDeploy = false 68 | NoZip = false 69 | NoNuGet = false 70 | Key = None 71 | } 72 | // https://github.com/fsharp/FAKE/issues/1477 73 | let args = fsi.CommandLineArgs |> Array.toList 74 | // let args = Environment.GetCommandLineArgs() |> Array.skip 1 |> Array.toList // args = ["build.fsx"; ...] 75 | loop defaultBuildParam args.Tail 76 | 77 | if buildParams.Help then 78 | printfn """FsRandom Build Script 79 | 80 | #Usage 81 | 82 | fsi.exe build.fsx [] 83 | 84 | # Options 85 | -h | --help Show this help 86 | --docs Build documentation files 87 | --docs-root Specify the root uri of the documentation 88 | Default: . 89 | --debug Debug build 90 | --deploy Create a zip archive and a NuGet package 91 | See --no-zip and --no-nuget 92 | --clean-deploy Clean up deploy directory before build 93 | --no-zip Do not create zip archive 94 | --no-nuget Do not build NuGet package 95 | --key Sign assembly with the specified key""" 96 | exit 0 97 | 98 | let addBuildProperties = 99 | let debugSymbol properties = 100 | match buildParams.Debug with 101 | | true -> ("DebugSymbols", "true") :: ("DebugType", "full") :: properties 102 | | false -> ("DebugSymbols", "false") :: ("DebugType", "pdbonly") :: properties 103 | let setKey properties = 104 | match buildParams.Key with 105 | | Some (path) when File.Exists (path) -> ("SignAssembly", "true") :: ("AssemblyOriginatorKeyFile", path) :: properties 106 | | Some (path) -> failwithf "Key file not found at %s" path 107 | | None -> properties 108 | debugSymbol >> setKey 109 | let configuration = "Configuration", if buildParams.Debug then "Debug" else "Release" 110 | 111 | Target "Clean" DoNothing 112 | 113 | Target "Build" (fun () -> 114 | targetFrameworks 115 | |> Seq.iter (fun framework -> 116 | DotNetCli.Build (fun p -> 117 | { p with 118 | Project = mainSolution 119 | Configuration = snd configuration 120 | Framework = framework 121 | Output = libDir % framework }) 122 | ) 123 | ) 124 | Target "EnsureDeploy" (fun () -> 125 | ensureDirectory deployDir 126 | ) 127 | 128 | let getMainAssemblyVersion assemblyPath = 129 | let assembly = Assembly.LoadFrom (assemblyPath) 130 | let infoVersion = assembly.GetCustomAttributes (typeof, false) 131 | (infoVersion.[0] :?> AssemblyInformationalVersionAttribute).InformationalVersion 132 | let updateNuGetParams version (p:NuGetParams) = { 133 | p with 134 | NoPackageAnalysis = false 135 | OutputPath = deployDir 136 | ToolPath = nugetToolPath 137 | WorkingDir = baseDir 138 | Version = version 139 | } 140 | let pack projectName = 141 | let assemblyName = sprintf "%s.dll" projectName 142 | let assemblyPath = libDir % "net45" % assemblyName 143 | let version = getMainAssemblyVersion assemblyPath 144 | let nuspecPath = % (sprintf "%s.nuspec" projectName) 145 | NuGetPack (updateNuGetParams version) nuspecPath 146 | Target "NuGet" (fun () -> 147 | ensureDirectory deployDir 148 | pack projectName 149 | ) 150 | 151 | Target "Documentation" (fun () -> 152 | let info = [ 153 | "project-name", "FsRandom" 154 | "project-author", "RecycleBin" 155 | "project-summary", "Purely functional random number generating framework designed for F#" 156 | "project-github", "https://github.com/fsprojects/FsRandom" 157 | "project-nuget", "https://nuget.org/packages/FsRandom" 158 | ] 159 | 160 | // Paths with template/source/output locations 161 | let content = % "docs" 162 | let templates = content % "templates" 163 | let formatting = % "packages/build/FSharp.Formatting" 164 | let docTemplate = formatting % "templates" % "docpage.cshtml" 165 | 166 | // Where to look for *.cshtml templates (in this order) 167 | let layoutRoots = [ templates; formatting % "templates"; formatting % "templates" % "reference" ] 168 | 169 | // Copy static files and CSS + JS from F# Formatting 170 | let copyFiles () = 171 | ensureDirectory (docsDir % "images") 172 | CopyRecursive (content % "images") (docsDir % "images") true 173 | |> Log "Copying images: " 174 | ensureDirectory (docsDir % "content") 175 | CopyRecursive (formatting % "styles") (docsDir % "content") true 176 | |> Log "Copying styles and scripts: " 177 | 178 | let fsi = FsiEvaluator () 179 | 180 | // Build documentation from `*.fsx` files in `docs` 181 | let buildDocumentation () = 182 | let fsx = Directory.EnumerateDirectories (content, "*.fsx", SearchOption.AllDirectories) 183 | let md = Directory.EnumerateDirectories (content, "*.md", SearchOption.AllDirectories) 184 | for dir in Seq.distinct <| Seq.concat [Seq.singleton content; fsx; md] do 185 | let sub = if dir.Length > content.Length then dir.Substring(content.Length + 1) else "." 186 | Literate.ProcessDirectory 187 | ( dir, docTemplate, docsDir % sub, replacements = ("root", buildParams.DocumentationRoot)::info, 188 | layoutRoots = layoutRoots, fsiEvaluator = fsi ) 189 | 190 | let buildReference () = 191 | let referenceDir = docsDir % "reference" 192 | ensureDirectory referenceDir 193 | for lib in Directory.GetFiles (buildDir, "*.dll") do 194 | MetadataFormat.Generate 195 | ( lib, referenceDir, layoutRoots, parameters = ("root", buildParams.DocumentationRoot)::info ) 196 | 197 | // Generate 198 | copyFiles() 199 | buildDocumentation() 200 | buildReference () 201 | ) 202 | 203 | Target "Zip" (fun () -> 204 | let files = 205 | if buildParams.Documentation && buildParams.DocumentationRoot = "." then 206 | !! (libDir % "**") ++ (docsDir % "**") 207 | else 208 | !! (libDir % "**") 209 | files 210 | |> Zip buildDir zipName 211 | ) 212 | 213 | Target "Deploy" (fun () -> 214 | !! (deployDir % "*.*") 215 | |> Log "Build-Output: " 216 | ) 217 | 218 | // Build dependency 219 | "Clean" 220 | ==> "Build" 221 | 222 | // Documentation dependency 223 | "Build" 224 | ==> "Documentation" 225 | 226 | // NuGet dependency 227 | "Build" 228 | ==> "EnsureDeploy" 229 | ==> "NuGet" 230 | 231 | // Zip dependency 232 | "Build" 233 | ==> "EnsureDeploy" 234 | ==> "Zip" 235 | 236 | // Deploy dependency 237 | "Build" 238 | =?> ("Documentation", buildParams.Documentation) 239 | =?> ("Zip", not buildParams.NoZip) 240 | =?> ("NuGet", not buildParams.NoNuGet) 241 | ==> "Deploy" 242 | 243 | let deploy = buildParams.Deploy && (not buildParams.NoZip || not buildParams.NoNuGet) 244 | Run <| if deploy then "Deploy" elif buildParams.Documentation then "Documentation" else "Build" 245 | --------------------------------------------------------------------------------