├── .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 | [](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 |
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 |
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 |
33 |
34 |
35 |
36 | @RenderBody()
37 |
38 |
39 |
65 |
66 |
67 |
68 |
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 | ///