diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..0bc6c81
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,263 @@
+# Remove the line below if you want to inherit .editorconfig settings from higher directories
+root = true
+
+# C# files
+[*.cs]
+
+#### Core EditorConfig Options ####
+
+# Indentation and spacing
+indent_size = 4
+indent_style = space
+tab_width = 4
+
+# New line preferences
+end_of_line = crlf
+insert_final_newline = false
+
+#### .NET Coding Conventions ####
+
+# Organize usings
+dotnet_separate_import_directive_groups = true
+dotnet_sort_system_directives_first = false
+file_header_template = unset
+
+# this. and Me. preferences
+dotnet_style_qualification_for_event = false
+dotnet_style_qualification_for_field = false
+dotnet_style_qualification_for_method = false
+dotnet_style_qualification_for_property = false
+
+# Language keywords vs BCL types preferences
+dotnet_style_predefined_type_for_locals_parameters_members = true
+dotnet_style_predefined_type_for_member_access = true
+
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
+
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members
+
+# Expression-level preferences
+dotnet_style_coalesce_expression = true
+dotnet_style_collection_initializer = true
+dotnet_style_explicit_tuple_names = true
+dotnet_style_namespace_match_folder = true
+dotnet_style_null_propagation = true
+dotnet_style_object_initializer = true
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+dotnet_style_prefer_auto_properties = false
+dotnet_style_prefer_collection_expression = when_types_loosely_match
+dotnet_style_prefer_compound_assignment = true
+dotnet_style_prefer_conditional_expression_over_assignment = true
+dotnet_style_prefer_conditional_expression_over_return = true
+dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
+dotnet_style_prefer_inferred_anonymous_type_member_names = true
+dotnet_style_prefer_inferred_tuple_names = true
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true
+dotnet_style_prefer_simplified_boolean_expressions = true
+dotnet_style_prefer_simplified_interpolation = true
+
+# Field preferences
+dotnet_style_readonly_field = true
+
+# Parameter preferences
+dotnet_code_quality_unused_parameters = all
+
+# Suppression preferences
+dotnet_remove_unnecessary_suppression_exclusions = none
+
+# New line preferences
+dotnet_style_allow_multiple_blank_lines_experimental = false:error
+dotnet_style_allow_statement_immediately_after_block_experimental = false:error
+
+#### C# Coding Conventions ####
+
+# var preferences
+csharp_style_var_elsewhere = false
+csharp_style_var_for_built_in_types = false
+csharp_style_var_when_type_is_apparent = false
+
+# Expression-bodied members
+csharp_style_expression_bodied_accessors = true:silent
+csharp_style_expression_bodied_constructors = when_on_single_line:silent
+csharp_style_expression_bodied_indexers = true:silent
+csharp_style_expression_bodied_lambdas = true:silent
+csharp_style_expression_bodied_local_functions = false:silent
+csharp_style_expression_bodied_methods = when_on_single_line:silent
+csharp_style_expression_bodied_operators = when_on_single_line:silent
+csharp_style_expression_bodied_properties = true:silent
+
+# Pattern matching preferences
+csharp_style_pattern_matching_over_as_with_null_check = true
+csharp_style_pattern_matching_over_is_with_cast_check = true
+csharp_style_prefer_extended_property_pattern = true
+csharp_style_prefer_not_pattern = true
+csharp_style_prefer_pattern_matching = true
+csharp_style_prefer_switch_expression = true
+
+# Null-checking preferences
+csharp_style_conditional_delegate_call = true
+
+# Modifier preferences
+csharp_prefer_static_local_function = true
+csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
+csharp_style_prefer_readonly_struct = true
+csharp_style_prefer_readonly_struct_member = true
+
+# Code-block preferences
+csharp_prefer_braces = when_multiline:silent
+csharp_prefer_simple_using_statement = true:silent
+csharp_style_namespace_declarations = file_scoped:silent
+csharp_style_prefer_method_group_conversion = false:silent
+csharp_style_prefer_primary_constructors = true:suggestion
+csharp_style_prefer_top_level_statements = true:silent
+
+# Expression-level preferences
+csharp_prefer_simple_default_expression = true
+csharp_style_deconstructed_variable_declaration = true
+csharp_style_implicit_object_creation_when_type_is_apparent = true
+csharp_style_inlined_variable_declaration = true
+csharp_style_prefer_index_operator = true
+csharp_style_prefer_local_over_anonymous_function = true
+csharp_style_prefer_null_check_over_type_check = true
+csharp_style_prefer_range_operator = true
+csharp_style_prefer_tuple_swap = true
+csharp_style_prefer_utf8_string_literals = true
+csharp_style_throw_expression = true
+csharp_style_unused_value_assignment_preference = discard_variable
+csharp_style_unused_value_expression_statement_preference = discard_variable
+
+# 'using' directive preferences
+csharp_using_directive_placement = outside_namespace:silent
+
+# New line preferences
+csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false:error
+csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:error
+csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:error
+csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:error
+csharp_style_allow_embedded_statements_on_same_line_experimental = false:error
+
+#### C# Formatting Rules ####
+
+# New line preferences
+csharp_new_line_before_catch = true
+csharp_new_line_before_else = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_open_brace = all
+csharp_new_line_between_query_expression_clauses = true
+
+# Indentation preferences
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents = true
+csharp_indent_case_contents_when_block = true
+csharp_indent_labels = one_less_than_current
+csharp_indent_switch_labels = true
+
+# Space preferences
+csharp_space_after_cast = false
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_comma = true
+csharp_space_after_dot = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_around_declaration_statements = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_name_and_open_parenthesis = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_between_square_brackets = false
+
+# Wrapping preferences
+csharp_preserve_single_line_blocks = true
+csharp_preserve_single_line_statements = false
+
+#### Naming styles ####
+
+# Naming rules
+
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
+dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
+dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
+
+dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.types_should_be_pascal_case.symbols = types
+dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
+
+dotnet_naming_rule.private_or_internal_field_should_be__fieldname.severity = suggestion
+dotnet_naming_rule.private_or_internal_field_should_be__fieldname.symbols = private_or_internal_field
+dotnet_naming_rule.private_or_internal_field_should_be__fieldname.style = _fieldname
+
+dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
+
+# Symbol specifications
+
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal
+dotnet_naming_symbols.interface.required_modifiers =
+
+dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
+dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private
+dotnet_naming_symbols.private_or_internal_field.required_modifiers =
+
+dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal
+dotnet_naming_symbols.types.required_modifiers =
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal
+dotnet_naming_symbols.non_field_members.required_modifiers =
+
+# Naming styles
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+
+dotnet_naming_style.begins_with_i.required_prefix = I
+dotnet_naming_style.begins_with_i.required_suffix =
+dotnet_naming_style.begins_with_i.word_separator =
+dotnet_naming_style.begins_with_i.capitalization = pascal_case
+
+dotnet_naming_style._fieldname.required_prefix = _
+dotnet_naming_style._fieldname.required_suffix =
+dotnet_naming_style._fieldname.word_separator =
+dotnet_naming_style._fieldname.capitalization = camel_case
+csharp_prefer_system_threading_lock = true:suggestion
+
+[*.{cs,vb}]
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
+dotnet_style_prefer_auto_properties = false:silent
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
+dotnet_style_prefer_conditional_expression_over_assignment = true:silent
+dotnet_style_prefer_conditional_expression_over_return = true:silent
+dotnet_style_explicit_tuple_names = true:suggestion
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+tab_width = 4
+indent_size = 4
+end_of_line = crlf
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
index a078412..1ff0c42 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -3,6 +3,15 @@
###############################################################################
* text=auto
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs diff=csharp
+
###############################################################################
# Set the merge driver for project and solution files
#
@@ -11,29 +20,8 @@
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
-# intervention with every merge. To do so, just comment the entries below and
-# uncomment the group further below
+# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
-
-*.sln text eol=crlf
-*.csproj text eol=crlf
-*.vbproj text eol=crlf
-*.vcxproj text eol=crlf
-*.vcproj text eol=crlf
-*.dbproj text eol=crlf
-*.fsproj text eol=crlf
-*.lsproj text eol=crlf
-*.wixproj text eol=crlf
-*.modelproj text eol=crlf
-*.sqlproj text eol=crlf
-*.wmaproj text eol=crlf
-
-*.xproj text eol=crlf
-*.props text eol=crlf
-*.filters text eol=crlf
-*.vcxitems text eol=crlf
-
-
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
@@ -47,7 +35,29 @@
#*.sqlproj merge=binary
#*.wwaproj merge=binary
-#*.xproj merge=binary
-#*.props merge=binary
-#*.filters merge=binary
-#*.vcxitems merge=binary
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg binary
+#*.png binary
+#*.gif binary
+
+###############################################################################
+# diff behavior for common document formats
+#
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the
+# entries below.
+###############################################################################
+#*.doc diff=astextplain
+#*.DOC diff=astextplain
+#*.docx diff=astextplain
+#*.DOCX diff=astextplain
+#*.dot diff=astextplain
+#*.DOT diff=astextplain
+#*.pdf diff=astextplain
+#*.PDF diff=astextplain
+#*.rtf diff=astextplain
+#*.RTF diff=astextplain
diff --git a/.gitignore b/.gitignore
index c3f28f0..de3f6b2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
+*.rsuser
*.suo
*.user
*.userosscache
@@ -11,8 +12,9 @@
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
-#Exclude bak
-**/*.bak
+
+# Mono auto generated files
+mono_crash.*
# Build results
[Dd]ebug/
@@ -21,43 +23,63 @@
[Rr]eleases/
x64/
x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
+[Oo]ut/
[Ll]og/
+[Ll]ogs/
-# Visual Studio 2015 cache/options directory
+# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
-# NUNIT
+# NUnit
*.VisualState.xml
TestResult.xml
+nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
-**/Properties/launchSettings.json
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
*_i.c
*_p.c
-*_i.h
+*_h.h
*.ilk
*.meta
*.obj
+*.iobj
*.pch
*.pdb
+*.ipdb
*.pgc
*.pgd
*.rsp
@@ -67,6 +89,7 @@ artifacts/
*.tlh
*.tmp
*.tmp_proj
+*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
@@ -95,6 +118,9 @@ ipch/
*.vspx
*.sap
+# Visual Studio Trace Files
+*.e2e
+
# TFS 2012 Local Workspace
$tf/
@@ -106,15 +132,21 @@ _ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
-# JustCode is a .NET coding add-in
-.JustCode
-
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*.json
+coverage*.xml
+coverage*.info
+
# Visual Studio code coverage results
*.coverage
*.coveragexml
@@ -150,7 +182,7 @@ publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
-# TODO: Comment the next line if you want to checkin your web deploy settings
+# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
@@ -162,12 +194,14 @@ PublishScripts/
# NuGet Packages
*.nupkg
+# NuGet Symbol Packages
+*.snupkg
# The packages folder can be ignored because of Package Restore
-**/packages/*
+**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
-!**/packages/build/
+!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
-#!**/packages/repositories.config
+#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
@@ -185,12 +219,15 @@ AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
-!*.[Cc]ache/
+!?*.[Cc]ache/
# Others
ClientBin/
@@ -199,10 +236,14 @@ ClientBin/
*.dbmdl
*.dbproj.schemaview
*.jfm
-# *.pfx
+*.pfx
*.publishsettings
orleans.codegen.cs
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
@@ -217,6 +258,8 @@ _UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
# SQL Server files
*.mdf
@@ -227,6 +270,10 @@ UpgradeLog*.htm
*.rdl.data
*.bim.layout
*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
@@ -238,9 +285,6 @@ FakesAssemblies/
.ntvs_analysis.dat
node_modules/
-# Typescript v1 declaration files
-typings/
-
# Visual Studio 6 build log
*.plg
@@ -265,12 +309,8 @@ paket-files/
# FAKE - F# Make
.fake/
-# JetBrains Rider
-.idea/
-*.sln.iml
-
-# CodeRush
-.cr/
+# CodeRush personal settings
+.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
@@ -280,6 +320,9 @@ __pycache__/
# tools/**
# !tools/packages.config
+# Tabs Studio
+*.tss
+
# Telerik's JustMock configuration file
*.jmconfig
@@ -289,4 +332,35 @@ __pycache__/
*.odx.cs
*.xsd.cs
-Src/SgkMobileApp/\.mfractor/
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
+
+# Fody - auto-generated XML schema
+FodyWeavers.xsd
+
+# Msi Files
+*.msi
diff --git a/.vs/ProjectSettings.json b/.vs/ProjectSettings.json
deleted file mode 100644
index f8b4888..0000000
--- a/.vs/ProjectSettings.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "CurrentProjectSetting": null
-}
\ No newline at end of file
diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json
deleted file mode 100644
index 6b61141..0000000
--- a/.vs/VSWorkspaceState.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "ExpandedNodes": [
- ""
- ],
- "PreviewInSolutionExplorer": false
-}
\ No newline at end of file
diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite
deleted file mode 100644
index aeaa3e8..0000000
Binary files a/.vs/slnx.sqlite and /dev/null differ
diff --git a/MathNet.Numerics/ArrayExtensions.cs b/MathNet.Numerics/ArrayExtensions.cs
new file mode 100644
index 0000000..281ee47
--- /dev/null
+++ b/MathNet.Numerics/ArrayExtensions.cs
@@ -0,0 +1,80 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2011 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+using Complex = System.Numerics.Complex;
+
+namespace MathNet.Numerics;
+
+///
+/// Useful extension methods for Arrays.
+///
+internal static class ArrayExtensions
+{
+ ///
+ /// Copies the values from on array to another.
+ ///
+ /// The source array.
+ /// The destination array.
+ public static void Copy(this double[] source, double[] dest)
+ {
+ Buffer.BlockCopy(source, 0, dest, 0, source.Length * Constants.SizeOfDouble);
+ }
+
+ ///
+ /// Copies the values from on array to another.
+ ///
+ /// The source array.
+ /// The destination array.
+ public static void Copy(this float[] source, float[] dest)
+ {
+ Buffer.BlockCopy(source, 0, dest, 0, source.Length * Constants.SizeOfFloat);
+ }
+
+ ///
+ /// Copies the values from on array to another.
+ ///
+ /// The source array.
+ /// The destination array.
+ public static void Copy(this Complex[] source, Complex[] dest)
+ {
+ Array.Copy(source, 0, dest, 0, source.Length);
+ }
+
+ ///
+ /// Copies the values from on array to another.
+ ///
+ /// The source array.
+ /// The destination array.
+ public static void Copy(this Complex32[] source, Complex32[] dest)
+ {
+ Array.Copy(source, 0, dest, 0, source.Length);
+ }
+}
diff --git a/MathNet.Numerics/Combinatorics.cs b/MathNet.Numerics/Combinatorics.cs
new file mode 100644
index 0000000..d1f38a3
--- /dev/null
+++ b/MathNet.Numerics/Combinatorics.cs
@@ -0,0 +1,440 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2015 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MathNet.Numerics;
+
+///
+/// Enumerative Combinatorics and Counting.
+///
+public static class Combinatorics
+{
+ ///
+ /// Count the number of possible variations without repetition.
+ /// The order matters and each object can be chosen only once.
+ ///
+ /// Number of elements in the set.
+ /// Number of elements to choose from the set. Each element is chosen at most once.
+ /// Maximum number of distinct variations.
+ public static double Variations(int n, int k)
+ {
+ if (k < 0 || n < 0 || k > n)
+ {
+ return 0;
+ }
+
+ return Math.Floor(
+ 0.5 + Math.Exp(
+ SpecialFunctions.FactorialLn(n)
+ - SpecialFunctions.FactorialLn(n - k)));
+ }
+
+ ///
+ /// Count the number of possible variations with repetition.
+ /// The order matters and each object can be chosen more than once.
+ ///
+ /// Number of elements in the set.
+ /// Number of elements to choose from the set. Each element is chosen 0, 1 or multiple times.
+ /// Maximum number of distinct variations with repetition.
+ public static double VariationsWithRepetition(int n, int k)
+ {
+ if (k < 0 || n < 0)
+ {
+ return 0;
+ }
+
+ return Math.Pow(n, k);
+ }
+
+ ///
+ /// Count the number of possible combinations without repetition.
+ /// The order does not matter and each object can be chosen only once.
+ ///
+ /// Number of elements in the set.
+ /// Number of elements to choose from the set. Each element is chosen at most once.
+ /// Maximum number of combinations.
+ public static double Combinations(int n, int k)
+ {
+ return SpecialFunctions.Binomial(n, k);
+ }
+
+ ///
+ /// Count the number of possible combinations with repetition.
+ /// The order does not matter and an object can be chosen more than once.
+ ///
+ /// Number of elements in the set.
+ /// Number of elements to choose from the set. Each element is chosen 0, 1 or multiple times.
+ /// Maximum number of combinations with repetition.
+ public static double CombinationsWithRepetition(int n, int k)
+ {
+ if (k < 0 || n < 0 || (n == 0 && k > 0))
+ {
+ return 0;
+ }
+
+ if (n == 0 && k == 0)
+ {
+ return 1;
+ }
+
+ return Math.Floor(
+ 0.5 + Math.Exp(
+ SpecialFunctions.FactorialLn(n + k - 1)
+ - SpecialFunctions.FactorialLn(k)
+ - SpecialFunctions.FactorialLn(n - 1)));
+ }
+
+ ///
+ /// Count the number of possible permutations (without repetition).
+ ///
+ /// Number of (distinguishable) elements in the set.
+ /// Maximum number of permutations without repetition.
+ public static double Permutations(int n)
+ {
+ return SpecialFunctions.Factorial(n);
+ }
+
+ ///
+ /// Generate a random permutation, without repetition, by generating the index numbers 0 to N-1 and shuffle them randomly.
+ /// Implemented using Fisher-Yates Shuffling.
+ ///
+ /// An array of length N that contains (in any order) the integers of the interval [0, N).
+ /// Number of (distinguishable) elements in the set.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ public static int[] GeneratePermutation(int n, System.Random randomSource = null)
+ {
+ if (n < 0)
+ throw new ArgumentOutOfRangeException(nameof(n), Resources.ArgumentNotNegative);
+
+ int[] indices = new int[n];
+ for (int i = 0; i < indices.Length; i++)
+ {
+ indices[i] = i;
+ }
+
+ SelectPermutationInplace(indices, randomSource);
+ return indices;
+ }
+
+ ///
+ /// Select a random permutation, without repetition, from a data array by reordering the provided array in-place.
+ /// Implemented using Fisher-Yates Shuffling. The provided data array will be modified.
+ ///
+ /// The data array to be reordered. The array will be modified by this routine.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ public static void SelectPermutationInplace(T[] data, System.Random randomSource = null)
+ {
+ var random = randomSource ?? SystemRandomSource.Default;
+
+ // Fisher-Yates Shuffling
+ for (int i = data.Length - 1; i > 0; i--)
+ {
+ int swapIndex = random.Next(i + 1);
+ T swap = data[i];
+ data[i] = data[swapIndex];
+ data[swapIndex] = swap;
+ }
+ }
+
+ ///
+ /// Select a random permutation from a data sequence by returning the provided data in random order.
+ /// Implemented using Fisher-Yates Shuffling.
+ ///
+ /// The data elements to be reordered.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ public static IEnumerable SelectPermutation(this IEnumerable data, System.Random randomSource = null)
+ {
+ var random = randomSource ?? SystemRandomSource.Default;
+ T[] array = data.ToArray();
+
+ // Fisher-Yates Shuffling
+ for (int i = array.Length - 1; i >= 0; i--)
+ {
+ int k = random.Next(i + 1);
+ yield return array[k];
+ array[k] = array[i];
+ }
+ }
+
+ ///
+ /// Generate a random combination, without repetition, by randomly selecting some of N elements.
+ ///
+ /// Number of elements in the set.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ /// Boolean mask array of length N, for each item true if it is selected.
+ public static bool[] GenerateCombination(int n, System.Random randomSource = null)
+ {
+ if (n < 0)
+ throw new ArgumentOutOfRangeException(nameof(n), Resources.ArgumentNotNegative);
+
+ var random = randomSource ?? SystemRandomSource.Default;
+
+ bool[] mask = new bool[n];
+ for (int i = 0; i < mask.Length; i++)
+ {
+ mask[i] = random.NextBoolean();
+ }
+
+ return mask;
+ }
+
+ ///
+ /// Generate a random combination, without repetition, by randomly selecting k of N elements.
+ ///
+ /// Number of elements in the set.
+ /// Number of elements to choose from the set. Each element is chosen at most once.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ /// Boolean mask array of length N, for each item true if it is selected.
+ public static bool[] GenerateCombination(int n, int k, System.Random randomSource = null)
+ {
+ if (n < 0)
+ throw new ArgumentOutOfRangeException(nameof(n), Resources.ArgumentNotNegative);
+ if (k < 0)
+ throw new ArgumentOutOfRangeException(nameof(k), Resources.ArgumentNotNegative);
+ if (k > n)
+ throw new ArgumentOutOfRangeException(nameof(k), string.Format(Resources.ArgumentOutOfRangeSmallerEqual, "k", "n"));
+
+ var random = randomSource ?? SystemRandomSource.Default;
+
+ bool[] mask = new bool[n];
+ if (k * 3 < n)
+ {
+ // just pick and try
+ int selectionCount = 0;
+ while (selectionCount < k)
+ {
+ int index = random.Next(n);
+ if (!mask[index])
+ {
+ mask[index] = true;
+ selectionCount++;
+ }
+ }
+
+ return mask;
+ }
+
+ // based on permutation
+ int[] permutation = GeneratePermutation(n, random);
+ for (int i = 0; i < k; i++)
+ {
+ mask[permutation[i]] = true;
+ }
+
+ return mask;
+ }
+
+ ///
+ /// Select a random combination, without repetition, from a data sequence by selecting k elements in original order.
+ ///
+ /// The data source to choose from.
+ /// Number of elements (k) to choose from the data set. Each element is chosen at most once.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ /// The chosen combination, in the original order.
+ public static IEnumerable SelectCombination(this IEnumerable data, int elementsToChoose, System.Random randomSource = null)
+ {
+ T[] array = data as T[] ?? data.ToArray();
+
+ if (elementsToChoose < 0)
+ throw new ArgumentOutOfRangeException(nameof(elementsToChoose), Resources.ArgumentNotNegative);
+ if (elementsToChoose > array.Length)
+ throw new ArgumentOutOfRangeException(nameof(elementsToChoose), string.Format(Resources.ArgumentOutOfRangeSmallerEqual, "elementsToChoose", "data.Count"));
+
+ bool[] mask = GenerateCombination(array.Length, elementsToChoose, randomSource);
+
+ for (int i = 0; i < mask.Length; i++)
+ {
+ if (mask[i])
+ {
+ yield return array[i];
+ }
+ }
+ }
+
+ ///
+ /// Generates a random combination, with repetition, by randomly selecting k of N elements.
+ ///
+ /// Number of elements in the set.
+ /// Number of elements to choose from the set. Elements can be chosen more than once.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ /// Integer mask array of length N, for each item the number of times it was selected.
+ public static int[] GenerateCombinationWithRepetition(int n, int k, System.Random randomSource = null)
+ {
+ if (n < 0)
+ throw new ArgumentOutOfRangeException(nameof(n), Resources.ArgumentNotNegative);
+ if (k < 0)
+ throw new ArgumentOutOfRangeException(nameof(k), Resources.ArgumentNotNegative);
+
+ var random = randomSource ?? SystemRandomSource.Default;
+
+ int[] mask = new int[n];
+ for (int i = 0; i < k; i++)
+ {
+ mask[random.Next(n)]++;
+ }
+
+ return mask;
+ }
+
+ ///
+ /// Select a random combination, with repetition, from a data sequence by selecting k elements in original order.
+ ///
+ /// The data source to choose from.
+ /// Number of elements (k) to choose from the data set. Elements can be chosen more than once.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ /// The chosen combination with repetition, in the original order.
+ public static IEnumerable SelectCombinationWithRepetition(this IEnumerable data, int elementsToChoose, System.Random randomSource = null)
+ {
+ if (elementsToChoose < 0)
+ throw new ArgumentOutOfRangeException(nameof(elementsToChoose), Resources.ArgumentNotNegative);
+
+ T[] array = data as T[] ?? data.ToArray();
+ int[] mask = GenerateCombinationWithRepetition(array.Length, elementsToChoose, randomSource);
+
+ for (int i = 0; i < mask.Length; i++)
+ {
+ for (int j = 0; j < mask[i]; j++)
+ {
+ yield return array[i];
+ }
+ }
+ }
+
+ ///
+ /// Generate a random variation, without repetition, by randomly selecting k of n elements with order.
+ /// Implemented using partial Fisher-Yates Shuffling.
+ ///
+ /// Number of elements in the set.
+ /// Number of elements to choose from the set. Each element is chosen at most once.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ /// An array of length K that contains the indices of the selections as integers of the interval [0, N).
+ public static int[] GenerateVariation(int n, int k, System.Random randomSource = null)
+ {
+ if (n < 0)
+ throw new ArgumentOutOfRangeException(nameof(n), Resources.ArgumentNotNegative);
+ if (k < 0)
+ throw new ArgumentOutOfRangeException(nameof(k), Resources.ArgumentNotNegative);
+ if (k > n)
+ throw new ArgumentOutOfRangeException(nameof(k), string.Format(Resources.ArgumentOutOfRangeSmallerEqual, "k", "n"));
+
+ var random = randomSource ?? SystemRandomSource.Default;
+
+ int[] indices = new int[n];
+ for (int i = 0; i < indices.Length; i++)
+ {
+ indices[i] = i;
+ }
+
+ // Partial Fisher-Yates Shuffling
+ int[] selection = new int[k];
+ for (int i = 0, j = indices.Length - 1; i < selection.Length; i++, j--)
+ {
+ int swapIndex = random.Next(j + 1);
+ selection[i] = indices[swapIndex];
+ indices[swapIndex] = indices[j];
+ }
+
+ return selection;
+ }
+
+ ///
+ /// Select a random variation, without repetition, from a data sequence by randomly selecting k elements in random order.
+ /// Implemented using partial Fisher-Yates Shuffling.
+ ///
+ /// The data source to choose from.
+ /// Number of elements (k) to choose from the set. Each element is chosen at most once.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ /// The chosen variation, in random order.
+ public static IEnumerable SelectVariation(this IEnumerable data, int elementsToChoose, System.Random randomSource = null)
+ {
+ var random = randomSource ?? SystemRandomSource.Default;
+ T[] array = data.ToArray();
+
+ if (elementsToChoose < 0)
+ throw new ArgumentOutOfRangeException(nameof(elementsToChoose), Resources.ArgumentNotNegative);
+ if (elementsToChoose > array.Length)
+ throw new ArgumentOutOfRangeException(nameof(elementsToChoose), string.Format(Resources.ArgumentOutOfRangeSmallerEqual, "elementsToChoose", "data.Count"));
+
+ // Partial Fisher-Yates Shuffling
+ for (int i = array.Length - 1; i >= array.Length - elementsToChoose; i--)
+ {
+ int swapIndex = random.Next(i + 1);
+ yield return array[swapIndex];
+ array[swapIndex] = array[i];
+ }
+ }
+
+ ///
+ /// Generate a random variation, with repetition, by randomly selecting k of n elements with order.
+ ///
+ /// Number of elements in the set.
+ /// Number of elements to choose from the set. Elements can be chosen more than once.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ /// An array of length K that contains the indices of the selections as integers of the interval [0, N).
+ public static int[] GenerateVariationWithRepetition(int n, int k, System.Random randomSource = null)
+ {
+ if (n < 0)
+ throw new ArgumentOutOfRangeException(nameof(n), Resources.ArgumentNotNegative);
+ if (k < 0)
+ throw new ArgumentOutOfRangeException(nameof(k), Resources.ArgumentNotNegative);
+
+ var random = randomSource ?? SystemRandomSource.Default;
+
+ int[] ret = new int[k];
+ random.NextInt32s(ret, 0, n);
+ return ret;
+ }
+
+ ///
+ /// Select a random variation, with repetition, from a data sequence by randomly selecting k elements in random order.
+ ///
+ /// The data source to choose from.
+ /// Number of elements (k) to choose from the data set. Elements can be chosen more than once.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ /// The chosen variation with repetition, in random order.
+ public static IEnumerable SelectVariationWithRepetition(this IEnumerable data, int elementsToChoose, System.Random randomSource = null)
+ {
+ if (elementsToChoose < 0)
+ throw new ArgumentOutOfRangeException(nameof(elementsToChoose), Resources.ArgumentNotNegative);
+
+ T[] array = data as T[] ?? data.ToArray();
+ int[] indices = GenerateVariationWithRepetition(array.Length, elementsToChoose, randomSource);
+
+ for (int i = 0; i < indices.Length; i++)
+ {
+ yield return array[indices[i]];
+ }
+ }
+}
diff --git a/MathNet.Numerics/Compatibility.cs b/MathNet.Numerics/Compatibility.cs
new file mode 100644
index 0000000..a214520
--- /dev/null
+++ b/MathNet.Numerics/Compatibility.cs
@@ -0,0 +1,158 @@
+#if NETSTANDARD1_3
+namespace MathNet.Numerics
+{
+ using System;
+ using System.Threading;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Threading.Tasks;
+
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
+ internal class SerializableAttribute : Attribute
+ {
+ }
+
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
+ internal class SpecialNameAttribute : Attribute
+ {
+ }
+
+ internal static class Partitioner
+ {
+ public static IEnumerable> Create(int fromInclusive, int toExclusive)
+ {
+ var rangeSize = Math.Max(1, (toExclusive - fromInclusive) / Control.MaxDegreeOfParallelism);
+ return Create(fromInclusive, toExclusive, rangeSize);
+ }
+
+ public static IEnumerable> Create(int fromInclusive, int toExclusive, int rangeSize)
+ {
+ if (toExclusive <= fromInclusive) throw new ArgumentOutOfRangeException(nameof(toExclusive));
+ if (rangeSize <= 0) throw new ArgumentOutOfRangeException(nameof(rangeSize));
+ return CreateRanges(fromInclusive, toExclusive, rangeSize);
+ }
+
+ private static IEnumerable> CreateRanges(int fromInclusive, int toExclusive, int rangeSize)
+ {
+ bool flag = false;
+ int num = fromInclusive;
+ while (num < toExclusive && !flag)
+ {
+ int item = num;
+ int num2;
+ try
+ {
+ num2 = checked(num + rangeSize);
+ }
+ catch (OverflowException)
+ {
+ num2 = toExclusive;
+ flag = true;
+ }
+ if (num2 > toExclusive)
+ {
+ num2 = toExclusive;
+ }
+ yield return new Tuple(item, num2);
+ num += rangeSize;
+ }
+ }
+ }
+
+ internal class ParallelOptions
+ {
+ public TaskScheduler TaskScheduler { get; set; }
+ public int MaxDegreeOfParallelism { get; set; }
+ public CancellationToken CancellationToken { get; set; }
+
+ public ParallelOptions()
+ {
+ TaskScheduler = TaskScheduler.Default;
+ MaxDegreeOfParallelism = -1;
+ CancellationToken = CancellationToken.None;
+ }
+ }
+
+ internal class ParallelLoopState
+ {
+ }
+
+ internal static class Parallel
+ {
+ public static void ForEach(IEnumerable source, ParallelOptions parallelOptions, Action body)
+ {
+ var chunks = source.ToArray();
+ var tasks = new Task[chunks.Length];
+
+ for (var i = 0; i < tasks.Length; i++)
+ {
+ var chunk = chunks[i];
+ tasks[i] = Task.Factory.StartNew(() => body(chunk), parallelOptions.CancellationToken, TaskCreationOptions.None, parallelOptions.TaskScheduler);
+ }
+
+ Task.WaitAll(tasks, parallelOptions.CancellationToken);
+ }
+
+ public static void Invoke(ParallelOptions parallelOptions, params Action[] actions)
+ {
+ var tasks = new Task[actions.Length];
+
+ for (var i = 0; i < tasks.Length; i++)
+ {
+ var action = actions[i];
+ if (action == null)
+ {
+ throw new ArgumentException(string.Format(Properties.Resources.ArgumentItemNull, nameof(actions)), nameof(actions));
+ }
+
+ tasks[i] = Task.Factory.StartNew(action, parallelOptions.CancellationToken, TaskCreationOptions.None, parallelOptions.TaskScheduler);
+ }
+
+ Task.WaitAll(tasks, parallelOptions.CancellationToken);
+ }
+
+ public static void ForEach(
+ IEnumerable source,
+ ParallelOptions parallelOptions,
+ Func localInit,
+ Func body,
+ Action localFinally)
+ {
+ var chunks = source.ToArray();
+ var tasks = new Task[chunks.Length];
+ var loopState = new ParallelLoopState();
+
+ for (var i = 0; i < tasks.Length; i++)
+ {
+ var chunk = chunks[i];
+ tasks[i] = Task.Factory.StartNew(() =>
+ {
+ var local = localInit();
+ local = body(chunk, loopState, local);
+ localFinally(local);
+ }, parallelOptions.CancellationToken, TaskCreationOptions.None, parallelOptions.TaskScheduler);
+ }
+
+ Task.WaitAll(tasks, parallelOptions.CancellationToken);
+ }
+ }
+}
+#endif
+
+#if NETSTANDARD1_3
+namespace MathNet.Numerics
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
+ internal class TargetedPatchingOptOutAttribute : Attribute
+ {
+ public string Reason { get; private set; }
+
+ public TargetedPatchingOptOutAttribute(string reason)
+ {
+ Reason = reason;
+ }
+ }
+}
+#endif
diff --git a/MathNet.Numerics/Complex32.cs b/MathNet.Numerics/Complex32.cs
new file mode 100644
index 0000000..46bd386
--- /dev/null
+++ b/MathNet.Numerics/Complex32.cs
@@ -0,0 +1,1526 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+
+using Complex = System.Numerics.Complex;
+using BigInteger = System.Numerics.BigInteger;
+
+#if !NETSTANDARD1_3
+using System.Runtime;
+#endif
+
+namespace MathNet.Numerics;
+
+///
+/// 32-bit single precision complex numbers class.
+///
+///
+///
+/// The class Complex32 provides all elementary operations
+/// on complex numbers. All the operators +, -,
+/// *, /, ==, != are defined in the
+/// canonical way. Additional complex trigonometric functions
+/// are also provided. Note that the Complex32 structures
+/// has two special constant values and
+/// .
+///
+///
+///
+/// Complex32 x = new Complex32(1f,2f);
+/// Complex32 y = Complex32.FromPolarCoordinates(1f, Math.Pi);
+/// Complex32 z = (x + y) / (x - y);
+///
+///
+///
+/// For mathematical details about complex numbers, please
+/// have a look at the
+/// Wikipedia
+///
+///
+[Serializable]
+[StructLayout(LayoutKind.Sequential)]
+[DataContract(Namespace = "urn:MathNet/Numerics")]
+public struct Complex32 : IFormattable, IEquatable
+{
+ ///
+ /// The real component of the complex number.
+ ///
+ [DataMember(Order = 1)]
+ private readonly float _real;
+
+ ///
+ /// The imaginary component of the complex number.
+ ///
+ [DataMember(Order = 2)]
+ private readonly float _imag;
+
+ ///
+ /// Initializes a new instance of the Complex32 structure with the given real
+ /// and imaginary parts.
+ ///
+ /// The value for the real component.
+ /// The value for the imaginary component.
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public Complex32(float real, float imaginary)
+ {
+ _real = real;
+ _imag = imaginary;
+ }
+
+ ///
+ /// Creates a complex number from a point's polar coordinates.
+ ///
+ /// A complex number.
+ /// The magnitude, which is the distance from the origin (the intersection of the x-axis and the y-axis) to the number.
+ /// The phase, which is the angle from the line to the horizontal axis, measured in radians.
+ public static Complex32 FromPolarCoordinates(float magnitude, float phase)
+ {
+ return new Complex32(magnitude * (float)Math.Cos(phase), magnitude * (float)Math.Sin(phase));
+ }
+
+ ///
+ /// Returns a new instance
+ /// with a real number equal to zero and an imaginary number equal to zero.
+ ///
+ public static readonly Complex32 Zero = new Complex32(0.0f, 0.0f);
+
+ ///
+ /// Returns a new instance
+ /// with a real number equal to one and an imaginary number equal to zero.
+ ///
+ public static readonly Complex32 One = new Complex32(1.0f, 0.0f);
+
+ ///
+ /// Returns a new instance
+ /// with a real number equal to zero and an imaginary number equal to one.
+ ///
+ public static readonly Complex32 ImaginaryOne = new Complex32(0, 1);
+
+ ///
+ /// Returns a new instance
+ /// with real and imaginary numbers positive infinite.
+ ///
+ public static readonly Complex32 PositiveInfinity = new Complex32(float.PositiveInfinity, float.PositiveInfinity);
+
+ ///
+ /// Returns a new instance
+ /// with real and imaginary numbers not a number.
+ ///
+ public static readonly Complex32 NaN = new Complex32(float.NaN, float.NaN);
+
+ ///
+ /// Gets the real component of the complex number.
+ ///
+ /// The real component of the complex number.
+ public float Real
+ {
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ get { return _real; }
+ }
+
+ ///
+ /// Gets the real imaginary component of the complex number.
+ ///
+ /// The real imaginary component of the complex number.
+ public float Imaginary
+ {
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ get { return _imag; }
+ }
+
+ ///
+ /// Gets the phase or argument of this Complex32.
+ ///
+ ///
+ /// Phase always returns a value bigger than negative Pi and
+ /// smaller or equal to Pi. If this Complex32 is zero, the Complex32
+ /// is assumed to be positive real with an argument of zero.
+ ///
+ /// The phase or argument of this Complex32
+ public float Phase
+ {
+ // NOTE: the special case for negative real numbers fixes negative-zero value behavior. Do not remove.
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ get { return _imag == 0f && _real < 0f ? (float)Constants.Pi : (float)Math.Atan2(_imag, _real); }
+ }
+
+ ///
+ /// Gets the magnitude (or absolute value) of a complex number.
+ ///
+ /// Assuming that magnitude of (inf,a) and (a,inf) and (inf,inf) is inf and (NaN,a), (a,NaN) and (NaN,NaN) is NaN
+ /// The magnitude of the current instance.
+ public float Magnitude
+ {
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ get
+ {
+ if (float.IsNaN(_real) || float.IsNaN(_imag))
+ return float.NaN;
+ if (float.IsInfinity(_real) || float.IsInfinity(_imag))
+ return float.PositiveInfinity;
+ float a = Math.Abs(_real);
+ float b = Math.Abs(_imag);
+ if (a > b)
+ {
+ double tmp = b / a;
+ return a * (float)Math.Sqrt(1.0f + tmp * tmp);
+
+ }
+
+ if (a == 0.0f) // one can write a >= float.Epsilon here
+ {
+ return b;
+ }
+ else
+ {
+ double tmp = a / b;
+ return b * (float)Math.Sqrt(1.0f + tmp * tmp);
+ }
+ }
+ }
+
+ ///
+ /// Gets the squared magnitude (or squared absolute value) of a complex number.
+ ///
+ /// The squared magnitude of the current instance.
+ public float MagnitudeSquared
+ {
+ get { return (_real * _real) + (_imag * _imag); }
+ }
+
+ ///
+ /// Gets the unity of this complex (same argument, but on the unit circle; exp(I*arg))
+ ///
+ /// The unity of this Complex32.
+ public Complex32 Sign
+ {
+ get
+ {
+ if (float.IsPositiveInfinity(_real) && float.IsPositiveInfinity(_imag))
+ {
+ return new Complex32((float)Constants.Sqrt1Over2, (float)Constants.Sqrt1Over2);
+ }
+
+ if (float.IsPositiveInfinity(_real) && float.IsNegativeInfinity(_imag))
+ {
+ return new Complex32((float)Constants.Sqrt1Over2, -(float)Constants.Sqrt1Over2);
+ }
+
+ if (float.IsNegativeInfinity(_real) && float.IsPositiveInfinity(_imag))
+ {
+ return new Complex32(-(float)Constants.Sqrt1Over2, -(float)Constants.Sqrt1Over2);
+ }
+
+ if (float.IsNegativeInfinity(_real) && float.IsNegativeInfinity(_imag))
+ {
+ return new Complex32(-(float)Constants.Sqrt1Over2, (float)Constants.Sqrt1Over2);
+ }
+
+ // don't replace this with "Magnitude"!
+ var mod = SpecialFunctions.Hypotenuse(_real, _imag);
+ if (mod == 0.0f)
+ {
+ return Zero;
+ }
+
+ return new Complex32(_real / mod, _imag / mod);
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the Complex32 is zero.
+ ///
+ /// true if this instance is zero; otherwise, false.
+ public bool IsZero()
+ {
+ return _real == 0.0f && _imag == 0.0f;
+ }
+
+ ///
+ /// Gets a value indicating whether the Complex32 is one.
+ ///
+ /// true if this instance is one; otherwise, false.
+ public bool IsOne()
+ {
+ return _real == 1.0f && _imag == 0.0f;
+ }
+
+ ///
+ /// Gets a value indicating whether the Complex32 is the imaginary unit.
+ ///
+ /// true if this instance is ImaginaryOne; otherwise, false.
+ public bool IsImaginaryOne()
+ {
+ return _real == 0.0f && _imag == 1.0f;
+ }
+
+ ///
+ /// Gets a value indicating whether the provided Complex32evaluates
+ /// to a value that is not a number.
+ ///
+ ///
+ /// true if this instance is ; otherwise,
+ /// false.
+ ///
+ public bool IsNaN()
+ {
+ return float.IsNaN(_real) || float.IsNaN(_imag);
+ }
+
+ ///
+ /// Gets a value indicating whether the provided Complex32 evaluates to an
+ /// infinite value.
+ ///
+ ///
+ /// true if this instance is infinite; otherwise, false.
+ ///
+ ///
+ /// True if it either evaluates to a complex infinity
+ /// or to a directed infinity.
+ ///
+ public bool IsInfinity()
+ {
+ return float.IsInfinity(_real) || float.IsInfinity(_imag);
+ }
+
+ ///
+ /// Gets a value indicating whether the provided Complex32 is real.
+ ///
+ /// true if this instance is a real number; otherwise, false.
+ public bool IsReal()
+ {
+ return _imag == 0.0f;
+ }
+
+ ///
+ /// Gets a value indicating whether the provided Complex32 is real and not negative, that is >= 0.
+ ///
+ ///
+ /// true if this instance is real nonnegative number; otherwise, false.
+ ///
+ public bool IsRealNonNegative()
+ {
+ return _imag == 0.0f && _real >= 0;
+ }
+
+ ///
+ /// Exponential of this Complex32 (exp(x), E^x).
+ ///
+ ///
+ /// The exponential of this complex number.
+ ///
+ public Complex32 Exponential()
+ {
+ var exp = (float)Math.Exp(_real);
+ if (IsReal())
+ {
+ return new Complex32(exp, 0.0f);
+ }
+
+ return new Complex32(exp * (float)Math.Cos(_imag), exp * (float)Math.Sin(_imag));
+ }
+
+ ///
+ /// Natural Logarithm of this Complex32 (Base E).
+ ///
+ /// The natural logarithm of this complex number.
+ public Complex32 NaturalLogarithm()
+ {
+ if (IsRealNonNegative())
+ {
+ return new Complex32((float)Math.Log(_real), 0.0f);
+ }
+
+ return new Complex32(0.5f * (float)Math.Log(MagnitudeSquared), Phase);
+ }
+
+ ///
+ /// Common Logarithm of this Complex32 (Base 10).
+ ///
+ /// The common logarithm of this complex number.
+ public Complex32 CommonLogarithm()
+ {
+ return NaturalLogarithm() / (float)Constants.Ln10;
+ }
+
+ ///
+ /// Logarithm of this Complex32 with custom base.
+ ///
+ /// The logarithm of this complex number.
+ public Complex32 Logarithm(float baseValue)
+ {
+ return NaturalLogarithm() / (float)Math.Log(baseValue);
+ }
+
+ ///
+ /// Raise this Complex32 to the given value.
+ ///
+ ///
+ /// The exponent.
+ ///
+ ///
+ /// The complex number raised to the given exponent.
+ ///
+ public Complex32 Power(Complex32 exponent)
+ {
+ if (IsZero())
+ {
+ if (exponent.IsZero())
+ {
+ return One;
+ }
+
+ if (exponent.Real > 0f)
+ {
+ return Zero;
+ }
+
+ if (exponent.Real < 0f)
+ {
+ return exponent.Imaginary == 0f
+ ? new Complex32(float.PositiveInfinity, 0f)
+ : new Complex32(float.PositiveInfinity, float.PositiveInfinity);
+ }
+
+ return NaN;
+ }
+
+ return (exponent * NaturalLogarithm()).Exponential();
+ }
+
+ ///
+ /// Raise this Complex32 to the inverse of the given value.
+ ///
+ ///
+ /// The root exponent.
+ ///
+ ///
+ /// The complex raised to the inverse of the given exponent.
+ ///
+ public Complex32 Root(Complex32 rootExponent)
+ {
+ return Power(1 / rootExponent);
+ }
+
+ ///
+ /// The Square (power 2) of this Complex32
+ ///
+ ///
+ /// The square of this complex number.
+ ///
+ public Complex32 Square()
+ {
+ if (IsReal())
+ {
+ return new Complex32(_real * _real, 0.0f);
+ }
+
+ return new Complex32((_real * _real) - (_imag * _imag), 2 * _real * _imag);
+ }
+
+ ///
+ /// The Square Root (power 1/2) of this Complex32
+ ///
+ ///
+ /// The square root of this complex number.
+ ///
+ public Complex32 SquareRoot()
+ {
+ if (IsRealNonNegative())
+ {
+ return new Complex32((float)Math.Sqrt(_real), 0.0f);
+ }
+
+ Complex32 result;
+
+ var absReal = Math.Abs(Real);
+ var absImag = Math.Abs(Imaginary);
+ double w;
+ if (absReal >= absImag)
+ {
+ var ratio = Imaginary / Real;
+ w = Math.Sqrt(absReal) * Math.Sqrt(0.5 * (1.0f + Math.Sqrt(1.0f + (ratio * ratio))));
+ }
+ else
+ {
+ var ratio = Real / Imaginary;
+ w = Math.Sqrt(absImag) * Math.Sqrt(0.5 * (Math.Abs(ratio) + Math.Sqrt(1.0f + (ratio * ratio))));
+ }
+
+ if (Real >= 0.0f)
+ {
+ result = new Complex32((float)w, (float)(Imaginary / (2.0f * w)));
+ }
+ else if (Imaginary >= 0.0f)
+ {
+ result = new Complex32((float)(absImag / (2.0 * w)), (float)w);
+ }
+ else
+ {
+ result = new Complex32((float)(absImag / (2.0 * w)), (float)-w);
+ }
+
+ return result;
+ }
+
+ ///
+ /// Evaluate all square roots of this Complex32.
+ ///
+ public Tuple SquareRoots()
+ {
+ var principal = SquareRoot();
+ return new Tuple(principal, -principal);
+ }
+
+ ///
+ /// Evaluate all cubic roots of this Complex32.
+ ///
+ public Tuple CubicRoots()
+ {
+ float r = (float)Math.Pow(Magnitude, 1d / 3d);
+ float theta = Phase / 3;
+ const float shift = (float)Constants.Pi2 / 3;
+ return new Tuple(
+ FromPolarCoordinates(r, theta),
+ FromPolarCoordinates(r, theta + shift),
+ FromPolarCoordinates(r, theta - shift));
+ }
+
+ ///
+ /// Equality test.
+ ///
+ /// One of complex numbers to compare.
+ /// The other complex numbers to compare.
+ /// true if the real and imaginary components of the two complex numbers are equal; false otherwise.
+ public static bool operator ==(Complex32 complex1, Complex32 complex2)
+ {
+ return complex1.Equals(complex2);
+ }
+
+ ///
+ /// Inequality test.
+ ///
+ /// One of complex numbers to compare.
+ /// The other complex numbers to compare.
+ /// true if the real or imaginary components of the two complex numbers are not equal; false otherwise.
+ public static bool operator !=(Complex32 complex1, Complex32 complex2)
+ {
+ return !complex1.Equals(complex2);
+ }
+
+ ///
+ /// Unary addition.
+ ///
+ /// The complex number to operate on.
+ /// Returns the same complex number.
+ public static Complex32 operator +(Complex32 summand)
+ {
+ return summand;
+ }
+
+ ///
+ /// Unary minus.
+ ///
+ /// The complex number to operate on.
+ /// The negated value of the .
+ public static Complex32 operator -(Complex32 subtrahend)
+ {
+ return new Complex32(-subtrahend._real, -subtrahend._imag);
+ }
+
+ /// Addition operator. Adds two complex numbers together.
+ /// The result of the addition.
+ /// One of the complex numbers to add.
+ /// The other complex numbers to add.
+ public static Complex32 operator +(Complex32 summand1, Complex32 summand2)
+ {
+ return new Complex32(summand1._real + summand2._real, summand1._imag + summand2._imag);
+ }
+
+ /// Subtraction operator. Subtracts two complex numbers.
+ /// The result of the subtraction.
+ /// The complex number to subtract from.
+ /// The complex number to subtract.
+ public static Complex32 operator -(Complex32 minuend, Complex32 subtrahend)
+ {
+ return new Complex32(minuend._real - subtrahend._real, minuend._imag - subtrahend._imag);
+ }
+
+ /// Addition operator. Adds a complex number and float together.
+ /// The result of the addition.
+ /// The complex numbers to add.
+ /// The float value to add.
+ public static Complex32 operator +(Complex32 summand1, float summand2)
+ {
+ return new Complex32(summand1._real + summand2, summand1._imag);
+ }
+
+ /// Subtraction operator. Subtracts float value from a complex value.
+ /// The result of the subtraction.
+ /// The complex number to subtract from.
+ /// The float value to subtract.
+ public static Complex32 operator -(Complex32 minuend, float subtrahend)
+ {
+ return new Complex32(minuend._real - subtrahend, minuend._imag);
+ }
+
+ /// Addition operator. Adds a complex number and float together.
+ /// The result of the addition.
+ /// The float value to add.
+ /// The complex numbers to add.
+ public static Complex32 operator +(float summand1, Complex32 summand2)
+ {
+ return new Complex32(summand2._real + summand1, summand2._imag);
+ }
+
+ /// Subtraction operator. Subtracts complex value from a float value.
+ /// The result of the subtraction.
+ /// The float vale to subtract from.
+ /// The complex value to subtract.
+ public static Complex32 operator -(float minuend, Complex32 subtrahend)
+ {
+ return new Complex32(minuend - subtrahend._real, -subtrahend._imag);
+ }
+
+ /// Multiplication operator. Multiplies two complex numbers.
+ /// The result of the multiplication.
+ /// One of the complex numbers to multiply.
+ /// The other complex number to multiply.
+ public static Complex32 operator *(Complex32 multiplicand, Complex32 multiplier)
+ {
+ return new Complex32(
+ (multiplicand._real * multiplier._real) - (multiplicand._imag * multiplier._imag),
+ (multiplicand._real * multiplier._imag) + (multiplicand._imag * multiplier._real));
+ }
+
+ /// Multiplication operator. Multiplies a complex number with a float value.
+ /// The result of the multiplication.
+ /// The float value to multiply.
+ /// The complex number to multiply.
+ public static Complex32 operator *(float multiplicand, Complex32 multiplier)
+ {
+ return new Complex32(multiplier._real * multiplicand, multiplier._imag * multiplicand);
+ }
+
+ /// Multiplication operator. Multiplies a complex number with a float value.
+ /// The result of the multiplication.
+ /// The complex number to multiply.
+ /// The float value to multiply.
+ public static Complex32 operator *(Complex32 multiplicand, float multiplier)
+ {
+ return new Complex32(multiplicand._real * multiplier, multiplicand._imag * multiplier);
+ }
+
+ /// Division operator. Divides a complex number by another.
+ /// Enhanced Smith's algorithm for dividing two complex numbers
+ ///
+ /// The result of the division.
+ /// The dividend.
+ /// The divisor.
+ public static Complex32 operator /(Complex32 dividend, Complex32 divisor)
+ {
+ if (dividend.IsZero() && divisor.IsZero())
+ {
+ return NaN;
+ }
+
+ if (divisor.IsZero())
+ {
+ return PositiveInfinity;
+ }
+
+ float a = dividend.Real;
+ float b = dividend.Imaginary;
+ float c = divisor.Real;
+ float d = divisor.Imaginary;
+ if (Math.Abs(d) <= Math.Abs(c))
+ return InternalDiv(a, b, c, d, false);
+ return InternalDiv(b, a, d, c, true);
+ }
+ ///
+ /// Helper method for dividing.
+ ///
+ /// Re first
+ /// Im first
+ /// Re second
+ /// Im second
+ ///
+ ///
+ private static Complex32 InternalDiv(float a, float b, float c, float d, bool swapped)
+ {
+ float r = d / c;
+ float t = 1 / (c + d * r);
+ float e, f;
+ if (r != 0.0f) // one can use r >= float.Epsilon || r <= float.Epsilon instead
+ {
+ e = (a + b * r) * t;
+ f = (b - a * r) * t;
+ }
+ else
+ {
+ e = (a + d * (b / c)) * t;
+ f = (b - d * (a / c)) * t;
+ }
+
+ if (swapped)
+ f = -f;
+ return new Complex32(e, f);
+ }
+
+ /// Division operator. Divides a float value by a complex number.
+ /// Algorithm based on Smith's algorithm
+ ///
+ /// The result of the division.
+ /// The dividend.
+ /// The divisor.
+ public static Complex32 operator /(float dividend, Complex32 divisor)
+ {
+ if (dividend == 0.0f && divisor.IsZero())
+ {
+ return NaN;
+ }
+
+ if (divisor.IsZero())
+ {
+ return PositiveInfinity;
+ }
+
+ float c = divisor.Real;
+ float d = divisor.Imaginary;
+ if (Math.Abs(d) <= Math.Abs(c))
+ return InternalDiv(dividend, 0, c, d, false);
+ return InternalDiv(0, dividend, d, c, true);
+ }
+
+ /// Division operator. Divides a complex number by a float value.
+ /// The result of the division.
+ /// The dividend.
+ /// The divisor.
+ public static Complex32 operator /(Complex32 dividend, float divisor)
+ {
+ if (dividend.IsZero() && divisor == 0.0f)
+ {
+ return NaN;
+ }
+
+ if (divisor == 0.0f)
+ {
+ return PositiveInfinity;
+ }
+
+ return new Complex32(dividend._real / divisor, dividend._imag / divisor);
+ }
+
+ ///
+ /// Computes the conjugate of a complex number and returns the result.
+ ///
+ public Complex32 Conjugate()
+ {
+ return new Complex32(_real, -_imag);
+ }
+
+ ///
+ /// Returns the multiplicative inverse of a complex number.
+ ///
+ public Complex32 Reciprocal()
+ {
+ if (IsZero())
+ {
+ return Zero;
+ }
+
+ return 1.0f / this;
+ }
+
+ #region IFormattable Members
+
+ ///
+ /// Converts the value of the current complex number to its equivalent string representation in Cartesian form.
+ ///
+ /// The string representation of the current instance in Cartesian form.
+ public override string ToString()
+ {
+ return string.Format(CultureInfo.CurrentCulture, "({0}, {1})", _real, _imag);
+ }
+
+ ///
+ /// Converts the value of the current complex number to its equivalent string representation
+ /// in Cartesian form by using the specified format for its real and imaginary parts.
+ ///
+ /// The string representation of the current instance in Cartesian form.
+ /// A standard or custom numeric format string.
+ ///
+ /// is not a valid format string.
+ public string ToString(string format)
+ {
+ return string.Format(CultureInfo.CurrentCulture, "({0}, {1})",
+ _real.ToString(format, CultureInfo.CurrentCulture),
+ _imag.ToString(format, CultureInfo.CurrentCulture));
+ }
+
+ ///
+ /// Converts the value of the current complex number to its equivalent string representation
+ /// in Cartesian form by using the specified culture-specific formatting information.
+ ///
+ /// The string representation of the current instance in Cartesian form, as specified by .
+ /// An object that supplies culture-specific formatting information.
+ public string ToString(IFormatProvider provider)
+ {
+ return string.Format(provider, "({0}, {1})", _real, _imag);
+ }
+
+ /// Converts the value of the current complex number to its equivalent string representation
+ /// in Cartesian form by using the specified format and culture-specific format information for its real and imaginary parts.
+ /// The string representation of the current instance in Cartesian form, as specified by and .
+ /// A standard or custom numeric format string.
+ /// An object that supplies culture-specific formatting information.
+ ///
+ /// is not a valid format string.
+ public string ToString(string format, IFormatProvider provider)
+ {
+ return string.Format(provider, "({0}, {1})",
+ _real.ToString(format, provider),
+ _imag.ToString(format, provider));
+ }
+
+ #endregion
+
+ #region IEquatable Members
+
+ ///
+ /// Checks if two complex numbers are equal. Two complex numbers are equal if their
+ /// corresponding real and imaginary components are equal.
+ ///
+ ///
+ /// Returns true if the two objects are the same object, or if their corresponding
+ /// real and imaginary components are equal, false otherwise.
+ ///
+ ///
+ /// The complex number to compare to with.
+ ///
+ public bool Equals(Complex32 other)
+ {
+ if (IsNaN() || other.IsNaN())
+ {
+ return false;
+ }
+
+ if (IsInfinity() && other.IsInfinity())
+ {
+ return true;
+ }
+
+ return _real.AlmostEqual(other._real) && _imag.AlmostEqual(other._imag);
+ }
+
+ ///
+ /// The hash code for the complex number.
+ ///
+ ///
+ /// The hash code of the complex number.
+ ///
+ ///
+ /// The hash code is calculated as
+ /// System.Math.Exp(ComplexMath.Absolute(complexNumber)).
+ ///
+ public override int GetHashCode()
+ {
+ int hash = 27;
+ hash = (13 * hash) + _real.GetHashCode();
+ hash = (13 * hash) + _imag.GetHashCode();
+ return hash;
+ }
+
+ ///
+ /// Checks if two complex numbers are equal. Two complex numbers are equal if their
+ /// corresponding real and imaginary components are equal.
+ ///
+ ///
+ /// Returns true if the two objects are the same object, or if their corresponding
+ /// real and imaginary components are equal, false otherwise.
+ ///
+ ///
+ /// The complex number to compare to with.
+ ///
+ public override bool Equals(object obj)
+ {
+ return (obj is Complex32) && Equals((Complex32)obj);
+ }
+
+ #endregion
+
+ #region Parse Functions
+
+ ///
+ /// Creates a complex number based on a string. The string can be in the
+ /// following formats (without the quotes): 'n', 'ni', 'n +/- ni',
+ /// 'ni +/- n', 'n,n', 'n,ni,' '(n,n)', or '(n,ni)', where n is a float.
+ ///
+ ///
+ /// A complex number containing the value specified by the given string.
+ ///
+ ///
+ /// the string to parse.
+ ///
+ ///
+ /// An that supplies culture-specific
+ /// formatting information.
+ ///
+ public static Complex32 Parse(string value, IFormatProvider formatProvider = null)
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value));
+ }
+
+ value = value.Trim();
+ if (value.Length == 0)
+ {
+ throw new FormatException();
+ }
+
+ // strip out parens
+ if (value.StartsWith("(", StringComparison.Ordinal))
+ {
+ if (!value.EndsWith(")", StringComparison.Ordinal))
+ {
+ throw new FormatException();
+ }
+
+ value = value.Substring(1, value.Length - 2).Trim();
+ }
+
+ // keywords
+ var numberFormatInfo = formatProvider.GetNumberFormatInfo();
+ var textInfo = formatProvider.GetTextInfo();
+ var keywords =
+ new[]
+ {
+ textInfo.ListSeparator, numberFormatInfo.NaNSymbol,
+ numberFormatInfo.NegativeInfinitySymbol, numberFormatInfo.PositiveInfinitySymbol,
+ "+", "-", "i", "j"
+ };
+
+ // lexing
+ var tokens = new LinkedList();
+ GlobalizationHelper.Tokenize(tokens.AddFirst(value), keywords, 0);
+ var token = tokens.First;
+
+ // parse the left part
+ bool isLeftPartImaginary;
+ var leftPart = ParsePart(ref token, out isLeftPartImaginary, formatProvider);
+ if (token == null)
+ {
+ return isLeftPartImaginary ? new Complex32(0, leftPart) : new Complex32(leftPart, 0);
+ }
+
+ // parse the right part
+ if (token.Value == textInfo.ListSeparator)
+ {
+ // format: real,imag
+ token = token.Next;
+
+ if (isLeftPartImaginary)
+ {
+ // left must not contain 'i', right doesn't matter.
+ throw new FormatException();
+ }
+
+ bool isRightPartImaginary;
+ var rightPart = ParsePart(ref token, out isRightPartImaginary, formatProvider);
+
+ return new Complex32(leftPart, rightPart);
+ }
+ else
+ {
+ // format: real + imag
+ bool isRightPartImaginary;
+ var rightPart = ParsePart(ref token, out isRightPartImaginary, formatProvider);
+
+ if (!(isLeftPartImaginary ^ isRightPartImaginary))
+ {
+ // either left or right part must contain 'i', but not both.
+ throw new FormatException();
+ }
+
+ return isLeftPartImaginary ? new Complex32(rightPart, leftPart) : new Complex32(leftPart, rightPart);
+ }
+ }
+
+ ///
+ /// Parse a part (real or complex) from a complex number.
+ ///
+ /// Start Token.
+ /// Is set to true if the part identified itself as being imaginary.
+ ///
+ /// An that supplies culture-specific
+ /// formatting information.
+ ///
+ /// Resulting part as float.
+ ///
+ private static float ParsePart(ref LinkedListNode token, out bool imaginary, IFormatProvider format)
+ {
+ imaginary = false;
+ if (token == null)
+ {
+ throw new FormatException();
+ }
+
+ // handle prefix modifiers
+ if (token.Value == "+")
+ {
+ token = token.Next;
+
+ if (token == null)
+ {
+ throw new FormatException();
+ }
+ }
+
+ var negative = false;
+ if (token.Value == "-")
+ {
+ negative = true;
+ token = token.Next;
+
+ if (token == null)
+ {
+ throw new FormatException();
+ }
+ }
+
+ // handle prefix imaginary symbol
+ if (string.Compare(token.Value, "i", StringComparison.OrdinalIgnoreCase) == 0
+ || string.Compare(token.Value, "j", StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ imaginary = true;
+ token = token.Next;
+
+ if (token == null)
+ {
+ return negative ? -1 : 1;
+ }
+ }
+
+#if NETSTANDARD1_3
+ var value = GlobalizationHelper.ParseSingle(ref token);
+#else
+ var value = GlobalizationHelper.ParseSingle(ref token, format.GetCultureInfo());
+#endif
+
+ // handle suffix imaginary symbol
+ if (token != null && (string.Compare(token.Value, "i", StringComparison.OrdinalIgnoreCase) == 0
+ || string.Compare(token.Value, "j", StringComparison.OrdinalIgnoreCase) == 0))
+ {
+ if (imaginary)
+ {
+ // only one time allowed: either prefix or suffix, or neither.
+ throw new FormatException();
+ }
+
+ imaginary = true;
+ token = token.Next;
+ }
+
+ return negative ? -value : value;
+ }
+
+ ///
+ /// Converts the string representation of a complex number to a single-precision complex number equivalent.
+ /// A return value indicates whether the conversion succeeded or failed.
+ ///
+ ///
+ /// A string containing a complex number to convert.
+ ///
+ ///
+ /// The parsed value.
+ ///
+ ///
+ /// If the conversion succeeds, the result will contain a complex number equivalent to value.
+ /// Otherwise the result will contain complex32.Zero. This parameter is passed uninitialized
+ ///
+ public static bool TryParse(string value, out Complex32 result)
+ {
+ return TryParse(value, null, out result);
+ }
+
+ ///
+ /// Converts the string representation of a complex number to single-precision complex number equivalent.
+ /// A return value indicates whether the conversion succeeded or failed.
+ ///
+ ///
+ /// A string containing a complex number to convert.
+ ///
+ ///
+ /// An that supplies culture-specific formatting information about value.
+ ///
+ ///
+ /// The parsed value.
+ ///
+ ///
+ /// If the conversion succeeds, the result will contain a complex number equivalent to value.
+ /// Otherwise the result will contain complex32.Zero. This parameter is passed uninitialized
+ ///
+ public static bool TryParse(string value, IFormatProvider formatProvider, out Complex32 result)
+ {
+ bool ret;
+ try
+ {
+ result = Parse(value, formatProvider);
+ ret = true;
+ }
+ catch (ArgumentNullException)
+ {
+ result = Zero;
+ ret = false;
+ }
+ catch (FormatException)
+ {
+ result = Zero;
+ ret = false;
+ }
+
+ return ret;
+ }
+
+ #endregion
+
+ #region Conversion
+
+ ///
+ /// Explicit conversion of a real decimal to a Complex32.
+ ///
+ /// The decimal value to convert.
+ /// The result of the conversion.
+ public static explicit operator Complex32(decimal value)
+ {
+ return new Complex32((float)value, 0.0f);
+ }
+
+ ///
+ /// Explicit conversion of a Complex to a Complex32.
+ ///
+ /// The decimal value to convert.
+ /// The result of the conversion.
+ public static explicit operator Complex32(Complex value)
+ {
+ return new Complex32((float)value.Real, (float)value.Imaginary);
+ }
+
+ ///
+ /// Implicit conversion of a real byte to a Complex32.
+ ///
+ /// The byte value to convert.
+ /// The result of the conversion.
+ public static implicit operator Complex32(byte value)
+ {
+ return new Complex32(value, 0.0f);
+ }
+
+ ///
+ /// Implicit conversion of a real short to a Complex32.
+ ///
+ /// The short value to convert.
+ /// The result of the conversion.
+ public static implicit operator Complex32(short value)
+ {
+ return new Complex32(value, 0.0f);
+ }
+
+ ///
+ /// Implicit conversion of a signed byte to a Complex32.
+ ///
+ /// The signed byte value to convert.
+ /// The result of the conversion.
+ [CLSCompliant(false)]
+ public static implicit operator Complex32(sbyte value)
+ {
+ return new Complex32(value, 0.0f);
+ }
+
+ ///
+ /// Implicit conversion of a unsigned real short to a Complex32.
+ ///
+ /// The unsigned short value to convert.
+ /// The result of the conversion.
+ [CLSCompliant(false)]
+ public static implicit operator Complex32(ushort value)
+ {
+ return new Complex32(value, 0.0f);
+ }
+
+ ///
+ /// Implicit conversion of a real int to a Complex32.
+ ///
+ /// The int value to convert.
+ /// The result of the conversion.
+ public static implicit operator Complex32(int value)
+ {
+ return new Complex32(value, 0.0f);
+ }
+
+ ///
+ /// Implicit conversion of a BigInteger int to a Complex32.
+ ///
+ /// The BigInteger value to convert.
+ /// The result of the conversion.
+ public static implicit operator Complex32(BigInteger value)
+ {
+ return new Complex32((long)value, 0.0f);
+ }
+
+ ///
+ /// Implicit conversion of a real long to a Complex32.
+ ///
+ /// The long value to convert.
+ /// The result of the conversion.
+ public static implicit operator Complex32(long value)
+ {
+ return new Complex32(value, 0.0f);
+ }
+
+ ///
+ /// Implicit conversion of a real uint to a Complex32.
+ ///
+ /// The uint value to convert.
+ /// The result of the conversion.
+ [CLSCompliant(false)]
+ public static implicit operator Complex32(uint value)
+ {
+ return new Complex32(value, 0.0f);
+ }
+
+ ///
+ /// Implicit conversion of a real ulong to a Complex32.
+ ///
+ /// The ulong value to convert.
+ /// The result of the conversion.
+ [CLSCompliant(false)]
+ public static implicit operator Complex32(ulong value)
+ {
+ return new Complex32(value, 0.0f);
+ }
+
+ ///
+ /// Implicit conversion of a real float to a Complex32.
+ ///
+ /// The float value to convert.
+ /// The result of the conversion.
+ public static implicit operator Complex32(float value)
+ {
+ return new Complex32(value, 0.0f);
+ }
+
+ ///
+ /// Implicit conversion of a real double to a Complex32.
+ ///
+ /// The double value to convert.
+ /// The result of the conversion.
+ public static explicit operator Complex32(double value)
+ {
+ return new Complex32((float)value, 0.0f);
+ }
+
+ ///
+ /// Converts this Complex32 to a .
+ ///
+ /// A with the same values as this Complex32.
+ public Complex ToComplex()
+ {
+ return new Complex(_real, _imag);
+ }
+
+ #endregion
+
+ ///
+ /// Returns the additive inverse of a specified complex number.
+ ///
+ /// The result of the real and imaginary components of the value parameter multiplied by -1.
+ /// A complex number.
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static Complex32 Negate(Complex32 value)
+ {
+ return -value;
+ }
+
+ ///
+ /// Computes the conjugate of a complex number and returns the result.
+ ///
+ /// The conjugate of .
+ /// A complex number.
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static Complex32 Conjugate(Complex32 value)
+ {
+ return value.Conjugate();
+ }
+
+ ///
+ /// Adds two complex numbers and returns the result.
+ ///
+ /// The sum of and .
+ /// The first complex number to add.
+ /// The second complex number to add.
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static Complex32 Add(Complex32 left, Complex32 right)
+ {
+ return left + right;
+ }
+
+ ///
+ /// Subtracts one complex number from another and returns the result.
+ ///
+ /// The result of subtracting from .
+ /// The value to subtract from (the minuend).
+ /// The value to subtract (the subtrahend).
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static Complex32 Subtract(Complex32 left, Complex32 right)
+ {
+ return left - right;
+ }
+
+ ///
+ /// Returns the product of two complex numbers.
+ ///
+ /// The product of the and parameters.
+ /// The first complex number to multiply.
+ /// The second complex number to multiply.
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static Complex32 Multiply(Complex32 left, Complex32 right)
+ {
+ return left * right;
+ }
+
+ ///
+ /// Divides one complex number by another and returns the result.
+ ///
+ /// The quotient of the division.
+ /// The complex number to be divided.
+ /// The complex number to divide by.
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static Complex32 Divide(Complex32 dividend, Complex32 divisor)
+ {
+ return dividend / divisor;
+ }
+ ///
+ /// Returns the multiplicative inverse of a complex number.
+ ///
+ /// The reciprocal of .
+ /// A complex number.
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static Complex32 Reciprocal(Complex32 value)
+ {
+ return value.Reciprocal();
+ }
+
+ ///
+ /// Returns the square root of a specified complex number.
+ ///
+ /// The square root of .
+ /// A complex number.
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static Complex32 Sqrt(Complex32 value)
+ {
+ return value.SquareRoot();
+ }
+
+ ///
+ /// Gets the absolute value (or magnitude) of a complex number.
+ ///
+ /// The absolute value of .
+ /// A complex number.
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static double Abs(Complex32 value)
+ {
+ return value.Magnitude;
+ }
+
+ ///
+ /// Returns e raised to the power specified by a complex number.
+ ///
+ /// The number e raised to the power .
+ /// A complex number that specifies a power.
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static Complex32 Exp(Complex32 value)
+ {
+ return value.Exponential();
+ }
+
+ ///
+ /// Returns a specified complex number raised to a power specified by a complex number.
+ ///
+ /// The complex number raised to the power .
+ /// A complex number to be raised to a power.
+ /// A complex number that specifies a power.
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static Complex32 Pow(Complex32 value, Complex32 power)
+ {
+ return value.Power(power);
+ }
+
+ ///
+ /// Returns a specified complex number raised to a power specified by a single-precision floating-point number.
+ ///
+ /// The complex number raised to the power .
+ /// A complex number to be raised to a power.
+ /// A single-precision floating-point number that specifies a power.
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static Complex32 Pow(Complex32 value, float power)
+ {
+ return value.Power(power);
+ }
+
+ ///
+ /// Returns the natural (base e) logarithm of a specified complex number.
+ ///
+ /// The natural (base e) logarithm of .
+ /// A complex number.
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static Complex32 Log(Complex32 value)
+ {
+ return value.NaturalLogarithm();
+ }
+
+ ///
+ /// Returns the logarithm of a specified complex number in a specified base.
+ ///
+ /// The logarithm of in base .
+ /// A complex number.
+ /// The base of the logarithm.
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static Complex32 Log(Complex32 value, float baseValue)
+ {
+ return value.Logarithm(baseValue);
+ }
+
+ ///
+ /// Returns the base-10 logarithm of a specified complex number.
+ ///
+ /// The base-10 logarithm of .
+ /// A complex number.
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static Complex32 Log10(Complex32 value)
+ {
+ return value.CommonLogarithm();
+ }
+
+ ///
+ /// Returns the sine of the specified complex number.
+ ///
+ /// The sine of .
+ /// A complex number.
+ public static Complex32 Sin(Complex32 value)
+ {
+ return (Complex32)Trig.Sin(value.ToComplex());
+ }
+
+ ///
+ /// Returns the cosine of the specified complex number.
+ ///
+ /// The cosine of .
+ /// A complex number.
+ public static Complex32 Cos(Complex32 value)
+ {
+ return (Complex32)Trig.Cos(value.ToComplex());
+ }
+
+ ///
+ /// Returns the tangent of the specified complex number.
+ ///
+ /// The tangent of .
+ /// A complex number.
+ public static Complex32 Tan(Complex32 value)
+ {
+ return (Complex32)Trig.Tan(value.ToComplex());
+ }
+
+ ///
+ /// Returns the angle that is the arc sine of the specified complex number.
+ ///
+ /// The angle which is the arc sine of .
+ /// A complex number.
+ public static Complex32 Asin(Complex32 value)
+ {
+ return (Complex32)Trig.Asin(value.ToComplex());
+ }
+
+ ///
+ /// Returns the angle that is the arc cosine of the specified complex number.
+ ///
+ /// The angle, measured in radians, which is the arc cosine of .
+ /// A complex number that represents a cosine.
+ public static Complex32 Acos(Complex32 value)
+ {
+ return (Complex32)Trig.Acos(value.ToComplex());
+ }
+
+ ///
+ /// Returns the angle that is the arc tangent of the specified complex number.
+ ///
+ /// The angle that is the arc tangent of .
+ /// A complex number.
+ public static Complex32 Atan(Complex32 value)
+ {
+ return (Complex32)Trig.Atan(value.ToComplex());
+ }
+
+ ///
+ /// Returns the hyperbolic sine of the specified complex number.
+ ///
+ /// The hyperbolic sine of .
+ /// A complex number.
+ public static Complex32 Sinh(Complex32 value)
+ {
+ return (Complex32)Trig.Sinh(value.ToComplex());
+ }
+
+ ///
+ /// Returns the hyperbolic cosine of the specified complex number.
+ ///
+ /// The hyperbolic cosine of .
+ /// A complex number.
+ public static Complex32 Cosh(Complex32 value)
+ {
+ return (Complex32)Trig.Cosh(value.ToComplex());
+ }
+
+ ///
+ /// Returns the hyperbolic tangent of the specified complex number.
+ ///
+ /// The hyperbolic tangent of .
+ /// A complex number.
+ public static Complex32 Tanh(Complex32 value)
+ {
+ return (Complex32)Trig.Tanh(value.ToComplex());
+ }
+}
diff --git a/MathNet.Numerics/ComplexExtensions.cs b/MathNet.Numerics/ComplexExtensions.cs
new file mode 100644
index 0000000..86bc826
--- /dev/null
+++ b/MathNet.Numerics/ComplexExtensions.cs
@@ -0,0 +1,765 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+
+using Complex = System.Numerics.Complex;
+
+#if !NETSTANDARD1_3
+using System.Runtime;
+#endif
+
+namespace MathNet.Numerics;
+
+///
+/// Extension methods for the Complex type provided by System.Numerics
+///
+public static class ComplexExtensions
+{
+ ///
+ /// Gets the squared magnitude of the Complex number.
+ ///
+ /// The number to perform this operation on.
+ /// The squared magnitude of the Complex number.
+ public static double MagnitudeSquared(this Complex32 complex)
+ {
+ return (complex.Real * complex.Real) + (complex.Imaginary * complex.Imaginary);
+ }
+
+ ///
+ /// Gets the squared magnitude of the Complex number.
+ ///
+ /// The number to perform this operation on.
+ /// The squared magnitude of the Complex number.
+ public static double MagnitudeSquared(this Complex complex)
+ {
+ return (complex.Real * complex.Real) + (complex.Imaginary * complex.Imaginary);
+ }
+
+ ///
+ /// Gets the unity of this complex (same argument, but on the unit circle; exp(I*arg))
+ ///
+ /// The unity of this Complex.
+ public static Complex Sign(this Complex complex)
+ {
+ if (double.IsPositiveInfinity(complex.Real) && double.IsPositiveInfinity(complex.Imaginary))
+ {
+ return new Complex(Constants.Sqrt1Over2, Constants.Sqrt1Over2);
+ }
+
+ if (double.IsPositiveInfinity(complex.Real) && double.IsNegativeInfinity(complex.Imaginary))
+ {
+ return new Complex(Constants.Sqrt1Over2, -Constants.Sqrt1Over2);
+ }
+
+ if (double.IsNegativeInfinity(complex.Real) && double.IsPositiveInfinity(complex.Imaginary))
+ {
+ return new Complex(-Constants.Sqrt1Over2, -Constants.Sqrt1Over2);
+ }
+
+ if (double.IsNegativeInfinity(complex.Real) && double.IsNegativeInfinity(complex.Imaginary))
+ {
+ return new Complex(-Constants.Sqrt1Over2, Constants.Sqrt1Over2);
+ }
+
+ // don't replace this with "Magnitude"!
+ var mod = SpecialFunctions.Hypotenuse(complex.Real, complex.Imaginary);
+ if (mod == 0.0d)
+ {
+ return Complex.Zero;
+ }
+
+ return new Complex(complex.Real / mod, complex.Imaginary / mod);
+ }
+
+ ///
+ /// Gets the conjugate of the Complex number.
+ ///
+ /// The number to perform this operation on.
+ ///
+ /// The semantic of setting the conjugate is such that
+ ///
+ /// // a, b of type Complex32
+ /// a.Conjugate = b;
+ ///
+ /// is equivalent to
+ ///
+ /// // a, b of type Complex32
+ /// a = b.Conjugate
+ ///
+ ///
+ /// The conjugate of the number.
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static Complex Conjugate(this Complex complex)
+ {
+ return Complex.Conjugate(complex);
+ }
+
+ ///
+ /// Returns the multiplicative inverse of a complex number.
+ ///
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static Complex Reciprocal(this Complex complex)
+ {
+ return Complex.Reciprocal(complex);
+ }
+
+ ///
+ /// Exponential of this Complex (exp(x), E^x).
+ ///
+ /// The number to perform this operation on.
+ ///
+ /// The exponential of this complex number.
+ ///
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static Complex Exp(this Complex complex)
+ {
+ return Complex.Exp(complex);
+ }
+
+ ///
+ /// Natural Logarithm of this Complex (Base E).
+ ///
+ /// The number to perform this operation on.
+ ///
+ /// The natural logarithm of this complex number.
+ ///
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static Complex Ln(this Complex complex)
+ {
+ return Complex.Log(complex);
+ }
+
+ ///
+ /// Common Logarithm of this Complex (Base 10).
+ ///
+ /// The common logarithm of this complex number.
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static Complex Log10(this Complex complex)
+ {
+ return Complex.Log10(complex);
+ }
+
+ ///
+ /// Logarithm of this Complex with custom base.
+ ///
+ /// The logarithm of this complex number.
+ [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
+ public static Complex Log(this Complex complex, double baseValue)
+ {
+ return Complex.Log(complex, baseValue);
+ }
+
+ ///
+ /// Raise this Complex to the given value.
+ ///
+ /// The number to perform this operation on.
+ ///
+ /// The exponent.
+ ///
+ ///
+ /// The complex number raised to the given exponent.
+ ///
+ public static Complex Power(this Complex complex, Complex exponent)
+ {
+ if (complex.IsZero())
+ {
+ if (exponent.IsZero())
+ {
+ return Complex.One;
+ }
+
+ if (exponent.Real > 0d)
+ {
+ return Complex.Zero;
+ }
+
+ if (exponent.Real < 0d)
+ {
+ return exponent.Imaginary == 0d
+ ? new Complex(double.PositiveInfinity, 0d)
+ : new Complex(double.PositiveInfinity, double.PositiveInfinity);
+ }
+
+ return new Complex(double.NaN, double.NaN);
+ }
+
+ return Complex.Pow(complex, exponent);
+ }
+
+ ///
+ /// Raise this Complex to the inverse of the given value.
+ ///
+ /// The number to perform this operation on.
+ ///
+ /// The root exponent.
+ ///
+ ///
+ /// The complex raised to the inverse of the given exponent.
+ ///
+ public static Complex Root(this Complex complex, Complex rootExponent)
+ {
+ return Complex.Pow(complex, 1 / rootExponent);
+ }
+
+ ///
+ /// The Square (power 2) of this Complex
+ ///
+ /// The number to perform this operation on.
+ ///
+ /// The square of this complex number.
+ ///
+ public static Complex Square(this Complex complex)
+ {
+ if (complex.IsReal())
+ {
+ return new Complex(complex.Real * complex.Real, 0.0);
+ }
+
+ return new Complex((complex.Real * complex.Real) - (complex.Imaginary * complex.Imaginary), 2 * complex.Real * complex.Imaginary);
+ }
+
+ ///
+ /// The Square Root (power 1/2) of this Complex
+ ///
+ /// The number to perform this operation on.
+ ///
+ /// The square root of this complex number.
+ ///
+ public static Complex SquareRoot(this Complex complex)
+ {
+ // Note: the following code should be equivalent to Complex.Sqrt(complex),
+ // but it turns out that is implemented poorly in System.Numerics,
+ // hence we provide our own implementation here. Do not replace.
+
+ if (complex.IsRealNonNegative())
+ {
+ return new Complex(Math.Sqrt(complex.Real), 0.0);
+ }
+
+ Complex result;
+
+ var absReal = Math.Abs(complex.Real);
+ var absImag = Math.Abs(complex.Imaginary);
+ double w;
+ if (absReal >= absImag)
+ {
+ var ratio = complex.Imaginary / complex.Real;
+ w = Math.Sqrt(absReal) * Math.Sqrt(0.5 * (1.0 + Math.Sqrt(1.0 + (ratio * ratio))));
+ }
+ else
+ {
+ var ratio = complex.Real / complex.Imaginary;
+ w = Math.Sqrt(absImag) * Math.Sqrt(0.5 * (Math.Abs(ratio) + Math.Sqrt(1.0 + (ratio * ratio))));
+ }
+
+ if (complex.Real >= 0.0)
+ {
+ result = new Complex(w, complex.Imaginary / (2.0 * w));
+ }
+ else if (complex.Imaginary >= 0.0)
+ {
+ result = new Complex(absImag / (2.0 * w), w);
+ }
+ else
+ {
+ result = new Complex(absImag / (2.0 * w), -w);
+ }
+
+ return result;
+ }
+
+ ///
+ /// Evaluate all square roots of this Complex.
+ ///
+ public static Tuple SquareRoots(this Complex complex)
+ {
+ var principal = SquareRoot(complex);
+ return new Tuple(principal, -principal);
+ }
+
+ ///
+ /// Evaluate all cubic roots of this Complex.
+ ///
+ public static Tuple CubicRoots(this Complex complex)
+ {
+ var r = Math.Pow(complex.Magnitude, 1d / 3d);
+ var theta = complex.Phase / 3;
+ const double shift = Constants.Pi2 / 3;
+ return new Tuple(
+ Complex.FromPolarCoordinates(r, theta),
+ Complex.FromPolarCoordinates(r, theta + shift),
+ Complex.FromPolarCoordinates(r, theta - shift));
+ }
+
+ ///
+ /// Gets a value indicating whether the Complex32 is zero.
+ ///
+ /// The number to perform this operation on.
+ /// true if this instance is zero; otherwise, false.
+ public static bool IsZero(this Complex complex)
+ {
+ return complex.Real == 0.0 && complex.Imaginary == 0.0;
+ }
+
+ ///
+ /// Gets a value indicating whether the Complex32 is one.
+ ///
+ /// The number to perform this operation on.
+ /// true if this instance is one; otherwise, false.
+ public static bool IsOne(this Complex complex)
+ {
+ return complex.Real == 1.0 && complex.Imaginary == 0.0;
+ }
+
+ ///
+ /// Gets a value indicating whether the Complex32 is the imaginary unit.
+ ///
+ /// true if this instance is ImaginaryOne; otherwise, false.
+ /// The number to perform this operation on.
+ public static bool IsImaginaryOne(this Complex complex)
+ {
+ return complex.Real == 0.0 && complex.Imaginary == 1.0;
+ }
+
+ ///
+ /// Gets a value indicating whether the provided Complex32evaluates
+ /// to a value that is not a number.
+ ///
+ /// The number to perform this operation on.
+ ///
+ /// true if this instance is NaN; otherwise,
+ /// false.
+ ///
+ public static bool IsNaN(this Complex complex)
+ {
+ return double.IsNaN(complex.Real) || double.IsNaN(complex.Imaginary);
+ }
+
+ ///
+ /// Gets a value indicating whether the provided Complex32 evaluates to an
+ /// infinite value.
+ ///
+ /// The number to perform this operation on.
+ ///
+ /// true if this instance is infinite; otherwise, false.
+ ///
+ ///
+ /// True if it either evaluates to a complex infinity
+ /// or to a directed infinity.
+ ///
+ public static bool IsInfinity(this Complex complex)
+ {
+ return double.IsInfinity(complex.Real) || double.IsInfinity(complex.Imaginary);
+ }
+
+ ///
+ /// Gets a value indicating whether the provided Complex32 is real.
+ ///
+ /// The number to perform this operation on.
+ /// true if this instance is a real number; otherwise, false.
+ public static bool IsReal(this Complex complex)
+ {
+ return complex.Imaginary == 0.0;
+ }
+
+ ///
+ /// Gets a value indicating whether the provided Complex32 is real and not negative, that is >= 0.
+ ///
+ /// The number to perform this operation on.
+ ///
+ /// true if this instance is real nonnegative number; otherwise, false.
+ ///
+ public static bool IsRealNonNegative(this Complex complex)
+ {
+ return complex.Imaginary == 0.0f && complex.Real >= 0;
+ }
+
+ ///
+ /// Returns a Norm of a value of this type, which is appropriate for measuring how
+ /// close this value is to zero.
+ ///
+ public static double Norm(this Complex complex)
+ {
+ return complex.MagnitudeSquared();
+ }
+
+ ///
+ /// Returns a Norm of a value of this type, which is appropriate for measuring how
+ /// close this value is to zero.
+ ///
+ public static double Norm(this Complex32 complex)
+ {
+ return complex.MagnitudeSquared;
+ }
+
+ ///
+ /// Returns a Norm of the difference of two values of this type, which is
+ /// appropriate for measuring how close together these two values are.
+ ///
+ public static double NormOfDifference(this Complex complex, Complex otherValue)
+ {
+ return (complex - otherValue).MagnitudeSquared();
+ }
+
+ ///
+ /// Returns a Norm of the difference of two values of this type, which is
+ /// appropriate for measuring how close together these two values are.
+ ///
+ public static double NormOfDifference(this Complex32 complex, Complex32 otherValue)
+ {
+ return (complex - otherValue).MagnitudeSquared;
+ }
+
+ ///
+ /// Creates a complex number based on a string. The string can be in the
+ /// following formats (without the quotes): 'n', 'ni', 'n +/- ni',
+ /// 'ni +/- n', 'n,n', 'n,ni,' '(n,n)', or '(n,ni)', where n is a double.
+ ///
+ ///
+ /// A complex number containing the value specified by the given string.
+ ///
+ ///
+ /// The string to parse.
+ ///
+ public static Complex ToComplex(this string value)
+ {
+ return value.ToComplex(null);
+ }
+
+ ///
+ /// Creates a complex number based on a string. The string can be in the
+ /// following formats (without the quotes): 'n', 'ni', 'n +/- ni',
+ /// 'ni +/- n', 'n,n', 'n,ni,' '(n,n)', or '(n,ni)', where n is a double.
+ ///
+ ///
+ /// A complex number containing the value specified by the given string.
+ ///
+ ///
+ /// the string to parse.
+ ///
+ ///
+ /// An that supplies culture-specific
+ /// formatting information.
+ ///
+ public static Complex ToComplex(this string value, IFormatProvider formatProvider)
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value));
+ }
+
+ value = value.Trim();
+ if (value.Length == 0)
+ {
+ throw new FormatException();
+ }
+
+ // strip out parens
+ if (value.StartsWith("(", StringComparison.Ordinal))
+ {
+ if (!value.EndsWith(")", StringComparison.Ordinal))
+ {
+ throw new FormatException();
+ }
+
+ value = value.Substring(1, value.Length - 2).Trim();
+ }
+
+ // keywords
+ var numberFormatInfo = formatProvider.GetNumberFormatInfo();
+ var textInfo = formatProvider.GetTextInfo();
+ var keywords =
+ new[]
+ {
+ textInfo.ListSeparator, numberFormatInfo.NaNSymbol,
+ numberFormatInfo.NegativeInfinitySymbol, numberFormatInfo.PositiveInfinitySymbol,
+ "+", "-", "i", "j"
+ };
+
+ // lexing
+ var tokens = new LinkedList();
+ GlobalizationHelper.Tokenize(tokens.AddFirst(value), keywords, 0);
+ var token = tokens.First;
+
+ // parse the left part
+ bool isLeftPartImaginary;
+ var leftPart = ParsePart(ref token, out isLeftPartImaginary, formatProvider);
+ if (token == null)
+ {
+ return isLeftPartImaginary ? new Complex(0, leftPart) : new Complex(leftPart, 0);
+ }
+
+ // parse the right part
+ if (token.Value == textInfo.ListSeparator)
+ {
+ // format: real,imag
+ token = token.Next;
+
+ if (isLeftPartImaginary)
+ {
+ // left must not contain 'i', right doesn't matter.
+ throw new FormatException();
+ }
+
+ bool isRightPartImaginary;
+ var rightPart = ParsePart(ref token, out isRightPartImaginary, formatProvider);
+
+ return new Complex(leftPart, rightPart);
+ }
+ else
+ {
+ // format: real + imag
+ bool isRightPartImaginary;
+ var rightPart = ParsePart(ref token, out isRightPartImaginary, formatProvider);
+
+ if (!(isLeftPartImaginary ^ isRightPartImaginary))
+ {
+ // either left or right part must contain 'i', but not both.
+ throw new FormatException();
+ }
+
+ return isLeftPartImaginary ? new Complex(rightPart, leftPart) : new Complex(leftPart, rightPart);
+ }
+ }
+
+ ///
+ /// Parse a part (real or complex) from a complex number.
+ ///
+ /// Start Token.
+ /// Is set to true if the part identified itself as being imaginary.
+ ///
+ /// An that supplies culture-specific
+ /// formatting information.
+ ///
+ /// Resulting part as double.
+ ///
+ private static double ParsePart(ref LinkedListNode token, out bool imaginary, IFormatProvider format)
+ {
+ imaginary = false;
+ if (token == null)
+ {
+ throw new FormatException();
+ }
+
+ // handle prefix modifiers
+ if (token.Value == "+")
+ {
+ token = token.Next;
+
+ if (token == null)
+ {
+ throw new FormatException();
+ }
+ }
+
+ var negative = false;
+ if (token.Value == "-")
+ {
+ negative = true;
+ token = token.Next;
+
+ if (token == null)
+ {
+ throw new FormatException();
+ }
+ }
+
+ // handle prefix imaginary symbol
+ if (string.Compare(token.Value, "i", StringComparison.OrdinalIgnoreCase) == 0
+ || string.Compare(token.Value, "j", StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ imaginary = true;
+ token = token.Next;
+
+ if (token == null)
+ {
+ return negative ? -1 : 1;
+ }
+ }
+
+#if NETSTANDARD1_3
+ var value = GlobalizationHelper.ParseDouble(ref token);
+#else
+ var value = GlobalizationHelper.ParseDouble(ref token, format.GetCultureInfo());
+#endif
+
+ // handle suffix imaginary symbol
+ if (token != null && (string.Compare(token.Value, "i", StringComparison.OrdinalIgnoreCase) == 0
+ || string.Compare(token.Value, "j", StringComparison.OrdinalIgnoreCase) == 0))
+ {
+ if (imaginary)
+ {
+ // only one time allowed: either prefix or suffix, or neither.
+ throw new FormatException();
+ }
+
+ imaginary = true;
+ token = token.Next;
+ }
+
+ return negative ? -value : value;
+ }
+
+ ///
+ /// Converts the string representation of a complex number to a double-precision complex number equivalent.
+ /// A return value indicates whether the conversion succeeded or failed.
+ ///
+ ///
+ /// A string containing a complex number to convert.
+ ///
+ ///
+ /// The parsed value.
+ ///
+ ///
+ /// If the conversion succeeds, the result will contain a complex number equivalent to value.
+ /// Otherwise the result will contain Complex.Zero. This parameter is passed uninitialized.
+ ///
+ public static bool TryToComplex(this string value, out Complex result)
+ {
+ return value.TryToComplex(null, out result);
+ }
+
+ ///
+ /// Converts the string representation of a complex number to double-precision complex number equivalent.
+ /// A return value indicates whether the conversion succeeded or failed.
+ ///
+ ///
+ /// A string containing a complex number to convert.
+ ///
+ ///
+ /// An that supplies culture-specific formatting information about value.
+ ///
+ ///
+ /// The parsed value.
+ ///
+ ///
+ /// If the conversion succeeds, the result will contain a complex number equivalent to value.
+ /// Otherwise the result will contain complex32.Zero. This parameter is passed uninitialized
+ ///
+ public static bool TryToComplex(this string value, IFormatProvider formatProvider, out Complex result)
+ {
+ bool ret;
+ try
+ {
+ result = value.ToComplex(formatProvider);
+ ret = true;
+ }
+ catch (ArgumentNullException)
+ {
+ result = Complex.Zero;
+ ret = false;
+ }
+ catch (FormatException)
+ {
+ result = Complex.Zero;
+ ret = false;
+ }
+
+ return ret;
+ }
+
+ ///
+ /// Creates a Complex32 number based on a string. The string can be in the
+ /// following formats (without the quotes): 'n', 'ni', 'n +/- ni',
+ /// 'ni +/- n', 'n,n', 'n,ni,' '(n,n)', or '(n,ni)', where n is a double.
+ ///
+ ///
+ /// A complex number containing the value specified by the given string.
+ ///
+ ///
+ /// the string to parse.
+ ///
+ public static Complex32 ToComplex32(this string value)
+ {
+ return Complex32.Parse(value);
+ }
+
+ ///
+ /// Creates a Complex32 number based on a string. The string can be in the
+ /// following formats (without the quotes): 'n', 'ni', 'n +/- ni',
+ /// 'ni +/- n', 'n,n', 'n,ni,' '(n,n)', or '(n,ni)', where n is a double.
+ ///
+ ///
+ /// A complex number containing the value specified by the given string.
+ ///
+ ///
+ /// the string to parse.
+ ///
+ ///
+ /// An that supplies culture-specific
+ /// formatting information.
+ ///
+ public static Complex32 ToComplex32(this string value, IFormatProvider formatProvider)
+ {
+ return Complex32.Parse(value, formatProvider);
+ }
+
+ ///
+ /// Converts the string representation of a complex number to a single-precision complex number equivalent.
+ /// A return value indicates whether the conversion succeeded or failed.
+ ///
+ ///
+ /// A string containing a complex number to convert.
+ ///
+ ///
+ /// The parsed value.
+ ///
+ ///
+ /// If the conversion succeeds, the result will contain a complex number equivalent to value.
+ /// Otherwise the result will contain complex32.Zero. This parameter is passed uninitialized.
+ ///
+ public static bool TryToComplex32(this string value, out Complex32 result)
+ {
+ return Complex32.TryParse(value, out result);
+ }
+
+ ///
+ /// Converts the string representation of a complex number to single-precision complex number equivalent.
+ /// A return value indicates whether the conversion succeeded or failed.
+ ///
+ ///
+ /// A string containing a complex number to convert.
+ ///
+ ///
+ /// An that supplies culture-specific formatting information about value.
+ ///
+ ///
+ /// The parsed value.
+ ///
+ ///
+ /// If the conversion succeeds, the result will contain a complex number equivalent to value.
+ /// Otherwise the result will contain Complex.Zero. This parameter is passed uninitialized.
+ ///
+ public static bool TryToComplex32(this string value, IFormatProvider formatProvider, out Complex32 result)
+ {
+ return Complex32.TryParse(value, formatProvider, out result);
+ }
+}
diff --git a/MathNet.Numerics/Constants.cs b/MathNet.Numerics/Constants.cs
new file mode 100644
index 0000000..79bc647
--- /dev/null
+++ b/MathNet.Numerics/Constants.cs
@@ -0,0 +1,467 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics;
+
+///
+/// A collection of frequently used mathematical constants.
+///
+public static class Constants
+{
+ #region Mathematical Constants
+
+ /// The number e
+ public const double E = 2.7182818284590452353602874713526624977572470937000d;
+
+ /// The number log[2](e)
+ public const double Log2E = 1.4426950408889634073599246810018921374266459541530d;
+
+ /// The number log[10](e)
+ public const double Log10E = 0.43429448190325182765112891891660508229439700580366d;
+
+ /// The number log[e](2)
+ public const double Ln2 = 0.69314718055994530941723212145817656807550013436026d;
+
+ /// The number log[e](10)
+ public const double Ln10 = 2.3025850929940456840179914546843642076011014886288d;
+
+ /// The number log[e](pi)
+ public const double LnPi = 1.1447298858494001741434273513530587116472948129153d;
+
+ /// The number log[e](2*pi)/2
+ public const double Ln2PiOver2 = 0.91893853320467274178032973640561763986139747363780d;
+
+ /// The number 1/e
+ public const double InvE = 0.36787944117144232159552377016146086744581113103176d;
+
+ /// The number sqrt(e)
+ public const double SqrtE = 1.6487212707001281468486507878141635716537761007101d;
+
+ /// The number sqrt(2)
+ public const double Sqrt2 = 1.4142135623730950488016887242096980785696718753769d;
+
+ /// The number sqrt(3)
+ public const double Sqrt3 = 1.7320508075688772935274463415058723669428052538104d;
+
+ /// The number sqrt(1/2) = 1/sqrt(2) = sqrt(2)/2
+ public const double Sqrt1Over2 = 0.70710678118654752440084436210484903928483593768845d;
+
+ /// The number sqrt(3)/2
+ public const double HalfSqrt3 = 0.86602540378443864676372317075293618347140262690520d;
+
+ /// The number pi
+ public const double Pi = 3.1415926535897932384626433832795028841971693993751d;
+
+ /// The number pi*2
+ public const double Pi2 = 6.2831853071795864769252867665590057683943387987502d;
+
+ /// The number pi/2
+ public const double PiOver2 = 1.5707963267948966192313216916397514420985846996876d;
+
+ /// The number pi*3/2
+ public const double Pi3Over2 = 4.71238898038468985769396507491925432629575409906266d;
+
+ /// The number pi/4
+ public const double PiOver4 = 0.78539816339744830961566084581987572104929234984378d;
+
+ /// The number sqrt(pi)
+ public const double SqrtPi = 1.7724538509055160272981674833411451827975494561224d;
+
+ /// The number sqrt(2pi)
+ public const double Sqrt2Pi = 2.5066282746310005024157652848110452530069867406099d;
+
+ /// The number sqrt(pi/2)
+ public const double SqrtPiOver2 = 1.2533141373155002512078826424055226265034933703050d;
+
+ /// The number sqrt(2*pi*e)
+ public const double Sqrt2PiE = 4.1327313541224929384693918842998526494455219169913d;
+
+ /// The number log(sqrt(2*pi))
+ public const double LogSqrt2Pi = 0.91893853320467274178032973640561763986139747363778;
+
+ /// The number log(sqrt(2*pi*e))
+ public const double LogSqrt2PiE = 1.4189385332046727417803297364056176398613974736378d;
+
+ /// The number log(2 * sqrt(e / pi))
+ public const double LogTwoSqrtEOverPi = 0.6207822376352452223455184457816472122518527279025978;
+
+ /// The number 1/pi
+ public const double InvPi = 0.31830988618379067153776752674502872406891929148091d;
+
+ /// The number 2/pi
+ public const double TwoInvPi = 0.63661977236758134307553505349005744813783858296182d;
+
+ /// The number 1/sqrt(pi)
+ public const double InvSqrtPi = 0.56418958354775628694807945156077258584405062932899d;
+
+ /// The number 1/sqrt(2pi)
+ public const double InvSqrt2Pi = 0.39894228040143267793994605993438186847585863116492d;
+
+ /// The number 2/sqrt(pi)
+ public const double TwoInvSqrtPi = 1.1283791670955125738961589031215451716881012586580d;
+
+ /// The number 2 * sqrt(e / pi)
+ public const double TwoSqrtEOverPi = 1.8603827342052657173362492472666631120594218414085755;
+
+ /// The number (pi)/180 - factor to convert from Degree (deg) to Radians (rad).
+ ///
+ ///
+ public const double Degree = 0.017453292519943295769236907684886127134428718885417d;
+
+ /// The number (pi)/200 - factor to convert from NewGrad (grad) to Radians (rad).
+ ///
+ ///
+ public const double Grad = 0.015707963267948966192313216916397514420985846996876d;
+
+ /// The number ln(10)/20 - factor to convert from Power Decibel (dB) to Neper (Np). Use this version when the Decibel represent a power gain but the compared values are not powers (e.g. amplitude, current, voltage).
+ public const double PowerDecibel = 0.11512925464970228420089957273421821038005507443144d;
+
+ /// The number ln(10)/10 - factor to convert from Neutral Decibel (dB) to Neper (Np). Use this version when either both or neither of the Decibel and the compared values represent powers.
+ public const double NeutralDecibel = 0.23025850929940456840179914546843642076011014886288d;
+
+ /// The Catalan constant
+ /// Sum(k=0 -> inf){ (-1)^k/(2*k + 1)2 }
+ public const double Catalan = 0.9159655941772190150546035149323841107741493742816721342664981196217630197762547694794d;
+
+ /// The Euler-Mascheroni constant
+ /// lim(n -> inf){ Sum(k=1 -> n) { 1/k - log(n) } }
+ public const double EulerMascheroni = 0.5772156649015328606065120900824024310421593359399235988057672348849d;
+
+ /// The number (1+sqrt(5))/2, also known as the golden ratio
+ public const double GoldenRatio = 1.6180339887498948482045868343656381177203091798057628621354486227052604628189024497072d;
+
+ /// The Glaisher constant
+ /// e^(1/12 - Zeta(-1))
+ public const double Glaisher = 1.2824271291006226368753425688697917277676889273250011920637400217404063088588264611297d;
+
+ /// The Khinchin constant
+ /// prod(k=1 -> inf){1+1/(k*(k+2))^log(k,2)}
+ public const double Khinchin = 2.6854520010653064453097148354817956938203822939944629530511523455572188595371520028011d;
+
+ ///
+ /// The size of a double in bytes.
+ ///
+ public const int SizeOfDouble = sizeof(double);
+
+ ///
+ /// The size of an int in bytes.
+ ///
+ public const int SizeOfInt = sizeof(int);
+
+ ///
+ /// The size of a float in bytes.
+ ///
+ public const int SizeOfFloat = sizeof(float);
+
+ ///
+ /// The size of a Complex in bytes.
+ ///
+ public const int SizeOfComplex = 2 * SizeOfDouble;
+
+ ///
+ /// The size of a Complex in bytes.
+ ///
+ public const int SizeOfComplex32 = 2 * SizeOfFloat;
+ #endregion
+
+ #region UNIVERSAL CONSTANTS
+
+ /// Speed of Light in Vacuum: c_0 = 2.99792458e8 [m s^-1] (defined, exact; 2007 CODATA)
+ public const double SpeedOfLight = 2.99792458e8;
+
+ /// Magnetic Permeability in Vacuum: mu_0 = 4*Pi * 10^-7 [N A^-2 = kg m A^-2 s^-2] (defined, exact; 2007 CODATA)
+ public const double MagneticPermeability = 1.2566370614359172953850573533118011536788677597500e-6;
+
+ /// Electric Permittivity in Vacuum: epsilon_0 = 1/(mu_0*c_0^2) [F m^-1 = A^2 s^4 kg^-1 m^-3] (defined, exact; 2007 CODATA)
+ public const double ElectricPermittivity = 8.8541878171937079244693661186959426889222899381429e-12;
+
+ /// Characteristic Impedance of Vacuum: Z_0 = mu_0*c_0 [Ohm = m^2 kg s^-3 A^-2] (defined, exact; 2007 CODATA)
+ public const double CharacteristicImpedanceVacuum = 376.73031346177065546819840042031930826862350835242;
+
+ /// Newtonian Constant of Gravitation: G = 6.67429e-11 [m^3 kg^-1 s^-2] (2007 CODATA)
+ public const double GravitationalConstant = 6.67429e-11;
+
+ /// Planck's constant: h = 6.62606896e-34 [J s = m^2 kg s^-1] (2007 CODATA)
+ public const double PlancksConstant = 6.62606896e-34;
+
+ /// Reduced Planck's constant: h_bar = h / (2*Pi) [J s = m^2 kg s^-1] (2007 CODATA)
+ public const double DiracsConstant = 1.054571629e-34;
+
+ /// Planck mass: m_p = (h_bar*c_0/G)^(1/2) [kg] (2007 CODATA)
+ public const double PlancksMass = 2.17644e-8;
+
+ /// Planck temperature: T_p = (h_bar*c_0^5/G)^(1/2)/k [K] (2007 CODATA)
+ public const double PlancksTemperature = 1.416786e32;
+
+ /// Planck length: l_p = h_bar/(m_p*c_0) [m] (2007 CODATA)
+ public const double PlancksLength = 1.616253e-35;
+
+ /// Planck time: t_p = l_p/c_0 [s] (2007 CODATA)
+ public const double PlancksTime = 5.39124e-44;
+
+ #endregion
+
+ #region ELECTROMAGNETIC CONSTANTS
+
+ /// Elementary Electron Charge: e = 1.602176487e-19 [C = A s] (2007 CODATA)
+ public const double ElementaryCharge = 1.602176487e-19;
+
+ /// Magnetic Flux Quantum: theta_0 = h/(2*e) [Wb = m^2 kg s^-2 A^-1] (2007 CODATA)
+ public const double MagneticFluxQuantum = 2.067833668e-15;
+
+ /// Conductance Quantum: G_0 = 2*e^2/h [S = m^-2 kg^-1 s^3 A^2] (2007 CODATA)
+ public const double ConductanceQuantum = 7.7480917005e-5;
+
+ /// Josephson Constant: K_J = 2*e/h [Hz V^-1] (2007 CODATA)
+ public const double JosephsonConstant = 483597.891e9;
+
+ /// Von Klitzing Constant: R_K = h/e^2 [Ohm = m^2 kg s^-3 A^-2] (2007 CODATA)
+ public const double VonKlitzingConstant = 25812.807557;
+
+ /// Bohr Magneton: mu_B = e*h_bar/2*m_e [J T^-1] (2007 CODATA)
+ public const double BohrMagneton = 927.400915e-26;
+
+ /// Nuclear Magneton: mu_N = e*h_bar/2*m_p [J T^-1] (2007 CODATA)
+ public const double NuclearMagneton = 5.05078324e-27;
+
+ #endregion
+
+ #region ATOMIC AND NUCLEAR CONSTANTS
+
+ /// Fine Structure Constant: alpha = e^2/4*Pi*e_0*h_bar*c_0 [1] (2007 CODATA)
+ public const double FineStructureConstant = 7.2973525376e-3;
+
+ /// Rydberg Constant: R_infty = alpha^2*m_e*c_0/2*h [m^-1] (2007 CODATA)
+ public const double RydbergConstant = 10973731.568528;
+
+ /// Bor Radius: a_0 = alpha/4*Pi*R_infty [m] (2007 CODATA)
+ public const double BohrRadius = 0.52917720859e-10;
+
+ /// Hartree Energy: E_h = 2*R_infty*h*c_0 [J] (2007 CODATA)
+ public const double HartreeEnergy = 4.35974394e-18;
+
+ /// Quantum of Circulation: h/2*m_e [m^2 s^-1] (2007 CODATA)
+ public const double QuantumOfCirculation = 3.6369475199e-4;
+
+ /// Fermi Coupling Constant: G_F/(h_bar*c_0)^3 [GeV^-2] (2007 CODATA)
+ public const double FermiCouplingConstant = 1.16637e-5;
+
+ /// Weak Mixin Angle: sin^2(theta_W) [1] (2007 CODATA)
+ public const double WeakMixingAngle = 0.22256;
+
+ /// Electron Mass: [kg] (2007 CODATA)
+ public const double ElectronMass = 9.10938215e-31;
+
+ /// Electron Mass Energy Equivalent: [J] (2007 CODATA)
+ public const double ElectronMassEnergyEquivalent = 8.18710438e-14;
+
+ /// Electron Molar Mass: [kg mol^-1] (2007 CODATA)
+ public const double ElectronMolarMass = 5.4857990943e-7;
+
+ /// Electron Compton Wavelength: [m] (2007 CODATA)
+ public const double ComptonWavelength = 2.4263102175e-12;
+
+ /// Classical Electron Radius: [m] (2007 CODATA)
+ public const double ClassicalElectronRadius = 2.8179402894e-15;
+
+ /// Thomson Cross Section: [m^2] (2002 CODATA)
+ public const double ThomsonCrossSection = 0.6652458558e-28;
+
+ /// Electron Magnetic Moment: [J T^-1] (2007 CODATA)
+ public const double ElectronMagneticMoment = -928.476377e-26;
+
+ /// Electon G-Factor: [1] (2007 CODATA)
+ public const double ElectronGFactor = -2.0023193043622;
+
+ /// Muon Mass: [kg] (2007 CODATA)
+ public const double MuonMass = 1.88353130e-28;
+
+ /// Muon Mass Energy Equivalent: [J] (2007 CODATA)
+ public const double MuonMassEnegryEquivalent = 1.692833511e-11;
+
+ /// Muon Molar Mass: [kg mol^-1] (2007 CODATA)
+ public const double MuonMolarMass = 0.1134289256e-3;
+
+ /// Muon Compton Wavelength: [m] (2007 CODATA)
+ public const double MuonComptonWavelength = 11.73444104e-15;
+
+ /// Muon Magnetic Moment: [J T^-1] (2007 CODATA)
+ public const double MuonMagneticMoment = -4.49044786e-26;
+
+ /// Muon G-Factor: [1] (2007 CODATA)
+ public const double MuonGFactor = -2.0023318414;
+
+ /// Tau Mass: [kg] (2007 CODATA)
+ public const double TauMass = 3.16777e-27;
+
+ /// Tau Mass Energy Equivalent: [J] (2007 CODATA)
+ public const double TauMassEnergyEquivalent = 2.84705e-10;
+
+ /// Tau Molar Mass: [kg mol^-1] (2007 CODATA)
+ public const double TauMolarMass = 1.90768e-3;
+
+ /// Tau Compton Wavelength: [m] (2007 CODATA)
+ public const double TauComptonWavelength = 0.69772e-15;
+
+ /// Proton Mass: [kg] (2007 CODATA)
+ public const double ProtonMass = 1.672621637e-27;
+
+ /// Proton Mass Energy Equivalent: [J] (2007 CODATA)
+ public const double ProtonMassEnergyEquivalent = 1.503277359e-10;
+
+ /// Proton Molar Mass: [kg mol^-1] (2007 CODATA)
+ public const double ProtonMolarMass = 1.00727646677e-3;
+
+ /// Proton Compton Wavelength: [m] (2007 CODATA)
+ public const double ProtonComptonWavelength = 1.3214098446e-15;
+
+ /// Proton Magnetic Moment: [J T^-1] (2007 CODATA)
+ public const double ProtonMagneticMoment = 1.410606662e-26;
+
+ /// Proton G-Factor: [1] (2007 CODATA)
+ public const double ProtonGFactor = 5.585694713;
+
+ /// Proton Shielded Magnetic Moment: [J T^-1] (2007 CODATA)
+ public const double ShieldedProtonMagneticMoment = 1.410570419e-26;
+
+ /// Proton Gyro-Magnetic Ratio: [s^-1 T^-1] (2007 CODATA)
+ public const double ProtonGyromagneticRatio = 2.675222099e8;
+
+ /// Proton Shielded Gyro-Magnetic Ratio: [s^-1 T^-1] (2007 CODATA)
+ public const double ShieldedProtonGyromagneticRatio = 2.675153362e8;
+
+ /// Neutron Mass: [kg] (2007 CODATA)
+ public const double NeutronMass = 1.674927212e-27;
+
+ /// Neutron Mass Energy Equivalent: [J] (2007 CODATA)
+ public const double NeutronMassEnegryEquivalent = 1.505349506e-10;
+
+ /// Neutron Molar Mass: [kg mol^-1] (2007 CODATA)
+ public const double NeutronMolarMass = 1.00866491597e-3;
+
+ /// Neuron Compton Wavelength: [m] (2007 CODATA)
+ public const double NeutronComptonWavelength = 1.3195908951e-1;
+
+ /// Neutron Magnetic Moment: [J T^-1] (2007 CODATA)
+ public const double NeutronMagneticMoment = -0.96623641e-26;
+
+ /// Neutron G-Factor: [1] (2007 CODATA)
+ public const double NeutronGFactor = -3.82608545;
+
+ /// Neutron Gyro-Magnetic Ratio: [s^-1 T^-1] (2007 CODATA)
+ public const double NeutronGyromagneticRatio = 1.83247185e8;
+
+ /// Deuteron Mass: [kg] (2007 CODATA)
+ public const double DeuteronMass = 3.34358320e-27;
+
+ /// Deuteron Mass Energy Equivalent: [J] (2007 CODATA)
+ public const double DeuteronMassEnegryEquivalent = 3.00506272e-10;
+
+ /// Deuteron Molar Mass: [kg mol^-1] (2007 CODATA)
+ public const double DeuteronMolarMass = 2.013553212725e-3;
+
+ /// Deuteron Magnetic Moment: [J T^-1] (2007 CODATA)
+ public const double DeuteronMagneticMoment = 0.433073465e-26;
+
+ /// Helion Mass: [kg] (2007 CODATA)
+ public const double HelionMass = 5.00641192e-27;
+
+ /// Helion Mass Energy Equivalent: [J] (2007 CODATA)
+ public const double HelionMassEnegryEquivalent = 4.49953864e-10;
+
+ /// Helion Molar Mass: [kg mol^-1] (2007 CODATA)
+ public const double HelionMolarMass = 3.0149322473e-3;
+
+ /// Avogadro constant: [mol^-1] (2010 CODATA)
+ public const double Avogadro = 6.0221412927e23;
+
+ #endregion
+
+ #region Scientific Prefixes
+ /// The SI prefix factor corresponding to 1 000 000 000 000 000 000 000 000
+ public const double Yotta = 1e24;
+
+ /// The SI prefix factor corresponding to 1 000 000 000 000 000 000 000
+ public const double Zetta = 1e21;
+
+ /// The SI prefix factor corresponding to 1 000 000 000 000 000 000
+ public const double Exa = 1e18;
+
+ /// The SI prefix factor corresponding to 1 000 000 000 000 000
+ public const double Peta = 1e15;
+
+ /// The SI prefix factor corresponding to 1 000 000 000 000
+ public const double Tera = 1e12;
+
+ /// The SI prefix factor corresponding to 1 000 000 000
+ public const double Giga = 1e9;
+
+ /// The SI prefix factor corresponding to 1 000 000
+ public const double Mega = 1e6;
+
+ /// The SI prefix factor corresponding to 1 000
+ public const double Kilo = 1e3;
+
+ /// The SI prefix factor corresponding to 100
+ public const double Hecto = 1e2;
+
+ /// The SI prefix factor corresponding to 10
+ public const double Deca = 1e1;
+
+ /// The SI prefix factor corresponding to 0.1
+ public const double Deci = 1e-1;
+
+ /// The SI prefix factor corresponding to 0.01
+ public const double Centi = 1e-2;
+
+ /// The SI prefix factor corresponding to 0.001
+ public const double Milli = 1e-3;
+
+ /// The SI prefix factor corresponding to 0.000 001
+ public const double Micro = 1e-6;
+
+ /// The SI prefix factor corresponding to 0.000 000 001
+ public const double Nano = 1e-9;
+
+ /// The SI prefix factor corresponding to 0.000 000 000 001
+ public const double Pico = 1e-12;
+
+ /// The SI prefix factor corresponding to 0.000 000 000 000 001
+ public const double Femto = 1e-15;
+
+ /// The SI prefix factor corresponding to 0.000 000 000 000 000 001
+ public const double Atto = 1e-18;
+
+ /// The SI prefix factor corresponding to 0.000 000 000 000 000 000 001
+ public const double Zepto = 1e-21;
+
+ /// The SI prefix factor corresponding to 0.000 000 000 000 000 000 000 001
+ public const double Yocto = 1e-24;
+ #endregion
+}
diff --git a/MathNet.Numerics/Control.cs b/MathNet.Numerics/Control.cs
new file mode 100644
index 0000000..5784fe3
--- /dev/null
+++ b/MathNet.Numerics/Control.cs
@@ -0,0 +1,342 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2018 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Providers.FourierTransform;
+using MathNet.Numerics.Providers.LinearAlgebra;
+
+using System;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MathNet.Numerics;
+
+///
+/// Sets parameters for the library.
+///
+public static class Control
+{
+ static int _maxDegreeOfParallelism;
+ static int _parallelizeOrder;
+ static int _parallelizeElements;
+ static string _nativeProviderHintPath;
+
+ static Control()
+ {
+ ConfigureAuto();
+ }
+
+ public static void ConfigureAuto()
+ {
+ // Random Numbers & Distributions
+ CheckDistributionParameters = true;
+
+ // Parallelization & Threading
+ ThreadSafeRandomNumberGenerators = true;
+ _maxDegreeOfParallelism = Environment.ProcessorCount;
+ _parallelizeOrder = 64;
+ _parallelizeElements = 300;
+ TaskScheduler = TaskScheduler.Default;
+ }
+
+ public static void UseManaged()
+ {
+ LinearAlgebraControl.UseManaged();
+ FourierTransformControl.UseManaged();
+ }
+
+ public static void UseManagedReference()
+ {
+ LinearAlgebraControl.UseManagedReference();
+ FourierTransformControl.UseManaged();
+ }
+
+ ///
+ /// Use a specific provider if configured, e.g. using
+ /// environment variables, or fall back to the best providers.
+ ///
+ public static void UseDefaultProviders()
+ {
+ LinearAlgebraControl.UseDefault();
+ FourierTransformControl.UseDefault();
+ }
+
+ ///
+ /// Use the best provider available.
+ ///
+ public static void UseBestProviders()
+ {
+ LinearAlgebraControl.UseBest();
+ FourierTransformControl.UseBest();
+ }
+
+#if NATIVE
+
+ ///
+ /// Use the Intel MKL native provider for linear algebra.
+ /// Throws if it is not available or failed to initialize, in which case the previous provider is still active.
+ ///
+ public static void UseNativeMKL()
+ {
+ LinearAlgebraControl.UseNativeMKL();
+ FourierTransformControl.UseNativeMKL();
+ }
+
+ ///
+ /// Use the Intel MKL native provider for linear algebra, with the specified configuration parameters.
+ /// Throws if it is not available or failed to initialize, in which case the previous provider is still active.
+ ///
+ [CLSCompliant(false)]
+ public static void UseNativeMKL(
+ Providers.Common.Mkl.MklConsistency consistency = Providers.Common.Mkl.MklConsistency.Auto,
+ Providers.Common.Mkl.MklPrecision precision = Providers.Common.Mkl.MklPrecision.Double,
+ Providers.Common.Mkl.MklAccuracy accuracy = Providers.Common.Mkl.MklAccuracy.High)
+ {
+ LinearAlgebraControl.UseNativeMKL(consistency, precision, accuracy);
+ FourierTransformControl.UseNativeMKL();
+ }
+
+ ///
+ /// Try to use the Intel MKL native provider for linear algebra.
+ ///
+ ///
+ /// True if the provider was found and initialized successfully.
+ /// False if it failed and the previous provider is still active.
+ ///
+ public static bool TryUseNativeMKL()
+ {
+ bool linearAlgebra = LinearAlgebraControl.TryUseNativeMKL();
+ bool fourierTransform = FourierTransformControl.TryUseNativeMKL();
+ return linearAlgebra || fourierTransform;
+ }
+
+ ///
+ /// Use the Nvidia CUDA native provider for linear algebra.
+ /// Throws if it is not available or failed to initialize, in which case the previous provider is still active.
+ ///
+ public static void UseNativeCUDA()
+ {
+ LinearAlgebraControl.UseNativeCUDA();
+ }
+
+ ///
+ /// Try to use the Nvidia CUDA native provider for linear algebra.
+ ///
+ ///
+ /// True if the provider was found and initialized successfully.
+ /// False if it failed and the previous provider is still active.
+ ///
+ public static bool TryUseNativeCUDA()
+ {
+ bool linearAlgebra = LinearAlgebraControl.TryUseNativeCUDA();
+ return linearAlgebra;
+ }
+
+ ///
+ /// Use the OpenBLAS native provider for linear algebra.
+ /// Throws if it is not available or failed to initialize, in which case the previous provider is still active.
+ ///
+ public static void UseNativeOpenBLAS()
+ {
+ LinearAlgebraControl.UseNativeOpenBLAS();
+ }
+
+ ///
+ /// Try to use the OpenBLAS native provider for linear algebra.
+ ///
+ ///
+ /// True if the provider was found and initialized successfully.
+ /// False if it failed and the previous provider is still active.
+ ///
+ public static bool TryUseNativeOpenBLAS()
+ {
+ bool linearAlgebra = LinearAlgebraControl.TryUseNativeOpenBLAS();
+ return linearAlgebra;
+ }
+
+ ///
+ /// Try to use any available native provider in an undefined order.
+ ///
+ ///
+ /// True if one of the native providers was found and successfully initialized.
+ /// False if it failed and the previous provider is still active.
+ ///
+ public static bool TryUseNative()
+ {
+ bool linearAlgebra = LinearAlgebraControl.TryUseNative();
+ bool fourierTransform = FourierTransformControl.TryUseNative();
+ return linearAlgebra || fourierTransform;
+ }
+#endif
+
+ public static void FreeResources()
+ {
+ LinearAlgebraControl.FreeResources();
+ FourierTransformControl.FreeResources();
+ }
+
+ public static void UseSingleThread()
+ {
+ _maxDegreeOfParallelism = 1;
+ ThreadSafeRandomNumberGenerators = false;
+
+ LinearAlgebraControl.Provider.InitializeVerify();
+ FourierTransformControl.Provider.InitializeVerify();
+ }
+
+ public static void UseMultiThreading()
+ {
+ _maxDegreeOfParallelism = Environment.ProcessorCount;
+ ThreadSafeRandomNumberGenerators = true;
+
+ LinearAlgebraControl.Provider.InitializeVerify();
+ FourierTransformControl.Provider.InitializeVerify();
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the distribution classes check validate each parameter.
+ /// For the multivariate distributions this could involve an expensive matrix factorization.
+ /// The default setting of this property is true.
+ ///
+ public static bool CheckDistributionParameters { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether to use thread safe random number generators (RNG).
+ /// Thread safe RNG about two and half time slower than non-thread safe RNG.
+ ///
+ ///
+ /// true to use thread safe random number generators ; otherwise, false.
+ ///
+ public static bool ThreadSafeRandomNumberGenerators { get; set; }
+
+ ///
+ /// Optional path to try to load native provider binaries from.
+ ///
+ public static string NativeProviderPath
+ {
+ get { return _nativeProviderHintPath; }
+ set
+ {
+ _nativeProviderHintPath = value;
+ LinearAlgebraControl.HintPath = value;
+ FourierTransformControl.HintPath = value;
+ }
+ }
+
+ ///
+ /// Gets or sets a value indicating how many parallel worker threads shall be used
+ /// when parallelization is applicable.
+ ///
+ /// Default to the number of processor cores, must be between 1 and 1024 (inclusive).
+ public static int MaxDegreeOfParallelism
+ {
+ get { return _maxDegreeOfParallelism; }
+ set
+ {
+ _maxDegreeOfParallelism = Math.Max(1, Math.Min(1024, value));
+
+ // Reinitialize providers:
+ LinearAlgebraControl.Provider.InitializeVerify();
+ FourierTransformControl.Provider.InitializeVerify();
+ }
+ }
+
+ ///
+ /// Gets or sets the TaskScheduler used to schedule the worker tasks.
+ ///
+ public static TaskScheduler TaskScheduler { get; set; }
+
+ ///
+ /// Gets or sets the order of the matrix when linear algebra provider
+ /// must calculate multiply in parallel threads.
+ ///
+ /// The order. Default 64, must be at least 3.
+ internal static int ParallelizeOrder
+ {
+ get { return _parallelizeOrder; }
+ set { _parallelizeOrder = Math.Max(3, value); }
+ }
+
+ ///
+ /// Gets or sets the number of elements a vector or matrix
+ /// must contain before we multiply threads.
+ ///
+ /// Number of elements. Default 300, must be at least 3.
+ internal static int ParallelizeElements
+ {
+ get { return _parallelizeElements; }
+ set { _parallelizeElements = Math.Max(3, value); }
+ }
+
+ public static string Describe()
+ {
+#if NET40
+ var versionAttribute = typeof(Control).Assembly
+ .GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false)
+ .OfType()
+ .FirstOrDefault();
+#else
+ var versionAttribute = typeof(Control).GetTypeInfo().Assembly.GetCustomAttribute(typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;
+#endif
+
+ var sb = new StringBuilder();
+ sb.AppendLine("Math.NET Numerics Configuration:");
+ sb.AppendLine($"Version {versionAttribute?.InformationalVersion}");
+#if NETSTANDARD1_3
+ sb.AppendLine("Built for .Net Standard 1.3");
+#elif NETSTANDARD2_0
+ sb.AppendLine("Built for .Net Standard 2.0");
+#elif NET40
+ sb.AppendLine("Built for .Net Framework 4.0");
+#elif NET461
+ sb.AppendLine("Built for .Net Framework 4.6.1");
+#endif
+#if !NATIVE
+ sb.AppendLine("No Native Provider Support");
+#endif
+ sb.AppendLine($"Linear Algebra Provider: {LinearAlgebraControl.Provider}");
+ sb.AppendLine($"Fourier Transform Provider: {FourierTransformControl.Provider}");
+ sb.AppendLine($"Max Degree of Parallelism: {MaxDegreeOfParallelism}");
+ sb.AppendLine($"Parallelize Elements: {ParallelizeElements}");
+ sb.AppendLine($"Parallelize Order: {ParallelizeOrder}");
+ sb.AppendLine($"Check Distribution Parameters: {CheckDistributionParameters}");
+ sb.AppendLine($"Thread-Safe RNGs: {ThreadSafeRandomNumberGenerators}");
+#if NETSTANDARD1_3 || NETSTANDARD2_0
+ // This would also work in .Net 4.0, but we don't want the dependency just for that.
+ sb.AppendLine($"Operating System: {RuntimeInformation.OSDescription}");
+ sb.AppendLine($"Operating System Architecture: {RuntimeInformation.OSArchitecture}");
+ sb.AppendLine($"Framework: {RuntimeInformation.FrameworkDescription}");
+ sb.AppendLine($"Process Architecture: {RuntimeInformation.ProcessArchitecture}");
+#else
+ sb.AppendLine($"Operating System: {Environment.OSVersion}");
+ sb.AppendLine($"Framework: {Environment.Version}");
+#endif
+ return sb.ToString();
+ }
+}
diff --git a/MathNet.Numerics/Differentiate.cs b/MathNet.Numerics/Differentiate.cs
new file mode 100644
index 0000000..81bf2ac
--- /dev/null
+++ b/MathNet.Numerics/Differentiate.cs
@@ -0,0 +1,207 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2015 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Differentiation;
+
+using System;
+
+namespace MathNet.Numerics;
+
+///
+/// Numerical Derivative.
+///
+public static class Differentiate
+{
+ ///
+ /// Initialized a NumericalDerivative with the given points and center.
+ ///
+ public static NumericalDerivative Points(int points, int center)
+ {
+ return new NumericalDerivative(points, center);
+ }
+
+ ///
+ /// Initialized a NumericalDerivative with the default points and center for the given order.
+ ///
+ public static NumericalDerivative Order(int order)
+ {
+ var points = order + (order.IsEven() ? 1 : 2);
+ return new NumericalDerivative(points, points / 2);
+ }
+
+ ///
+ /// Evaluates the derivative of a scalar univariate function.
+ ///
+ /// Univariate function handle.
+ /// Point at which to evaluate the derivative.
+ /// Derivative order.
+ public static double Derivative(Func f, double x, int order)
+ {
+ return Order(order).EvaluateDerivative(f, x, order);
+ }
+
+ ///
+ /// Creates a function handle for the derivative of a scalar univariate function.
+ ///
+ /// Univariate function handle.
+ /// Derivative order.
+ public static Func DerivativeFunc(Func f, int order)
+ {
+ return Order(order).CreateDerivativeFunctionHandle(f, order);
+ }
+
+ ///
+ /// Evaluates the first derivative of a scalar univariate function.
+ ///
+ /// Univariate function handle.
+ /// Point at which to evaluate the derivative.
+ public static double FirstDerivative(Func f, double x)
+ {
+ return Order(1).EvaluateDerivative(f, x, 1);
+ }
+
+ ///
+ /// Creates a function handle for the first derivative of a scalar univariate function.
+ ///
+ /// Univariate function handle.
+ public static Func FirstDerivativeFunc(Func f)
+ {
+ return Order(1).CreateDerivativeFunctionHandle(f, 1);
+ }
+
+ ///
+ /// Evaluates the second derivative of a scalar univariate function.
+ ///
+ /// Univariate function handle.
+ /// Point at which to evaluate the derivative.
+ public static double SecondDerivative(Func f, double x)
+ {
+ return Order(2).EvaluateDerivative(f, x, 2);
+ }
+
+ ///
+ /// Creates a function handle for the second derivative of a scalar univariate function.
+ ///
+ /// Univariate function handle.
+ public static Func SecondDerivativeFunc(Func f)
+ {
+ return Order(2).CreateDerivativeFunctionHandle(f, 2);
+ }
+
+ ///
+ /// Evaluates the partial derivative of a multivariate function.
+ ///
+ /// Multivariate function handle.
+ /// Vector at which to evaluate the derivative.
+ /// Index of independent variable for partial derivative.
+ /// Derivative order.
+ public static double PartialDerivative(Func f, double[] x, int parameterIndex, int order)
+ {
+ return Order(order).EvaluatePartialDerivative(f, x, parameterIndex, order);
+ }
+
+ ///
+ /// Creates a function handle for the partial derivative of a multivariate function.
+ ///
+ /// Multivariate function handle.
+ /// Index of independent variable for partial derivative.
+ /// Derivative order.
+ public static Func PartialDerivativeFunc(Func f, int parameterIndex, int order)
+ {
+ return Order(order).CreatePartialDerivativeFunctionHandle(f, parameterIndex, order);
+ }
+
+ ///
+ /// Evaluates the first partial derivative of a multivariate function.
+ ///
+ /// Multivariate function handle.
+ /// Vector at which to evaluate the derivative.
+ /// Index of independent variable for partial derivative.
+ public static double FirstPartialDerivative(Func f, double[] x, int parameterIndex)
+ {
+ return PartialDerivative(f, x, parameterIndex, 1);
+ }
+
+ ///
+ /// Creates a function handle for the first partial derivative of a multivariate function.
+ ///
+ /// Multivariate function handle.
+ /// Index of independent variable for partial derivative.
+ public static Func FirstPartialDerivativeFunc(Func f, int parameterIndex)
+ {
+ return PartialDerivativeFunc(f, parameterIndex, 1);
+ }
+
+ ///
+ /// Evaluates the partial derivative of a bivariate function.
+ ///
+ /// Bivariate function handle.
+ /// First argument at which to evaluate the derivative.
+ /// Second argument at which to evaluate the derivative.
+ /// Index of independent variable for partial derivative.
+ /// Derivative order.
+ public static double PartialDerivative2(Func f, double x, double y, int parameterIndex, int order)
+ {
+ return Order(order).EvaluatePartialDerivative(array => f(array[0], array[1]), new[] { x, y }, parameterIndex, order);
+ }
+
+ ///
+ /// Creates a function handle for the partial derivative of a bivariate function.
+ ///
+ /// Bivariate function handle.
+ /// Index of independent variable for partial derivative.
+ /// Derivative order.
+ public static Func PartialDerivative2Func(Func f, int parameterIndex, int order)
+ {
+ var handle = Order(order).CreatePartialDerivativeFunctionHandle(array => f(array[0], array[1]), parameterIndex, order);
+ return (x, y) => handle(new[] { x, y });
+ }
+
+ ///
+ /// Evaluates the first partial derivative of a bivariate function.
+ ///
+ /// Bivariate function handle.
+ /// First argument at which to evaluate the derivative.
+ /// Second argument at which to evaluate the derivative.
+ /// Index of independent variable for partial derivative.
+ public static double FirstPartialDerivative2(Func f, double x, double y, int parameterIndex)
+ {
+ return PartialDerivative2(f, x, y, parameterIndex, 1);
+ }
+
+ ///
+ /// Creates a function handle for the first partial derivative of a bivariate function.
+ ///
+ /// Bivariate function handle.
+ /// Index of independent variable for partial derivative.
+ public static Func FirstPartialDerivative2Func(Func f, int parameterIndex)
+ {
+ return PartialDerivative2Func(f, parameterIndex, 1);
+ }
+}
diff --git a/MathNet.Numerics/Differentiation/FiniteDifferenceCoefficients.cs b/MathNet.Numerics/Differentiation/FiniteDifferenceCoefficients.cs
new file mode 100644
index 0000000..bb4cfdb
--- /dev/null
+++ b/MathNet.Numerics/Differentiation/FiniteDifferenceCoefficients.cs
@@ -0,0 +1,144 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2015 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.LinearAlgebra.Double;
+
+using System;
+
+namespace MathNet.Numerics.Differentiation;
+
+///
+/// Class to calculate finite difference coefficients using Taylor series expansion method.
+///
+///
+/// For n points, coefficients are calculated up to the maximum derivative order possible (n-1).
+/// The current function value position specifies the "center" for surrounding coefficients.
+/// Selecting the first, middle or last positions represent forward, backwards and central difference methods.
+///
+///
+///
+public class FiniteDifferenceCoefficients
+{
+ ///
+ /// Number of points for finite difference coefficients. Changing this value recalculates the coefficients table.
+ ///
+ public int Points
+ {
+ get { return _points; }
+ set
+ {
+ CalculateCoefficients(value);
+ _points = value;
+ }
+ }
+
+ private double[][,] _coefficients;
+ private int _points;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Number of finite difference coefficients.
+ public FiniteDifferenceCoefficients(int points)
+ {
+ Points = points;
+ CalculateCoefficients(Points);
+ }
+
+ ///
+ /// Gets the finite difference coefficients for a specified center and order.
+ ///
+ /// Current function position with respect to coefficients. Must be within point range.
+ /// Order of finite difference coefficients.
+ /// Vector of finite difference coefficients.
+ public double[] GetCoefficients(int center, int order)
+ {
+ if (center >= _coefficients.Length)
+ throw new ArgumentOutOfRangeException(nameof(center), "Center position must be within the point range.");
+ if (order >= _coefficients.Length)
+ throw new ArgumentOutOfRangeException(nameof(order), "Maximum difference order is points-1.");
+
+ // Return proper row
+ var columns = _coefficients[center].GetLength(1);
+ var array = new double[columns];
+ for (int i = 0; i < columns; ++i)
+ array[i] = _coefficients[center][order, i];
+ return array;
+ }
+
+ ///
+ /// Gets the finite difference coefficients for all orders at a specified center.
+ ///
+ /// Current function position with respect to coefficients. Must be within point range.
+ /// Rectangular array of coefficients, with columns specifying order.
+ public double[,] GetCoefficientsForAllOrders(int center)
+ {
+ if (center >= _coefficients.Length)
+ throw new ArgumentOutOfRangeException(nameof(center), "Center position must be within the point range.");
+
+ return _coefficients[center];
+ }
+
+ private void CalculateCoefficients(int points)
+ {
+ var c = new double[points][,];
+
+ // For ever possible center given the number of points, compute ever possible coefficient for all possible orders.
+ for (int center = 0; center < points; center++)
+ {
+ // Deltas matrix for center located at 'center'.
+ var A = new DenseMatrix(points);
+ var l = points - center - 1;
+ for (int row = points - 1; row >= 0; row--)
+ {
+ A[row, 0] = 1.0;
+ for (int col = 1; col < points; col++)
+ {
+ A[row, col] = A[row, col - 1] * l / col;
+ }
+
+ l -= 1;
+ }
+
+ c[center] = A.Inverse().ToArray();
+
+ // "Polish" results by rounding.
+ var fac = SpecialFunctions.Factorial(points);
+ for (int j = 0; j < points; j++)
+ {
+ for (int k = 0; k < points; k++)
+ {
+ c[center][j, k] = (Math.Round(c[center][j, k] * fac, MidpointRounding.AwayFromZero)) / fac;
+ }
+ }
+ }
+
+ _coefficients = c;
+ }
+}
diff --git a/MathNet.Numerics/Differentiation/NumericalDerivative.cs b/MathNet.Numerics/Differentiation/NumericalDerivative.cs
new file mode 100644
index 0000000..b55dfb9
--- /dev/null
+++ b/MathNet.Numerics/Differentiation/NumericalDerivative.cs
@@ -0,0 +1,467 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2015 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Linq;
+
+namespace MathNet.Numerics.Differentiation;
+
+///
+/// Type of finite different step size.
+///
+public enum StepType
+{
+ ///
+ /// The absolute step size value will be used in numerical derivatives, regardless of order or function parameters.
+ ///
+ Absolute,
+
+ ///
+ /// A base step size value, h, will be scaled according to the function input parameter. A common example is hx = h*(1+abs(x)), however
+ /// this may vary depending on implementation. This definition only guarantees that the only scaling will be relative to the
+ /// function input parameter and not the order of the finite difference derivative.
+ ///
+ RelativeX,
+
+ ///
+ /// A base step size value, eps (typically machine precision), is scaled according to the finite difference coefficient order
+ /// and function input parameter. The initial scaling according to finite different coefficient order can be thought of as producing a
+ /// base step size, h, that is equivalent to scaling. This step size is then scaled according to the function
+ /// input parameter. Although implementation may vary, an example of second order accurate scaling may be (eps)^(1/3)*(1+abs(x)).
+ ///
+ Relative
+};
+
+///
+/// Class to evaluate the numerical derivative of a function using finite difference approximations.
+/// Variable point and center methods can be initialized .
+/// This class can also be used to return function handles (delegates) for a fixed derivative order and variable.
+/// It is possible to evaluate the derivative and partial derivative of univariate and multivariate functions respectively.
+///
+public class NumericalDerivative
+{
+ readonly int _points;
+ int _center;
+ double _stepSize = Math.Pow(2, -10);
+ double _epsilon = Precision.PositiveMachineEpsilon;
+ double _baseStepSize = Math.Pow(2, -26);
+ StepType _stepType = StepType.Relative;
+ readonly FiniteDifferenceCoefficients _coefficients;
+
+ ///
+ /// Initializes a NumericalDerivative class with the default 3 point center difference method.
+ ///
+ public NumericalDerivative() : this(3, 1)
+ {
+ }
+
+ ///
+ /// Initialized a NumericalDerivative class.
+ ///
+ /// Number of points for finite difference derivatives.
+ /// Location of the center with respect to other points. Value ranges from zero to points-1.
+ public NumericalDerivative(int points, int center)
+ {
+ if (points < 2)
+ {
+ throw new ArgumentOutOfRangeException(nameof(points), "Points must be two or greater.");
+ }
+
+ _center = center;
+ _points = points;
+ Center = center;
+ _coefficients = new FiniteDifferenceCoefficients(points);
+ }
+
+ ///
+ /// Sets and gets the finite difference step size. This value is for each function evaluation if relative step size types are used.
+ /// If the base step size used in scaling is desired, see .
+ ///
+ ///
+ /// Setting then getting the StepSize may return a different value. This is not unusual since a user-defined step size is converted to a
+ /// base-2 representable number to improve finite difference accuracy.
+ ///
+ public double StepSize
+ {
+ get { return _stepSize; }
+ set
+ {
+ //Base 2 yields more accurate results...
+ var p = Math.Log(Math.Abs(value)) / Math.Log(2);
+ _stepSize = Math.Pow(2, Math.Round(p));
+ }
+ }
+
+ ///
+ /// Sets and gets the base finite difference step size. This assigned value to this parameter is only used if is set to RelativeX.
+ /// However, if the StepType is Relative, it will contain the base step size computed from based on the finite difference order.
+ ///
+ public double BaseStepSize
+ {
+ get { return _baseStepSize; }
+ set
+ {
+ //Base 2 yields more accurate results...
+ var p = Math.Log(Math.Abs(value)) / Math.Log(2);
+ _baseStepSize = Math.Pow(2, Math.Round(p));
+ }
+ }
+
+ ///
+ /// Sets and gets the base finite difference step size. This parameter is only used if is set to Relative.
+ /// By default this is set to machine epsilon, from which is computed.
+ ///
+ public double Epsilon
+ {
+ get { return _epsilon; }
+ set
+ {
+ //Base 2 yields more accurate results...
+ var p = Math.Log(Math.Abs(value)) / Math.Log(2);
+ _epsilon = Math.Pow(2, Math.Round(p));
+ }
+ }
+
+ ///
+ /// Sets and gets the location of the center point for the finite difference derivative.
+ ///
+ public int Center
+ {
+ get { return _center; }
+ set
+ {
+ if (value >= _points || value < 0)
+ throw new ArgumentOutOfRangeException(nameof(value), "Center must lie between 0 and points -1");
+ _center = value;
+ }
+ }
+
+ ///
+ /// Number of times a function is evaluated for numerical derivatives.
+ ///
+ public int Evaluations { get; private set; }
+
+ ///
+ /// Type of step size for computing finite differences. If set to absolute, dx = h.
+ /// If set to relative, dx = (1+abs(x))*h^(2/(order+1)). This provides accurate results when
+ /// h is approximately equal to the square-root of machine accuracy, epsilon.
+ ///
+ public StepType StepType
+ {
+ get { return _stepType; }
+ set { _stepType = value; }
+ }
+
+ ///
+ /// Evaluates the derivative of equidistant points using the finite difference method.
+ ///
+ /// Vector of points StepSize apart.
+ /// Derivative order.
+ /// Finite difference step size.
+ /// Derivative of points of the specified order.
+ public double EvaluateDerivative(double[] points, int order, double stepSize)
+ {
+ if (points == null)
+ throw new ArgumentNullException(nameof(points));
+
+ if (order >= _points || order < 0)
+ throw new ArgumentOutOfRangeException(nameof(order), "Order must be between zero and points-1.");
+
+ var c = _coefficients.GetCoefficients(Center, order);
+ var result = c.Select((t, i) => t * points[i]).Sum();
+ result /= Math.Pow(stepSize, order);
+ return result;
+ }
+
+ ///
+ /// Evaluates the derivative of a scalar univariate function.
+ ///
+ ///
+ /// Supplying the optional argument currentValue will reduce the number of function evaluations
+ /// required to calculate the finite difference derivative.
+ ///
+ /// Function handle.
+ /// Point at which to compute the derivative.
+ /// Derivative order.
+ /// Current function value at center.
+ /// Function derivative at x of the specified order.
+ public double EvaluateDerivative(Func f, double x, int order, double? currentValue = null)
+ {
+ var c = _coefficients.GetCoefficients(Center, order);
+ var h = CalculateStepSize(_points, x, order);
+
+ var points = new double[_points];
+ for (int i = 0; i < _points; i++)
+ {
+ if (i == Center && currentValue.HasValue)
+ points[i] = currentValue.Value;
+ else if (c[i] != 0) // Only evaluate function if it will actually be used.
+ {
+ points[i] = f(x + (i - Center) * h);
+ Evaluations++;
+ }
+ }
+
+ return EvaluateDerivative(points, order, h);
+ }
+
+ ///
+ /// Creates a function handle for the derivative of a scalar univariate function.
+ ///
+ /// Input function handle.
+ /// Derivative order.
+ /// Function handle that evaluates the derivative of input function at a fixed order.
+ public Func CreateDerivativeFunctionHandle(Func f, int order)
+ {
+ return x => EvaluateDerivative(f, x, order);
+ }
+
+ ///
+ /// Evaluates the partial derivative of a multivariate function.
+ ///
+ /// Multivariate function handle.
+ /// Vector at which to evaluate the derivative.
+ /// Index of independent variable for partial derivative.
+ /// Derivative order.
+ /// Current function value at center.
+ /// Function partial derivative at x of the specified order.
+ public double EvaluatePartialDerivative(Func f, double[] x, int parameterIndex, int order, double? currentValue = null)
+ {
+ var xi = x[parameterIndex];
+ var c = _coefficients.GetCoefficients(Center, order);
+ var h = CalculateStepSize(_points, x[parameterIndex], order);
+
+ var points = new double[_points];
+ for (int i = 0; i < _points; i++)
+ {
+ if (i == Center && currentValue.HasValue)
+ points[i] = currentValue.Value;
+ else if (c[i] != 0) // Only evaluate function if it will actually be used.
+ {
+ x[parameterIndex] = xi + (i - Center) * h;
+ points[i] = f(x);
+ Evaluations++;
+ }
+ }
+
+ //restore original value
+ x[parameterIndex] = xi;
+ return EvaluateDerivative(points, order, h);
+ }
+
+ ///
+ /// Evaluates the partial derivatives of a multivariate function array.
+ ///
+ ///
+ /// This function assumes the input vector x is of the correct length for f.
+ ///
+ /// Multivariate vector function array handle.
+ /// Vector at which to evaluate the derivatives.
+ /// Index of independent variable for partial derivative.
+ /// Derivative order.
+ /// Current function value at center.
+ /// Vector of functions partial derivatives at x of the specified order.
+ public double[] EvaluatePartialDerivative(Func[] f, double[] x, int parameterIndex, int order, double?[] currentValue = null)
+ {
+ var df = new double[f.Length];
+ for (int i = 0; i < f.Length; i++)
+ {
+ if (currentValue != null && currentValue[i].HasValue)
+ df[i] = EvaluatePartialDerivative(f[i], x, parameterIndex, order, currentValue[i].Value);
+ else
+ df[i] = EvaluatePartialDerivative(f[i], x, parameterIndex, order);
+ }
+
+ return df;
+ }
+
+ ///
+ /// Creates a function handle for the partial derivative of a multivariate function.
+ ///
+ /// Input function handle.
+ /// Index of the independent variable for partial derivative.
+ /// Derivative order.
+ /// Function handle that evaluates partial derivative of input function at a fixed order.
+ public Func CreatePartialDerivativeFunctionHandle(Func f, int parameterIndex,
+ int order)
+ {
+ return x => EvaluatePartialDerivative(f, x, parameterIndex, order);
+ }
+
+ ///
+ /// Creates a function handle for the partial derivative of a vector multivariate function.
+ ///
+ /// Input function handle.
+ /// Index of the independent variable for partial derivative.
+ /// Derivative order.
+ /// Function handle that evaluates partial derivative of input function at fixed order.
+ public Func CreatePartialDerivativeFunctionHandle(Func[] f,
+ int parameterIndex,
+ int order)
+ {
+ return x => EvaluatePartialDerivative(f, x, parameterIndex, order);
+ }
+
+ ///
+ /// Evaluates the mixed partial derivative of variable order for multivariate functions.
+ ///
+ ///
+ /// This function recursively uses to evaluate mixed partial derivative.
+ /// Therefore, it is more efficient to call for higher order derivatives of
+ /// a single independent variable.
+ ///
+ /// Multivariate function handle.
+ /// Points at which to evaluate the derivative.
+ /// Vector of indices for the independent variables at descending derivative orders.
+ /// Highest order of differentiation.
+ /// Current function value at center.
+ /// Function mixed partial derivative at x of the specified order.
+ public double EvaluateMixedPartialDerivative(Func f, double[] x, int[] parameterIndex,
+ int order, double? currentValue = null)
+ {
+ if (parameterIndex.Length != order)
+ throw new ArgumentOutOfRangeException(nameof(parameterIndex),
+ "The number of parameters must match derivative order.");
+
+ if (order == 1)
+ return EvaluatePartialDerivative(f, x, parameterIndex[0], order, currentValue);
+
+ int reducedOrder = order - 1;
+ var reducedParameterIndex = new int[reducedOrder];
+ Array.Copy(parameterIndex, 0, reducedParameterIndex, 0, reducedOrder);
+
+ var points = new double[_points];
+ var currentParameterIndex = parameterIndex[order - 1];
+ var h = CalculateStepSize(_points, x[currentParameterIndex], order);
+
+ var xi = x[currentParameterIndex];
+ for (int i = 0; i < _points; i++)
+ {
+ x[currentParameterIndex] = xi + (i - Center) * h;
+ points[i] = EvaluateMixedPartialDerivative(f, x, reducedParameterIndex, reducedOrder);
+ }
+
+ // restore original value
+ x[currentParameterIndex] = xi;
+
+ // This will always be to the first order
+ return EvaluateDerivative(points, 1, h);
+ }
+
+ ///
+ /// Evaluates the mixed partial derivative of variable order for multivariate function arrays.
+ ///
+ ///
+ /// This function recursively uses to evaluate mixed partial derivative.
+ /// Therefore, it is more efficient to call for higher order derivatives of
+ /// a single independent variable.
+ ///
+ /// Multivariate function array handle.
+ /// Vector at which to evaluate the derivative.
+ /// Vector of indices for the independent variables at descending derivative orders.
+ /// Highest order of differentiation.
+ /// Current function value at center.
+ /// Function mixed partial derivatives at x of the specified order.
+ public double[] EvaluateMixedPartialDerivative(Func[] f, double[] x, int[] parameterIndex,
+ int order, double?[] currentValue = null)
+ {
+ var df = new double[f.Length];
+ for (int i = 0; i < f.Length; i++)
+ {
+ if (currentValue != null && currentValue[i].HasValue)
+ df[i] = EvaluateMixedPartialDerivative(f[i], x, parameterIndex, order, currentValue[i].Value);
+ else
+ df[i] = EvaluateMixedPartialDerivative(f[i], x, parameterIndex, order);
+ }
+
+ return df;
+ }
+
+ ///
+ /// Creates a function handle for the mixed partial derivative of a multivariate function.
+ ///
+ /// Input function handle.
+ /// Vector of indices for the independent variables at descending derivative orders.
+ /// Highest derivative order.
+ /// Function handle that evaluates the fixed mixed partial derivative of input function at fixed order.
+ public Func CreateMixedPartialDerivativeFunctionHandle(Func f,
+ int[] parameterIndex, int order)
+ {
+ return x => EvaluateMixedPartialDerivative(f, x, parameterIndex, order);
+ }
+
+ ///
+ /// Creates a function handle for the mixed partial derivative of a multivariate vector function.
+ ///
+ /// Input vector function handle.
+ /// Vector of indices for the independent variables at descending derivative orders.
+ /// Highest derivative order.
+ /// Function handle that evaluates the fixed mixed partial derivative of input function at fixed order.
+ public Func CreateMixedPartialDerivativeFunctionHandle(Func[] f,
+ int[] parameterIndex, int order)
+ {
+ return x => EvaluateMixedPartialDerivative(f, x, parameterIndex, order);
+ }
+
+ ///
+ /// Resets the evaluation counter.
+ ///
+ public void ResetEvaluations()
+ {
+ Evaluations = 0;
+ }
+
+ private double[] CalculateStepSize(int points, double[] x, double order)
+ {
+ var h = new double[x.Length];
+ for (int i = 1; i < h.Length; i++)
+ h[i] = CalculateStepSize(points, x[i], order);
+
+ return h;
+ }
+
+ private double CalculateStepSize(int points, double x, double order)
+ {
+ // Step size relative to function input parameter
+ if (StepType == StepType.RelativeX)
+ {
+ StepSize = BaseStepSize * (1 + Math.Abs(x));
+ }
+ // Step size relative to function input parameter and order
+ else if (StepType == StepType.Relative)
+ {
+ var accuracy = points - order;
+ BaseStepSize = Math.Pow(Epsilon, (1 / (accuracy + order)));
+ StepSize = BaseStepSize * (1 + Math.Abs(x));
+ }
+ // Do nothing for absolute step size.
+
+ return StepSize;
+ }
+}
diff --git a/MathNet.Numerics/Differentiation/NumericalHessian.cs b/MathNet.Numerics/Differentiation/NumericalHessian.cs
new file mode 100644
index 0000000..84c28bd
--- /dev/null
+++ b/MathNet.Numerics/Differentiation/NumericalHessian.cs
@@ -0,0 +1,118 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2015 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MathNet.Numerics.Differentiation;
+
+///
+/// Class for evaluating the Hessian of a smooth continuously differentiable function using finite differences.
+/// By default, a central 3-point method is used.
+///
+public class NumericalHessian
+{
+ ///
+ /// Number of function evaluations.
+ ///
+ public int FunctionEvaluations
+ {
+ get { return _df.Evaluations; }
+ }
+
+ private readonly NumericalDerivative _df;
+
+ ///
+ /// Creates a numerical Hessian object with a three point central difference method.
+ ///
+ public NumericalHessian() : this(3, 1) { }
+
+ ///
+ /// Creates a numerical Hessian with a specified differentiation scheme.
+ ///
+ /// Number of points for Hessian evaluation.
+ /// Center point for differentiation.
+ public NumericalHessian(int points, int center)
+ {
+ _df = new NumericalDerivative(points, center);
+ }
+
+ ///
+ /// Evaluates the Hessian of the scalar univariate function f at points x.
+ ///
+ /// Scalar univariate function handle.
+ /// Point at which to evaluate Hessian.
+ /// Hessian tensor.
+ public double[] Evaluate(Func f, double x)
+ {
+ return new[] { _df.EvaluateDerivative(f, x, 2) };
+ }
+
+ ///
+ /// Evaluates the Hessian of a multivariate function f at points x.
+ ///
+ ///
+ /// This method of computing the Hessian is only valid for Lipschitz continuous functions.
+ /// The function mirrors the Hessian along the diagonal since d2f/dxdy = d2f/dydx for continuously differentiable functions.
+ ///
+ /// Multivariate function handle.>
+ /// Points at which to evaluate Hessian.>
+ /// Hessian tensor.
+ public double[,] Evaluate(Func f, double[] x)
+ {
+ var hessian = new double[x.Length, x.Length];
+
+ // Compute diagonal elements
+ for (var row = 0; row < x.Length; row++)
+ {
+ hessian[row, row] = _df.EvaluatePartialDerivative(f, x, row, 2);
+ }
+
+ // Compute non-diagonal elements
+ for (var row = 0; row < x.Length; row++)
+ {
+ for (var col = 0; col < row; col++)
+ {
+ var mixedPartial = _df.EvaluateMixedPartialDerivative(f, x, new[] { row, col }, 2);
+
+ hessian[row, col] = mixedPartial;
+ hessian[col, row] = mixedPartial;
+ }
+ }
+
+ return hessian;
+ }
+
+ ///
+ /// Resets the function evaluation counter for the Hessian.
+ ///
+ public void ResetFunctionEvaluations()
+ {
+ _df.ResetEvaluations();
+ }
+}
diff --git a/MathNet.Numerics/Differentiation/NumericalJacobian.cs b/MathNet.Numerics/Differentiation/NumericalJacobian.cs
new file mode 100644
index 0000000..90408c3
--- /dev/null
+++ b/MathNet.Numerics/Differentiation/NumericalJacobian.cs
@@ -0,0 +1,170 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2015 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MathNet.Numerics.Differentiation;
+
+///
+/// Class for evaluating the Jacobian of a function using finite differences.
+/// By default, a central 3-point method is used.
+///
+public class NumericalJacobian
+{
+ ///
+ /// Number of function evaluations.
+ ///
+ public int FunctionEvaluations
+ {
+ get { return _df.Evaluations; }
+ }
+
+ private readonly NumericalDerivative _df;
+
+ ///
+ /// Creates a numerical Jacobian object with a three point central difference method.
+ ///
+ public NumericalJacobian() : this(3, 1) { }
+
+ ///
+ /// Creates a numerical Jacobian with a specified differentiation scheme.
+ ///
+ /// Number of points for Jacobian evaluation.
+ /// Center point for differentiation.
+ public NumericalJacobian(int points, int center)
+ {
+ _df = new NumericalDerivative(points, center);
+ }
+
+ ///
+ /// Evaluates the Jacobian of scalar univariate function f at point x.
+ ///
+ /// Scalar univariate function handle.
+ /// Point at which to evaluate Jacobian.
+ /// Jacobian vector.
+ public double[] Evaluate(Func f, double x)
+ {
+ return new[] { _df.EvaluateDerivative(f, x, 1) };
+ }
+
+ ///
+ /// Evaluates the Jacobian of a multivariate function f at vector x.
+ ///
+ ///
+ /// This function assumes that the length of vector x consistent with the argument count of f.
+ ///
+ /// Multivariate function handle.
+ /// Points at which to evaluate Jacobian.
+ /// Jacobian vector.
+ public double[] Evaluate(Func f, double[] x)
+ {
+ var jacobian = new double[x.Length];
+
+ for (var i = 0; i < jacobian.Length; i++)
+ jacobian[i] = _df.EvaluatePartialDerivative(f, x, i, 1);
+
+ return jacobian;
+ }
+
+ ///
+ /// Evaluates the Jacobian of a multivariate function f at vector x given a current function value.
+ ///
+ ///
+ /// To minimize the number of function evaluations, a user can supply the current value of the function
+ /// to be used in computing the Jacobian. This value must correspond to the "center" location for the
+ /// finite differencing. If a scheme is used where the center value is not evaluated, this will provide no
+ /// added efficiency. This method also assumes that the length of vector x consistent with the argument count of f.
+ ///
+ /// Multivariate function handle.
+ /// Points at which to evaluate Jacobian.
+ /// Current function value at finite difference center.
+ /// Jacobian vector.
+ public double[] Evaluate(Func f, double[] x, double currentValue)
+ {
+ var jacobian = new double[x.Length];
+
+ for (var i = 0; i < jacobian.Length; i++)
+ jacobian[i] = _df.EvaluatePartialDerivative(f, x, i, 1, currentValue);
+
+ return jacobian;
+ }
+
+ ///
+ /// Evaluates the Jacobian of a multivariate function array f at vector x.
+ ///
+ /// Multivariate function array handle.
+ /// Vector at which to evaluate Jacobian.
+ /// Jacobian matrix.
+ public double[,] Evaluate(Func[] f, double[] x)
+ {
+ var jacobian = new double[f.Length, x.Length];
+ for (int i = 0; i < f.Length; i++)
+ {
+ var gradient = Evaluate(f[i], x);
+ for (int j = 0; j < gradient.Length; j++)
+ jacobian[i, j] = gradient[j];
+ }
+
+ return jacobian;
+ }
+
+ ///
+ /// Evaluates the Jacobian of a multivariate function array f at vector x given a vector of current function values.
+ ///
+ ///
+ /// To minimize the number of function evaluations, a user can supply a vector of current values of the functions
+ /// to be used in computing the Jacobian. These value must correspond to the "center" location for the
+ /// finite differencing. If a scheme is used where the center value is not evaluated, this will provide no
+ /// added efficiency. This method also assumes that the length of vector x consistent with the argument count of f.
+ ///
+ /// Multivariate function array handle.
+ /// Vector at which to evaluate Jacobian.
+ /// Vector of current function values.
+ /// Jacobian matrix.
+ public double[,] Evaluate(Func[] f, double[] x, double[] currentValues)
+ {
+ var jacobian = new double[f.Length, x.Length];
+ for (int i = 0; i < f.Length; i++)
+ {
+ var gradient = Evaluate(f[i], x, currentValues[i]);
+ for (int j = 0; j < gradient.Length; j++)
+ jacobian[i, j] = gradient[j];
+ }
+
+ return jacobian;
+ }
+
+ ///
+ /// Resets the function evaluation counter for the Jacobian.
+ ///
+ public void ResetFunctionEvaluations()
+ {
+ _df.ResetEvaluations();
+ }
+}
diff --git a/MathNet.Numerics/Distance.cs b/MathNet.Numerics/Distance.cs
new file mode 100644
index 0000000..db7ef31
--- /dev/null
+++ b/MathNet.Numerics/Distance.cs
@@ -0,0 +1,580 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2013 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.LinearAlgebra;
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Providers.LinearAlgebra;
+using MathNet.Numerics.Statistics;
+
+using System;
+using System.Collections.Generic;
+
+namespace MathNet.Numerics;
+
+///
+/// Metrics to measure the distance between two structures.
+///
+public static class Distance
+{
+ ///
+ /// Sum of Absolute Difference (SAD), i.e. the L1-norm (Manhattan) of the difference.
+ ///
+ public static double SAD(Vector a, Vector b) where T : struct, IEquatable, IFormattable
+ {
+ return (a - b).L1Norm();
+ }
+
+ ///
+ /// Sum of Absolute Difference (SAD), i.e. the L1-norm (Manhattan) of the difference.
+ ///
+ public static double SAD(double[] a, double[] b)
+ {
+ if (a.Length != b.Length)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ double sum = 0d;
+ for (var i = 0; i < a.Length; i++)
+ {
+ sum += Math.Abs(a[i] - b[i]);
+ }
+
+ return sum;
+ }
+
+ ///
+ /// Sum of Absolute Difference (SAD), i.e. the L1-norm (Manhattan) of the difference.
+ ///
+ public static float SAD(float[] a, float[] b)
+ {
+ if (a.Length != b.Length)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ float sum = 0f;
+ for (var i = 0; i < a.Length; i++)
+ {
+ sum += Math.Abs(a[i] - b[i]);
+ }
+
+ return sum;
+ }
+
+ ///
+ /// Mean-Absolute Error (MAE), i.e. the normalized L1-norm (Manhattan) of the difference.
+ ///
+ public static double MAE(Vector a, Vector b) where T : struct, IEquatable, IFormattable
+ {
+ return (a - b).L1Norm() / a.Count;
+ }
+
+ ///
+ /// Mean-Absolute Error (MAE), i.e. the normalized L1-norm (Manhattan) of the difference.
+ ///
+ public static double MAE(double[] a, double[] b)
+ {
+ return SAD(a, b) / a.Length;
+ }
+
+ ///
+ /// Mean-Absolute Error (MAE), i.e. the normalized L1-norm (Manhattan) of the difference.
+ ///
+ public static float MAE(float[] a, float[] b)
+ {
+ return SAD(a, b) / a.Length;
+ }
+
+ ///
+ /// Sum of Squared Difference (SSD), i.e. the squared L2-norm (Euclidean) of the difference.
+ ///
+ public static double SSD(Vector a, Vector b) where T : struct, IEquatable, IFormattable
+ {
+ var norm = (a - b).L2Norm();
+ return norm * norm;
+ }
+
+ ///
+ /// Sum of Squared Difference (SSD), i.e. the squared L2-norm (Euclidean) of the difference.
+ ///
+ public static double SSD(double[] a, double[] b)
+ {
+ if (a.Length != b.Length)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ var diff = new double[a.Length];
+ LinearAlgebraControl.Provider.SubtractArrays(a, b, diff);
+ return LinearAlgebraControl.Provider.DotProduct(diff, diff);
+ }
+
+ ///
+ /// Sum of Squared Difference (SSD), i.e. the squared L2-norm (Euclidean) of the difference.
+ ///
+ public static float SSD(float[] a, float[] b)
+ {
+ if (a.Length != b.Length)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ var diff = new float[a.Length];
+ LinearAlgebraControl.Provider.SubtractArrays(a, b, diff);
+ return LinearAlgebraControl.Provider.DotProduct(diff, diff);
+ }
+
+ ///
+ /// Mean-Squared Error (MSE), i.e. the normalized squared L2-norm (Euclidean) of the difference.
+ ///
+ public static double MSE(Vector a, Vector b) where T : struct, IEquatable, IFormattable
+ {
+ var norm = (a - b).L2Norm();
+ return norm * norm / a.Count;
+ }
+
+ ///
+ /// Mean-Squared Error (MSE), i.e. the normalized squared L2-norm (Euclidean) of the difference.
+ ///
+ public static double MSE(double[] a, double[] b)
+ {
+ return SSD(a, b) / a.Length;
+ }
+
+ ///
+ /// Mean-Squared Error (MSE), i.e. the normalized squared L2-norm (Euclidean) of the difference.
+ ///
+ public static float MSE(float[] a, float[] b)
+ {
+ return SSD(a, b) / a.Length;
+ }
+
+ ///
+ /// Euclidean Distance, i.e. the L2-norm of the difference.
+ ///
+ public static double Euclidean(Vector a, Vector b) where T : struct, IEquatable, IFormattable
+ {
+ return (a - b).L2Norm();
+ }
+
+ ///
+ /// Euclidean Distance, i.e. the L2-norm of the difference.
+ ///
+ public static double Euclidean(double[] a, double[] b)
+ {
+ return Math.Sqrt(SSD(a, b));
+ }
+
+ ///
+ /// Euclidean Distance, i.e. the L2-norm of the difference.
+ ///
+ public static float Euclidean(float[] a, float[] b)
+ {
+ return (float)Math.Sqrt(SSD(a, b));
+ }
+
+ ///
+ /// Manhattan Distance, i.e. the L1-norm of the difference.
+ ///
+ public static double Manhattan(Vector a, Vector b) where T : struct, IEquatable, IFormattable
+ {
+ return (a - b).L1Norm();
+ }
+
+ ///
+ /// Manhattan Distance, i.e. the L1-norm of the difference.
+ ///
+ public static double Manhattan(double[] a, double[] b)
+ {
+ return SAD(a, b);
+ }
+
+ ///
+ /// Manhattan Distance, i.e. the L1-norm of the difference.
+ ///
+ public static float Manhattan(float[] a, float[] b)
+ {
+ return SAD(a, b);
+ }
+
+ ///
+ /// Chebyshev Distance, i.e. the Infinity-norm of the difference.
+ ///
+ public static double Chebyshev(Vector a, Vector b) where T : struct, IEquatable, IFormattable
+ {
+ return (a - b).InfinityNorm();
+ }
+
+ ///
+ /// Chebyshev Distance, i.e. the Infinity-norm of the difference.
+ ///
+ public static double Chebyshev(double[] a, double[] b)
+ {
+ if (a.Length != b.Length)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ double max = Math.Abs(a[0] - b[0]);
+ for (int i = 1; i < a.Length; i++)
+ {
+ var next = Math.Abs(a[i] - b[i]);
+ if (next > max)
+ {
+ max = next;
+ }
+ }
+
+ return max;
+ }
+
+ ///
+ /// Chebyshev Distance, i.e. the Infinity-norm of the difference.
+ ///
+ public static float Chebyshev(float[] a, float[] b)
+ {
+ if (a.Length != b.Length)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ float max = Math.Abs(a[0] - b[0]);
+ for (int i = 1; i < a.Length; i++)
+ {
+ var next = Math.Abs(a[i] - b[i]);
+ if (next > max)
+ {
+ max = next;
+ }
+ }
+
+ return max;
+ }
+
+ ///
+ /// Minkowski Distance, i.e. the generalized p-norm of the difference.
+ ///
+ public static double Minkowski(double p, Vector a, Vector b) where T : struct, IEquatable, IFormattable
+ {
+ return (a - b).Norm(p);
+ }
+
+ ///
+ /// Minkowski Distance, i.e. the generalized p-norm of the difference.
+ ///
+ public static double Minkowski(double p, double[] a, double[] b)
+ {
+ if (a.Length != b.Length)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ if (p < 0d)
+ {
+ throw new ArgumentOutOfRangeException(nameof(p));
+ }
+
+ if (p == 1d)
+ {
+ return Manhattan(a, b);
+ }
+
+ if (p == 2d)
+ {
+ return Euclidean(a, b);
+ }
+
+ if (double.IsPositiveInfinity(p))
+ {
+ return Chebyshev(a, b);
+ }
+
+ double sum = 0d;
+ for (var i = 0; i < a.Length; i++)
+ {
+ sum += Math.Pow(Math.Abs(a[i] - b[i]), p);
+ }
+
+ return Math.Pow(sum, 1.0 / p);
+ }
+
+ ///
+ /// Minkowski Distance, i.e. the generalized p-norm of the difference.
+ ///
+ public static float Minkowski(double p, float[] a, float[] b)
+ {
+ if (a.Length != b.Length)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ if (p < 0d)
+ {
+ throw new ArgumentOutOfRangeException(nameof(p));
+ }
+
+ if (p == 1d)
+ {
+ return Manhattan(a, b);
+ }
+
+ if (p == 2d)
+ {
+ return Euclidean(a, b);
+ }
+
+ if (double.IsPositiveInfinity(p))
+ {
+ return Chebyshev(a, b);
+ }
+
+ double sum = 0d;
+ for (var i = 0; i < a.Length; i++)
+ {
+ sum += Math.Pow(Math.Abs(a[i] - b[i]), p);
+ }
+
+ return (float)Math.Pow(sum, 1.0 / p);
+ }
+
+ ///
+ /// Canberra Distance, a weighted version of the L1-norm of the difference.
+ ///
+ public static double Canberra(double[] a, double[] b)
+ {
+ if (a.Length != b.Length)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ double sum = 0d;
+ for (var i = 0; i < a.Length; i++)
+ {
+ sum += Math.Abs(a[i] - b[i]) / (Math.Abs(a[i]) + Math.Abs(b[i]));
+ }
+
+ return sum;
+ }
+
+ ///
+ /// Canberra Distance, a weighted version of the L1-norm of the difference.
+ ///
+ public static float Canberra(float[] a, float[] b)
+ {
+ if (a.Length != b.Length)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ float sum = 0f;
+ for (var i = 0; i < a.Length; i++)
+ {
+ sum += Math.Abs(a[i] - b[i]) / (Math.Abs(a[i]) + Math.Abs(b[i]));
+ }
+
+ return sum;
+ }
+
+ ///
+ /// Cosine Distance, representing the angular distance while ignoring the scale.
+ ///
+ public static double Cosine(double[] a, double[] b)
+ {
+ if (a.Length != b.Length)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ var ab = LinearAlgebraControl.Provider.DotProduct(a, b);
+ var a2 = LinearAlgebraControl.Provider.DotProduct(a, a);
+ var b2 = LinearAlgebraControl.Provider.DotProduct(b, b);
+ return 1d - ab / Math.Sqrt(a2 * b2);
+ }
+
+ ///
+ /// Cosine Distance, representing the angular distance while ignoring the scale.
+ ///
+ public static float Cosine(float[] a, float[] b)
+ {
+ if (a.Length != b.Length)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ var ab = LinearAlgebraControl.Provider.DotProduct(a, b);
+ var a2 = LinearAlgebraControl.Provider.DotProduct(a, a);
+ var b2 = LinearAlgebraControl.Provider.DotProduct(b, b);
+ return (float)(1d - ab / Math.Sqrt(a2 * b2));
+ }
+
+ ///
+ /// Hamming Distance, i.e. the number of positions that have different values in the vectors.
+ ///
+ public static double Hamming(double[] a, double[] b)
+ {
+ if (a.Length != b.Length)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ int count = 0;
+ for (int i = 0; i < a.Length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ count++;
+ }
+ }
+
+ return count;
+ }
+
+ ///
+ /// Hamming Distance, i.e. the number of positions that have different values in the vectors.
+ ///
+ public static float Hamming(float[] a, float[] b)
+ {
+ if (a.Length != b.Length)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ int count = 0;
+ for (int i = 0; i < a.Length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ count++;
+ }
+ }
+
+ return count;
+ }
+
+ ///
+ /// Pearson's distance, i.e. 1 - the person correlation coefficient.
+ ///
+ public static double Pearson(IEnumerable a, IEnumerable b)
+ {
+ return 1.0 - Correlation.Pearson(a, b);
+ }
+
+ ///
+ /// Jaccard distance, i.e. 1 - the Jaccard index.
+ ///
+ /// Thrown if a or b are null.
+ /// Throw if a and b are of different lengths.
+ /// Jaccard distance.
+ public static double Jaccard(double[] a, double[] b)
+ {
+ int intersection = 0, union = 0;
+
+ if (a == null)
+ {
+ throw new ArgumentNullException(nameof(a));
+ }
+
+ if (b == null)
+ {
+ throw new ArgumentNullException(nameof(b));
+ }
+
+ if (a.Length != b.Length)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ if (a.Length == 0 && b.Length == 0)
+ {
+ return 0;
+ }
+
+ for (int x = 0, len = a.Length; x < len; x++)
+ {
+ if (a[x] != 0 && b[x] != 0)
+ {
+ if (a[x] == b[x])
+ {
+ intersection++;
+ }
+
+ union++;
+ }
+ }
+
+ return 1.0 - ((double)intersection / (double)union);
+ }
+
+ ///
+ /// Jaccard distance, i.e. 1 - the Jaccard index.
+ ///
+ /// Thrown if a or b are null.
+ /// Throw if a and b are of different lengths.
+ /// Jaccard distance.
+ public static double Jaccard(float[] a, float[] b)
+ {
+ int intersection = 0, union = 0;
+
+ if (a == null)
+ {
+ throw new ArgumentNullException(nameof(a));
+ }
+
+ if (b == null)
+ {
+ throw new ArgumentNullException(nameof(b));
+ }
+
+ if (a.Length != b.Length)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ if (a.Length == 0 && b.Length == 0)
+ {
+ return 0;
+ }
+
+ for (int x = 0, len = a.Length; x < len; x++)
+ {
+ if (a[x] != 0 && b[x] != 0)
+ {
+ if (a[x] == b[x])
+ {
+ intersection++;
+ }
+
+ union++;
+ }
+ }
+
+ return 1.0 - ((float)intersection / (float)union);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/Bernoulli.cs b/MathNet.Numerics/Distributions/Bernoulli.cs
new file mode 100644
index 0000000..517bf01
--- /dev/null
+++ b/MathNet.Numerics/Distributions/Bernoulli.cs
@@ -0,0 +1,485 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2014 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+using MathNet.Numerics.Threading;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Discrete Univariate Bernoulli distribution.
+/// The Bernoulli distribution is a distribution over bits. The parameter
+/// p specifies the probability that a 1 is generated.
+/// Wikipedia - Bernoulli distribution.
+///
+public class Bernoulli : IDiscreteDistribution
+{
+ System.Random _random;
+
+ readonly double _p;
+
+ ///
+ /// Initializes a new instance of the Bernoulli class.
+ ///
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ /// If the Bernoulli parameter is not in the range [0,1].
+ public Bernoulli(double p)
+ {
+ if (!IsValidParameterSet(p))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _p = p;
+ }
+
+ ///
+ /// Initializes a new instance of the Bernoulli class.
+ ///
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ /// The random number generator which is used to draw random samples.
+ /// If the Bernoulli parameter is not in the range [0,1].
+ public Bernoulli(double p, System.Random randomSource)
+ {
+ if (!IsValidParameterSet(p))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _p = p;
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ /// a string representation of the distribution.
+ public override string ToString()
+ {
+ return "Bernoulli(p = " + _p + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ public static bool IsValidParameterSet(double p)
+ {
+ return p >= 0.0 && p <= 1.0;
+ }
+
+ ///
+ /// Gets the probability of generating a one. Range: 0 ≤ p ≤ 1.
+ ///
+ public double P
+ {
+ get { return _p; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the distribution.
+ ///
+ public double Mean
+ {
+ get { return _p; }
+ }
+
+ ///
+ /// Gets the standard deviation of the distribution.
+ ///
+ public double StdDev
+ {
+ get { return Math.Sqrt(_p * (1.0 - _p)); }
+ }
+
+ ///
+ /// Gets the variance of the distribution.
+ ///
+ public double Variance
+ {
+ get { return _p * (1.0 - _p); }
+ }
+
+ ///
+ /// Gets the entropy of the distribution.
+ ///
+ public double Entropy
+ {
+ get { return -(_p * Math.Log(_p)) - ((1.0 - _p) * Math.Log(1.0 - _p)); }
+ }
+
+ ///
+ /// Gets the skewness of the distribution.
+ ///
+ public double Skewness
+ {
+ get { return (1.0 - (2.0 * _p)) / Math.Sqrt(_p * (1.0 - _p)); }
+ }
+
+ ///
+ /// Gets the smallest element in the domain of the distributions which can be represented by an integer.
+ ///
+ public int Minimum
+ {
+ get { return 0; }
+ }
+
+ ///
+ /// Gets the largest element in the domain of the distributions which can be represented by an integer.
+ ///
+ public int Maximum
+ {
+ get { return 1; }
+ }
+
+ ///
+ /// Gets the mode of the distribution.
+ ///
+ public int Mode
+ {
+ get { return _p > 0.5 ? 1 : 0; }
+ }
+
+ ///
+ /// Gets all modes of the distribution.
+ ///
+ public int[] Modes
+ {
+ get { return _p < 0.5 ? new[] { 0 } : P > 0.5 ? new[] { 1 } : new[] { 0, 1 }; }
+ }
+
+ ///
+ /// Gets the median of the distribution.
+ ///
+ public double Median
+ {
+ get { return _p < 0.5 ? 0.0 : _p > 0.5 ? 1.0 : 0.5; }
+ }
+
+ ///
+ /// Computes the probability mass (PMF) at k, i.e. P(X = k).
+ ///
+ /// The location in the domain where we want to evaluate the probability mass function.
+ /// the probability mass at location .
+ public double Probability(int k)
+ {
+ if (k == 0)
+ {
+ return 1.0 - _p;
+ }
+
+ if (k == 1)
+ {
+ return _p;
+ }
+
+ return 0.0;
+ }
+
+ ///
+ /// Computes the log probability mass (lnPMF) at k, i.e. ln(P(X = k)).
+ ///
+ /// The location in the domain where we want to evaluate the log probability mass function.
+ /// the log probability mass at location .
+ public double ProbabilityLn(int k)
+ {
+ if (k == 0)
+ {
+ return Math.Log(1.0 - _p);
+ }
+
+ return k == 1 ? Math.Log(_p) : double.NegativeInfinity;
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ public double CumulativeDistribution(double x)
+ {
+ if (x < 0.0)
+ {
+ return 0.0;
+ }
+
+ if (x >= 1.0)
+ {
+ return 1.0;
+ }
+
+ return 1.0 - _p;
+ }
+
+ ///
+ /// Computes the probability mass (PMF) at k, i.e. P(X = k).
+ ///
+ /// The location in the domain where we want to evaluate the probability mass function.
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ /// the probability mass at location .
+ public static double PMF(double p, int k)
+ {
+ if (!(p >= 0.0 && p <= 1.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (k == 0)
+ {
+ return 1.0 - p;
+ }
+
+ if (k == 1)
+ {
+ return p;
+ }
+
+ return 0.0;
+ }
+
+ ///
+ /// Computes the log probability mass (lnPMF) at k, i.e. ln(P(X = k)).
+ ///
+ /// The location in the domain where we want to evaluate the log probability mass function.
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ /// the log probability mass at location .
+ public static double PMFLn(double p, int k)
+ {
+ if (!(p >= 0.0 && p <= 1.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (k == 0)
+ {
+ return Math.Log(1.0 - p);
+ }
+
+ return k == 1 ? Math.Log(p) : double.NegativeInfinity;
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(double p, double x)
+ {
+ if (!(p >= 0.0 && p <= 1.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (x < 0.0)
+ {
+ return 0.0;
+ }
+
+ if (x >= 1.0)
+ {
+ return 1.0;
+ }
+
+ return 1.0 - p;
+ }
+
+ ///
+ /// Generates one sample from the Bernoulli distribution.
+ ///
+ /// The random source to use.
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ /// A random sample from the Bernoulli distribution.
+ static int SampleUnchecked(System.Random rnd, double p)
+ {
+ if (rnd.NextDouble() < p)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ static void SamplesUnchecked(System.Random rnd, int[] values, double p)
+ {
+ var uniform = rnd.NextDoubles(values.Length);
+ CommonParallel.For(0, values.Length, 4096, (a, b) =>
+ {
+ for (int i = a; i < b; i++)
+ {
+ values[i] = uniform[i] < p ? 1 : 0;
+ }
+ });
+ }
+
+ static IEnumerable SamplesUnchecked(System.Random rnd, double p)
+ {
+ return rnd.NextDoubleSequence().Select(r => r < p ? 1 : 0);
+ }
+
+ ///
+ /// Samples a Bernoulli distributed random variable.
+ ///
+ /// A sample from the Bernoulli distribution.
+ public int Sample()
+ {
+ return SampleUnchecked(_random, _p);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ public void Samples(int[] values)
+ {
+ SamplesUnchecked(_random, values, _p);
+ }
+
+ ///
+ /// Samples an array of Bernoulli distributed random variables.
+ ///
+ /// a sequence of samples from the distribution.
+ public IEnumerable Samples()
+ {
+ while (true)
+ {
+ yield return SampleUnchecked(_random, _p);
+ }
+ }
+
+ ///
+ /// Samples a Bernoulli distributed random variable.
+ ///
+ /// The random number generator to use.
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ /// A sample from the Bernoulli distribution.
+ public static int Sample(System.Random rnd, double p)
+ {
+ if (!(p >= 0.0 && p <= 1.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(rnd, p);
+ }
+
+ ///
+ /// Samples a sequence of Bernoulli distributed random variables.
+ ///
+ /// The random number generator to use.
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(System.Random rnd, double p)
+ {
+ if (!(p >= 0.0 && p <= 1.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(rnd, p);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ /// a sequence of samples from the distribution.
+ public static void Samples(System.Random rnd, int[] values, double p)
+ {
+ if (!(p >= 0.0 && p <= 1.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(rnd, values, p);
+ }
+
+ ///
+ /// Samples a Bernoulli distributed random variable.
+ ///
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ /// A sample from the Bernoulli distribution.
+ public static int Sample(double p)
+ {
+ if (!(p >= 0.0 && p <= 1.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(SystemRandomSource.Default, p);
+ }
+
+ ///
+ /// Samples a sequence of Bernoulli distributed random variables.
+ ///
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(double p)
+ {
+ if (!(p >= 0.0 && p <= 1.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(SystemRandomSource.Default, p);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ /// a sequence of samples from the distribution.
+ public static void Samples(int[] values, double p)
+ {
+ if (!(p >= 0.0 && p <= 1.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(SystemRandomSource.Default, values, p);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/Beta.cs b/MathNet.Numerics/Distributions/Beta.cs
new file mode 100644
index 0000000..9dfc2c4
--- /dev/null
+++ b/MathNet.Numerics/Distributions/Beta.cs
@@ -0,0 +1,764 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2015 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+using MathNet.Numerics.RootFinding;
+using MathNet.Numerics.Threading;
+
+using System;
+using System.Collections.Generic;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Continuous Univariate Beta distribution.
+/// For details about this distribution, see
+/// Wikipedia - Beta distribution.
+///
+///
+/// There are a few special cases for the parameterization of the Beta distribution. When both
+/// shape parameters are positive infinity, the Beta distribution degenerates to a point distribution
+/// at 0.5. When one of the shape parameters is positive infinity, the distribution degenerates to a point
+/// distribution at the positive infinity. When both shape parameters are 0.0, the Beta distribution
+/// degenerates to a Bernoulli distribution with parameter 0.5. When one shape parameter is 0.0, the
+/// distribution degenerates to a point distribution at the non-zero shape parameter.
+///
+public class Beta : IContinuousDistribution
+{
+ System.Random _random;
+
+ readonly double _shapeA;
+ readonly double _shapeB;
+
+ ///
+ /// Initializes a new instance of the Beta class.
+ ///
+ /// The α shape parameter of the Beta distribution. Range: α ≥ 0.
+ /// The β shape parameter of the Beta distribution. Range: β ≥ 0.
+ public Beta(double a, double b)
+ {
+ if (!IsValidParameterSet(a, b))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _shapeA = a;
+ _shapeB = b;
+ }
+
+ ///
+ /// Initializes a new instance of the Beta class.
+ ///
+ /// The α shape parameter of the Beta distribution. Range: α ≥ 0.
+ /// The β shape parameter of the Beta distribution. Range: β ≥ 0.
+ /// The random number generator which is used to draw random samples.
+ public Beta(double a, double b, System.Random randomSource)
+ {
+ if (!IsValidParameterSet(a, b))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _shapeA = a;
+ _shapeB = b;
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ /// A string representation of the Beta distribution.
+ public override string ToString()
+ {
+ return "Beta(α = " + _shapeA + ", β = " + _shapeB + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The α shape parameter of the Beta distribution. Range: α ≥ 0.
+ /// The β shape parameter of the Beta distribution. Range: β ≥ 0.
+ public static bool IsValidParameterSet(double a, double b)
+ {
+ return a >= 0.0 && b >= 0.0;
+ }
+
+ ///
+ /// Gets the α shape parameter of the Beta distribution. Range: α ≥ 0.
+ ///
+ public double A
+ {
+ get { return _shapeA; }
+ }
+
+ ///
+ /// Gets the β shape parameter of the Beta distribution. Range: β ≥ 0.
+ ///
+ public double B
+ {
+ get { return _shapeB; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the Beta distribution.
+ ///
+ public double Mean
+ {
+ get
+ {
+ if (_shapeA == 0.0 && _shapeB == 0.0)
+ {
+ return 0.5;
+ }
+
+ if (_shapeA == 0.0)
+ {
+ return 0.0;
+ }
+
+ if (_shapeB == 0.0)
+ {
+ return 1.0;
+ }
+
+ if (double.IsPositiveInfinity(_shapeA) && double.IsPositiveInfinity(_shapeB))
+ {
+ return 0.5;
+ }
+
+ if (double.IsPositiveInfinity(_shapeA))
+ {
+ return 1.0;
+ }
+
+ if (double.IsPositiveInfinity(_shapeB))
+ {
+ return 0.0;
+ }
+
+ return _shapeA / (_shapeA + _shapeB);
+ }
+ }
+
+ ///
+ /// Gets the variance of the Beta distribution.
+ ///
+ public double Variance
+ {
+ get { return (_shapeA * _shapeB) / ((_shapeA + _shapeB) * (_shapeA + _shapeB) * (_shapeA + _shapeB + 1.0)); }
+ }
+
+ ///
+ /// Gets the standard deviation of the Beta distribution.
+ ///
+ public double StdDev
+ {
+ get { return Math.Sqrt((_shapeA * _shapeB) / ((_shapeA + _shapeB) * (_shapeA + _shapeB) * (_shapeA + _shapeB + 1.0))); }
+ }
+
+ ///
+ /// Gets the entropy of the Beta distribution.
+ ///
+ public double Entropy
+ {
+ get
+ {
+ if (double.IsPositiveInfinity(_shapeA) || double.IsPositiveInfinity(_shapeB))
+ {
+ return 0.0;
+ }
+
+ if (_shapeA == 0.0 && _shapeB == 0.0)
+ {
+ return -Math.Log(0.5);
+ }
+
+ if (_shapeA == 0.0 || _shapeB == 0.0)
+ {
+ return 0.0;
+ }
+
+ return SpecialFunctions.BetaLn(_shapeA, _shapeB)
+ - ((_shapeA - 1.0) * SpecialFunctions.DiGamma(_shapeA))
+ - ((_shapeB - 1.0) * SpecialFunctions.DiGamma(_shapeB))
+ + ((_shapeA + _shapeB - 2.0) * SpecialFunctions.DiGamma(_shapeA + _shapeB));
+ }
+ }
+
+ ///
+ /// Gets the skewness of the Beta distribution.
+ ///
+ public double Skewness
+ {
+ get
+ {
+ if (double.IsPositiveInfinity(_shapeA) && double.IsPositiveInfinity(_shapeB))
+ {
+ return 0.0;
+ }
+
+ if (double.IsPositiveInfinity(_shapeA))
+ {
+ return -2.0;
+ }
+
+ if (double.IsPositiveInfinity(_shapeB))
+ {
+ return 2.0;
+ }
+
+ if (_shapeA == 0.0 && _shapeB == 0.0)
+ {
+ return 0.0;
+ }
+
+ if (_shapeA == 0.0)
+ {
+ return 2.0;
+ }
+
+ if (_shapeB == 0.0)
+ {
+ return -2.0;
+ }
+
+ return 2.0 * (_shapeB - _shapeA) * Math.Sqrt(_shapeA + _shapeB + 1.0)
+ / ((_shapeA + _shapeB + 2.0) * Math.Sqrt(_shapeA * _shapeB));
+ }
+ }
+
+ ///
+ /// Gets the mode of the Beta distribution; when there are multiple answers, this routine will return 0.5.
+ ///
+ public double Mode
+ {
+ get
+ {
+ if (_shapeA == 0.0 && _shapeB == 0.0)
+ {
+ return 0.5;
+ }
+
+ if (_shapeA == 0.0)
+ {
+ return 0.0;
+ }
+
+ if (_shapeB == 0.0)
+ {
+ return 1.0;
+ }
+
+ if (double.IsPositiveInfinity(_shapeA) && double.IsPositiveInfinity(_shapeB))
+ {
+ return 0.5;
+ }
+
+ if (double.IsPositiveInfinity(_shapeA))
+ {
+ return 1.0;
+ }
+
+ if (double.IsPositiveInfinity(_shapeB))
+ {
+ return 0.0;
+ }
+
+ if (_shapeA == 1.0 && _shapeB == 1.0)
+ {
+ return 0.5;
+ }
+
+ return (_shapeA - 1) / (_shapeA + _shapeB - 2);
+ }
+ }
+
+ ///
+ /// Gets the median of the Beta distribution.
+ ///
+ public double Median
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the minimum of the Beta distribution.
+ ///
+ public double Minimum
+ {
+ get { return 0.0; }
+ }
+
+ ///
+ /// Gets the maximum of the Beta distribution.
+ ///
+ public double Maximum
+ {
+ get { return 1.0; }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public double Density(double x)
+ {
+ return PDF(_shapeA, _shapeB, x);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The location at which to compute the log density.
+ /// the log density at .
+ ///
+ public double DensityLn(double x)
+ {
+ return PDFLn(_shapeA, _shapeB, x);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ ///
+ public double CumulativeDistribution(double x)
+ {
+ return CDF(_shapeA, _shapeB, x);
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability. This is also known as the quantile or percent point function.
+ ///
+ /// The location at which to compute the inverse cumulative density.
+ /// the inverse cumulative density at .
+ ///
+ /// WARNING: currently not an explicit implementation, hence slow and unreliable.
+ public double InverseCumulativeDistribution(double p)
+ {
+ return InvCDF(_shapeA, _shapeB, p);
+ }
+
+ ///
+ /// Generates a sample from the Beta distribution.
+ ///
+ /// a sample from the distribution.
+ public double Sample()
+ {
+ return SampleUnchecked(_random, _shapeA, _shapeB);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ public void Samples(double[] values)
+ {
+ SamplesUnchecked(_random, values, _shapeA, _shapeB);
+ }
+
+ ///
+ /// Generates a sequence of samples from the Beta distribution.
+ ///
+ /// a sequence of samples from the distribution.
+ public IEnumerable Samples()
+ {
+ return SamplesUnchecked(_random, _shapeA, _shapeB);
+ }
+
+ ///
+ /// Samples Beta distributed random variables by sampling two Gamma variables and normalizing.
+ ///
+ /// The random number generator to use.
+ /// The α shape parameter of the Beta distribution. Range: α ≥ 0.
+ /// The β shape parameter of the Beta distribution. Range: β ≥ 0.
+ /// a random number from the Beta distribution.
+ internal static double SampleUnchecked(System.Random rnd, double a, double b)
+ {
+ var x = Gamma.SampleUnchecked(rnd, a, 1.0);
+ var y = Gamma.SampleUnchecked(rnd, b, 1.0);
+ return x / (x + y);
+ }
+
+ internal static void SamplesUnchecked(System.Random rnd, double[] values, double a, double b)
+ {
+ var y = new double[values.Length];
+ Gamma.SamplesUnchecked(rnd, values, a, 1.0);
+ Gamma.SamplesUnchecked(rnd, y, b, 1.0);
+ CommonParallel.For(0, values.Length, 4096, (aa, bb) =>
+ {
+ for (int i = aa; i < bb; i++)
+ {
+ values[i] = values[i] / (values[i] + y[i]);
+ }
+ });
+ }
+
+ static IEnumerable SamplesUnchecked(System.Random rnd, double a, double b)
+ {
+ while (true)
+ {
+ yield return SampleUnchecked(rnd, a, b);
+ }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The α shape parameter of the Beta distribution. Range: α ≥ 0.
+ /// The β shape parameter of the Beta distribution. Range: β ≥ 0.
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public static double PDF(double a, double b, double x)
+ {
+ if (a < 0.0 || b < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (x < 0.0 || x > 1.0)
+ {
+ return 0.0;
+ }
+
+ if (double.IsPositiveInfinity(a) && double.IsPositiveInfinity(b))
+ {
+ return x == 0.5 ? double.PositiveInfinity : 0.0;
+ }
+
+ if (double.IsPositiveInfinity(a))
+ {
+ return x == 1.0 ? double.PositiveInfinity : 0.0;
+ }
+
+ if (double.IsPositiveInfinity(b))
+ {
+ return x == 0.0 ? double.PositiveInfinity : 0.0;
+ }
+
+ if (a == 0.0 && b == 0.0)
+ {
+ if (x == 0.0 || x == 1.0)
+ {
+ return double.PositiveInfinity;
+ }
+
+ return 0.0;
+ }
+
+ if (a == 0.0)
+ {
+ return x == 0.0 ? double.PositiveInfinity : 0.0;
+ }
+
+ if (b == 0.0)
+ {
+ return x == 1.0 ? double.PositiveInfinity : 0.0;
+ }
+
+ if (a == 1.0 && b == 1.0)
+ {
+ return 1.0;
+ }
+
+ if (a > 80.0 || b > 80.0)
+ {
+ return Math.Exp(PDFLn(a, b, x));
+ }
+
+ var bb = SpecialFunctions.Gamma(a + b) / (SpecialFunctions.Gamma(a) * SpecialFunctions.Gamma(b));
+ return bb * Math.Pow(x, a - 1.0) * Math.Pow(1.0 - x, b - 1.0);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The α shape parameter of the Beta distribution. Range: α ≥ 0.
+ /// The β shape parameter of the Beta distribution. Range: β ≥ 0.
+ /// The location at which to compute the density.
+ /// the log density at .
+ ///
+ public static double PDFLn(double a, double b, double x)
+ {
+ if (a < 0.0 || b < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (x < 0.0 || x > 1.0)
+ {
+ return double.NegativeInfinity;
+ }
+
+ if (double.IsPositiveInfinity(a) && double.IsPositiveInfinity(b))
+ {
+ return x == 0.5 ? double.PositiveInfinity : double.NegativeInfinity;
+ }
+
+ if (double.IsPositiveInfinity(a))
+ {
+ return x == 1.0 ? double.PositiveInfinity : double.NegativeInfinity;
+ }
+
+ if (double.IsPositiveInfinity(b))
+ {
+ return x == 0.0 ? double.PositiveInfinity : double.NegativeInfinity;
+ }
+
+ if (a == 0.0 && b == 0.0)
+ {
+ return x == 0.0 || x == 1.0 ? double.PositiveInfinity : double.NegativeInfinity;
+ }
+
+ if (a == 0.0)
+ {
+ return x == 0.0 ? double.PositiveInfinity : double.NegativeInfinity;
+ }
+
+ if (b == 0.0)
+ {
+ return x == 1.0 ? double.PositiveInfinity : double.NegativeInfinity;
+ }
+
+ if (a == 1.0 && b == 1.0)
+ {
+ return 0.0;
+ }
+
+ var aa = SpecialFunctions.GammaLn(a + b) - SpecialFunctions.GammaLn(a) - SpecialFunctions.GammaLn(b);
+ var bb = x == 0.0 ? (a == 1.0 ? 0.0 : double.NegativeInfinity) : (a - 1.0) * Math.Log(x);
+ var cc = x == 1.0 ? (b == 1.0 ? 0.0 : double.NegativeInfinity) : (b - 1.0) * Math.Log(1.0 - x);
+
+ return aa + bb + cc;
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// The α shape parameter of the Beta distribution. Range: α ≥ 0.
+ /// The β shape parameter of the Beta distribution. Range: β ≥ 0.
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(double a, double b, double x)
+ {
+ if (a < 0.0 || b < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (x < 0.0)
+ {
+ return 0.0;
+ }
+
+ if (x >= 1.0)
+ {
+ return 1.0;
+ }
+
+ if (double.IsPositiveInfinity(a) && double.IsPositiveInfinity(b))
+ {
+ return x < 0.5 ? 0.0 : 1.0;
+ }
+
+ if (double.IsPositiveInfinity(a))
+ {
+ return x < 1.0 ? 0.0 : 1.0;
+ }
+
+ if (double.IsPositiveInfinity(b))
+ {
+ return x >= 0.0 ? 1.0 : 0.0;
+ }
+
+ if (a == 0.0 && b == 0.0)
+ {
+ if (x >= 0.0 && x < 1.0)
+ {
+ return 0.5;
+ }
+
+ return 1.0;
+ }
+
+ if (a == 0.0)
+ {
+ return 1.0;
+ }
+
+ if (b == 0.0)
+ {
+ return x >= 1.0 ? 1.0 : 0.0;
+ }
+
+ if (a == 1.0 && b == 1.0)
+ {
+ return x;
+ }
+
+ return SpecialFunctions.BetaRegularized(a, b, x);
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability. This is also known as the quantile or percent point function.
+ ///
+ /// The location at which to compute the inverse cumulative density.
+ /// The α shape parameter of the Beta distribution. Range: α ≥ 0.
+ /// The β shape parameter of the Beta distribution. Range: β ≥ 0.
+ /// the inverse cumulative density at .
+ ///
+ /// WARNING: currently not an explicit implementation, hence slow and unreliable.
+ public static double InvCDF(double a, double b, double p)
+ {
+ if (a < 0.0 || b < 0.0 || p < 0.0 || p > 1.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return Brent.FindRoot(x => SpecialFunctions.BetaRegularized(a, b, x) - p, 0.0, 1.0, accuracy: 1e-12);
+ }
+
+ ///
+ /// Generates a sample from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The α shape parameter of the Beta distribution. Range: α ≥ 0.
+ /// The β shape parameter of the Beta distribution. Range: β ≥ 0.
+ /// a sample from the distribution.
+ public static double Sample(System.Random rnd, double a, double b)
+ {
+ if (a < 0.0 || b < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(rnd, a, b);
+ }
+
+ ///
+ /// Generates a sequence of samples from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The α shape parameter of the Beta distribution. Range: α ≥ 0.
+ /// The β shape parameter of the Beta distribution. Range: β ≥ 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(System.Random rnd, double a, double b)
+ {
+ if (a < 0.0 || b < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(rnd, a, b);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// The α shape parameter of the Beta distribution. Range: α ≥ 0.
+ /// The β shape parameter of the Beta distribution. Range: β ≥ 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(System.Random rnd, double[] values, double a, double b)
+ {
+ if (a < 0.0 || b < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(rnd, values, a, b);
+ }
+
+ ///
+ /// Generates a sample from the distribution.
+ ///
+ /// The α shape parameter of the Beta distribution. Range: α ≥ 0.
+ /// The β shape parameter of the Beta distribution. Range: β ≥ 0.
+ /// a sample from the distribution.
+ public static double Sample(double a, double b)
+ {
+ if (a < 0.0 || b < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(SystemRandomSource.Default, a, b);
+ }
+
+ ///
+ /// Generates a sequence of samples from the distribution.
+ ///
+ /// The α shape parameter of the Beta distribution. Range: α ≥ 0.
+ /// The β shape parameter of the Beta distribution. Range: β ≥ 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(double a, double b)
+ {
+ if (a < 0.0 || b < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(SystemRandomSource.Default, a, b);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// The α shape parameter of the Beta distribution. Range: α ≥ 0.
+ /// The β shape parameter of the Beta distribution. Range: β ≥ 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(double[] values, double a, double b)
+ {
+ if (a < 0.0 || b < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(SystemRandomSource.Default, values, a, b);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/BetaScaled.cs b/MathNet.Numerics/Distributions/BetaScaled.cs
new file mode 100644
index 0000000..05e4406
--- /dev/null
+++ b/MathNet.Numerics/Distributions/BetaScaled.cs
@@ -0,0 +1,624 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2015 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+using MathNet.Numerics.Threading;
+
+using System;
+using System.Collections.Generic;
+
+namespace MathNet.Numerics.Distributions;
+
+public class BetaScaled : IContinuousDistribution
+{
+ System.Random _random;
+
+ readonly double _shapeA;
+ readonly double _shapeB;
+ readonly double _location;
+ readonly double _scale;
+
+ ///
+ /// Initializes a new instance of the BetaScaled class.
+ ///
+ /// The α shape parameter of the BetaScaled distribution. Range: α > 0.
+ /// The β shape parameter of the BetaScaled distribution. Range: β > 0.
+ /// The location (μ) of the distribution.
+ /// The scale (σ) of the distribution. Range: σ > 0.
+ public BetaScaled(double a, double b, double location, double scale)
+ {
+ if (!IsValidParameterSet(a, b, location, scale))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _shapeA = a;
+ _shapeB = b;
+ _location = location;
+ _scale = scale;
+ }
+
+ ///
+ /// Initializes a new instance of the BetaScaled class.
+ ///
+ /// The α shape parameter of the BetaScaled distribution. Range: α > 0.
+ /// The β shape parameter of the BetaScaled distribution. Range: β > 0.
+ /// The location (μ) of the distribution.
+ /// The scale (σ) of the distribution. Range: σ > 0.
+ /// The random number generator which is used to draw random samples.
+ public BetaScaled(double a, double b, double location, double scale, System.Random randomSource)
+ {
+ if (!IsValidParameterSet(a, b, location, scale))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _shapeA = a;
+ _shapeB = b;
+ _location = location;
+ _scale = scale;
+ }
+
+ ///
+ /// Create a Beta PERT distribution, used in risk analysis and other domains where an expert forecast
+ /// is used to construct an underlying beta distribution.
+ ///
+ /// The minimum value.
+ /// The maximum value.
+ /// The most likely value (mode).
+ /// The random number generator which is used to draw random samples.
+ /// The Beta distribution derived from the PERT parameters.
+ public static BetaScaled PERT(double min, double max, double likely, System.Random randomSource = null)
+ {
+ if (min > max || likely > max || likely < min)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ // specified to make the formulas match the literature;
+ // traditionally set to 4 so that the range between min and max
+ // represents six standard deviations (sometimes called
+ // "the six-sigma assumption").
+ const double lambda = 4;
+
+ // calculate the mean
+ double mean = (min + max + lambda * likely) / (lambda + 2);
+
+ // derive the shape parameters a and b
+ double a;
+
+ // special case where mean and mode are identical
+ if (mean == likely)
+ {
+ a = (lambda / 2) + 1;
+ }
+ else
+ {
+ a = ((mean - min) * (2 * likely - min - max)) / ((likely - mean) * (max - min));
+ }
+
+ double b = (a * (max - mean)) / (mean - min);
+
+ return new BetaScaled(a, b, min, max - min, randomSource);
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ /// A string representation of the BetaScaled distribution.
+ public override string ToString()
+ {
+ return "BetaScaled(α = " + _shapeA + ", β = " + _shapeB + ", μ = " + _location + ", σ = " + _scale + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The α shape parameter of the BetaScaled distribution. Range: α > 0.
+ /// The β shape parameter of the BetaScaled distribution. Range: β > 0.
+ /// The location (μ) of the distribution.
+ /// The scale (σ) of the distribution. Range: σ > 0.
+ public static bool IsValidParameterSet(double a, double b, double location, double scale)
+ {
+ return a > 0.0 && b > 0.0 && scale > 0.0 && !double.IsNaN(location);
+ }
+
+ ///
+ /// Gets the α shape parameter of the BetaScaled distribution. Range: α > 0.
+ ///
+ public double A
+ {
+ get { return _shapeA; }
+ }
+
+ ///
+ /// Gets the β shape parameter of the BetaScaled distribution. Range: β > 0.
+ ///
+ public double B
+ {
+ get { return _shapeB; }
+ }
+
+ ///
+ /// Gets the location (μ) of the BetaScaled distribution.
+ ///
+ public double Location
+ {
+ get { return _location; }
+ }
+
+ ///
+ /// Gets the scale (σ) of the BetaScaled distribution. Range: σ > 0.
+ ///
+ public double Scale
+ {
+ get { return _scale; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the BetaScaled distribution.
+ ///
+ public double Mean
+ {
+ get
+ {
+ if (double.IsPositiveInfinity(_shapeA) && double.IsPositiveInfinity(_shapeB))
+ {
+ return _location + 0.5 * _scale;
+ }
+
+ if (double.IsPositiveInfinity(_shapeA))
+ {
+ return _location + _scale;
+ }
+
+ if (double.IsPositiveInfinity(_shapeB))
+ {
+ return _location;
+ }
+
+ return (_shapeB * _location + _shapeA * (_location + _scale)) / (_shapeA + _shapeB);
+ }
+ }
+
+ ///
+ /// Gets the variance of the BetaScaled distribution.
+ ///
+ public double Variance
+ {
+ get
+ {
+ double sum = _shapeA + _shapeB;
+ return (_shapeA * _shapeB * _scale * _scale) / (sum * sum * (1.0 + sum));
+ }
+ }
+
+ ///
+ /// Gets the standard deviation of the BetaScaled distribution.
+ ///
+ public double StdDev
+ {
+ get { return Math.Sqrt(Variance); }
+ }
+
+ ///
+ /// Gets the entropy of the BetaScaled distribution.
+ ///
+ public double Entropy
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the skewness of the BetaScaled distribution.
+ ///
+ public double Skewness
+ {
+ get
+ {
+ if (double.IsPositiveInfinity(_shapeA) && double.IsPositiveInfinity(_shapeB))
+ {
+ return 0.0;
+ }
+
+ if (double.IsPositiveInfinity(_shapeA))
+ {
+ return -2.0 * _scale / Math.Sqrt(_shapeB * _scale * _scale);
+ }
+
+ if (double.IsPositiveInfinity(_shapeB))
+ {
+ return 2.0 * _scale / Math.Sqrt(_shapeA * _scale * _scale);
+ }
+
+ double sum = _shapeA + _shapeB;
+ double variance = (_shapeA * _shapeB * _scale * _scale) / (sum * sum * (1.0 + sum));
+ return 2.0 * (_shapeB - _shapeA) * _scale / (sum * (2.0 + sum) * Math.Sqrt(variance));
+ }
+ }
+
+ ///
+ /// Gets the mode of the BetaScaled distribution; when there are multiple answers, this routine will return 0.5.
+ ///
+ public double Mode
+ {
+ get
+ {
+ if (double.IsPositiveInfinity(_shapeA) && double.IsPositiveInfinity(_shapeB))
+ {
+ return _location + 0.5 * _scale;
+ }
+
+ if (double.IsPositiveInfinity(_shapeA))
+ {
+ return _location + _scale;
+ }
+
+ if (double.IsPositiveInfinity(_shapeB))
+ {
+ return _location;
+ }
+
+ if (_shapeA == 1.0 && _shapeB == 1.0)
+ {
+ return _location + 0.5 * _scale;
+ }
+
+ return ((_shapeA - 1) / (_shapeA + _shapeB - 2)) * _scale + _location;
+ }
+ }
+
+ ///
+ /// Gets the median of the BetaScaled distribution.
+ ///
+ public double Median
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the minimum of the BetaScaled distribution.
+ ///
+ public double Minimum
+ {
+ get { return _location; }
+ }
+
+ ///
+ /// Gets the maximum of the BetaScaled distribution.
+ ///
+ public double Maximum
+ {
+ get { return _location + _scale; }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public double Density(double x)
+ {
+ return PDF(_shapeA, _shapeB, _location, _scale, x);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The location at which to compute the log density.
+ /// the log density at .
+ ///
+ public double DensityLn(double x)
+ {
+ return PDFLn(_shapeA, _shapeB, _location, _scale, x);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ ///
+ public double CumulativeDistribution(double x)
+ {
+ return CDF(_shapeA, _shapeB, _location, _scale, x);
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability. This is also known as the quantile or percent point function.
+ ///
+ /// The location at which to compute the inverse cumulative density.
+ /// the inverse cumulative density at .
+ ///
+ /// WARNING: currently not an explicit implementation, hence slow and unreliable.
+ public double InverseCumulativeDistribution(double p)
+ {
+ return InvCDF(_shapeA, _shapeB, _location, _scale, p);
+ }
+
+ ///
+ /// Generates a sample from the distribution.
+ ///
+ /// a sample from the distribution.
+ public double Sample()
+ {
+ return SampleUnchecked(_random, _shapeA, _shapeB, _location, _scale);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ public void Samples(double[] values)
+ {
+ SamplesUnchecked(_random, values, _shapeA, _shapeB, _location, _scale);
+ }
+
+ ///
+ /// Generates a sequence of samples from the distribution.
+ ///
+ /// a sequence of samples from the distribution.
+ public IEnumerable Samples()
+ {
+ return SamplesUnchecked(_random, _shapeA, _shapeB, _location, _scale);
+ }
+
+ static double SampleUnchecked(System.Random rnd, double a, double b, double location, double scale)
+ {
+ return Beta.SampleUnchecked(rnd, a, b) * scale + location;
+ }
+
+ static void SamplesUnchecked(System.Random rnd, double[] values, double a, double b, double location, double scale)
+ {
+ Beta.SamplesUnchecked(rnd, values, a, b);
+ CommonParallel.For(0, values.Length, 4096, (aa, bb) =>
+ {
+ for (int i = aa; i < bb; i++)
+ {
+ values[i] = values[i] * scale + location;
+ }
+ });
+ }
+
+ static IEnumerable SamplesUnchecked(System.Random rnd, double a, double b, double location, double scale)
+ {
+ while (true)
+ {
+ yield return SampleUnchecked(rnd, a, b, location, scale);
+ }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The α shape parameter of the BetaScaled distribution. Range: α > 0.
+ /// The β shape parameter of the BetaScaled distribution. Range: β > 0.
+ /// The location (μ) of the distribution.
+ /// The scale (σ) of the distribution. Range: σ > 0.
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public static double PDF(double a, double b, double location, double scale, double x)
+ {
+ if (!(a > 0.0 && b > 0.0 && scale > 0.0) || double.IsNaN(location))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return Beta.PDF(a, b, (x - location) / scale) / Math.Abs(scale);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The α shape parameter of the BetaScaled distribution. Range: α > 0.
+ /// The β shape parameter of the BetaScaled distribution. Range: β > 0.
+ /// The location (μ) of the distribution.
+ /// The scale (σ) of the distribution. Range: σ > 0.
+ /// The location at which to compute the density.
+ /// the log density at .
+ ///
+ public static double PDFLn(double a, double b, double location, double scale, double x)
+ {
+ if (!(a > 0.0 && b > 0.0 && scale > 0.0) || double.IsNaN(location))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return Beta.PDFLn(a, b, (x - location) / scale) - Math.Log(Math.Abs(scale));
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The α shape parameter of the BetaScaled distribution. Range: α > 0.
+ /// The β shape parameter of the BetaScaled distribution. Range: β > 0.
+ /// The location (μ) of the distribution.
+ /// The scale (σ) of the distribution. Range: σ > 0.
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(double a, double b, double location, double scale, double x)
+ {
+ if (!(a > 0.0 && b > 0.0 && scale > 0.0) || double.IsNaN(location))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return Beta.CDF(a, b, (x - location) / scale);
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability. This is also known as the quantile or percent point function.
+ ///
+ /// The α shape parameter of the BetaScaled distribution. Range: α > 0.
+ /// The β shape parameter of the BetaScaled distribution. Range: β > 0.
+ /// The location (μ) of the distribution.
+ /// The scale (σ) of the distribution. Range: σ > 0.
+ /// The location at which to compute the inverse cumulative density.
+ /// the inverse cumulative density at .
+ ///
+ /// WARNING: currently not an explicit implementation, hence slow and unreliable.
+ public static double InvCDF(double a, double b, double location, double scale, double p)
+ {
+ if (!(a > 0.0 && b > 0.0 && scale > 0.0) || double.IsNaN(location))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return Beta.InvCDF(a, b, p) * scale + location;
+ }
+
+ ///
+ /// Generates a sample from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The α shape parameter of the BetaScaled distribution. Range: α > 0.
+ /// The β shape parameter of the BetaScaled distribution. Range: β > 0.
+ /// The location (μ) of the distribution.
+ /// The scale (σ) of the distribution. Range: σ > 0.
+ /// a sample from the distribution.
+ public static double Sample(System.Random rnd, double a, double b, double location, double scale)
+ {
+ if (!(a > 0.0 && b > 0.0 && scale > 0.0) || double.IsNaN(location))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(rnd, a, b, location, scale);
+ }
+
+ ///
+ /// Generates a sequence of samples from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The α shape parameter of the BetaScaled distribution. Range: α > 0.
+ /// The β shape parameter of the BetaScaled distribution. Range: β > 0.
+ /// The location (μ) of the distribution.
+ /// The scale (σ) of the distribution. Range: σ > 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(System.Random rnd, double a, double b, double location, double scale)
+ {
+ if (!(a > 0.0 && b > 0.0 && scale > 0.0) || double.IsNaN(location))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(rnd, a, b, location, scale);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// The α shape parameter of the BetaScaled distribution. Range: α > 0.
+ /// The β shape parameter of the BetaScaled distribution. Range: β > 0.
+ /// The location (μ) of the distribution.
+ /// The scale (σ) of the distribution. Range: σ > 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(System.Random rnd, double[] values, double a, double b, double location, double scale)
+ {
+ if (!(a > 0.0 && b > 0.0 && scale > 0.0) || double.IsNaN(location))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(rnd, values, a, b, location, scale);
+ }
+
+ ///
+ /// Generates a sample from the distribution.
+ ///
+ /// The α shape parameter of the BetaScaled distribution. Range: α > 0.
+ /// The β shape parameter of the BetaScaled distribution. Range: β > 0.
+ /// The location (μ) of the distribution.
+ /// The scale (σ) of the distribution. Range: σ > 0.
+ /// a sample from the distribution.
+ public static double Sample(double a, double b, double location, double scale)
+ {
+ if (!(a > 0.0 && b > 0.0 && scale > 0.0) || double.IsNaN(location))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(SystemRandomSource.Default, a, b, location, scale);
+ }
+
+ ///
+ /// Generates a sequence of samples from the distribution.
+ ///
+ /// The α shape parameter of the BetaScaled distribution. Range: α > 0.
+ /// The β shape parameter of the BetaScaled distribution. Range: β > 0.
+ /// The location (μ) of the distribution.
+ /// The scale (σ) of the distribution. Range: σ > 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(double a, double b, double location, double scale)
+ {
+ if (!(a > 0.0 && b > 0.0 && scale > 0.0) || double.IsNaN(location))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(SystemRandomSource.Default, a, b, location, scale);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// The α shape parameter of the BetaScaled distribution. Range: α > 0.
+ /// The β shape parameter of the BetaScaled distribution. Range: β > 0.
+ /// The location (μ) of the distribution.
+ /// The scale (σ) of the distribution. Range: σ > 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(double[] values, double a, double b, double location, double scale)
+ {
+ if (!(a > 0.0 && b > 0.0 && scale > 0.0) || double.IsNaN(location))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(SystemRandomSource.Default, values, a, b, location, scale);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/Binomial.cs b/MathNet.Numerics/Distributions/Binomial.cs
new file mode 100644
index 0000000..142f50b
--- /dev/null
+++ b/MathNet.Numerics/Distributions/Binomial.cs
@@ -0,0 +1,554 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2014 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+using MathNet.Numerics.Threading;
+
+using System;
+using System.Collections.Generic;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Discrete Univariate Binomial distribution.
+/// For details about this distribution, see
+/// Wikipedia - Binomial distribution.
+///
+///
+/// The distribution is parameterized by a probability (between 0.0 and 1.0).
+///
+public class Binomial : IDiscreteDistribution
+{
+ System.Random _random;
+
+ readonly double _p;
+ readonly int _trials;
+
+ ///
+ /// Initializes a new instance of the Binomial class.
+ ///
+ /// The success probability (p) in each trial. Range: 0 ≤ p ≤ 1.
+ /// The number of trials (n). Range: n ≥ 0.
+ /// If is not in the interval [0.0,1.0].
+ /// If is negative.
+ public Binomial(double p, int n)
+ {
+ if (!IsValidParameterSet(p, n))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _p = p;
+ _trials = n;
+ }
+
+ ///
+ /// Initializes a new instance of the Binomial class.
+ ///
+ /// The success probability (p) in each trial. Range: 0 ≤ p ≤ 1.
+ /// The number of trials (n). Range: n ≥ 0.
+ /// The random number generator which is used to draw random samples.
+ /// If is not in the interval [0.0,1.0].
+ /// If is negative.
+ public Binomial(double p, int n, System.Random randomSource)
+ {
+ if (!IsValidParameterSet(p, n))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _p = p;
+ _trials = n;
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ /// a string representation of the distribution.
+ public override string ToString()
+ {
+ return "Binomial(p = " + _p + ", n = " + _trials + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The success probability (p) in each trial. Range: 0 ≤ p ≤ 1.
+ /// The number of trials (n). Range: n ≥ 0.
+ public static bool IsValidParameterSet(double p, int n)
+ {
+ return p >= 0.0 && p <= 1.0 && n >= 0;
+ }
+
+ ///
+ /// Gets the success probability in each trial. Range: 0 ≤ p ≤ 1.
+ ///
+ public double P
+ {
+ get { return _p; }
+ }
+
+ ///
+ /// Gets the number of trials. Range: n ≥ 0.
+ ///
+ public int N
+ {
+ get { return _trials; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the distribution.
+ ///
+ public double Mean
+ {
+ get { return _p * _trials; }
+ }
+
+ ///
+ /// Gets the standard deviation of the distribution.
+ ///
+ public double StdDev
+ {
+ get { return Math.Sqrt(_p * (1.0 - _p) * _trials); }
+ }
+
+ ///
+ /// Gets the variance of the distribution.
+ ///
+ public double Variance
+ {
+ get { return _p * (1.0 - _p) * _trials; }
+ }
+
+ ///
+ /// Gets the entropy of the distribution.
+ ///
+ public double Entropy
+ {
+ get
+ {
+ if (_p == 0.0 || _p == 1.0)
+ {
+ return 0.0;
+ }
+
+ var e = 0.0;
+ for (var i = 0; i <= _trials; i++)
+ {
+ var p = Probability(i);
+ e -= p * Math.Log(p);
+ }
+
+ return e;
+ }
+ }
+
+ ///
+ /// Gets the skewness of the distribution.
+ ///
+ public double Skewness
+ {
+ get { return (1.0 - (2.0 * _p)) / Math.Sqrt(_trials * _p * (1.0 - _p)); }
+ }
+
+ ///
+ /// Gets the smallest element in the domain of the distributions which can be represented by an integer.
+ ///
+ public int Minimum
+ {
+ get { return 0; }
+ }
+
+ ///
+ /// Gets the largest element in the domain of the distributions which can be represented by an integer.
+ ///
+ public int Maximum
+ {
+ get { return _trials; }
+ }
+
+ ///
+ /// Gets the mode of the distribution.
+ ///
+ public int Mode
+ {
+ get
+ {
+ if (_p == 1.0)
+ {
+ return _trials;
+ }
+
+ if (_p == 0.0)
+ {
+ return 0;
+ }
+
+ return (int)Math.Floor((_trials + 1) * _p);
+ }
+ }
+
+ ///
+ /// Gets all modes of the distribution.
+ ///
+ public int[] Modes
+ {
+ get
+ {
+ if (_p == 1.0)
+ {
+ return new[] { _trials };
+ }
+
+ if (_p == 0.0)
+ {
+ return new[] { 0 };
+ }
+
+ double td = (_trials + 1) * _p;
+ int t = (int)Math.Floor(td);
+ return t != td ? new[] { t } : new[] { t, t - 1 };
+ }
+ }
+
+ ///
+ /// Gets the median of the distribution.
+ ///
+ public double Median
+ {
+ get { return Math.Floor(_p * _trials); }
+ }
+
+ ///
+ /// Computes the probability mass (PMF) at k, i.e. P(X = k).
+ ///
+ /// The location in the domain where we want to evaluate the probability mass function.
+ /// the probability mass at location .
+ public double Probability(int k)
+ {
+ return PMF(_p, _trials, k);
+ }
+
+ ///
+ /// Computes the log probability mass (lnPMF) at k, i.e. ln(P(X = k)).
+ ///
+ /// The location in the domain where we want to evaluate the log probability mass function.
+ /// the log probability mass at location .
+ public double ProbabilityLn(int k)
+ {
+ return PMFLn(_p, _trials, k);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ public double CumulativeDistribution(double x)
+ {
+ return CDF(_p, _trials, x);
+ }
+
+ ///
+ /// Computes the probability mass (PMF) at k, i.e. P(X = k).
+ ///
+ /// The location in the domain where we want to evaluate the probability mass function.
+ /// The success probability (p) in each trial. Range: 0 ≤ p ≤ 1.
+ /// The number of trials (n). Range: n ≥ 0.
+ /// the probability mass at location .
+ public static double PMF(double p, int n, int k)
+ {
+ if (!(p >= 0.0 && p <= 1.0 && n >= 0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (k < 0 || k > n)
+ {
+ return 0.0;
+ }
+
+ if (p == 0.0)
+ {
+ return k == 0 ? 1.0 : 0.0;
+ }
+
+ if (p == 1.0)
+ {
+ return k == n ? 1.0 : 0.0;
+ }
+
+ return Math.Exp(SpecialFunctions.BinomialLn(n, k) + (k * Math.Log(p)) + ((n - k) * Math.Log(1.0 - p)));
+ }
+
+ ///
+ /// Computes the log probability mass (lnPMF) at k, i.e. ln(P(X = k)).
+ ///
+ /// The location in the domain where we want to evaluate the log probability mass function.
+ /// The success probability (p) in each trial. Range: 0 ≤ p ≤ 1.
+ /// The number of trials (n). Range: n ≥ 0.
+ /// the log probability mass at location .
+ public static double PMFLn(double p, int n, int k)
+ {
+ if (!(p >= 0.0 && p <= 1.0 && n >= 0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (k < 0 || k > n)
+ {
+ return double.NegativeInfinity;
+ }
+
+ if (p == 0.0)
+ {
+ return k == 0 ? 0.0 : double.NegativeInfinity;
+ }
+
+ if (p == 1.0)
+ {
+ return k == n ? 0.0 : double.NegativeInfinity;
+ }
+
+ return SpecialFunctions.BinomialLn(n, k) + (k * Math.Log(p)) + ((n - k) * Math.Log(1.0 - p));
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// The success probability (p) in each trial. Range: 0 ≤ p ≤ 1.
+ /// The number of trials (n). Range: n ≥ 0.
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(double p, int n, double x)
+ {
+ if (!(p >= 0.0 && p <= 1.0 && n >= 0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (x < 0.0)
+ {
+ return 0.0;
+ }
+
+ if (x > n)
+ {
+ return 1.0;
+ }
+
+ double k = Math.Floor(x);
+ return SpecialFunctions.BetaRegularized(n - k, k + 1, 1 - p);
+ }
+
+ ///
+ /// Generates a sample from the Binomial distribution without doing parameter checking.
+ ///
+ /// The random number generator to use.
+ /// The success probability (p) in each trial. Range: 0 ≤ p ≤ 1.
+ /// The number of trials (n). Range: n ≥ 0.
+ /// The number of successful trials.
+ static int SampleUnchecked(System.Random rnd, double p, int n)
+ {
+ var k = 0;
+ for (var i = 0; i < n; i++)
+ {
+ k += rnd.NextDouble() < p ? 1 : 0;
+ }
+
+ return k;
+ }
+
+ static void SamplesUnchecked(System.Random rnd, int[] values, double p, int n)
+ {
+ var uniform = rnd.NextDoubles(values.Length * n);
+ CommonParallel.For(0, values.Length, 4096, (a, b) =>
+ {
+ for (int i = a; i < b; i++)
+ {
+ int k = i * n;
+ int sum = 0;
+ for (int j = 0; j < n; j++)
+ {
+ sum += uniform[k + j] < p ? 1 : 0;
+ }
+
+ values[i] = sum;
+ }
+ });
+ }
+
+ static IEnumerable SamplesUnchecked(System.Random rnd, double p, int n)
+ {
+ while (true)
+ {
+ yield return SampleUnchecked(rnd, p, n);
+ }
+ }
+
+ ///
+ /// Samples a Binomially distributed random variable.
+ ///
+ /// The number of successes in N trials.
+ public int Sample()
+ {
+ return SampleUnchecked(_random, _p, _trials);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ public void Samples(int[] values)
+ {
+ SamplesUnchecked(_random, values, _p, _trials);
+ }
+
+ ///
+ /// Samples an array of Binomially distributed random variables.
+ ///
+ /// a sequence of successes in N trials.
+ public IEnumerable Samples()
+ {
+ return SamplesUnchecked(_random, _p, _trials);
+ }
+
+ ///
+ /// Samples a binomially distributed random variable.
+ ///
+ /// The random number generator to use.
+ /// The success probability (p) in each trial. Range: 0 ≤ p ≤ 1.
+ /// The number of trials (n). Range: n ≥ 0.
+ /// The number of successes in trials.
+ public static int Sample(System.Random rnd, double p, int n)
+ {
+ if (!(p >= 0.0 && p <= 1.0 && n >= 0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(rnd, p, n);
+ }
+
+ ///
+ /// Samples a sequence of binomially distributed random variable.
+ ///
+ /// The random number generator to use.
+ /// The success probability (p) in each trial. Range: 0 ≤ p ≤ 1.
+ /// The number of trials (n). Range: n ≥ 0.
+ /// a sequence of successes in trials.
+ public static IEnumerable Samples(System.Random rnd, double p, int n)
+ {
+ if (!(p >= 0.0 && p <= 1.0 && n >= 0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(rnd, p, n);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// The success probability (p) in each trial. Range: 0 ≤ p ≤ 1.
+ /// The number of trials (n). Range: n ≥ 0.
+ /// a sequence of successes in trials.
+ public static void Samples(System.Random rnd, int[] values, double p, int n)
+ {
+ if (!(p >= 0.0 && p <= 1.0 && n >= 0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(rnd, values, p, n);
+ }
+
+ ///
+ /// Samples a binomially distributed random variable.
+ ///
+ /// The success probability (p) in each trial. Range: 0 ≤ p ≤ 1.
+ /// The number of trials (n). Range: n ≥ 0.
+ /// The number of successes in trials.
+ public static int Sample(double p, int n)
+ {
+ if (!(p >= 0.0 && p <= 1.0 && n >= 0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(SystemRandomSource.Default, p, n);
+ }
+
+ ///
+ /// Samples a sequence of binomially distributed random variable.
+ ///
+ /// The success probability (p) in each trial. Range: 0 ≤ p ≤ 1.
+ /// The number of trials (n). Range: n ≥ 0.
+ /// a sequence of successes in trials.
+ public static IEnumerable Samples(double p, int n)
+ {
+ if (!(p >= 0.0 && p <= 1.0 && n >= 0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(SystemRandomSource.Default, p, n);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// The success probability (p) in each trial. Range: 0 ≤ p ≤ 1.
+ /// The number of trials (n). Range: n ≥ 0.
+ /// a sequence of successes in trials.
+ public static void Samples(int[] values, double p, int n)
+ {
+ if (!(p >= 0.0 && p <= 1.0 && n >= 0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(SystemRandomSource.Default, values, p, n);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/Burr.cs b/MathNet.Numerics/Distributions/Burr.cs
new file mode 100644
index 0000000..4193998
--- /dev/null
+++ b/MathNet.Numerics/Distributions/Burr.cs
@@ -0,0 +1,438 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2019 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+
+using System;
+using System.Collections.Generic;
+
+namespace MathNet.Numerics.Distributions;
+
+public class Burr : IContinuousDistribution
+{
+ private System.Random _random;
+
+ ///
+ /// Gets the scale (a) of the distribution. Range: a > 0.
+ ///
+ public double a { get; }
+
+ ///
+ /// Gets the first shape parameter (c) of the distribution. Range: c > 0.
+ ///
+ public double c { get; }
+
+ ///
+ /// Gets the second shape parameter (k) of the distribution. Range: k > 0.
+ ///
+ public double k { get; }
+
+ ///
+ /// Initializes a new instance of the Burr Type XII class.
+ ///
+ /// The scale parameter a of the Burr distribution. Range: a > 0.
+ /// The first shape parameter c of the Burr distribution. Range: c > 0.
+ /// The second shape parameter k of the Burr distribution. Range: k > 0.
+ /// The random number generator which is used to draw random samples. Optional, can be null.
+ public Burr(double a, double c, double k, System.Random randomSource = null)
+ {
+ if (!IsValidParameterSet(a, c, k))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ this.a = a;
+ this.c = c;
+ this.k = k;
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ /// a string representation of the distribution.
+ public override string ToString()
+ {
+ return "Burr(a = " + a + ", c = " + c + ", k = " + k + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The scale parameter a of the Burr distribution. Range: a > 0.
+ /// The first shape parameter c of the Burr distribution. Range: c > 0.
+ /// The second shape parameter k of the Burr distribution. Range: k > 0.
+ public static bool IsValidParameterSet(double a, double c, double k)
+ {
+ var allFinite = a.IsFinite() && c.IsFinite() && k.IsFinite();
+ return allFinite && a > 0.0 && c > 0.0 && k > 0.0;
+ }
+
+ ///
+ /// Gets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the Burr distribution.
+ ///
+ public double Mean
+ {
+ get
+ {
+ return (1 / SpecialFunctions.Gamma(k)) * a * SpecialFunctions.Gamma(1 + 1 / c) * SpecialFunctions.Gamma(k - 1 / c);
+ }
+ }
+
+ ///
+ /// Gets the variance of the Burr distribution.
+ ///
+ public double Variance
+ {
+ get
+ {
+ return (1 / SpecialFunctions.Gamma(k)) * Math.Pow(a, 2) * SpecialFunctions.Gamma(1 + 2 / c) * SpecialFunctions.Gamma(k - 2 / c)
+ - Math.Pow((1 / SpecialFunctions.Gamma(k)) * a * SpecialFunctions.Gamma(1 + 1 / c) * SpecialFunctions.Gamma(k - 1 / c), 2);
+ }
+ }
+
+ ///
+ /// Gets the standard deviation of the Burr distribution.
+ ///
+ public double StdDev
+ {
+ get
+ {
+ return Math.Sqrt(Variance);
+ }
+ }
+
+ ///
+ /// Gets the mode of the Burr distribution.
+ ///
+ public double Mode
+ {
+ get
+ {
+ return a * Math.Pow((c - 1) / (c * k + 1), 1 / c);
+ }
+ }
+
+ ///
+ /// Gets the minimum of the Burr distribution.
+ ///
+ public double Minimum
+ {
+ get { return 0.0; }
+ }
+
+ ///
+ /// Gets the maximum of the Burr distribution.
+ ///
+ public double Maximum
+ {
+ get { return double.PositiveInfinity; }
+ }
+
+ ///
+ /// Gets the entropy of the Burr distribution (currently not supported).
+ ///
+ public double Entropy
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the skewness of the Burr distribution.
+ ///
+ public double Skewness
+ {
+ get
+ {
+ var mean = Mean;
+ var variance = Variance;
+ var std = StdDev;
+ return (GetMoment(3) - 3 * mean * variance - mean * mean * mean) / (std * std * std);
+ }
+ }
+
+ ///
+ /// Gets the median of the Burr distribution.
+ ///
+ public double Median
+ {
+ get
+ {
+ return a * Math.Pow(Math.Pow(2, 1 / k) - 1, 1 / c);
+ }
+ }
+
+ ///
+ /// Generates a sample from the Burr distribution.
+ ///
+ /// a sample from the distribution.
+ public double Sample()
+ {
+ return SampleUnchecked(_random, a, c, k);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ public void Samples(double[] values)
+ {
+ SamplesUnchecked(_random, values, a, c, k);
+ }
+
+ ///
+ /// Generates a sequence of samples from the Burr distribution.
+ ///
+ /// a sequence of samples from the distribution.
+ public IEnumerable Samples()
+ {
+ return SamplesUnchecked(_random, a, c, k);
+ }
+
+ ///
+ /// Generates a sample from the Burr distribution.
+ ///
+ /// The random number generator to use.
+ /// The scale parameter a of the Burr distribution. Range: a > 0.
+ /// The first shape parameter c of the Burr distribution. Range: c > 0.
+ /// The second shape parameter k of the Burr distribution. Range: k > 0.
+ /// a sample from the distribution.
+ public static double Sample(System.Random rnd, double a, double c, double k)
+ {
+ if (!IsValidParameterSet(a, c, k))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(rnd, a, c, k);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// The scale parameter a of the Burr distribution. Range: a > 0.
+ /// The first shape parameter c of the Burr distribution. Range: c > 0.
+ /// The second shape parameter k of the Burr distribution. Range: k > 0.
+ public static void Samples(System.Random rnd, double[] values, double a, double c, double k)
+ {
+ if (!IsValidParameterSet(a, c, k))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(rnd, values, a, c, k);
+ }
+
+ ///
+ /// Generates a sequence of samples from the Burr distribution.
+ ///
+ /// The random number generator to use.
+ /// The scale parameter a of the Burr distribution. Range: a > 0.
+ /// The first shape parameter c of the Burr distribution. Range: c > 0.
+ /// The second shape parameter k of the Burr distribution. Range: k > 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(System.Random rnd, double a, double c, double k)
+ {
+ if (!IsValidParameterSet(a, c, k))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(rnd, a, c, k);
+ }
+
+ internal static double SampleUnchecked(System.Random rnd, double a, double c, double k)
+ {
+ var k_inv = 1 / k;
+ var c_inv = 1 / c;
+ double u = rnd.NextDouble();
+ return a * Math.Pow(Math.Pow(1 - u, -k_inv) - 1, c_inv);
+ }
+
+ internal static void SamplesUnchecked(System.Random rnd, double[] values, double a, double c, double k)
+ {
+ if (values.Length == 0)
+ {
+ return;
+ }
+
+ var k_inv = 1 / k;
+ var c_inv = 1 / c;
+ double[] u = rnd.NextDoubles(values.Length);
+
+ for (var j = 0; j < values.Length; ++j)
+ {
+ values[j] = a * Math.Pow(Math.Pow(1 - u[j], -k_inv) - 1, c_inv);
+ }
+ }
+
+ internal static IEnumerable SamplesUnchecked(System.Random rnd, double a, double c, double k)
+ {
+ while (true)
+ {
+ yield return SampleUnchecked(rnd, a, c, k);
+ }
+ }
+
+ ///
+ /// Gets the n-th raw moment of the distribution.
+ ///
+ /// The order (n) of the moment. Range: n ≥ 1.
+ /// the n-th moment of the distribution.
+ public double GetMoment(double n)
+ {
+ if (n > k * c)
+ {
+ throw new ArgumentException(Resources.ArgumentParameterSetInvalid);
+ }
+
+ var lambda_n = (n / c) * SpecialFunctions.Gamma(n / c) * SpecialFunctions.Gamma(k - n / c);
+ return Math.Pow(a, n) * lambda_n / SpecialFunctions.Gamma(k);
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public double Density(double x)
+ {
+ return DensityImpl(a, c, k, x);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The location at which to compute the log density.
+ /// the log density at .
+ ///
+ public double DensityLn(double x)
+ {
+ return DensityLnImpl(a, c, k, x);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ ///
+ public double CumulativeDistribution(double x)
+ {
+ return CumulativeDistributionImpl(a, c, k, x);
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The scale parameter a of the Burr distribution. Range: a > 0.
+ /// The first shape parameter c of the Burr distribution. Range: c > 0.
+ /// The second shape parameter k of the Burr distribution. Range: k > 0.
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public static double PDF(double a, double c, double k, double x)
+ {
+ if (!IsValidParameterSet(a, c, k))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return DensityImpl(a, c, k, x);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The scale parameter a of the Burr distribution. Range: a > 0.
+ /// The first shape parameter c of the Burr distribution. Range: c > 0.
+ /// The second shape parameter k of the Burr distribution. Range: k > 0.
+ /// The location at which to compute the log density.
+ /// the log density at .
+ ///
+ public static double PDFLn(double a, double c, double k, double x)
+ {
+ if (!IsValidParameterSet(a, c, k))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return DensityLnImpl(a, c, k, x);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The scale parameter a of the Burr distribution. Range: a > 0.
+ /// The first shape parameter c of the Burr distribution. Range: c > 0.
+ /// The second shape parameter k of the Burr distribution. Range: k > 0.
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(double a, double c, double k, double x)
+ {
+ if (!IsValidParameterSet(a, c, k))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return CumulativeDistributionImpl(a, c, k, x);
+ }
+
+ internal static double DensityImpl(double a, double c, double k, double x)
+ {
+ var numerator = (k * c / a) * Math.Pow(x / a, c - 1);
+ var denominator = Math.Pow(1 + Math.Pow(x / a, c), k + 1);
+ return numerator / denominator;
+ }
+
+ internal static double DensityLnImpl(double a, double c, double k, double x)
+ {
+ return Math.Log(DensityImpl(a, c, k, x));
+ }
+
+ internal static double CumulativeDistributionImpl(double a, double c, double k, double x)
+ {
+ var denominator = Math.Pow(1 + Math.Pow(x / a, c), k);
+ return 1 - 1 / denominator;
+ }
+}
diff --git a/MathNet.Numerics/Distributions/Categorical.cs b/MathNet.Numerics/Distributions/Categorical.cs
new file mode 100644
index 0000000..e2a6a74
--- /dev/null
+++ b/MathNet.Numerics/Distributions/Categorical.cs
@@ -0,0 +1,839 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2014 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+using MathNet.Numerics.Statistics;
+using MathNet.Numerics.Threading;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Discrete Univariate Categorical distribution.
+/// For details about this distribution, see
+/// Wikipedia - Categorical distribution. This
+/// distribution is sometimes called the Discrete distribution.
+///
+///
+/// The distribution is parameterized by a vector of ratios: in other words, the parameter
+/// does not have to be normalized and sum to 1. The reason is that some vectors can't be exactly normalized
+/// to sum to 1 in floating point representation.
+///
+///
+/// Support: 0..k where k = length(probability mass array)-1
+///
+public class Categorical : IDiscreteDistribution
+{
+ System.Random _random;
+
+ readonly double[] _pmfNormalized;
+ readonly double[] _cdfUnnormalized;
+
+ ///
+ /// Initializes a new instance of the Categorical class.
+ ///
+ /// An array of nonnegative ratios: this array does not need to be normalized
+ /// as this is often impossible using floating point arithmetic.
+ /// If any of the probabilities are negative or do not sum to one.
+ public Categorical(double[] probabilityMass)
+ : this(probabilityMass, SystemRandomSource.Default)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the Categorical class.
+ ///
+ /// An array of nonnegative ratios: this array does not need to be normalized
+ /// as this is often impossible using floating point arithmetic.
+ /// The random number generator which is used to draw random samples.
+ /// If any of the probabilities are negative or do not sum to one.
+ public Categorical(double[] probabilityMass, System.Random randomSource)
+ {
+ if (Control.CheckDistributionParameters && !IsValidProbabilityMass(probabilityMass))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+
+ // Extract unnormalized cumulative distribution
+ _cdfUnnormalized = new double[probabilityMass.Length];
+ _cdfUnnormalized[0] = probabilityMass[0];
+ for (int i = 1; i < probabilityMass.Length; i++)
+ {
+ _cdfUnnormalized[i] = _cdfUnnormalized[i - 1] + probabilityMass[i];
+ }
+
+ // Extract normalized probability mass
+ var sum = _cdfUnnormalized[_cdfUnnormalized.Length - 1];
+ _pmfNormalized = new double[probabilityMass.Length];
+ for (int i = 0; i < probabilityMass.Length; i++)
+ {
+ _pmfNormalized[i] = probabilityMass[i] / sum;
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the Categorical class from a . The distribution
+ /// will not be automatically updated when the histogram changes. The categorical distribution will have
+ /// one value for each bucket and a probability for that value proportional to the bucket count.
+ ///
+ /// The histogram from which to create the categorical variable.
+ public Categorical(Histogram histogram)
+ {
+ if (histogram == null)
+ {
+ throw new ArgumentNullException(nameof(histogram));
+ }
+
+ // The probability distribution vector.
+ var p = new double[histogram.BucketCount];
+
+ // Fill in the distribution vector.
+ for (var i = 0; i < histogram.BucketCount; i++)
+ {
+ p[i] = histogram[i].Count;
+ }
+
+ _random = SystemRandomSource.Default;
+
+ if (Control.CheckDistributionParameters && !IsValidProbabilityMass(p))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ // Extract unnormalized cumulative distribution
+ _cdfUnnormalized = new double[p.Length];
+ _cdfUnnormalized[0] = p[0];
+ for (int i1 = 1; i1 < p.Length; i1++)
+ {
+ _cdfUnnormalized[i1] = _cdfUnnormalized[i1 - 1] + p[i1];
+ }
+
+ // Extract normalized probability mass
+ var sum = _cdfUnnormalized[_cdfUnnormalized.Length - 1];
+ _pmfNormalized = new double[p.Length];
+ for (int i2 = 0; i2 < p.Length; i2++)
+ {
+ _pmfNormalized[i2] = p[i2] / sum;
+ }
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ /// a string representation of the distribution.
+ public override string ToString()
+ {
+ return "Categorical(Dimension = " + _pmfNormalized.Length + ")";
+ }
+
+ ///
+ /// Checks whether the parameters of the distribution are valid.
+ ///
+ /// An array of nonnegative ratios: this array does not need to be normalized as this is often impossible using floating point arithmetic.
+ /// If any of the probabilities are negative returns false, or if the sum of parameters is 0.0; otherwise true
+ public static bool IsValidProbabilityMass(double[] p)
+ {
+ var sum = 0.0;
+ for (int i = 0; i < p.Length; i++)
+ {
+ double t = p[i];
+ if (t < 0.0 || double.IsNaN(t))
+ {
+ return false;
+ }
+
+ sum += t;
+ }
+
+ return sum > 0.0;
+ }
+
+ ///
+ /// Checks whether the parameters of the distribution are valid.
+ ///
+ /// An array of nonnegative ratios: this array does not need to be normalized as this is often impossible using floating point arithmetic.
+ /// If any of the probabilities are negative returns false, or if the sum of parameters is 0.0; otherwise true
+ public static bool IsValidCumulativeDistribution(double[] cdf)
+ {
+ var last = 0.0;
+ for (int i = 0; i < cdf.Length; i++)
+ {
+ double t = cdf[i];
+ if (t < 0.0 || double.IsNaN(t) || t < last)
+ {
+ return false;
+ }
+
+ last = t;
+ }
+
+ return last > 0.0;
+ }
+
+ ///
+ /// Gets the probability mass vector (non-negative ratios) of the multinomial.
+ ///
+ /// Sometimes the normalized probability vector cannot be represented exactly in a floating point representation.
+ public double[] P
+ {
+ get { return (double[])_pmfNormalized.Clone(); }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the distribution.
+ ///
+ public double Mean
+ {
+ get
+ {
+ // Mean = E[X] = Sum(x * p(x), x=0..N-1)
+ // where f(x) is the probability mass function, and N is the number of categories.
+ var sum = 0.0;
+ for (int i = 0; i < _pmfNormalized.Length; i++)
+ {
+ sum += i * _pmfNormalized[i];
+ }
+
+ return sum;
+ }
+ }
+
+ ///
+ /// Gets the standard deviation of the distribution.
+ ///
+ public double StdDev
+ {
+ get { return Math.Sqrt(Variance); }
+ }
+
+ ///
+ /// Gets the variance of the distribution.
+ ///
+ public double Variance
+ {
+ get
+ {
+ // Variance = E[(X-E[X])^2] = E[X^2] - (E[X])^2 = Sum(p(x) * (x - E[X])^2), x=0..N-1)
+ var m = Mean;
+ var sum = 0.0;
+ for (int i = 0; i < _pmfNormalized.Length; i++)
+ {
+ var r = i - m;
+ sum += r * r * _pmfNormalized[i];
+ }
+
+ return sum;
+ }
+ }
+
+ ///
+ /// Gets the entropy of the distribution.
+ ///
+ public double Entropy
+ {
+ get { return -_pmfNormalized.Sum(p => p * Math.Log(p)); }
+ }
+
+ ///
+ /// Gets the skewness of the distribution.
+ ///
+ /// Throws a .
+ public double Skewness
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the smallest element in the domain of the distributions which can be represented by an integer.
+ ///
+ public int Minimum
+ {
+ get { return 0; }
+ }
+
+ ///
+ /// Gets the largest element in the domain of the distributions which can be represented by an integer.
+ ///
+ public int Maximum
+ {
+ get { return _pmfNormalized.Length - 1; }
+ }
+
+ ///
+ /// Gets he mode of the distribution.
+ ///
+ /// Throws a .
+ public int Mode
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the median of the distribution.
+ ///
+ public double Median
+ {
+ get { return InverseCumulativeDistribution(0.5); }
+ }
+
+ ///
+ /// Computes the probability mass (PMF) at k, i.e. P(X = k).
+ ///
+ /// The location in the domain where we want to evaluate the probability mass function.
+ /// the probability mass at location .
+ public double Probability(int k)
+ {
+ if (k < 0)
+ {
+ return 0.0;
+ }
+
+ if (k >= _pmfNormalized.Length)
+ {
+ return 0.0;
+ }
+
+ return _pmfNormalized[k];
+ }
+
+ ///
+ /// Computes the log probability mass (lnPMF) at k, i.e. ln(P(X = k)).
+ ///
+ /// The location in the domain where we want to evaluate the log probability mass function.
+ /// the log probability mass at location .
+ public double ProbabilityLn(int k)
+ {
+ if (k < 0)
+ {
+ return 0.0;
+ }
+
+ if (k >= _pmfNormalized.Length)
+ {
+ return 0.0;
+ }
+
+ return Math.Log(_pmfNormalized[k]);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ public double CumulativeDistribution(double x)
+ {
+ if (x < 0.0)
+ {
+ return 0.0;
+ }
+
+ if (x >= _cdfUnnormalized.Length)
+ {
+ return 1.0;
+ }
+
+ return _cdfUnnormalized[(int)Math.Floor(x)] / _cdfUnnormalized[_cdfUnnormalized.Length - 1];
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability.
+ ///
+ /// A real number between 0 and 1.
+ /// An integer between 0 and the size of the categorical (exclusive), that corresponds to the inverse CDF for the given probability.
+ public int InverseCumulativeDistribution(double probability)
+ {
+ if (probability < 0.0 || probability > 1.0 || double.IsNaN(probability))
+ {
+ throw new ArgumentOutOfRangeException(nameof(probability));
+ }
+
+ var denormalizedProbability = probability * _cdfUnnormalized[_cdfUnnormalized.Length - 1];
+ int idx = Array.BinarySearch(_cdfUnnormalized, denormalizedProbability);
+ if (idx < 0)
+ {
+ idx = ~idx;
+ }
+
+ return idx;
+ }
+
+ ///
+ /// Computes the probability mass (PMF) at k, i.e. P(X = k).
+ ///
+ /// The location in the domain where we want to evaluate the probability mass function.
+ /// An array of nonnegative ratios: this array does not need to be normalized
+ /// as this is often impossible using floating point arithmetic.
+ /// the probability mass at location .
+ public static double PMF(double[] probabilityMass, int k)
+ {
+ if (Control.CheckDistributionParameters && !IsValidProbabilityMass(probabilityMass))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (k < 0)
+ {
+ return 0.0;
+ }
+
+ if (k >= probabilityMass.Length)
+ {
+ return 0.0;
+ }
+
+ return probabilityMass[k] / probabilityMass.Sum();
+ }
+
+ ///
+ /// Computes the log probability mass (lnPMF) at k, i.e. ln(P(X = k)).
+ ///
+ /// The location in the domain where we want to evaluate the log probability mass function.
+ /// An array of nonnegative ratios: this array does not need to be normalized
+ /// as this is often impossible using floating point arithmetic.
+ /// the log probability mass at location .
+ public static double PMFLn(double[] probabilityMass, int k)
+ {
+ return Math.Log(PMF(probabilityMass, k));
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// An array of nonnegative ratios: this array does not need to be normalized
+ /// as this is often impossible using floating point arithmetic.
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(double[] probabilityMass, double x)
+ {
+ if (Control.CheckDistributionParameters && !IsValidProbabilityMass(probabilityMass))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (x < 0.0)
+ {
+ return 0.0;
+ }
+
+ if (x >= probabilityMass.Length)
+ {
+ return 1.0;
+ }
+
+ var cdfUnnormalized = ProbabilityMassToCumulativeDistribution(probabilityMass);
+ return cdfUnnormalized[(int)Math.Floor(x)] / cdfUnnormalized[cdfUnnormalized.Length - 1];
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability.
+ ///
+ /// An array of nonnegative ratios: this array does not need to be normalized
+ /// as this is often impossible using floating point arithmetic.
+ /// A real number between 0 and 1.
+ /// An integer between 0 and the size of the categorical (exclusive), that corresponds to the inverse CDF for the given probability.
+ public static int InvCDF(double[] probabilityMass, double probability)
+ {
+ if (Control.CheckDistributionParameters && !IsValidProbabilityMass(probabilityMass))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (probability < 0.0 || probability > 1.0 || double.IsNaN(probability))
+ {
+ throw new ArgumentOutOfRangeException(nameof(probability));
+ }
+
+ var cdfUnnormalized = ProbabilityMassToCumulativeDistribution(probabilityMass);
+ var denormalizedProbability = probability * cdfUnnormalized[cdfUnnormalized.Length - 1];
+ int idx = Array.BinarySearch(cdfUnnormalized, denormalizedProbability);
+ if (idx < 0)
+ {
+ idx = ~idx;
+ }
+
+ return idx;
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability.
+ ///
+ /// An array corresponding to a CDF for a categorical distribution. Not assumed to be normalized.
+ /// A real number between 0 and 1.
+ /// An integer between 0 and the size of the categorical (exclusive), that corresponds to the inverse CDF for the given probability.
+ public static int InvCDFWithCumulativeDistribution(double[] cdfUnnormalized, double probability)
+ {
+ if (Control.CheckDistributionParameters && !IsValidCumulativeDistribution(cdfUnnormalized))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (probability < 0.0 || probability > 1.0 || double.IsNaN(probability))
+ {
+ throw new ArgumentOutOfRangeException(nameof(probability));
+ }
+
+ var denormalizedProbability = probability * cdfUnnormalized[cdfUnnormalized.Length - 1];
+ int idx = Array.BinarySearch(cdfUnnormalized, denormalizedProbability);
+ if (idx < 0)
+ {
+ idx = ~idx;
+ }
+
+ return idx;
+ }
+
+ ///
+ /// Computes the cumulative distribution function. This method performs no parameter checking.
+ /// If the probability mass was normalized, the resulting cumulative distribution is normalized as well (up to numerical errors).
+ ///
+ /// An array of nonnegative ratios: this array does not need to be normalized
+ /// as this is often impossible using floating point arithmetic.
+ /// An array representing the unnormalized cumulative distribution function.
+ internal static double[] ProbabilityMassToCumulativeDistribution(double[] probabilityMass)
+ {
+ var cdfUnnormalized = new double[probabilityMass.Length];
+ cdfUnnormalized[0] = probabilityMass[0];
+ for (int i = 1; i < probabilityMass.Length; i++)
+ {
+ cdfUnnormalized[i] = cdfUnnormalized[i - 1] + probabilityMass[i];
+ }
+
+ return cdfUnnormalized;
+ }
+
+ ///
+ /// Returns one trials from the categorical distribution.
+ ///
+ /// The random number generator to use.
+ /// The (unnormalized) cumulative distribution of the probability distribution.
+ /// One sample from the categorical distribution implied by .
+ internal static int SampleUnchecked(System.Random rnd, double[] cdfUnnormalized)
+ {
+ // TODO : use binary search to speed up this procedure.
+ double u = rnd.NextDouble() * cdfUnnormalized[cdfUnnormalized.Length - 1];
+ var idx = 0;
+
+ if (u == 0.0d)
+ {
+ // skip zero-probability categories
+ while (0.0d == cdfUnnormalized[idx])
+ {
+ idx++;
+ }
+ }
+
+ while (u > cdfUnnormalized[idx])
+ {
+ idx++;
+ }
+
+ return idx;
+ }
+
+ static void SamplesUnchecked(System.Random rnd, int[] values, double[] cdfUnnormalized)
+ {
+ // TODO : use binary search to speed up this procedure.
+ double[] uniform = rnd.NextDoubles(values.Length);
+ double w = cdfUnnormalized[cdfUnnormalized.Length - 1];
+ CommonParallel.For(0, values.Length, 4096, (a, b) =>
+ {
+ for (int i = a; i < b; i++)
+ {
+ var u = uniform[i] * w;
+ var idx = 0;
+
+ if (u == 0.0d)
+ {
+ // skip zero-probability categories
+ while (0.0d == cdfUnnormalized[idx])
+ {
+ idx++;
+ }
+ }
+
+ while (u > cdfUnnormalized[idx])
+ {
+ idx++;
+ }
+
+ values[i] = idx;
+ }
+ });
+ }
+
+ static IEnumerable SamplesUnchecked(System.Random rnd, double[] cdfUnnormalized)
+ {
+ while (true)
+ {
+ yield return SampleUnchecked(rnd, cdfUnnormalized);
+ }
+ }
+
+ ///
+ /// Samples a Binomially distributed random variable.
+ ///
+ /// The number of successful trials.
+ public int Sample()
+ {
+ return SampleUnchecked(_random, _cdfUnnormalized);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ public void Samples(int[] values)
+ {
+ SamplesUnchecked(_random, values, _cdfUnnormalized);
+ }
+
+ ///
+ /// Samples an array of Bernoulli distributed random variables.
+ ///
+ /// a sequence of successful trial counts.
+ public IEnumerable Samples()
+ {
+ return SamplesUnchecked(_random, _cdfUnnormalized);
+ }
+
+ ///
+ /// Samples one categorical distributed random variable; also known as the Discrete distribution.
+ ///
+ /// The random number generator to use.
+ /// An array of nonnegative ratios. Not assumed to be normalized.
+ /// One random integer between 0 and the size of the categorical (exclusive).
+ public static int Sample(System.Random rnd, double[] probabilityMass)
+ {
+ if (Control.CheckDistributionParameters && !IsValidProbabilityMass(probabilityMass))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ var cdf = ProbabilityMassToCumulativeDistribution(probabilityMass);
+ return SampleUnchecked(rnd, cdf);
+ }
+
+ ///
+ /// Samples a categorically distributed random variable.
+ ///
+ /// The random number generator to use.
+ /// An array of nonnegative ratios. Not assumed to be normalized.
+ /// random integers between 0 and the size of the categorical (exclusive).
+ public static IEnumerable Samples(System.Random rnd, double[] probabilityMass)
+ {
+ if (Control.CheckDistributionParameters && !IsValidProbabilityMass(probabilityMass))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ var cdf = ProbabilityMassToCumulativeDistribution(probabilityMass);
+ return SamplesUnchecked(rnd, cdf);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// An array of nonnegative ratios. Not assumed to be normalized.
+ /// random integers between 0 and the size of the categorical (exclusive).
+ public static void Samples(System.Random rnd, int[] values, double[] probabilityMass)
+ {
+ if (Control.CheckDistributionParameters && !IsValidProbabilityMass(probabilityMass))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ var cdf = ProbabilityMassToCumulativeDistribution(probabilityMass);
+ SamplesUnchecked(rnd, values, cdf);
+ }
+
+ ///
+ /// Samples one categorical distributed random variable; also known as the Discrete distribution.
+ ///
+ /// An array of nonnegative ratios. Not assumed to be normalized.
+ /// One random integer between 0 and the size of the categorical (exclusive).
+ public static int Sample(double[] probabilityMass)
+ {
+ if (Control.CheckDistributionParameters && !IsValidProbabilityMass(probabilityMass))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ var cdf = ProbabilityMassToCumulativeDistribution(probabilityMass);
+ return SampleUnchecked(SystemRandomSource.Default, cdf);
+ }
+
+ ///
+ /// Samples a categorically distributed random variable.
+ ///
+ /// An array of nonnegative ratios. Not assumed to be normalized.
+ /// random integers between 0 and the size of the categorical (exclusive).
+ public static IEnumerable Samples(double[] probabilityMass)
+ {
+ if (Control.CheckDistributionParameters && !IsValidProbabilityMass(probabilityMass))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ var cdf = ProbabilityMassToCumulativeDistribution(probabilityMass);
+ return SamplesUnchecked(SystemRandomSource.Default, cdf);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// An array of nonnegative ratios. Not assumed to be normalized.
+ /// random integers between 0 and the size of the categorical (exclusive).
+ public static void Samples(int[] values, double[] probabilityMass)
+ {
+ if (Control.CheckDistributionParameters && !IsValidProbabilityMass(probabilityMass))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ var cdf = ProbabilityMassToCumulativeDistribution(probabilityMass);
+ SamplesUnchecked(SystemRandomSource.Default, values, cdf);
+ }
+
+ ///
+ /// Samples one categorical distributed random variable; also known as the Discrete distribution.
+ ///
+ /// The random number generator to use.
+ /// An array of the cumulative distribution. Not assumed to be normalized.
+ /// One random integer between 0 and the size of the categorical (exclusive).
+ public static int SampleWithCumulativeDistribution(System.Random rnd, double[] cdfUnnormalized)
+ {
+ if (Control.CheckDistributionParameters && !IsValidCumulativeDistribution(cdfUnnormalized))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(rnd, cdfUnnormalized);
+ }
+
+ ///
+ /// Samples a categorically distributed random variable.
+ ///
+ /// The random number generator to use.
+ /// An array of the cumulative distribution. Not assumed to be normalized.
+ /// random integers between 0 and the size of the categorical (exclusive).
+ public static IEnumerable SamplesWithCumulativeDistribution(System.Random rnd, double[] cdfUnnormalized)
+ {
+ if (Control.CheckDistributionParameters && !IsValidCumulativeDistribution(cdfUnnormalized))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(rnd, cdfUnnormalized);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// An array of the cumulative distribution. Not assumed to be normalized.
+ /// random integers between 0 and the size of the categorical (exclusive).
+ public static void SamplesWithCumulativeDistribution(System.Random rnd, int[] values, double[] cdfUnnormalized)
+ {
+ if (Control.CheckDistributionParameters && !IsValidCumulativeDistribution(cdfUnnormalized))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(rnd, values, cdfUnnormalized);
+ }
+
+ ///
+ /// Samples one categorical distributed random variable; also known as the Discrete distribution.
+ ///
+ /// An array of the cumulative distribution. Not assumed to be normalized.
+ /// One random integer between 0 and the size of the categorical (exclusive).
+ public static int SampleWithCumulativeDistribution(double[] cdfUnnormalized)
+ {
+ if (Control.CheckDistributionParameters && !IsValidCumulativeDistribution(cdfUnnormalized))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(SystemRandomSource.Default, cdfUnnormalized);
+ }
+
+ ///
+ /// Samples a categorically distributed random variable.
+ ///
+ /// An array of the cumulative distribution. Not assumed to be normalized.
+ /// random integers between 0 and the size of the categorical (exclusive).
+ public static IEnumerable SamplesWithCumulativeDistribution(double[] cdfUnnormalized)
+ {
+ if (Control.CheckDistributionParameters && !IsValidCumulativeDistribution(cdfUnnormalized))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(SystemRandomSource.Default, cdfUnnormalized);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// An array of the cumulative distribution. Not assumed to be normalized.
+ /// random integers between 0 and the size of the categorical (exclusive).
+ public static void SamplesWithCumulativeDistribution(int[] values, double[] cdfUnnormalized)
+ {
+ if (Control.CheckDistributionParameters && !IsValidCumulativeDistribution(cdfUnnormalized))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(SystemRandomSource.Default, values, cdfUnnormalized);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/Cauchy.cs b/MathNet.Numerics/Distributions/Cauchy.cs
new file mode 100644
index 0000000..d05623d
--- /dev/null
+++ b/MathNet.Numerics/Distributions/Cauchy.cs
@@ -0,0 +1,480 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2013 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+using MathNet.Numerics.Threading;
+
+using System;
+using System.Collections.Generic;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Continuous Univariate Cauchy distribution.
+/// The Cauchy distribution is a symmetric continuous probability distribution. For details about this distribution, see
+/// Wikipedia - Cauchy distribution.
+///
+public class Cauchy : IContinuousDistribution
+{
+ System.Random _random;
+
+ readonly double _location;
+ readonly double _scale;
+
+ ///
+ /// Initializes a new instance of the class with the location parameter set to 0 and the scale parameter set to 1
+ ///
+ public Cauchy() : this(0, 1)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The location (x0) of the distribution.
+ /// The scale (γ) of the distribution. Range: γ > 0.
+ public Cauchy(double location, double scale)
+ {
+ if (!IsValidParameterSet(location, scale))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _location = location;
+ _scale = scale;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The location (x0) of the distribution.
+ /// The scale (γ) of the distribution. Range: γ > 0.
+ /// The random number generator which is used to draw random samples.
+ public Cauchy(double location, double scale, System.Random randomSource)
+ {
+ if (!IsValidParameterSet(location, scale))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _location = location;
+ _scale = scale;
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ /// a string representation of the distribution.
+ public override string ToString()
+ {
+ return "Cauchy(x0 = " + _location + ", γ = " + _scale + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The location (x0) of the distribution.
+ /// The scale (γ) of the distribution. Range: γ > 0.
+ public static bool IsValidParameterSet(double location, double scale)
+ {
+ return scale > 0.0 && !double.IsNaN(location);
+ }
+
+ ///
+ /// Gets the location (x0) of the distribution.
+ ///
+ public double Location
+ {
+ get { return _location; }
+ }
+
+ ///
+ /// Gets the scale (γ) of the distribution. Range: γ > 0.
+ ///
+ public double Scale
+ {
+ get { return _scale; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the distribution.
+ ///
+ public double Mean
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the variance of the distribution.
+ ///
+ public double Variance
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the standard deviation of the distribution.
+ ///
+ public double StdDev
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the entropy of the distribution.
+ ///
+ public double Entropy
+ {
+ get { return Math.Log(4.0 * Constants.Pi * _scale); }
+ }
+
+ ///
+ /// Gets the skewness of the distribution.
+ ///
+ public double Skewness
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the mode of the distribution.
+ ///
+ public double Mode
+ {
+ get { return _location; }
+ }
+
+ ///
+ /// Gets the median of the distribution.
+ ///
+ public double Median
+ {
+ get { return _location; }
+ }
+
+ ///
+ /// Gets the minimum of the distribution.
+ ///
+ public double Minimum
+ {
+ get { return double.NegativeInfinity; }
+ }
+
+ ///
+ /// Gets the maximum of the distribution.
+ ///
+ public double Maximum
+ {
+ get { return double.PositiveInfinity; }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public double Density(double x)
+ {
+ return 1.0 / (Constants.Pi * _scale * (1.0 + (((x - _location) / _scale) * ((x - _location) / _scale))));
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The location at which to compute the log density.
+ /// the log density at .
+ ///
+ public double DensityLn(double x)
+ {
+ return -Math.Log(Constants.Pi * _scale * (1.0 + (((x - _location) / _scale) * ((x - _location) / _scale))));
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ ///
+ public double CumulativeDistribution(double x)
+ {
+ return ((1.0 / Constants.Pi) * Math.Atan((x - _location) / _scale)) + 0.5;
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability. This is also known as the quantile or percent point function.
+ ///
+ /// The location at which to compute the inverse cumulative density.
+ /// the inverse cumulative density at .
+ ///
+ public double InverseCumulativeDistribution(double p)
+ {
+ return p <= 0.0 ? double.NegativeInfinity : p >= 1.0 ? double.PositiveInfinity
+ : _location + _scale * Math.Tan((p - 0.5) * Constants.Pi);
+ }
+
+ ///
+ /// Draws a random sample from the distribution.
+ ///
+ /// A random number from this distribution.
+ public double Sample()
+ {
+ return SampleUnchecked(_random, _location, _scale);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ public void Samples(double[] values)
+ {
+ SamplesUnchecked(_random, values, _location, _scale);
+ }
+
+ ///
+ /// Generates a sequence of samples from the Cauchy distribution.
+ ///
+ /// a sequence of samples from the distribution.
+ public IEnumerable Samples()
+ {
+ return SamplesUnchecked(_random, _location, _scale);
+ }
+
+ static double SampleUnchecked(System.Random rnd, double location, double scale)
+ {
+ return location + scale * Math.Tan(Constants.Pi * (rnd.NextDouble() - 0.5));
+ }
+
+ static void SamplesUnchecked(System.Random rnd, double[] values, double location, double scale)
+ {
+ rnd.NextDoubles(values);
+ CommonParallel.For(0, values.Length, 4096, (a, b) =>
+ {
+ for (int i = a; i < b; i++)
+ {
+ values[i] = location + scale * Math.Tan(Constants.Pi * (values[i] - 0.5));
+ }
+ });
+ }
+
+ static IEnumerable SamplesUnchecked(System.Random rnd, double location, double scale)
+ {
+ while (true)
+ {
+ yield return location + scale * Math.Tan(Constants.Pi * (rnd.NextDouble() - 0.5));
+ }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The location (x0) of the distribution.
+ /// The scale (γ) of the distribution. Range: γ > 0.
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public static double PDF(double location, double scale, double x)
+ {
+ if (scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return 1.0 / (Constants.Pi * scale * (1.0 + (((x - location) / scale) * ((x - location) / scale))));
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The location (x0) of the distribution.
+ /// The scale (γ) of the distribution. Range: γ > 0.
+ /// The location at which to compute the density.
+ /// the log density at .
+ ///
+ public static double PDFLn(double location, double scale, double x)
+ {
+ if (scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return -Math.Log(Constants.Pi * scale * (1.0 + (((x - location) / scale) * ((x - location) / scale))));
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// The location (x0) of the distribution.
+ /// The scale (γ) of the distribution. Range: γ > 0.
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(double location, double scale, double x)
+ {
+ if (scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return Math.Atan((x - location) / scale) / Constants.Pi + 0.5;
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability. This is also known as the quantile or percent point function.
+ ///
+ /// The location at which to compute the inverse cumulative density.
+ /// The location (x0) of the distribution.
+ /// The scale (γ) of the distribution. Range: γ > 0.
+ /// the inverse cumulative density at .
+ ///
+ public static double InvCDF(double location, double scale, double p)
+ {
+ if (scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return p <= 0.0 ? double.NegativeInfinity : p >= 1.0 ? double.PositiveInfinity
+ : location + scale * Math.Tan((p - 0.5) * Constants.Pi);
+ }
+
+ ///
+ /// Generates a sample from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The location (x0) of the distribution.
+ /// The scale (γ) of the distribution. Range: γ > 0.
+ /// a sample from the distribution.
+ public static double Sample(System.Random rnd, double location, double scale)
+ {
+ if (scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(rnd, location, scale);
+ }
+
+ ///
+ /// Generates a sequence of samples from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The location (x0) of the distribution.
+ /// The scale (γ) of the distribution. Range: γ > 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(System.Random rnd, double location, double scale)
+ {
+ if (scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(rnd, location, scale);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// The location (x0) of the distribution.
+ /// The scale (γ) of the distribution. Range: γ > 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(System.Random rnd, double[] values, double location, double scale)
+ {
+ if (scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(rnd, values, location, scale);
+ }
+
+ ///
+ /// Generates a sample from the distribution.
+ ///
+ /// The location (x0) of the distribution.
+ /// The scale (γ) of the distribution. Range: γ > 0.
+ /// a sample from the distribution.
+ public static double Sample(double location, double scale)
+ {
+ if (scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(SystemRandomSource.Default, location, scale);
+ }
+
+ ///
+ /// Generates a sequence of samples from the distribution.
+ ///
+ /// The location (x0) of the distribution.
+ /// The scale (γ) of the distribution. Range: γ > 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(double location, double scale)
+ {
+ if (scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(SystemRandomSource.Default, location, scale);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// The location (x0) of the distribution.
+ /// The scale (γ) of the distribution. Range: γ > 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(double[] values, double location, double scale)
+ {
+ if (scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(SystemRandomSource.Default, values, location, scale);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/Chi.cs b/MathNet.Numerics/Distributions/Chi.cs
new file mode 100644
index 0000000..d7a4219
--- /dev/null
+++ b/MathNet.Numerics/Distributions/Chi.cs
@@ -0,0 +1,476 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2014 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+using MathNet.Numerics.Threading;
+
+using System;
+using System.Collections.Generic;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Continuous Univariate Chi distribution.
+/// This distribution is a continuous probability distribution. The distribution usually arises when a k-dimensional vector's orthogonal
+/// components are independent and each follow a standard normal distribution. The length of the vector will
+/// then have a chi distribution.
+/// Wikipedia - Chi distribution.
+///
+public class Chi : IContinuousDistribution
+{
+ System.Random _random;
+
+ readonly double _freedom;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ public Chi(double freedom)
+ {
+ if (!IsValidParameterSet(freedom))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _freedom = freedom;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// The random number generator which is used to draw random samples.
+ public Chi(double freedom, System.Random randomSource)
+ {
+ if (!IsValidParameterSet(freedom))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _freedom = freedom;
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ /// a string representation of the distribution.
+ public override string ToString()
+ {
+ return "Chi(k = " + _freedom + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ public static bool IsValidParameterSet(double freedom)
+ {
+ return freedom > 0.0;
+ }
+
+ ///
+ /// Gets the degrees of freedom (k) of the Chi distribution. Range: k > 0.
+ ///
+ public double DegreesOfFreedom
+ {
+ get { return _freedom; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the distribution.
+ ///
+ public double Mean
+ {
+ get { return Constants.Sqrt2 * (SpecialFunctions.Gamma((_freedom + 1.0) / 2.0) / SpecialFunctions.Gamma(_freedom / 2.0)); }
+ }
+
+ ///
+ /// Gets the variance of the distribution.
+ ///
+ public double Variance
+ {
+ get { return _freedom - (Mean * Mean); }
+ }
+
+ ///
+ /// Gets the standard deviation of the distribution.
+ ///
+ public double StdDev
+ {
+ get { return Math.Sqrt(Variance); }
+ }
+
+ ///
+ /// Gets the entropy of the distribution.
+ ///
+ public double Entropy
+ {
+ get { return SpecialFunctions.GammaLn(_freedom / 2.0) + ((_freedom - Math.Log(2) - ((_freedom - 1.0) * SpecialFunctions.DiGamma(_freedom / 2.0))) / 2.0); }
+ }
+
+ ///
+ /// Gets the skewness of the distribution.
+ ///
+ public double Skewness
+ {
+ get
+ {
+ var sigma = StdDev;
+ return (Mean * (1.0 - (2.0 * (sigma * sigma)))) / (sigma * sigma * sigma);
+ }
+ }
+
+ ///
+ /// Gets the mode of the distribution.
+ ///
+ public double Mode
+ {
+ get
+ {
+ if (_freedom < 1)
+ {
+ throw new NotSupportedException();
+ }
+
+ return Math.Sqrt(_freedom - 1.0);
+ }
+ }
+
+ ///
+ /// Gets the median of the distribution.
+ ///
+ public double Median
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the minimum of the distribution.
+ ///
+ public double Minimum
+ {
+ get { return 0.0; }
+ }
+
+ ///
+ /// Gets the maximum of the distribution.
+ ///
+ public double Maximum
+ {
+ get { return double.PositiveInfinity; }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public double Density(double x)
+ {
+ return PDF(_freedom, x);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The location at which to compute the log density.
+ /// the log density at .
+ ///
+ public double DensityLn(double x)
+ {
+ return PDFLn(_freedom, x);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ ///
+ public double CumulativeDistribution(double x)
+ {
+ return CDF(_freedom, x);
+ }
+
+ ///
+ /// Generates a sample from the Chi distribution.
+ ///
+ /// a sample from the distribution.
+ public double Sample()
+ {
+ return SampleUnchecked(_random, (int)_freedom);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ public void Samples(double[] values)
+ {
+ SamplesUnchecked(_random, values, (int)_freedom);
+ }
+
+ ///
+ /// Generates a sequence of samples from the Chi distribution.
+ ///
+ /// a sequence of samples from the distribution.
+ public IEnumerable Samples()
+ {
+ return SamplesUnchecked(_random, (int)_freedom);
+ }
+
+ ///
+ /// Samples the distribution.
+ ///
+ /// The random number generator to use.
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// a random number from the distribution.
+ static double SampleUnchecked(System.Random rnd, int freedom)
+ {
+ double sum = 0;
+ for (var i = 0; i < freedom; i++)
+ {
+ sum += Math.Pow(Normal.Sample(rnd, 0.0, 1.0), 2);
+ }
+
+ return Math.Sqrt(sum);
+ }
+
+ static void SamplesUnchecked(System.Random rnd, double[] values, int freedom)
+ {
+ var standard = new double[values.Length * freedom];
+ Normal.SamplesUnchecked(rnd, standard, 0.0, 1.0);
+ CommonParallel.For(0, values.Length, 4096, (a, b) =>
+ {
+ for (int i = a; i < b; i++)
+ {
+ int k = i * freedom;
+ double sum = 0;
+ for (int j = 0; j < freedom; j++)
+ {
+ sum += standard[k + j] * standard[k + j];
+ }
+
+ values[i] = Math.Sqrt(sum);
+ }
+ });
+ }
+
+ static IEnumerable SamplesUnchecked(System.Random rnd, int freedom)
+ {
+ while (true)
+ {
+ yield return SampleUnchecked(rnd, freedom);
+ }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public static double PDF(double freedom, double x)
+ {
+ if (freedom <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (double.IsPositiveInfinity(freedom) || double.IsPositiveInfinity(x) || x == 0.0)
+ {
+ return 0.0;
+ }
+
+ if (freedom > 160.0)
+ {
+ return Math.Exp(PDFLn(freedom, x));
+ }
+
+ return (Math.Pow(2.0, 1.0 - (freedom / 2.0)) * Math.Pow(x, freedom - 1.0) * Math.Exp(-x * x / 2.0)) / SpecialFunctions.Gamma(freedom / 2.0);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// The location at which to compute the density.
+ /// the log density at .
+ ///
+ public static double PDFLn(double freedom, double x)
+ {
+ if (freedom <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (double.IsPositiveInfinity(freedom) || double.IsPositiveInfinity(x) || x == 0.0)
+ {
+ return double.NegativeInfinity;
+ }
+
+ return ((1.0 - (freedom / 2.0)) * Math.Log(2.0)) + ((freedom - 1.0) * Math.Log(x)) - (x * x / 2.0) - SpecialFunctions.GammaLn(freedom / 2.0);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(double freedom, double x)
+ {
+ if (freedom <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (double.IsPositiveInfinity(x))
+ {
+ return 1.0;
+ }
+
+ if (double.IsPositiveInfinity(freedom))
+ {
+ return 1.0;
+ }
+
+ return SpecialFunctions.GammaLowerRegularized(freedom / 2.0, x * x / 2.0);
+ }
+
+ ///
+ /// Generates a sample from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// a sample from the distribution.
+ public static double Sample(System.Random rnd, int freedom)
+ {
+ if (freedom <= 0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(rnd, freedom);
+ }
+
+ ///
+ /// Generates a sequence of samples from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(System.Random rnd, int freedom)
+ {
+ if (freedom <= 0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(rnd, freedom);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(System.Random rnd, double[] values, int freedom)
+ {
+ if (freedom <= 0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(rnd, values, freedom);
+ }
+
+ ///
+ /// Generates a sample from the distribution.
+ ///
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// a sample from the distribution.
+ public static double Sample(int freedom)
+ {
+ if (freedom <= 0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(SystemRandomSource.Default, freedom);
+ }
+
+ ///
+ /// Generates a sequence of samples from the distribution.
+ ///
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(int freedom)
+ {
+ if (freedom <= 0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(SystemRandomSource.Default, freedom);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(double[] values, int freedom)
+ {
+ if (freedom <= 0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(SystemRandomSource.Default, values, freedom);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/ChiSquared.cs b/MathNet.Numerics/Distributions/ChiSquared.cs
new file mode 100644
index 0000000..d8eb6e6
--- /dev/null
+++ b/MathNet.Numerics/Distributions/ChiSquared.cs
@@ -0,0 +1,510 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2014 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+using MathNet.Numerics.Threading;
+
+using System;
+using System.Collections.Generic;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Continuous Univariate Chi-Squared distribution.
+/// This distribution is a sum of the squares of k independent standard normal random variables.
+/// Wikipedia - ChiSquare distribution.
+///
+public class ChiSquared : IContinuousDistribution
+{
+ System.Random _random;
+
+ readonly double _freedom;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ public ChiSquared(double freedom)
+ {
+ if (!IsValidParameterSet(freedom))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _freedom = freedom;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// The random number generator which is used to draw random samples.
+ public ChiSquared(double freedom, System.Random randomSource)
+ {
+ if (!IsValidParameterSet(freedom))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _freedom = freedom;
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ /// a string representation of the distribution.
+ public override string ToString()
+ {
+ return "ChiSquared(k = " + _freedom + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ public static bool IsValidParameterSet(double freedom)
+ {
+ return freedom > 0.0;
+ }
+
+ ///
+ /// Gets the degrees of freedom (k) of the Chi-Squared distribution. Range: k > 0.
+ ///
+ public double DegreesOfFreedom
+ {
+ get { return _freedom; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the distribution.
+ ///
+ public double Mean
+ {
+ get { return _freedom; }
+ }
+
+ ///
+ /// Gets the variance of the distribution.
+ ///
+ public double Variance
+ {
+ get { return 2.0 * _freedom; }
+ }
+
+ ///
+ /// Gets the standard deviation of the distribution.
+ ///
+ public double StdDev
+ {
+ get { return Math.Sqrt(2.0 * _freedom); }
+ }
+
+ ///
+ /// Gets the entropy of the distribution.
+ ///
+ public double Entropy
+ {
+ get { return (_freedom / 2.0) + Math.Log(2.0 * SpecialFunctions.Gamma(_freedom / 2.0)) + ((1.0 - (_freedom / 2.0)) * SpecialFunctions.DiGamma(_freedom / 2.0)); }
+ }
+
+ ///
+ /// Gets the skewness of the distribution.
+ ///
+ public double Skewness
+ {
+ get { return Math.Sqrt(8.0 / _freedom); }
+ }
+
+ ///
+ /// Gets the mode of the distribution.
+ ///
+ public double Mode
+ {
+ get { return _freedom - 2.0; }
+ }
+
+ ///
+ /// Gets the median of the distribution.
+ ///
+ public double Median
+ {
+ get { return _freedom - (2.0 / 3.0); }
+ }
+
+ ///
+ /// Gets the minimum of the distribution.
+ ///
+ public double Minimum
+ {
+ get { return 0.0; }
+ }
+
+ ///
+ /// Gets the maximum of the distribution.
+ ///
+ public double Maximum
+ {
+ get { return double.PositiveInfinity; }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public double Density(double x)
+ {
+ return PDF(_freedom, x);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The location at which to compute the log density.
+ /// the log density at .
+ ///
+ public double DensityLn(double x)
+ {
+ return PDFLn(_freedom, x);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ ///
+ public double CumulativeDistribution(double x)
+ {
+ return CDF(_freedom, x);
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability. This is also known as the quantile or percent point function.
+ ///
+ /// The location at which to compute the inverse cumulative density.
+ /// the inverse cumulative density at .
+ ///
+ public double InverseCumulativeDistribution(double p)
+ {
+ return InvCDF(_freedom, p);
+ }
+
+ ///
+ /// Generates a sample from the ChiSquare distribution.
+ ///
+ /// a sample from the distribution.
+ public double Sample()
+ {
+ return SampleUnchecked(_random, _freedom);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ public void Samples(double[] values)
+ {
+ SamplesUnchecked(_random, values, _freedom);
+ }
+
+ ///
+ /// Generates a sequence of samples from the ChiSquare distribution.
+ ///
+ /// a sequence of samples from the distribution.
+ public IEnumerable Samples()
+ {
+ return SamplesUnchecked(_random, _freedom);
+ }
+
+ ///
+ /// Samples the distribution.
+ ///
+ /// The random number generator to use.
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// a random number from the distribution.
+ static double SampleUnchecked(System.Random rnd, double freedom)
+ {
+ // Use the simple method if the degrees of freedom is an integer anyway
+ if (Math.Floor(freedom) == freedom && freedom < int.MaxValue)
+ {
+ double sum = 0;
+ var n = (int)freedom;
+ for (var i = 0; i < n; i++)
+ {
+ sum += Math.Pow(Normal.Sample(rnd, 0.0, 1.0), 2);
+ }
+
+ return sum;
+ }
+
+ // Call the gamma function (see http://en.wikipedia.org/wiki/Gamma_distribution#Specializations
+ // for a justification)
+ return Gamma.SampleUnchecked(rnd, freedom / 2.0, .5);
+ }
+
+ internal static void SamplesUnchecked(System.Random rnd, double[] values, double freedom)
+ {
+ // Use the simple method if the degrees of freedom is an integer anyway
+ if (Math.Floor(freedom) == freedom && freedom < int.MaxValue)
+ {
+ var n = (int)freedom;
+ var standard = new double[values.Length * n];
+ Normal.SamplesUnchecked(rnd, standard, 0.0, 1.0);
+ CommonParallel.For(0, values.Length, 4096, (a, b) =>
+ {
+ for (int i = a; i < b; i++)
+ {
+ int k = i * n;
+ double sum = 0;
+ for (int j = 0; j < n; j++)
+ {
+ sum += standard[k + j] * standard[k + j];
+ }
+
+ values[i] = sum;
+ }
+ });
+ return;
+ }
+
+ // Call the gamma function (see http://en.wikipedia.org/wiki/Gamma_distribution#Specializations
+ // for a justification)
+ Gamma.SamplesUnchecked(rnd, values, freedom / 2.0, .5);
+ }
+
+ static IEnumerable SamplesUnchecked(System.Random rnd, double freedom)
+ {
+ while (true)
+ {
+ yield return SampleUnchecked(rnd, freedom);
+ }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public static double PDF(double freedom, double x)
+ {
+ if (freedom <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (double.IsPositiveInfinity(freedom) || double.IsPositiveInfinity(x) || x == 0.0)
+ {
+ return 0.0;
+ }
+
+ if (freedom > 160.0)
+ {
+ return Math.Exp(PDFLn(freedom, x));
+ }
+
+ return (Math.Pow(x, (freedom / 2.0) - 1.0) * Math.Exp(-x / 2.0)) / (Math.Pow(2.0, freedom / 2.0) * SpecialFunctions.Gamma(freedom / 2.0));
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// The location at which to compute the density.
+ /// the log density at .
+ ///
+ public static double PDFLn(double freedom, double x)
+ {
+ if (freedom <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (double.IsPositiveInfinity(freedom) || double.IsPositiveInfinity(x) || x == 0.0)
+ {
+ return double.NegativeInfinity;
+ }
+
+ return (-x / 2.0) + (((freedom / 2.0) - 1.0) * Math.Log(x)) - ((freedom / 2.0) * Math.Log(2)) - SpecialFunctions.GammaLn(freedom / 2.0);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(double freedom, double x)
+ {
+ if (freedom <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (double.IsPositiveInfinity(x))
+ {
+ return 1.0;
+ }
+
+ if (double.IsPositiveInfinity(freedom))
+ {
+ return 1.0;
+ }
+
+ return SpecialFunctions.GammaLowerRegularized(freedom / 2.0, x / 2.0);
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability. This is also known as the quantile or percent point function.
+ ///
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// The location at which to compute the inverse cumulative density.
+ /// the inverse cumulative density at .
+ public static double InvCDF(double freedom, double p)
+ {
+ if (!IsValidParameterSet(freedom))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SpecialFunctions.GammaLowerRegularizedInv(freedom / 2.0, p) / 0.5;
+ }
+
+ ///
+ /// Generates a sample from the ChiSquare distribution.
+ ///
+ /// The random number generator to use.
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// a sample from the distribution.
+ public static double Sample(System.Random rnd, double freedom)
+ {
+ if (freedom <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(rnd, freedom);
+ }
+
+ ///
+ /// Generates a sequence of samples from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// a sample from the distribution.
+ public static IEnumerable Samples(System.Random rnd, double freedom)
+ {
+ if (freedom <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(rnd, freedom);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// a sample from the distribution.
+ public static void Samples(System.Random rnd, double[] values, double freedom)
+ {
+ if (freedom <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(rnd, values, freedom);
+ }
+
+ ///
+ /// Generates a sample from the ChiSquare distribution.
+ ///
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// a sample from the distribution.
+ public static double Sample(double freedom)
+ {
+ if (freedom <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(SystemRandomSource.Default, freedom);
+ }
+
+ ///
+ /// Generates a sequence of samples from the distribution.
+ ///
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// a sample from the distribution.
+ public static IEnumerable Samples(double freedom)
+ {
+ if (freedom <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(SystemRandomSource.Default, freedom);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// The degrees of freedom (k) of the distribution. Range: k > 0.
+ /// a sample from the distribution.
+ public static void Samples(double[] values, double freedom)
+ {
+ if (freedom <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(SystemRandomSource.Default, values, freedom);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/ContinuousUniform.cs b/MathNet.Numerics/Distributions/ContinuousUniform.cs
new file mode 100644
index 0000000..e4a1785
--- /dev/null
+++ b/MathNet.Numerics/Distributions/ContinuousUniform.cs
@@ -0,0 +1,485 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2013 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+using MathNet.Numerics.Threading;
+
+using System;
+using System.Collections.Generic;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Continuous Univariate Uniform distribution.
+/// The continuous uniform distribution is a distribution over real numbers. For details about this distribution, see
+/// Wikipedia - Continuous uniform distribution.
+///
+public class ContinuousUniform : IContinuousDistribution
+{
+ System.Random _random;
+
+ readonly double _lower;
+ readonly double _upper;
+
+ ///
+ /// Initializes a new instance of the ContinuousUniform class with lower bound 0 and upper bound 1.
+ ///
+ public ContinuousUniform() : this(0.0, 1.0)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the ContinuousUniform class with given lower and upper bounds.
+ ///
+ /// Lower bound. Range: lower ≤ upper.
+ /// Upper bound. Range: lower ≤ upper.
+ /// If the upper bound is smaller than the lower bound.
+ public ContinuousUniform(double lower, double upper)
+ {
+ if (!IsValidParameterSet(lower, upper))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _lower = lower;
+ _upper = upper;
+ }
+
+ ///
+ /// Initializes a new instance of the ContinuousUniform class with given lower and upper bounds.
+ ///
+ /// Lower bound. Range: lower ≤ upper.
+ /// Upper bound. Range: lower ≤ upper.
+ /// The random number generator which is used to draw random samples.
+ /// If the upper bound is smaller than the lower bound.
+ public ContinuousUniform(double lower, double upper, System.Random randomSource)
+ {
+ if (!IsValidParameterSet(lower, upper))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _lower = lower;
+ _upper = upper;
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ /// a string representation of the distribution.
+ public override string ToString()
+ {
+ return "ContinuousUniform(Lower = " + _lower + ", Upper = " + _upper + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// Lower bound. Range: lower ≤ upper.
+ /// Upper bound. Range: lower ≤ upper.
+ public static bool IsValidParameterSet(double lower, double upper)
+ {
+ return lower <= upper;
+ }
+
+ ///
+ /// Gets the lower bound of the distribution.
+ ///
+ public double LowerBound
+ {
+ get { return _lower; }
+ }
+
+ ///
+ /// Gets the upper bound of the distribution.
+ ///
+ public double UpperBound
+ {
+ get { return _upper; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the distribution.
+ ///
+ public double Mean
+ {
+ get { return (_lower + _upper) / 2.0; }
+ }
+
+ ///
+ /// Gets the variance of the distribution.
+ ///
+ public double Variance
+ {
+ get { return (_upper - _lower) * (_upper - _lower) / 12.0; }
+ }
+
+ ///
+ /// Gets the standard deviation of the distribution.
+ ///
+ public double StdDev
+ {
+ get { return (_upper - _lower) / Math.Sqrt(12.0); }
+ }
+
+ ///
+ /// Gets the entropy of the distribution.
+ ///
+ ///
+ public double Entropy
+ {
+ get { return Math.Log(_upper - _lower); }
+ }
+
+ ///
+ /// Gets the skewness of the distribution.
+ ///
+ public double Skewness
+ {
+ get { return 0.0; }
+ }
+
+ ///
+ /// Gets the mode of the distribution.
+ ///
+ ///
+ public double Mode
+ {
+ get { return (_lower + _upper) / 2.0; }
+ }
+
+ ///
+ /// Gets the median of the distribution.
+ ///
+ ///
+ public double Median
+ {
+ get { return (_lower + _upper) / 2.0; }
+ }
+
+ ///
+ /// Gets the minimum of the distribution.
+ ///
+ public double Minimum
+ {
+ get { return _lower; }
+ }
+
+ ///
+ /// Gets the maximum of the distribution.
+ ///
+ public double Maximum
+ {
+ get { return _upper; }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public double Density(double x)
+ {
+ return x < _lower || x > _upper ? 0.0 : 1.0 / (_upper - _lower);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The location at which to compute the log density.
+ /// the log density at .
+ ///
+ public double DensityLn(double x)
+ {
+ return x < _lower || x > _upper ? double.NegativeInfinity : -Math.Log(_upper - _lower);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ ///
+ public double CumulativeDistribution(double x)
+ {
+ return x <= _lower ? 0.0 : x >= _upper ? 1.0 : (x - _lower) / (_upper - _lower);
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability. This is also known as the quantile or percent point function.
+ ///
+ /// The location at which to compute the inverse cumulative density.
+ /// the inverse cumulative density at .
+ ///
+ public double InverseCumulativeDistribution(double p)
+ {
+ return p <= 0.0 ? _lower : p >= 1.0 ? _upper : _lower * (1.0 - p) + _upper * p;
+ }
+
+ ///
+ /// Generates a sample from the ContinuousUniform distribution.
+ ///
+ /// a sample from the distribution.
+ public double Sample()
+ {
+ return SampleUnchecked(_random, _lower, _upper);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ public void Samples(double[] values)
+ {
+ SamplesUnchecked(_random, values, _lower, _upper);
+ }
+
+ ///
+ /// Generates a sequence of samples from the ContinuousUniform distribution.
+ ///
+ /// a sequence of samples from the distribution.
+ public IEnumerable Samples()
+ {
+ return SamplesUnchecked(_random, _lower, _upper);
+ }
+
+ static double SampleUnchecked(System.Random rnd, double lower, double upper)
+ {
+ return lower + rnd.NextDouble() * (upper - lower);
+ }
+
+ static IEnumerable SamplesUnchecked(System.Random rnd, double lower, double upper)
+ {
+ double difference = upper - lower;
+ while (true)
+ {
+ yield return lower + rnd.NextDouble() * difference;
+ }
+ }
+
+ internal static void SamplesUnchecked(System.Random rnd, double[] values, double lower, double upper)
+ {
+ rnd.NextDoubles(values);
+ var difference = upper - lower;
+ CommonParallel.For(0, values.Length, 4096, (a, b) =>
+ {
+ for (int i = a; i < b; i++)
+ {
+ values[i] = lower + values[i] * difference;
+ }
+ });
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// Lower bound. Range: lower ≤ upper.
+ /// Upper bound. Range: lower ≤ upper.
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public static double PDF(double lower, double upper, double x)
+ {
+ if (upper < lower)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return x < lower || x > upper ? 0.0 : 1.0 / (upper - lower);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// Lower bound. Range: lower ≤ upper.
+ /// Upper bound. Range: lower ≤ upper.
+ /// The location at which to compute the density.
+ /// the log density at .
+ ///
+ public static double PDFLn(double lower, double upper, double x)
+ {
+ if (upper < lower)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return x < lower || x > upper ? double.NegativeInfinity : -Math.Log(upper - lower);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// Lower bound. Range: lower ≤ upper.
+ /// Upper bound. Range: lower ≤ upper.
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(double lower, double upper, double x)
+ {
+ if (upper < lower)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return x <= lower ? 0.0 : x >= upper ? 1.0 : (x - lower) / (upper - lower);
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability. This is also known as the quantile or percent point function.
+ ///
+ /// The location at which to compute the inverse cumulative density.
+ /// Lower bound. Range: lower ≤ upper.
+ /// Upper bound. Range: lower ≤ upper.
+ /// the inverse cumulative density at .
+ ///
+ public static double InvCDF(double lower, double upper, double p)
+ {
+ if (upper < lower)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return p <= 0.0 ? lower : p >= 1.0 ? upper : lower * (1.0 - p) + upper * p;
+ }
+
+ ///
+ /// Generates a sample from the ContinuousUniform distribution.
+ ///
+ /// The random number generator to use.
+ /// Lower bound. Range: lower ≤ upper.
+ /// Upper bound. Range: lower ≤ upper.
+ /// a uniformly distributed sample.
+ public static double Sample(System.Random rnd, double lower, double upper)
+ {
+ if (upper < lower)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(rnd, lower, upper);
+ }
+
+ ///
+ /// Generates a sequence of samples from the ContinuousUniform distribution.
+ ///
+ /// The random number generator to use.
+ /// Lower bound. Range: lower ≤ upper.
+ /// Upper bound. Range: lower ≤ upper.
+ /// a sequence of uniformly distributed samples.
+ public static IEnumerable Samples(System.Random rnd, double lower, double upper)
+ {
+ if (upper < lower)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(rnd, lower, upper);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// Lower bound. Range: lower ≤ upper.
+ /// Upper bound. Range: lower ≤ upper.
+ /// a sequence of samples from the distribution.
+ public static void Samples(System.Random rnd, double[] values, double lower, double upper)
+ {
+ if (upper < lower)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(rnd, values, lower, upper);
+ }
+
+ ///
+ /// Generates a sample from the ContinuousUniform distribution.
+ ///
+ /// Lower bound. Range: lower ≤ upper.
+ /// Upper bound. Range: lower ≤ upper.
+ /// a uniformly distributed sample.
+ public static double Sample(double lower, double upper)
+ {
+ if (upper < lower)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(SystemRandomSource.Default, lower, upper);
+ }
+
+ ///
+ /// Generates a sequence of samples from the ContinuousUniform distribution.
+ ///
+ /// Lower bound. Range: lower ≤ upper.
+ /// Upper bound. Range: lower ≤ upper.
+ /// a sequence of uniformly distributed samples.
+ public static IEnumerable Samples(double lower, double upper)
+ {
+ if (upper < lower)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(SystemRandomSource.Default, lower, upper);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// Lower bound. Range: lower ≤ upper.
+ /// Upper bound. Range: lower ≤ upper.
+ /// a sequence of samples from the distribution.
+ public static void Samples(double[] values, double lower, double upper)
+ {
+ if (upper < lower)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(SystemRandomSource.Default, values, lower, upper);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/ConwayMaxwellPoisson.cs b/MathNet.Numerics/Distributions/ConwayMaxwellPoisson.cs
new file mode 100644
index 0000000..08382b8
--- /dev/null
+++ b/MathNet.Numerics/Distributions/ConwayMaxwellPoisson.cs
@@ -0,0 +1,679 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2014 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+using MathNet.Numerics.Threading;
+
+using System;
+using System.Collections.Generic;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Discrete Univariate Conway-Maxwell-Poisson distribution.
+/// The Conway-Maxwell-Poisson distribution is a generalization of the Poisson, Geometric and Bernoulli
+/// distributions. It is parameterized by two real numbers "lambda" and "nu". For
+///
+/// - nu = 0 the distribution reverts to a Geometric distribution
+/// - nu = 1 the distribution reverts to the Poisson distribution
+/// - nu -> infinity the distribution converges to a Bernoulli distribution
+///
+/// This implementation will cache the value of the normalization constant.
+/// Wikipedia - ConwayMaxwellPoisson distribution.
+///
+public class ConwayMaxwellPoisson : IDiscreteDistribution
+{
+ System.Random _random;
+
+ readonly double _lambda;
+ readonly double _nu;
+
+ ///
+ /// The mean of the distribution.
+ ///
+ double _mean = double.MinValue;
+
+ ///
+ /// The variance of the distribution.
+ ///
+ double _variance = double.MinValue;
+
+ ///
+ /// Caches the value of the normalization constant.
+ ///
+ double _z = double.MinValue;
+
+ ///
+ /// Since many properties of the distribution can only be computed approximately, the tolerance
+ /// level specifies how much error we accept.
+ ///
+ const double Tolerance = 1e-12;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The lambda (λ) parameter. Range: λ > 0.
+ /// The rate of decay (ν) parameter. Range: ν ≥ 0.
+ public ConwayMaxwellPoisson(double lambda, double nu)
+ {
+ if (!IsValidParameterSet(lambda, nu))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _lambda = lambda;
+ _nu = nu;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The lambda (λ) parameter. Range: λ > 0.
+ /// The rate of decay (ν) parameter. Range: ν ≥ 0.
+ /// The random number generator which is used to draw random samples.
+ public ConwayMaxwellPoisson(double lambda, double nu, System.Random randomSource)
+ {
+ if (!IsValidParameterSet(lambda, nu))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _lambda = lambda;
+ _nu = nu;
+ }
+
+ ///
+ /// Returns a that represents this instance.
+ ///
+ /// A that represents this instance.
+ public override string ToString()
+ {
+ return "ConwayMaxwellPoisson(λ = " + _lambda + ", ν = " + _nu + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The lambda (λ) parameter. Range: λ > 0.
+ /// The rate of decay (ν) parameter. Range: ν ≥ 0.
+ public static bool IsValidParameterSet(double lambda, double nu)
+ {
+ return lambda > 0.0 && nu >= 0.0;
+ }
+
+ ///
+ /// Gets the lambda (λ) parameter. Range: λ > 0.
+ ///
+ public double Lambda
+ {
+ get { return _lambda; }
+ }
+
+ ///
+ /// Gets the rate of decay (ν) parameter. Range: ν ≥ 0.
+ ///
+ public double Nu
+ {
+ get { return _nu; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the distribution.
+ ///
+ public double Mean
+ {
+ get
+ {
+ // Special case requiring no computation.
+ if (_lambda == 0)
+ {
+ return 0.0;
+ }
+
+ if (_mean != double.MinValue)
+ {
+ return _mean;
+ }
+
+ // The normalization constant for the distribution.
+ var z = 1 + _lambda;
+
+ // The probability of the next term.
+ var a1 = _lambda * _lambda / Math.Pow(2, _nu);
+
+ // The unnormalized mean.
+ var zx = _lambda;
+
+ // The contribution of the next term to the mean.
+ var ax1 = 2 * a1;
+
+ for (var i = 3; i < 1000; i++)
+ {
+ var e = _lambda / Math.Pow(i, _nu);
+ var ex = _lambda / Math.Pow(i, _nu - 1) / (i - 1);
+ var a2 = a1 * e;
+ var ax2 = ax1 * ex;
+
+ if ((ax2 < ax1) && (a2 < a1))
+ {
+ var m = zx / z;
+ var upper = (zx + (ax1 / (1 - (ax2 / ax1)))) / z;
+ var lower = zx / (z + (a1 / (1 - (a2 / a1))));
+
+ var r = (upper - lower) / m;
+ if (r < Tolerance)
+ {
+ break;
+ }
+ }
+
+ z = z + a1;
+ zx = zx + ax1;
+ a1 = a2;
+ ax1 = ax2;
+ }
+
+ _mean = zx / z;
+ return _mean;
+ }
+ }
+
+ ///
+ /// Gets the variance of the distribution.
+ ///
+ public double Variance
+ {
+ get
+ {
+ // Special case requiring no computation.
+ if (_lambda == 0)
+ {
+ return 0.0;
+ }
+
+ if (_variance != double.MinValue)
+ {
+ return _variance;
+ }
+
+ // The normalization constant for the distribution.
+ var z = 1 + _lambda;
+
+ // The probability of the next term.
+ var a1 = _lambda * _lambda / Math.Pow(2, _nu);
+
+ // The unnormalized second moment.
+ var zxx = _lambda;
+
+ // The contribution of the next term to the second moment.
+ var axx1 = 4 * a1;
+
+ for (var i = 3; i < 1000; i++)
+ {
+ var e = _lambda / Math.Pow(i, _nu);
+ var exx = _lambda / Math.Pow(i, _nu - 2) / (i - 1) / (i - 1);
+ var a2 = a1 * e;
+ var axx2 = axx1 * exx;
+
+ if ((axx2 < axx1) && (a2 < a1))
+ {
+ var m = zxx / z;
+ var upper = (zxx + (axx1 / (1 - (axx2 / axx1)))) / z;
+ var lower = zxx / (z + (a1 / (1 - (a2 / a1))));
+
+ var r = (upper - lower) / m;
+ if (r < Tolerance)
+ {
+ break;
+ }
+ }
+
+ z = z + a1;
+ zxx = zxx + axx1;
+ a1 = a2;
+ axx1 = axx2;
+ }
+
+ var mean = Mean;
+ _variance = (zxx / z) - (mean * mean);
+ return _variance;
+ }
+ }
+
+ ///
+ /// Gets the standard deviation of the distribution.
+ ///
+ public double StdDev
+ {
+ get { return Math.Sqrt(Variance); }
+ }
+
+ ///
+ /// Gets the entropy of the distribution.
+ ///
+ public double Entropy
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the skewness of the distribution.
+ ///
+ public double Skewness
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the mode of the distribution
+ ///
+ public int Mode
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the median of the distribution.
+ ///
+ public double Median
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the smallest element in the domain of the distributions which can be represented by an integer.
+ ///
+ public int Minimum
+ {
+ get { return 0; }
+ }
+
+ ///
+ /// Gets the largest element in the domain of the distributions which can be represented by an integer.
+ ///
+ public int Maximum
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Computes the probability mass (PMF) at k, i.e. P(X = k).
+ ///
+ /// The location in the domain where we want to evaluate the probability mass function.
+ /// the probability mass at location .
+ public double Probability(int k)
+ {
+ return Math.Pow(_lambda, k) / Math.Pow(SpecialFunctions.Factorial(k), _nu) / Z;
+ }
+
+ ///
+ /// Computes the log probability mass (lnPMF) at k, i.e. ln(P(X = k)).
+ ///
+ /// The location in the domain where we want to evaluate the log probability mass function.
+ /// the log probability mass at location .
+ public double ProbabilityLn(int k)
+ {
+ return k * Math.Log(_lambda) - _nu * SpecialFunctions.FactorialLn(k) - Math.Log(Z);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ public double CumulativeDistribution(double x)
+ {
+ var z = Z;
+ double sum = 0;
+ for (var i = 0; i < x + 1; i++)
+ {
+ sum += Math.Pow(_lambda, i) / Math.Pow(SpecialFunctions.Factorial(i), _nu) / z;
+ }
+
+ return sum;
+ }
+
+ ///
+ /// Computes the probability mass (PMF) at k, i.e. P(X = k).
+ ///
+ /// The location in the domain where we want to evaluate the probability mass function.
+ /// The lambda (λ) parameter. Range: λ > 0.
+ /// The rate of decay (ν) parameter. Range: ν ≥ 0.
+ /// the probability mass at location .
+ public static double PMF(double lambda, double nu, int k)
+ {
+ if (!(lambda > 0.0 && nu >= 0.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ var z = Normalization(lambda, nu);
+ return Math.Pow(lambda, k) / Math.Pow(SpecialFunctions.Factorial(k), nu) / z;
+ }
+
+ ///
+ /// Computes the log probability mass (lnPMF) at k, i.e. ln(P(X = k)).
+ ///
+ /// The location in the domain where we want to evaluate the log probability mass function.
+ /// The lambda (λ) parameter. Range: λ > 0.
+ /// The rate of decay (ν) parameter. Range: ν ≥ 0.
+ /// the log probability mass at location .
+ public static double PMFLn(double lambda, double nu, int k)
+ {
+ if (!(lambda > 0.0 && nu >= 0.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ var z = Normalization(lambda, nu);
+ return k * Math.Log(lambda) - nu * SpecialFunctions.FactorialLn(k) - Math.Log(z);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// The lambda (λ) parameter. Range: λ > 0.
+ /// The rate of decay (ν) parameter. Range: ν ≥ 0.
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(double lambda, double nu, double x)
+ {
+ if (!(lambda > 0.0 && nu >= 0.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ var z = Normalization(lambda, nu);
+ double sum = 0;
+ for (var i = 0; i < x + 1; i++)
+ {
+ sum += Math.Pow(lambda, i) / Math.Pow(SpecialFunctions.Factorial(i), nu) / z;
+ }
+
+ return sum;
+ }
+
+ ///
+ /// Gets the normalization constant of the Conway-Maxwell-Poisson distribution.
+ ///
+ double Z
+ {
+ get
+ {
+ if (_z != double.MinValue)
+ {
+ return _z;
+ }
+
+ _z = Normalization(_lambda, _nu);
+ return _z;
+ }
+ }
+
+ ///
+ /// Computes an approximate normalization constant for the CMP distribution.
+ ///
+ /// The lambda (λ) parameter for the CMP distribution.
+ /// The rate of decay (ν) parameter for the CMP distribution.
+ ///
+ /// an approximate normalization constant for the CMP distribution.
+ ///
+ static double Normalization(double lambda, double nu)
+ {
+ // Initialize Z with the first two terms.
+ var z = 1.0 + lambda;
+
+ // Remembers the last term added.
+ var t = lambda;
+
+ // Start adding more terms until convergence.
+ for (var i = 2; i < 1000; i++)
+ {
+ // The new addition for term i.
+ var e = lambda / Math.Pow(i, nu);
+
+ // The new term.
+ t = t * e;
+
+ // The updated normalization constant.
+ z = z + t;
+
+ // The stopping criterion.
+ if (e < 1)
+ {
+ if (t / (1 - e) / z < Tolerance)
+ {
+ break;
+ }
+ }
+ }
+
+ return z;
+ }
+
+ ///
+ /// Returns one trials from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The lambda (λ) parameter. Range: λ > 0.
+ /// The rate of decay (ν) parameter. Range: ν ≥ 0.
+ /// The z parameter.
+ ///
+ /// One sample from the distribution implied by , , and .
+ ///
+ static int SampleUnchecked(System.Random rnd, double lambda, double nu, double z)
+ {
+ var u = rnd.NextDouble();
+ var p = 1.0 / z;
+ var cdf = p;
+ var i = 0;
+
+ while (u > cdf)
+ {
+ i++;
+ p = p * lambda / Math.Pow(i, nu);
+ cdf += p;
+ }
+
+ return i;
+ }
+
+ static void SamplesUnchecked(System.Random rnd, int[] values, double lambda, double nu, double z)
+ {
+ var uniform = rnd.NextDoubles(values.Length);
+ CommonParallel.For(0, values.Length, 4096, (a, b) =>
+ {
+ for (int i = a; i < b; i++)
+ {
+ var u = uniform[i];
+ var p = 1.0 / z;
+ var cdf = p;
+ var k = 0;
+ while (u > cdf)
+ {
+ k++;
+ p = p * lambda / Math.Pow(k, nu);
+ cdf += p;
+ }
+
+ values[i] = k;
+ }
+ });
+ }
+
+ static IEnumerable SamplesUnchecked(System.Random rnd, double lambda, double nu, double z)
+ {
+ while (true)
+ {
+ yield return SampleUnchecked(rnd, lambda, nu, z);
+ }
+ }
+
+ ///
+ /// Samples a Conway-Maxwell-Poisson distributed random variable.
+ ///
+ /// a sample from the distribution.
+ public int Sample()
+ {
+ return SampleUnchecked(_random, _lambda, _nu, Z);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ public void Samples(int[] values)
+ {
+ SamplesUnchecked(_random, values, _lambda, _nu, Z);
+ }
+
+ ///
+ /// Samples a sequence of a Conway-Maxwell-Poisson distributed random variables.
+ ///
+ ///
+ /// a sequence of samples from a Conway-Maxwell-Poisson distribution.
+ ///
+ public IEnumerable Samples()
+ {
+ return SamplesUnchecked(_random, _lambda, _nu, Z);
+ }
+
+ ///
+ /// Samples a random variable.
+ ///
+ /// The random number generator to use.
+ /// The lambda (λ) parameter. Range: λ > 0.
+ /// The rate of decay (ν) parameter. Range: ν ≥ 0.
+ public static int Sample(System.Random rnd, double lambda, double nu)
+ {
+ if (!(lambda > 0.0 && nu >= 0.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ var z = Normalization(lambda, nu);
+ return SampleUnchecked(rnd, lambda, nu, z);
+ }
+
+ ///
+ /// Samples a sequence of this random variable.
+ ///
+ /// The random number generator to use.
+ /// The lambda (λ) parameter. Range: λ > 0.
+ /// The rate of decay (ν) parameter. Range: ν ≥ 0.
+ public static IEnumerable Samples(System.Random rnd, double lambda, double nu)
+ {
+ if (!(lambda > 0.0 && nu >= 0.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ var z = Normalization(lambda, nu);
+ return SamplesUnchecked(rnd, lambda, nu, z);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// The lambda (λ) parameter. Range: λ > 0.
+ /// The rate of decay (ν) parameter. Range: ν ≥ 0.
+ public static void Samples(System.Random rnd, int[] values, double lambda, double nu)
+ {
+ if (!(lambda > 0.0 && nu >= 0.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ var z = Normalization(lambda, nu);
+ SamplesUnchecked(rnd, values, lambda, nu, z);
+ }
+
+ ///
+ /// Samples a random variable.
+ ///
+ /// The lambda (λ) parameter. Range: λ > 0.
+ /// The rate of decay (ν) parameter. Range: ν ≥ 0.
+ public static int Sample(double lambda, double nu)
+ {
+ if (!(lambda > 0.0 && nu >= 0.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ var z = Normalization(lambda, nu);
+ return SampleUnchecked(SystemRandomSource.Default, lambda, nu, z);
+ }
+
+ ///
+ /// Samples a sequence of this random variable.
+ ///
+ /// The lambda (λ) parameter. Range: λ > 0.
+ /// The rate of decay (ν) parameter. Range: ν ≥ 0.
+ public static IEnumerable Samples(double lambda, double nu)
+ {
+ if (!(lambda > 0.0 && nu >= 0.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ var z = Normalization(lambda, nu);
+ return SamplesUnchecked(SystemRandomSource.Default, lambda, nu, z);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// The lambda (λ) parameter. Range: λ > 0.
+ /// The rate of decay (ν) parameter. Range: ν ≥ 0.
+ public static void Samples(int[] values, double lambda, double nu)
+ {
+ if (!(lambda > 0.0 && nu >= 0.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ var z = Normalization(lambda, nu);
+ SamplesUnchecked(SystemRandomSource.Default, values, lambda, nu, z);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/Dirichlet.cs b/MathNet.Numerics/Distributions/Dirichlet.cs
new file mode 100644
index 0000000..4b71a8f
--- /dev/null
+++ b/MathNet.Numerics/Distributions/Dirichlet.cs
@@ -0,0 +1,357 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2011 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+
+using System;
+using System.Linq;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Multivariate Dirichlet distribution. For details about this distribution, see
+/// Wikipedia - Dirichlet distribution.
+///
+public class Dirichlet : IDistribution
+{
+ System.Random _random;
+
+ readonly double[] _alpha;
+
+ ///
+ /// Initializes a new instance of the Dirichlet class. The distribution will
+ /// be initialized with the default random number generator.
+ ///
+ /// An array with the Dirichlet parameters.
+ public Dirichlet(double[] alpha)
+ {
+ if (Control.CheckDistributionParameters && !IsValidParameterSet(alpha))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _alpha = (double[])alpha.Clone();
+ }
+
+ ///
+ /// Initializes a new instance of the Dirichlet class. The distribution will
+ /// be initialized with the default random number generator.
+ ///
+ /// An array with the Dirichlet parameters.
+ /// The random number generator which is used to draw random samples.
+ public Dirichlet(double[] alpha, System.Random randomSource)
+ {
+ if (Control.CheckDistributionParameters && !IsValidParameterSet(alpha))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _alpha = (double[])alpha.Clone();
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ /// random number generator.
+ /// The value of each parameter of the Dirichlet distribution.
+ /// The dimension of the Dirichlet distribution.
+ public Dirichlet(double alpha, int k)
+ {
+ // Create a parameter structure.
+ var parm = new double[k];
+ for (var i = 0; i < k; i++)
+ {
+ parm[i] = alpha;
+ }
+
+ _random = SystemRandomSource.Default;
+ if (Control.CheckDistributionParameters && !IsValidParameterSet(parm))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _alpha = (double[])parm.Clone();
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ /// random number generator.
+ /// The value of each parameter of the Dirichlet distribution.
+ /// The dimension of the Dirichlet distribution.
+ /// The random number generator which is used to draw random samples.
+ public Dirichlet(double alpha, int k, System.Random randomSource)
+ {
+ // Create a parameter structure.
+ var parm = new double[k];
+ for (var i = 0; i < k; i++)
+ {
+ parm[i] = alpha;
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ if (Control.CheckDistributionParameters && !IsValidParameterSet(parm))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _alpha = (double[])parm.Clone();
+ }
+
+ ///
+ /// Returns a that represents this instance.
+ ///
+ ///
+ /// A that represents this instance.
+ ///
+ public override string ToString()
+ {
+ return "Dirichlet(Dimension = " + Dimension + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ /// No parameter can be less than zero and at least one parameter should be larger than zero.
+ ///
+ /// The parameters of the Dirichlet distribution.
+ public static bool IsValidParameterSet(double[] alpha)
+ {
+ var allzero = true;
+
+ for (int i = 0; i < alpha.Length; i++)
+ {
+ var t = alpha[i];
+ if (t < 0.0)
+ {
+ return false;
+ }
+
+ if (t > 0.0)
+ {
+ allzero = false;
+ }
+ }
+
+ return !allzero;
+ }
+
+ ///
+ /// Gets or sets the parameters of the Dirichlet distribution.
+ ///
+ public double[] Alpha
+ {
+ get { return _alpha; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the dimension of the Dirichlet distribution.
+ ///
+ public int Dimension
+ {
+ get { return _alpha.Length; }
+ }
+
+ ///
+ /// Gets the sum of the Dirichlet parameters.
+ ///
+ double AlphaSum
+ {
+ get { return _alpha.Sum(); }
+ }
+
+ ///
+ /// Gets the mean of the Dirichlet distribution.
+ ///
+ public double[] Mean
+ {
+ get
+ {
+ var sum = AlphaSum;
+ var parm = new double[Dimension];
+ for (var i = 0; i < Dimension; i++)
+ {
+ parm[i] = _alpha[i] / sum;
+ }
+
+ return parm;
+ }
+ }
+
+ ///
+ /// Gets the variance of the Dirichlet distribution.
+ ///
+ public double[] Variance
+ {
+ get
+ {
+ var s = AlphaSum;
+ var v = new double[_alpha.Length];
+ for (var i = 0; i < _alpha.Length; i++)
+ {
+ v[i] = _alpha[i] * (s - _alpha[i]) / (s * s * (s + 1.0));
+ }
+
+ return v;
+ }
+ }
+
+ ///
+ /// Gets the entropy of the distribution.
+ ///
+ public double Entropy
+ {
+ get
+ {
+ var num = _alpha.Sum(t => (t - 1) * SpecialFunctions.DiGamma(t));
+ return SpecialFunctions.GammaLn(AlphaSum) + ((AlphaSum - Dimension) * SpecialFunctions.DiGamma(AlphaSum)) - num;
+ }
+ }
+
+ ///
+ /// Computes the density of the distribution.
+ ///
+ /// The locations at which to compute the density.
+ /// the density at .
+ /// The Dirichlet distribution requires that the sum of the components of x equals 1.
+ /// You can also leave out the last component, and it will be computed from the others.
+ public double Density(double[] x)
+ {
+ return Math.Exp(DensityLn(x));
+ }
+
+ ///
+ /// Computes the log density of the distribution.
+ ///
+ /// The locations at which to compute the density.
+ /// the density at .
+ public double DensityLn(double[] x)
+ {
+ if (x == null)
+ {
+ throw new ArgumentNullException(nameof(x));
+ }
+
+ var shortVersion = x.Length == (_alpha.Length - 1);
+ if ((x.Length != _alpha.Length) && !shortVersion)
+ {
+ throw new ArgumentException("x");
+ }
+
+ var term = 0.0;
+ var sumxi = 0.0;
+ var sumalpha = 0.0;
+ for (var i = 0; i < x.Length; i++)
+ {
+ var xi = x[i];
+ if ((xi <= 0.0) || (xi >= 1.0))
+ {
+ return 0.0;
+ }
+
+ term += (_alpha[i] - 1.0) * Math.Log(xi) - SpecialFunctions.GammaLn(_alpha[i]);
+ sumxi += xi;
+ sumalpha += _alpha[i];
+ }
+
+ // Calculate x[Length - 1] element, if needed
+ if (shortVersion)
+ {
+ if (sumxi >= 1.0)
+ {
+ return 0.0;
+ }
+
+ term += (_alpha[_alpha.Length - 1] - 1.0) * Math.Log(1.0 - sumxi) - SpecialFunctions.GammaLn(_alpha[_alpha.Length - 1]);
+ sumalpha += _alpha[_alpha.Length - 1];
+ }
+ else if (!sumxi.AlmostEqualRelative(1.0, 8))
+ {
+ return 0.0;
+ }
+
+ return term + SpecialFunctions.GammaLn(sumalpha);
+ }
+
+ ///
+ /// Samples a Dirichlet distributed random vector.
+ ///
+ /// A sample from this distribution.
+ public double[] Sample()
+ {
+ return Sample(_random, _alpha);
+ }
+
+ ///
+ /// Samples a Dirichlet distributed random vector.
+ ///
+ /// The random number generator to use.
+ /// The Dirichlet distribution parameter.
+ /// a sample from the distribution.
+ public static double[] Sample(System.Random rnd, double[] alpha)
+ {
+ if (Control.CheckDistributionParameters && !IsValidParameterSet(alpha))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ var n = alpha.Length;
+ var gv = new double[n];
+ var sum = 0.0;
+ for (var i = 0; i < n; i++)
+ {
+ if (alpha[i] == 0.0)
+ {
+ gv[i] = 0.0;
+ }
+ else
+ {
+ gv[i] = Gamma.Sample(rnd, alpha[i], 1.0);
+ }
+
+ sum += gv[i];
+ }
+
+ for (var i = 0; i < n; i++)
+ {
+ gv[i] /= sum;
+ }
+
+ return gv;
+ }
+}
diff --git a/MathNet.Numerics/Distributions/DiscreteUniform.cs b/MathNet.Numerics/Distributions/DiscreteUniform.cs
new file mode 100644
index 0000000..57aab87
--- /dev/null
+++ b/MathNet.Numerics/Distributions/DiscreteUniform.cs
@@ -0,0 +1,444 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2014 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+
+using System;
+using System.Collections.Generic;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Discrete Univariate Uniform distribution.
+/// The discrete uniform distribution is a distribution over integers. The distribution
+/// is parameterized by a lower and upper bound (both inclusive).
+/// Wikipedia - Discrete uniform distribution.
+///
+public class DiscreteUniform : IDiscreteDistribution
+{
+ System.Random _random;
+
+ readonly int _lower;
+ readonly int _upper;
+
+ ///
+ /// Initializes a new instance of the DiscreteUniform class.
+ ///
+ /// Lower bound, inclusive. Range: lower ≤ upper.
+ /// Upper bound, inclusive. Range: lower ≤ upper.
+ public DiscreteUniform(int lower, int upper)
+ {
+ if (!IsValidParameterSet(lower, upper))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _lower = lower;
+ _upper = upper;
+ }
+
+ ///
+ /// Initializes a new instance of the DiscreteUniform class.
+ ///
+ /// Lower bound, inclusive. Range: lower ≤ upper.
+ /// Upper bound, inclusive. Range: lower ≤ upper.
+ /// The random number generator which is used to draw random samples.
+ public DiscreteUniform(int lower, int upper, System.Random randomSource)
+ {
+ if (!IsValidParameterSet(lower, upper))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _lower = lower;
+ _upper = upper;
+ }
+
+ ///
+ /// Returns a that represents this instance.
+ ///
+ ///
+ /// A that represents this instance.
+ ///
+ public override string ToString()
+ {
+ return "DiscreteUniform(Lower = " + _lower + ", Upper = " + _upper + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// Lower bound, inclusive. Range: lower ≤ upper.
+ /// Upper bound, inclusive. Range: lower ≤ upper.
+ public static bool IsValidParameterSet(int lower, int upper)
+ {
+ return lower <= upper;
+ }
+
+ ///
+ /// Gets the inclusive lower bound of the probability distribution.
+ ///
+ public int LowerBound
+ {
+ get { return _lower; }
+ }
+
+ ///
+ /// Gets the inclusive upper bound of the probability distribution.
+ ///
+ public int UpperBound
+ {
+ get { return _upper; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the distribution.
+ ///
+ public double Mean
+ {
+ get { return (_lower + _upper) / 2.0; }
+ }
+
+ ///
+ /// Gets the standard deviation of the distribution.
+ ///
+ public double StdDev
+ {
+ get { return Math.Sqrt((((_upper - _lower + 1.0) * (_upper - _lower + 1.0)) - 1.0) / 12.0); }
+ }
+
+ ///
+ /// Gets the variance of the distribution.
+ ///
+ public double Variance
+ {
+ get { return (((_upper - _lower + 1.0) * (_upper - _lower + 1.0)) - 1.0) / 12.0; }
+ }
+
+ ///
+ /// Gets the entropy of the distribution.
+ ///
+ public double Entropy
+ {
+ get { return Math.Log(_upper - _lower + 1.0); }
+ }
+
+ ///
+ /// Gets the skewness of the distribution.
+ ///
+ public double Skewness
+ {
+ get { return 0.0; }
+ }
+
+ ///
+ /// Gets the smallest element in the domain of the distributions which can be represented by an integer.
+ ///
+ public int Minimum
+ {
+ get { return _lower; }
+ }
+
+ ///
+ /// Gets the largest element in the domain of the distributions which can be represented by an integer.
+ ///
+ public int Maximum
+ {
+ get { return _upper; }
+ }
+
+ ///
+ /// Gets the mode of the distribution; since every element in the domain has the same probability this method returns the middle one.
+ ///
+ public int Mode
+ {
+ get { return (int)Math.Floor((_lower + _upper) / 2.0); }
+ }
+
+ ///
+ /// Gets the median of the distribution.
+ ///
+ public double Median
+ {
+ get { return (_lower + _upper) / 2.0; }
+ }
+
+ ///
+ /// Computes the probability mass (PMF) at k, i.e. P(X = k).
+ ///
+ /// The location in the domain where we want to evaluate the probability mass function.
+ /// the probability mass at location .
+ public double Probability(int k)
+ {
+ return PMF(_lower, _upper, k);
+ }
+
+ ///
+ /// Computes the log probability mass (lnPMF) at k, i.e. ln(P(X = k)).
+ ///
+ /// The location in the domain where we want to evaluate the log probability mass function.
+ /// the log probability mass at location .
+ public double ProbabilityLn(int k)
+ {
+ return PMFLn(_lower, _upper, k);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ public double CumulativeDistribution(double x)
+ {
+ return CDF(_lower, _upper, x);
+ }
+
+ ///
+ /// Computes the probability mass (PMF) at k, i.e. P(X = k).
+ ///
+ /// The location in the domain where we want to evaluate the probability mass function.
+ /// Lower bound, inclusive. Range: lower ≤ upper.
+ /// Upper bound, inclusive. Range: lower ≤ upper.
+ /// the probability mass at location .
+ public static double PMF(int lower, int upper, int k)
+ {
+ if (!(lower <= upper))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return k >= lower && k <= upper ? 1.0 / (upper - lower + 1) : 0.0;
+ }
+
+ ///
+ /// Computes the log probability mass (lnPMF) at k, i.e. ln(P(X = k)).
+ ///
+ /// The location in the domain where we want to evaluate the log probability mass function.
+ /// Lower bound, inclusive. Range: lower ≤ upper.
+ /// Upper bound, inclusive. Range: lower ≤ upper.
+ /// the log probability mass at location .
+ public static double PMFLn(int lower, int upper, int k)
+ {
+ if (!(lower <= upper))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return k >= lower && k <= upper ? -Math.Log(upper - lower + 1) : double.NegativeInfinity;
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// Lower bound, inclusive. Range: lower ≤ upper.
+ /// Upper bound, inclusive. Range: lower ≤ upper.
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(int lower, int upper, double x)
+ {
+ if (!(lower <= upper))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (x < lower)
+ {
+ return 0.0;
+ }
+
+ if (x >= upper)
+ {
+ return 1.0;
+ }
+
+ return Math.Min(1.0, (Math.Floor(x) - lower + 1) / (upper - lower + 1));
+ }
+
+ ///
+ /// Generates one sample from the discrete uniform distribution. This method does not do any parameter checking.
+ ///
+ /// The random source to use.
+ /// Lower bound, inclusive. Range: lower ≤ upper.
+ /// Upper bound, inclusive. Range: lower ≤ upper.
+ /// A random sample from the discrete uniform distribution.
+ static int SampleUnchecked(System.Random rnd, int lower, int upper)
+ {
+ return rnd.Next(lower, upper + 1);
+ }
+
+ static void SamplesUnchecked(System.Random rnd, int[] values, int lower, int upper)
+ {
+ rnd.NextInt32s(values, lower, upper + 1);
+ }
+
+ static IEnumerable SamplesUnchecked(System.Random rnd, int lower, int upper)
+ {
+ return rnd.NextInt32Sequence(lower, upper + 1);
+ }
+
+ ///
+ /// Draws a random sample from the distribution.
+ ///
+ /// a sample from the distribution.
+ public int Sample()
+ {
+ return SampleUnchecked(_random, _lower, _upper);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ public void Samples(int[] values)
+ {
+ SamplesUnchecked(_random, values, _lower, _upper);
+ }
+
+ ///
+ /// Samples an array of uniformly distributed random variables.
+ ///
+ /// a sequence of samples from the distribution.
+ public IEnumerable Samples()
+ {
+ return SamplesUnchecked(_random, _lower, _upper);
+ }
+
+ ///
+ /// Samples a uniformly distributed random variable.
+ ///
+ /// The random number generator to use.
+ /// Lower bound, inclusive. Range: lower ≤ upper.
+ /// Upper bound, inclusive. Range: lower ≤ upper.
+ /// A sample from the discrete uniform distribution.
+ public static int Sample(System.Random rnd, int lower, int upper)
+ {
+ if (!(lower <= upper))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(rnd, lower, upper);
+ }
+
+ ///
+ /// Samples a sequence of uniformly distributed random variables.
+ ///
+ /// The random number generator to use.
+ /// Lower bound, inclusive. Range: lower ≤ upper.
+ /// Upper bound, inclusive. Range: lower ≤ upper.
+ /// a sequence of samples from the discrete uniform distribution.
+ public static IEnumerable Samples(System.Random rnd, int lower, int upper)
+ {
+ if (!(lower <= upper))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(rnd, lower, upper);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// Lower bound, inclusive. Range: lower ≤ upper.
+ /// Upper bound, inclusive. Range: lower ≤ upper.
+ /// a sequence of samples from the discrete uniform distribution.
+ public static void Samples(System.Random rnd, int[] values, int lower, int upper)
+ {
+ if (!(lower <= upper))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(rnd, values, lower, upper);
+ }
+
+ ///
+ /// Samples a uniformly distributed random variable.
+ ///
+ /// Lower bound, inclusive. Range: lower ≤ upper.
+ /// Upper bound, inclusive. Range: lower ≤ upper.
+ /// A sample from the discrete uniform distribution.
+ public static int Sample(int lower, int upper)
+ {
+ if (!(lower <= upper))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(SystemRandomSource.Default, lower, upper);
+ }
+
+ ///
+ /// Samples a sequence of uniformly distributed random variables.
+ ///
+ /// Lower bound, inclusive. Range: lower ≤ upper.
+ /// Upper bound, inclusive. Range: lower ≤ upper.
+ /// a sequence of samples from the discrete uniform distribution.
+ public static IEnumerable Samples(int lower, int upper)
+ {
+ if (!(lower <= upper))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(SystemRandomSource.Default, lower, upper);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// Lower bound, inclusive. Range: lower ≤ upper.
+ /// Upper bound, inclusive. Range: lower ≤ upper.
+ /// a sequence of samples from the discrete uniform distribution.
+ public static void Samples(int[] values, int lower, int upper)
+ {
+ if (!(lower <= upper))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(SystemRandomSource.Default, values, lower, upper);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/Erlang.cs b/MathNet.Numerics/Distributions/Erlang.cs
new file mode 100644
index 0000000..2ae4b97
--- /dev/null
+++ b/MathNet.Numerics/Distributions/Erlang.cs
@@ -0,0 +1,548 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2014 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+
+using System;
+using System.Collections.Generic;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Continuous Univariate Erlang distribution.
+/// This distribution is a continuous probability distribution with wide applicability primarily due to its
+/// relation to the exponential and Gamma distributions.
+/// Wikipedia - Erlang distribution.
+///
+public class Erlang : IContinuousDistribution
+{
+ System.Random _random;
+
+ readonly int _shape;
+ readonly double _rate;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The shape (k) of the Erlang distribution. Range: k ≥ 0.
+ /// The rate or inverse scale (λ) of the Erlang distribution. Range: λ ≥ 0.
+ public Erlang(int shape, double rate)
+ {
+ if (!IsValidParameterSet(shape, rate))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _shape = shape;
+ _rate = rate;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The shape (k) of the Erlang distribution. Range: k ≥ 0.
+ /// The rate or inverse scale (λ) of the Erlang distribution. Range: λ ≥ 0.
+ /// The random number generator which is used to draw random samples.
+ public Erlang(int shape, double rate, System.Random randomSource)
+ {
+ if (!IsValidParameterSet(shape, rate))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _shape = shape;
+ _rate = rate;
+ }
+
+ ///
+ /// Constructs a Erlang distribution from a shape and scale parameter. The distribution will
+ /// be initialized with the default random number generator.
+ ///
+ /// The shape (k) of the Erlang distribution. Range: k ≥ 0.
+ /// The scale (μ) of the Erlang distribution. Range: μ ≥ 0.
+ /// The random number generator which is used to draw random samples. Optional, can be null.
+ public static Erlang WithShapeScale(int shape, double scale, System.Random randomSource = null)
+ {
+ return new Erlang(shape, 1.0 / scale, randomSource);
+ }
+
+ ///
+ /// Constructs a Erlang distribution from a shape and inverse scale parameter. The distribution will
+ /// be initialized with the default random number generator.
+ ///
+ /// The shape (k) of the Erlang distribution. Range: k ≥ 0.
+ /// The rate or inverse scale (λ) of the Erlang distribution. Range: λ ≥ 0.
+ /// The random number generator which is used to draw random samples. Optional, can be null.
+ public static Erlang WithShapeRate(int shape, double rate, System.Random randomSource = null)
+ {
+ return new Erlang(shape, rate, randomSource);
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ /// a string representation of the distribution.
+ public override string ToString()
+ {
+ return "Erlang(k = " + _shape + ", λ = " + _rate + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The shape (k) of the Erlang distribution. Range: k ≥ 0.
+ /// The rate or inverse scale (λ) of the Erlang distribution. Range: λ ≥ 0.
+ public static bool IsValidParameterSet(int shape, double rate)
+ {
+ return shape >= 0 && rate >= 0.0;
+ }
+
+ ///
+ /// Gets the shape (k) of the Erlang distribution. Range: k ≥ 0.
+ ///
+ public int Shape
+ {
+ get { return _shape; }
+ }
+
+ ///
+ /// Gets the rate or inverse scale (λ) of the Erlang distribution. Range: λ ≥ 0.
+ ///
+ public double Rate
+ {
+ get { return _rate; }
+ }
+
+ ///
+ /// Gets the scale of the Erlang distribution.
+ ///
+ public double Scale
+ {
+ get { return 1.0 / _rate; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the distribution.
+ ///
+ public double Mean
+ {
+ get
+ {
+ if (double.IsPositiveInfinity(_rate))
+ {
+ return _shape;
+ }
+
+ if (_rate == 0.0 && _shape == 0.0)
+ {
+ return double.NaN;
+ }
+
+ return _shape / _rate;
+ }
+ }
+
+ ///
+ /// Gets the variance of the distribution.
+ ///
+ public double Variance
+ {
+ get
+ {
+ if (double.IsPositiveInfinity(_rate))
+ {
+ return 0.0;
+ }
+
+ if (_rate == 0.0 && _shape == 0.0)
+ {
+ return double.NaN;
+ }
+
+ return _shape / (_rate * _rate);
+ }
+ }
+
+ ///
+ /// Gets the standard deviation of the distribution.
+ ///
+ public double StdDev
+ {
+ get
+ {
+ if (double.IsPositiveInfinity(_rate))
+ {
+ return 0.0;
+ }
+
+ if (_rate == 0.0 && _shape == 0.0)
+ {
+ return double.NaN;
+ }
+
+ return Math.Sqrt(_shape) / _rate;
+ }
+ }
+
+ ///
+ /// Gets the entropy of the distribution.
+ ///
+ public double Entropy
+ {
+ get
+ {
+ if (double.IsPositiveInfinity(_rate))
+ {
+ return 0.0;
+ }
+
+ if (_rate == 0.0 && _shape == 0.0)
+ {
+ return double.NaN;
+ }
+
+ return _shape - Math.Log(_rate) + SpecialFunctions.GammaLn(_shape) + ((1.0 - _shape) * SpecialFunctions.DiGamma(_shape));
+ }
+ }
+
+ ///
+ /// Gets the skewness of the distribution.
+ ///
+ public double Skewness
+ {
+ get
+ {
+ if (double.IsPositiveInfinity(_rate))
+ {
+ return 0.0;
+ }
+
+ if (_rate == 0.0 && _shape == 0.0)
+ {
+ return double.NaN;
+ }
+
+ return 2.0 / Math.Sqrt(_shape);
+ }
+ }
+
+ ///
+ /// Gets the mode of the distribution.
+ ///
+ public double Mode
+ {
+ get
+ {
+ if (_shape < 1)
+ {
+ throw new NotSupportedException();
+ }
+
+ if (double.IsPositiveInfinity(_rate))
+ {
+ return _shape;
+ }
+
+ if (_rate == 0.0 && _shape == 0.0)
+ {
+ return double.NaN;
+ }
+
+ return (_shape - 1.0) / _rate;
+ }
+ }
+
+ ///
+ /// Gets the median of the distribution.
+ ///
+ public double Median
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the minimum value.
+ ///
+ public double Minimum
+ {
+ get { return 0.0; }
+ }
+
+ ///
+ /// Gets the Maximum value.
+ ///
+ public double Maximum
+ {
+ get { return double.PositiveInfinity; }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public double Density(double x)
+ {
+ return PDF(_shape, _rate, x);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The location at which to compute the log density.
+ /// the log density at .
+ ///
+ public double DensityLn(double x)
+ {
+ return PDFLn(_shape, _rate, x);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ ///
+ public double CumulativeDistribution(double x)
+ {
+ return CDF(_shape, _rate, x);
+ }
+
+ ///
+ /// Generates a sample from the Erlang distribution.
+ ///
+ /// a sample from the distribution.
+ public double Sample()
+ {
+ return Gamma.SampleUnchecked(_random, _shape, _rate);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ public void Samples(double[] values)
+ {
+ Gamma.SamplesUnchecked(_random, values, _shape, _rate);
+ }
+
+ ///
+ /// Generates a sequence of samples from the Erlang distribution.
+ ///
+ /// a sequence of samples from the distribution.
+ public IEnumerable Samples()
+ {
+ while (true)
+ {
+ yield return Gamma.SampleUnchecked(_random, _shape, _rate);
+ }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The shape (k) of the Erlang distribution. Range: k ≥ 0.
+ /// The rate or inverse scale (λ) of the Erlang distribution. Range: λ ≥ 0.
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public static double PDF(int shape, double rate, double x)
+ {
+ if (shape < 0.0 || rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (double.IsPositiveInfinity(rate))
+ {
+ return x == shape ? double.PositiveInfinity : 0.0;
+ }
+
+ if (shape == 0.0 && rate == 0.0)
+ {
+ return 0.0;
+ }
+
+ if (shape == 1.0)
+ {
+ return rate * Math.Exp(-rate * x);
+ }
+
+ if (shape > 160.0)
+ {
+ return Math.Exp(PDFLn(shape, rate, x));
+ }
+
+ return Math.Pow(rate, shape) * Math.Pow(x, shape - 1.0) * Math.Exp(-rate * x) / SpecialFunctions.Gamma(shape);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The shape (k) of the Erlang distribution. Range: k ≥ 0.
+ /// The rate or inverse scale (λ) of the Erlang distribution. Range: λ ≥ 0.
+ /// The location at which to compute the density.
+ /// the log density at .
+ ///
+ public static double PDFLn(int shape, double rate, double x)
+ {
+ if (shape < 0.0 || rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (double.IsPositiveInfinity(rate))
+ {
+ return x == shape ? double.PositiveInfinity : double.NegativeInfinity;
+ }
+
+ if (shape == 0.0 && rate == 0.0)
+ {
+ return double.NegativeInfinity;
+ }
+
+ if (shape == 1.0)
+ {
+ return Math.Log(rate) - (rate * x);
+ }
+
+ return (shape * Math.Log(rate)) + ((shape - 1.0) * Math.Log(x)) - (rate * x) - SpecialFunctions.GammaLn(shape);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// The shape (k) of the Erlang distribution. Range: k ≥ 0.
+ /// The rate or inverse scale (λ) of the Erlang distribution. Range: λ ≥ 0.
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(int shape, double rate, double x)
+ {
+ if (shape < 0.0 || rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (double.IsPositiveInfinity(rate))
+ {
+ return x >= shape ? 1.0 : 0.0;
+ }
+
+ if (shape == 0.0 && rate == 0.0)
+ {
+ return 0.0;
+ }
+
+ return SpecialFunctions.GammaLowerRegularized(shape, x * rate);
+ }
+
+ ///
+ /// Generates a sample from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The shape (k) of the Erlang distribution. Range: k ≥ 0.
+ /// The rate or inverse scale (λ) of the Erlang distribution. Range: λ ≥ 0.
+ /// a sample from the distribution.
+ public static double Sample(System.Random rnd, int shape, double rate)
+ {
+ return Gamma.Sample(rnd, shape, rate);
+ }
+
+ ///
+ /// Generates a sequence of samples from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The shape (k) of the Erlang distribution. Range: k ≥ 0.
+ /// The rate or inverse scale (λ) of the Erlang distribution. Range: λ ≥ 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(System.Random rnd, int shape, double rate)
+ {
+ return Gamma.Samples(rnd, shape, rate);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// The shape (k) of the Erlang distribution. Range: k ≥ 0.
+ /// The rate or inverse scale (λ) of the Erlang distribution. Range: λ ≥ 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(System.Random rnd, double[] values, int shape, double rate)
+ {
+ Gamma.Samples(rnd, values, shape, rate);
+ }
+
+ ///
+ /// Generates a sample from the distribution.
+ ///
+ /// The shape (k) of the Erlang distribution. Range: k ≥ 0.
+ /// The rate or inverse scale (λ) of the Erlang distribution. Range: λ ≥ 0.
+ /// a sample from the distribution.
+ public static double Sample(int shape, double rate)
+ {
+ return Gamma.Sample(shape, rate);
+ }
+
+ ///
+ /// Generates a sequence of samples from the distribution.
+ ///
+ /// The shape (k) of the Erlang distribution. Range: k ≥ 0.
+ /// The rate or inverse scale (λ) of the Erlang distribution. Range: λ ≥ 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(int shape, double rate)
+ {
+ return Gamma.Samples(shape, rate);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// The shape (k) of the Erlang distribution. Range: k ≥ 0.
+ /// The rate or inverse scale (λ) of the Erlang distribution. Range: λ ≥ 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(double[] values, int shape, double rate)
+ {
+ Gamma.Samples(values, shape, rate);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/Exponential.cs b/MathNet.Numerics/Distributions/Exponential.cs
new file mode 100644
index 0000000..0bbdbd6
--- /dev/null
+++ b/MathNet.Numerics/Distributions/Exponential.cs
@@ -0,0 +1,458 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2014 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+using MathNet.Numerics.Threading;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Continuous Univariate Exponential distribution.
+/// The exponential distribution is a distribution over the real numbers parameterized by one non-negative parameter.
+/// Wikipedia - exponential distribution.
+///
+public class Exponential : IContinuousDistribution
+{
+ System.Random _random;
+
+ readonly double _rate;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The rate (λ) parameter of the distribution. Range: λ ≥ 0.
+ public Exponential(double rate)
+ {
+ if (!IsValidParameterSet(rate))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _rate = rate;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The rate (λ) parameter of the distribution. Range: λ ≥ 0.
+ /// The random number generator which is used to draw random samples.
+ public Exponential(double rate, System.Random randomSource)
+ {
+ if (!IsValidParameterSet(rate))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _rate = rate;
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ /// a string representation of the distribution.
+ public override string ToString()
+ {
+ return "Exponential(λ = " + _rate + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The rate (λ) parameter of the distribution. Range: λ ≥ 0.
+ public static bool IsValidParameterSet(double rate)
+ {
+ return rate >= 0.0;
+ }
+
+ ///
+ /// Gets the rate (λ) parameter of the distribution. Range: λ ≥ 0.
+ ///
+ public double Rate
+ {
+ get { return _rate; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the distribution.
+ ///
+ public double Mean
+ {
+ get { return 1.0 / _rate; }
+ }
+
+ ///
+ /// Gets the variance of the distribution.
+ ///
+ public double Variance
+ {
+ get { return 1.0 / (_rate * _rate); }
+ }
+
+ ///
+ /// Gets the standard deviation of the distribution.
+ ///
+ public double StdDev
+ {
+ get { return 1.0 / _rate; }
+ }
+
+ ///
+ /// Gets the entropy of the distribution.
+ ///
+ public double Entropy
+ {
+ get { return 1.0 - Math.Log(_rate); }
+ }
+
+ ///
+ /// Gets the skewness of the distribution.
+ ///
+ public double Skewness
+ {
+ get { return 2.0; }
+ }
+
+ ///
+ /// Gets the mode of the distribution.
+ ///
+ public double Mode
+ {
+ get { return 0.0; }
+ }
+
+ ///
+ /// Gets the median of the distribution.
+ ///
+ public double Median
+ {
+ get { return Math.Log(2.0) / _rate; }
+ }
+
+ ///
+ /// Gets the minimum of the distribution.
+ ///
+ public double Minimum
+ {
+ get { return 0.0; }
+ }
+
+ ///
+ /// Gets the maximum of the distribution.
+ ///
+ public double Maximum
+ {
+ get { return double.PositiveInfinity; }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public double Density(double x)
+ {
+ return x < 0.0 ? 0.0 : _rate * Math.Exp(-_rate * x);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The location at which to compute the log density.
+ /// the log density at .
+ ///
+ public double DensityLn(double x)
+ {
+ return Math.Log(_rate) - (_rate * x);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ ///
+ public double CumulativeDistribution(double x)
+ {
+ return x < 0.0 ? 0.0 : 1.0 - Math.Exp(-_rate * x);
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability. This is also known as the quantile or percent point function.
+ ///
+ /// The location at which to compute the inverse cumulative density.
+ /// the inverse cumulative density at .
+ ///
+ public double InverseCumulativeDistribution(double p)
+ {
+ return p >= 1.0 ? double.PositiveInfinity : -Math.Log(1 - p) / _rate;
+ }
+
+ ///
+ /// Draws a random sample from the distribution.
+ ///
+ /// A random number from this distribution.
+ public double Sample()
+ {
+ return SampleUnchecked(_random, _rate);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ public void Samples(double[] values)
+ {
+ SamplesUnchecked(_random, values, _rate);
+ }
+
+ ///
+ /// Generates a sequence of samples from the Exponential distribution.
+ ///
+ /// a sequence of samples from the distribution.
+ public IEnumerable Samples()
+ {
+ return SamplesUnchecked(_random, _rate);
+ }
+
+ static double SampleUnchecked(System.Random rnd, double rate)
+ {
+ var r = rnd.NextDouble();
+ while (r == 0.0)
+ {
+ r = rnd.NextDouble();
+ }
+
+ return -Math.Log(r) / rate;
+ }
+
+ internal static void SamplesUnchecked(System.Random rnd, double[] values, double rate)
+ {
+ rnd.NextDoubles(values);
+ CommonParallel.For(0, values.Length, 4096, (a, b) =>
+ {
+ for (int i = a; i < b; i++)
+ {
+ // this happens very rarely
+ var r = values[i];
+ while (r == 0.0)
+ {
+ r = rnd.NextDouble();
+ }
+
+ values[i] = -Math.Log(r) / rate;
+ }
+ });
+ }
+
+ static IEnumerable SamplesUnchecked(System.Random rnd, double rate)
+ {
+ return rnd.NextDoubleSequence().Where(r => r != 0.0).Select(r => -Math.Log(r) / rate);
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The rate (λ) parameter of the distribution. Range: λ ≥ 0.
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public static double PDF(double rate, double x)
+ {
+ if (rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return x < 0.0 ? 0.0 : rate * Math.Exp(-rate * x);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The rate (λ) parameter of the distribution. Range: λ ≥ 0.
+ /// The location at which to compute the density.
+ /// the log density at .
+ ///
+ public static double PDFLn(double rate, double x)
+ {
+ if (rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return Math.Log(rate) - (rate * x);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// The rate (λ) parameter of the distribution. Range: λ ≥ 0.
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(double rate, double x)
+ {
+ if (rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return x < 0.0 ? 0.0 : 1.0 - Math.Exp(-rate * x);
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability. This is also known as the quantile or percent point function.
+ ///
+ /// The location at which to compute the inverse cumulative density.
+ /// The rate (λ) parameter of the distribution. Range: λ ≥ 0.
+ /// the inverse cumulative density at .
+ ///
+ public static double InvCDF(double rate, double p)
+ {
+ if (rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return p >= 1.0 ? double.PositiveInfinity : -Math.Log(1 - p) / rate;
+ }
+
+ ///
+ /// Draws a random sample from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The rate (λ) parameter of the distribution. Range: λ ≥ 0.
+ /// A random number from this distribution.
+ public static double Sample(System.Random rnd, double rate)
+ {
+ if (rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(rnd, rate);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// The rate (λ) parameter of the distribution. Range: λ ≥ 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(System.Random rnd, double[] values, double rate)
+ {
+ if (rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(rnd, values, rate);
+ }
+
+ ///
+ /// Generates a sequence of samples from the Exponential distribution.
+ ///
+ /// The random number generator to use.
+ /// The rate (λ) parameter of the distribution. Range: λ ≥ 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(System.Random rnd, double rate)
+ {
+ if (rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(rnd, rate);
+ }
+
+ ///
+ /// Draws a random sample from the distribution.
+ ///
+ /// The rate (λ) parameter of the distribution. Range: λ ≥ 0.
+ /// A random number from this distribution.
+ public static double Sample(double rate)
+ {
+ if (rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(SystemRandomSource.Default, rate);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// The rate (λ) parameter of the distribution. Range: λ ≥ 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(double[] values, double rate)
+ {
+ if (rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(SystemRandomSource.Default, values, rate);
+ }
+
+ ///
+ /// Generates a sequence of samples from the Exponential distribution.
+ ///
+ /// The rate (λ) parameter of the distribution. Range: λ ≥ 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(double rate)
+ {
+ if (rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(SystemRandomSource.Default, rate);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/FisherSnedecor.cs b/MathNet.Numerics/Distributions/FisherSnedecor.cs
new file mode 100644
index 0000000..8020f98
--- /dev/null
+++ b/MathNet.Numerics/Distributions/FisherSnedecor.cs
@@ -0,0 +1,512 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2013 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+using MathNet.Numerics.RootFinding;
+using MathNet.Numerics.Threading;
+
+using System;
+using System.Collections.Generic;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Continuous Univariate F-distribution, also known as Fisher-Snedecor distribution.
+/// For details about this distribution, see
+/// Wikipedia - FisherSnedecor distribution.
+///
+public class FisherSnedecor : IContinuousDistribution
+{
+ System.Random _random;
+
+ readonly double _freedom1;
+ readonly double _freedom2;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The first degree of freedom (d1) of the distribution. Range: d1 > 0.
+ /// The second degree of freedom (d2) of the distribution. Range: d2 > 0.
+ public FisherSnedecor(double d1, double d2)
+ {
+ if (!IsValidParameterSet(d1, d2))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _freedom1 = d1;
+ _freedom2 = d2;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The first degree of freedom (d1) of the distribution. Range: d1 > 0.
+ /// The second degree of freedom (d2) of the distribution. Range: d2 > 0.
+ /// The random number generator which is used to draw random samples.
+ public FisherSnedecor(double d1, double d2, System.Random randomSource)
+ {
+ if (!IsValidParameterSet(d1, d2))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _freedom1 = d1;
+ _freedom2 = d2;
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ /// a string representation of the distribution.
+ public override string ToString()
+ {
+ return "FisherSnedecor(d1 = " + _freedom1 + ", d2 = " + _freedom2 + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The first degree of freedom (d1) of the distribution. Range: d1 > 0.
+ /// The second degree of freedom (d2) of the distribution. Range: d2 > 0.
+ public static bool IsValidParameterSet(double d1, double d2)
+ {
+ return d1 > 0.0 && d2 > 0.0;
+ }
+
+ ///
+ /// Gets the first degree of freedom (d1) of the distribution. Range: d1 > 0.
+ ///
+ public double DegreesOfFreedom1
+ {
+ get { return _freedom1; }
+ }
+
+ ///
+ /// Gets the second degree of freedom (d2) of the distribution. Range: d2 > 0.
+ ///
+ public double DegreesOfFreedom2
+ {
+ get { return _freedom2; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the distribution.
+ ///
+ public double Mean
+ {
+ get
+ {
+ if (_freedom2 <= 2)
+ {
+ throw new NotSupportedException();
+ }
+
+ return _freedom2 / (_freedom2 - 2.0);
+ }
+ }
+
+ ///
+ /// Gets the variance of the distribution.
+ ///
+ public double Variance
+ {
+ get
+ {
+ if (_freedom2 <= 4)
+ {
+ throw new NotSupportedException();
+ }
+
+ return (2.0 * _freedom2 * _freedom2 * (_freedom1 + _freedom2 - 2.0)) / (_freedom1 * (_freedom2 - 2.0) * (_freedom2 - 2.0) * (_freedom2 - 4.0));
+ }
+ }
+
+ ///
+ /// Gets the standard deviation of the distribution.
+ ///
+ public double StdDev
+ {
+ get { return Math.Sqrt(Variance); }
+ }
+
+ ///
+ /// Gets the entropy of the distribution.
+ ///
+ public double Entropy
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the skewness of the distribution.
+ ///
+ public double Skewness
+ {
+ get
+ {
+ if (_freedom2 <= 6)
+ {
+ throw new NotSupportedException();
+ }
+
+ return (((2.0 * _freedom1) + _freedom2 - 2.0) * Math.Sqrt(8.0 * (_freedom2 - 4.0))) / ((_freedom2 - 6.0) * Math.Sqrt(_freedom1 * (_freedom1 + _freedom2 - 2.0)));
+ }
+ }
+
+ ///
+ /// Gets the mode of the distribution.
+ ///
+ public double Mode
+ {
+ get
+ {
+ if (_freedom1 <= 2)
+ {
+ throw new NotSupportedException();
+ }
+
+ return (_freedom2 * (_freedom1 - 2.0)) / (_freedom1 * (_freedom2 + 2.0));
+ }
+ }
+
+ ///
+ /// Gets the median of the distribution.
+ ///
+ public double Median
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the minimum of the distribution.
+ ///
+ public double Minimum
+ {
+ get { return 0.0; }
+ }
+
+ ///
+ /// Gets the maximum of the distribution.
+ ///
+ public double Maximum
+ {
+ get { return double.PositiveInfinity; }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public double Density(double x)
+ {
+ return Math.Sqrt(Math.Pow(_freedom1 * x, _freedom1) * Math.Pow(_freedom2, _freedom2) / Math.Pow((_freedom1 * x) + _freedom2, _freedom1 + _freedom2)) / (x * SpecialFunctions.Beta(_freedom1 / 2.0, _freedom2 / 2.0));
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The location at which to compute the log density.
+ /// the log density at .
+ ///
+ public double DensityLn(double x)
+ {
+ return Math.Log(Density(x));
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ ///
+ public double CumulativeDistribution(double x)
+ {
+ return SpecialFunctions.BetaRegularized(_freedom1 / 2.0, _freedom2 / 2.0, _freedom1 * x / ((_freedom1 * x) + _freedom2));
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability. This is also known as the quantile or percent point function.
+ ///
+ /// The location at which to compute the inverse cumulative density.
+ /// the inverse cumulative density at .
+ ///
+ /// WARNING: currently not an explicit implementation, hence slow and unreliable.
+ public double InverseCumulativeDistribution(double p)
+ {
+ return InvCDF(_freedom1, _freedom2, p);
+ }
+
+ ///
+ /// Generates a sample from the FisherSnedecor distribution.
+ ///
+ /// a sample from the distribution.
+ public double Sample()
+ {
+ return SampleUnchecked(_random, _freedom1, _freedom2);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ public void Samples(double[] values)
+ {
+ SamplesUnchecked(_random, values, _freedom1, _freedom2);
+ }
+
+ ///
+ /// Generates a sequence of samples from the FisherSnedecor distribution.
+ ///
+ /// a sequence of samples from the distribution.
+ public IEnumerable Samples()
+ {
+ return SamplesUnchecked(_random, _freedom1, _freedom2);
+ }
+
+ ///
+ /// Generates one sample from the FisherSnedecor distribution without parameter checking.
+ ///
+ /// The random number generator to use.
+ /// The first degree of freedom (d1) of the distribution. Range: d1 > 0.
+ /// The second degree of freedom (d2) of the distribution. Range: d2 > 0.
+ /// a FisherSnedecor distributed random number.
+ static double SampleUnchecked(System.Random rnd, double d1, double d2)
+ {
+ return (ChiSquared.Sample(rnd, d1) * d2) / (ChiSquared.Sample(rnd, d2) * d1);
+ }
+
+ static void SamplesUnchecked(System.Random rnd, double[] values, double d1, double d2)
+ {
+ var values2 = new double[values.Length];
+ ChiSquared.SamplesUnchecked(rnd, values, d1);
+ ChiSquared.SamplesUnchecked(rnd, values2, d2);
+ CommonParallel.For(0, values.Length, 4096, (a, b) =>
+ {
+ for (int i = a; i < b; i++)
+ {
+ values[i] = (values[i] * d2) / (values2[i] * d1);
+ }
+ });
+ }
+
+ static IEnumerable SamplesUnchecked(System.Random rnd, double d1, double d2)
+ {
+ while (true)
+ {
+ yield return SampleUnchecked(rnd, d1, d2);
+ }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The first degree of freedom (d1) of the distribution. Range: d1 > 0.
+ /// The second degree of freedom (d2) of the distribution. Range: d2 > 0.
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public static double PDF(double d1, double d2, double x)
+ {
+ if (d1 <= 0.0 || d2 <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return Math.Sqrt(Math.Pow(d1 * x, d1) * Math.Pow(d2, d2) / Math.Pow((d1 * x) + d2, d1 + d2)) / (x * SpecialFunctions.Beta(d1 / 2.0, d2 / 2.0));
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The first degree of freedom (d1) of the distribution. Range: d1 > 0.
+ /// The second degree of freedom (d2) of the distribution. Range: d2 > 0.
+ /// The location at which to compute the density.
+ /// the log density at .
+ ///
+ public static double PDFLn(double d1, double d2, double x)
+ {
+ return Math.Log(PDF(d1, d2, x));
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// The first degree of freedom (d1) of the distribution. Range: d1 > 0.
+ /// The second degree of freedom (d2) of the distribution. Range: d2 > 0.
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(double d1, double d2, double x)
+ {
+ if (d1 <= 0.0 || d2 <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SpecialFunctions.BetaRegularized(d1 / 2.0, d2 / 2.0, d1 * x / (d1 * x + d2));
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability. This is also known as the quantile or percent point function.
+ ///
+ /// The location at which to compute the inverse cumulative density.
+ /// The first degree of freedom (d1) of the distribution. Range: d1 > 0.
+ /// The second degree of freedom (d2) of the distribution. Range: d2 > 0.
+ /// the inverse cumulative density at .
+ ///
+ /// WARNING: currently not an explicit implementation, hence slow and unreliable.
+ public static double InvCDF(double d1, double d2, double p)
+ {
+ if (d1 <= 0.0 || d2 <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return Brent.FindRoot(
+ x => SpecialFunctions.BetaRegularized(d1 / 2.0, d2 / 2.0, d1 * x / (d1 * x + d2)) - p,
+ 0, 1000, accuracy: 1e-12);
+ }
+
+ ///
+ /// Generates a sample from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The first degree of freedom (d1) of the distribution. Range: d1 > 0.
+ /// The second degree of freedom (d2) of the distribution. Range: d2 > 0.
+ /// a sample from the distribution.
+ public static double Sample(System.Random rnd, double d1, double d2)
+ {
+ if (d1 <= 0.0 || d2 <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(rnd, d1, d2);
+ }
+
+ ///
+ /// Generates a sequence of samples from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The first degree of freedom (d1) of the distribution. Range: d1 > 0.
+ /// The second degree of freedom (d2) of the distribution. Range: d2 > 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(System.Random rnd, double d1, double d2)
+ {
+ if (d1 <= 0.0 || d2 <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(rnd, d1, d2);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// The first degree of freedom (d1) of the distribution. Range: d1 > 0.
+ /// The second degree of freedom (d2) of the distribution. Range: d2 > 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(System.Random rnd, double[] values, double d1, double d2)
+ {
+ if (d1 <= 0.0 || d2 <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(rnd, values, d1, d2);
+ }
+
+ ///
+ /// Generates a sample from the distribution.
+ ///
+ /// The first degree of freedom (d1) of the distribution. Range: d1 > 0.
+ /// The second degree of freedom (d2) of the distribution. Range: d2 > 0.
+ /// a sample from the distribution.
+ public static double Sample(double d1, double d2)
+ {
+ if (d1 <= 0.0 || d2 <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(SystemRandomSource.Default, d1, d2);
+ }
+
+ ///
+ /// Generates a sequence of samples from the distribution.
+ ///
+ /// The first degree of freedom (d1) of the distribution. Range: d1 > 0.
+ /// The second degree of freedom (d2) of the distribution. Range: d2 > 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(double d1, double d2)
+ {
+ if (d1 <= 0.0 || d2 <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(SystemRandomSource.Default, d1, d2);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// The first degree of freedom (d1) of the distribution. Range: d1 > 0.
+ /// The second degree of freedom (d2) of the distribution. Range: d2 > 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(double[] values, double d1, double d2)
+ {
+ if (d1 <= 0.0 || d2 <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(SystemRandomSource.Default, values, d1, d2);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/Gamma.cs b/MathNet.Numerics/Distributions/Gamma.cs
new file mode 100644
index 0000000..85f5408
--- /dev/null
+++ b/MathNet.Numerics/Distributions/Gamma.cs
@@ -0,0 +1,680 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2014 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+
+using System;
+using System.Collections.Generic;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Continuous Univariate Gamma distribution.
+/// For details about this distribution, see
+/// Wikipedia - Gamma distribution.
+///
+///
+/// The Gamma distribution is parametrized by a shape and inverse scale parameter. When we want
+/// to specify a Gamma distribution which is a point distribution we set the shape parameter to be the
+/// location of the point distribution and the inverse scale as positive infinity. The distribution
+/// with shape and inverse scale both zero is undefined.
+///
+/// Random number generation for the Gamma distribution is based on the algorithm in:
+/// "A Simple Method for Generating Gamma Variables" - Marsaglia & Tsang
+/// ACM Transactions on Mathematical Software, Vol. 26, No. 3, September 2000, Pages 363–372.
+///
+public class Gamma : IContinuousDistribution
+{
+ System.Random _random;
+
+ readonly double _shape;
+ readonly double _rate;
+
+ ///
+ /// Initializes a new instance of the Gamma class.
+ ///
+ /// The shape (k, α) of the Gamma distribution. Range: α ≥ 0.
+ /// The rate or inverse scale (β) of the Gamma distribution. Range: β ≥ 0.
+ public Gamma(double shape, double rate)
+ {
+ if (!IsValidParameterSet(shape, rate))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _shape = shape;
+ _rate = rate;
+ }
+
+ ///
+ /// Initializes a new instance of the Gamma class.
+ ///
+ /// The shape (k, α) of the Gamma distribution. Range: α ≥ 0.
+ /// The rate or inverse scale (β) of the Gamma distribution. Range: β ≥ 0.
+ /// The random number generator which is used to draw random samples.
+ public Gamma(double shape, double rate, System.Random randomSource)
+ {
+ if (!IsValidParameterSet(shape, rate))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _shape = shape;
+ _rate = rate;
+ }
+
+ ///
+ /// Constructs a Gamma distribution from a shape and scale parameter. The distribution will
+ /// be initialized with the default random number generator.
+ ///
+ /// The shape (k) of the Gamma distribution. Range: k ≥ 0.
+ /// The scale (θ) of the Gamma distribution. Range: θ ≥ 0
+ /// The random number generator which is used to draw random samples. Optional, can be null.
+ public static Gamma WithShapeScale(double shape, double scale, System.Random randomSource = null)
+ {
+ return new Gamma(shape, 1.0 / scale, randomSource);
+ }
+
+ ///
+ /// Constructs a Gamma distribution from a shape and inverse scale parameter. The distribution will
+ /// be initialized with the default random number generator.
+ ///
+ /// The shape (k, α) of the Gamma distribution. Range: α ≥ 0.
+ /// The rate or inverse scale (β) of the Gamma distribution. Range: β ≥ 0.
+ /// The random number generator which is used to draw random samples. Optional, can be null.
+ public static Gamma WithShapeRate(double shape, double rate, System.Random randomSource = null)
+ {
+ return new Gamma(shape, rate, randomSource);
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ /// a string representation of the distribution.
+ public override string ToString()
+ {
+ return "Gamma(α = " + _shape + ", β = " + _rate + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The shape (k, α) of the Gamma distribution. Range: α ≥ 0.
+ /// The rate or inverse scale (β) of the Gamma distribution. Range: β ≥ 0.
+ public static bool IsValidParameterSet(double shape, double rate)
+ {
+ return shape >= 0.0 && rate >= 0.0;
+ }
+
+ ///
+ /// Gets or sets the shape (k, α) of the Gamma distribution. Range: α ≥ 0.
+ ///
+ public double Shape
+ {
+ get { return _shape; }
+ }
+
+ ///
+ /// Gets or sets the rate or inverse scale (β) of the Gamma distribution. Range: β ≥ 0.
+ ///
+ public double Rate
+ {
+ get { return _rate; }
+ }
+
+ ///
+ /// Gets or sets the scale (θ) of the Gamma distribution.
+ ///
+ public double Scale
+ {
+ get { return 1.0 / _rate; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the Gamma distribution.
+ ///
+ public double Mean
+ {
+ get
+ {
+ if (double.IsPositiveInfinity(_rate))
+ {
+ return _shape;
+ }
+
+ if (_rate == 0.0 && _shape == 0.0)
+ {
+ return double.NaN;
+ }
+
+ return _shape / _rate;
+ }
+ }
+
+ ///
+ /// Gets the variance of the Gamma distribution.
+ ///
+ public double Variance
+ {
+ get
+ {
+ if (double.IsPositiveInfinity(_rate))
+ {
+ return 0.0;
+ }
+
+ if (_rate == 0.0 && _shape == 0.0)
+ {
+ return double.NaN;
+ }
+
+ return _shape / (_rate * _rate);
+ }
+ }
+
+ ///
+ /// Gets the standard deviation of the Gamma distribution.
+ ///
+ public double StdDev
+ {
+ get
+ {
+ if (double.IsPositiveInfinity(_rate))
+ {
+ return 0.0;
+ }
+
+ if (_rate == 0.0 && _shape == 0.0)
+ {
+ return double.NaN;
+ }
+
+ return Math.Sqrt(_shape / (_rate * _rate));
+ }
+ }
+
+ ///
+ /// Gets the entropy of the Gamma distribution.
+ ///
+ public double Entropy
+ {
+ get
+ {
+ if (double.IsPositiveInfinity(_rate))
+ {
+ return 0.0;
+ }
+
+ if (_rate == 0.0 && _shape == 0.0)
+ {
+ return double.NaN;
+ }
+
+ return _shape - Math.Log(_rate) + SpecialFunctions.GammaLn(_shape) + ((1.0 - _shape) * SpecialFunctions.DiGamma(_shape));
+ }
+ }
+
+ ///
+ /// Gets the skewness of the Gamma distribution.
+ ///
+ public double Skewness
+ {
+ get
+ {
+ if (double.IsPositiveInfinity(_rate))
+ {
+ return 0.0;
+ }
+
+ if (_rate == 0.0 && _shape == 0.0)
+ {
+ return double.NaN;
+ }
+
+ return 2.0 / Math.Sqrt(_shape);
+ }
+ }
+
+ ///
+ /// Gets the mode of the Gamma distribution.
+ ///
+ public double Mode
+ {
+ get
+ {
+ if (double.IsPositiveInfinity(_rate))
+ {
+ return _shape;
+ }
+
+ if (_rate == 0.0 && _shape == 0.0)
+ {
+ return double.NaN;
+ }
+
+ return (_shape - 1.0) / _rate;
+ }
+ }
+
+ ///
+ /// Gets the median of the Gamma distribution.
+ ///
+ public double Median
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the minimum of the Gamma distribution.
+ ///
+ public double Minimum
+ {
+ get { return 0.0; }
+ }
+
+ ///
+ /// Gets the maximum of the Gamma distribution.
+ ///
+ public double Maximum
+ {
+ get { return double.PositiveInfinity; }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public double Density(double x)
+ {
+ return PDF(_shape, _rate, x);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The location at which to compute the log density.
+ /// the log density at .
+ ///
+ public double DensityLn(double x)
+ {
+ return PDFLn(_shape, _rate, x);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ ///
+ public double CumulativeDistribution(double x)
+ {
+ return CDF(_shape, _rate, x);
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability. This is also known as the quantile or percent point function.
+ ///
+ /// The location at which to compute the inverse cumulative density.
+ /// the inverse cumulative density at .
+ ///
+ public double InverseCumulativeDistribution(double p)
+ {
+ return InvCDF(_shape, _rate, p);
+ }
+
+ ///
+ /// Generates a sample from the Gamma distribution.
+ ///
+ /// a sample from the distribution.
+ public double Sample()
+ {
+ return SampleUnchecked(_random, _shape, _rate);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ public void Samples(double[] values)
+ {
+ SamplesUnchecked(_random, values, _shape, _rate);
+ }
+
+ ///
+ /// Generates a sequence of samples from the Gamma distribution.
+ ///
+ /// a sequence of samples from the distribution.
+ public IEnumerable Samples()
+ {
+ return SamplesUnchecked(_random, _shape, _rate);
+ }
+
+ ///
+ /// Sampling implementation based on:
+ /// "A Simple Method for Generating Gamma Variables" - Marsaglia & Tsang
+ /// ACM Transactions on Mathematical Software, Vol. 26, No. 3, September 2000, Pages 363–372.
+ /// This method performs no parameter checks.
+ ///
+ /// The random number generator to use.
+ /// The shape (k, α) of the Gamma distribution. Range: α ≥ 0.
+ /// The rate or inverse scale (β) of the Gamma distribution. Range: β ≥ 0.
+ /// A sample from a Gamma distributed random variable.
+ internal static double SampleUnchecked(System.Random rnd, double shape, double rate)
+ {
+ if (double.IsPositiveInfinity(rate))
+ {
+ return shape;
+ }
+
+ var a = shape;
+ var alphafix = 1.0;
+
+ // Fix when alpha is less than one.
+ if (shape < 1.0)
+ {
+ a = shape + 1.0;
+ alphafix = Math.Pow(rnd.NextDouble(), 1.0 / shape);
+ }
+
+ var d = a - (1.0 / 3.0);
+ var c = 1.0 / Math.Sqrt(9.0 * d);
+ while (true)
+ {
+ var x = Normal.Sample(rnd, 0.0, 1.0);
+ var v = 1.0 + (c * x);
+ while (v <= 0.0)
+ {
+ x = Normal.Sample(rnd, 0.0, 1.0);
+ v = 1.0 + (c * x);
+ }
+
+ v = v * v * v;
+ var u = rnd.NextDouble();
+ x = x * x;
+ if (u < 1.0 - (0.0331 * x * x))
+ {
+ return alphafix * d * v / rate;
+ }
+
+ if (Math.Log(u) < (0.5 * x) + (d * (1.0 - v + Math.Log(v))))
+ {
+ return alphafix * d * v / rate;
+ }
+ }
+ }
+
+ internal static void SamplesUnchecked(System.Random rnd, double[] values, double shape, double rate)
+ {
+ for (int i = 0; i < values.Length; i++)
+ {
+ values[i] = SampleUnchecked(rnd, shape, rate);
+ }
+ }
+
+ internal static IEnumerable SamplesUnchecked(System.Random rnd, double location, double scale)
+ {
+ while (true)
+ {
+ yield return SampleUnchecked(rnd, location, scale);
+ }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The shape (k, α) of the Gamma distribution. Range: α ≥ 0.
+ /// The rate or inverse scale (β) of the Gamma distribution. Range: β ≥ 0.
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public static double PDF(double shape, double rate, double x)
+ {
+ if (shape < 0.0 || rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (double.IsPositiveInfinity(rate))
+ {
+ return x == shape ? double.PositiveInfinity : 0.0;
+ }
+
+ if (shape == 0.0 && rate == 0.0)
+ {
+ return 0.0;
+ }
+
+ if (shape == 1.0)
+ {
+ return rate * Math.Exp(-rate * x);
+ }
+
+ if (shape > 160.0)
+ {
+ return Math.Exp(PDFLn(shape, rate, x));
+ }
+
+ return Math.Pow(rate, shape) * Math.Pow(x, shape - 1.0) * Math.Exp(-rate * x) / SpecialFunctions.Gamma(shape);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The shape (k, α) of the Gamma distribution. Range: α ≥ 0.
+ /// The rate or inverse scale (β) of the Gamma distribution. Range: β ≥ 0.
+ /// The location at which to compute the density.
+ /// the log density at .
+ ///
+ public static double PDFLn(double shape, double rate, double x)
+ {
+ if (shape < 0.0 || rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (double.IsPositiveInfinity(rate))
+ {
+ return x == shape ? double.PositiveInfinity : double.NegativeInfinity;
+ }
+
+ if (shape == 0.0 && rate == 0.0)
+ {
+ return double.NegativeInfinity;
+ }
+
+ if (shape == 1.0)
+ {
+ return Math.Log(rate) - (rate * x);
+ }
+
+ return (shape * Math.Log(rate)) + ((shape - 1.0) * Math.Log(x)) - (rate * x) - SpecialFunctions.GammaLn(shape);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// The shape (k, α) of the Gamma distribution. Range: α ≥ 0.
+ /// The rate or inverse scale (β) of the Gamma distribution. Range: β ≥ 0.
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(double shape, double rate, double x)
+ {
+ if (shape < 0.0 || rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (double.IsPositiveInfinity(rate))
+ {
+ return x >= shape ? 1.0 : 0.0;
+ }
+
+ if (shape == 0.0 && rate == 0.0)
+ {
+ return 0.0;
+ }
+
+ return SpecialFunctions.GammaLowerRegularized(shape, x * rate);
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability. This is also known as the quantile or percent point function.
+ ///
+ /// The location at which to compute the inverse cumulative density.
+ /// The shape (k, α) of the Gamma distribution. Range: α ≥ 0.
+ /// The rate or inverse scale (β) of the Gamma distribution. Range: β ≥ 0.
+ /// the inverse cumulative density at .
+ ///
+ public static double InvCDF(double shape, double rate, double p)
+ {
+ if (shape < 0.0 || rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SpecialFunctions.GammaLowerRegularizedInv(shape, p) / rate;
+ }
+
+ ///
+ /// Generates a sample from the Gamma distribution.
+ ///
+ /// The random number generator to use.
+ /// The shape (k, α) of the Gamma distribution. Range: α ≥ 0.
+ /// The rate or inverse scale (β) of the Gamma distribution. Range: β ≥ 0.
+ /// a sample from the distribution.
+ public static double Sample(System.Random rnd, double shape, double rate)
+ {
+ if (shape < 0.0 || rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(rnd, shape, rate);
+ }
+
+ ///
+ /// Generates a sequence of samples from the Gamma distribution.
+ ///
+ /// The random number generator to use.
+ /// The shape (k, α) of the Gamma distribution. Range: α ≥ 0.
+ /// The rate or inverse scale (β) of the Gamma distribution. Range: β ≥ 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(System.Random rnd, double shape, double rate)
+ {
+ if (shape < 0.0 || rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(rnd, shape, rate);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// The shape (k, α) of the Gamma distribution. Range: α ≥ 0.
+ /// The rate or inverse scale (β) of the Gamma distribution. Range: β ≥ 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(System.Random rnd, double[] values, double shape, double rate)
+ {
+ if (shape < 0.0 || rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(rnd, values, shape, rate);
+ }
+
+ ///
+ /// Generates a sample from the Gamma distribution.
+ ///
+ /// The shape (k, α) of the Gamma distribution. Range: α ≥ 0.
+ /// The rate or inverse scale (β) of the Gamma distribution. Range: β ≥ 0.
+ /// a sample from the distribution.
+ public static double Sample(double shape, double rate)
+ {
+ if (shape < 0.0 || rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(SystemRandomSource.Default, shape, rate);
+ }
+
+ ///
+ /// Generates a sequence of samples from the Gamma distribution.
+ ///
+ /// The shape (k, α) of the Gamma distribution. Range: α ≥ 0.
+ /// The rate or inverse scale (β) of the Gamma distribution. Range: β ≥ 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(double shape, double rate)
+ {
+ if (shape < 0.0 || rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(SystemRandomSource.Default, shape, rate);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// The shape (k, α) of the Gamma distribution. Range: α ≥ 0.
+ /// The rate or inverse scale (β) of the Gamma distribution. Range: β ≥ 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(double[] values, double shape, double rate)
+ {
+ if (shape < 0.0 || rate < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(SystemRandomSource.Default, values, shape, rate);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/Geometric.cs b/MathNet.Numerics/Distributions/Geometric.cs
new file mode 100644
index 0000000..595c4aa
--- /dev/null
+++ b/MathNet.Numerics/Distributions/Geometric.cs
@@ -0,0 +1,451 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2014 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+using MathNet.Numerics.Threading;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Discrete Univariate Geometric distribution.
+/// The Geometric distribution is a distribution over positive integers parameterized by one positive real number.
+/// This implementation of the Geometric distribution will never generate 0's.
+/// Wikipedia - geometric distribution.
+///
+public class Geometric : IDiscreteDistribution
+{
+ System.Random _random;
+
+ readonly double _p;
+
+ ///
+ /// Initializes a new instance of the Geometric class.
+ ///
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ public Geometric(double p)
+ {
+ if (!IsValidParameterSet(p))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _p = p;
+ }
+
+ ///
+ /// Initializes a new instance of the Geometric class.
+ ///
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ /// The random number generator which is used to draw random samples.
+ public Geometric(double p, System.Random randomSource)
+ {
+ if (!IsValidParameterSet(p))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _p = p;
+ }
+
+ ///
+ /// Returns a that represents this instance.
+ ///
+ /// A that represents this instance.
+ public override string ToString()
+ {
+ return "Geometric(p = " + _p + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ public static bool IsValidParameterSet(double p)
+ {
+ return p >= 0.0 && p <= 1.0;
+ }
+
+ ///
+ /// Gets the probability of generating a one. Range: 0 ≤ p ≤ 1.
+ ///
+ public double P
+ {
+ get { return _p; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the distribution.
+ ///
+ public double Mean
+ {
+ get { return 1.0 / _p; }
+ }
+
+ ///
+ /// Gets the variance of the distribution.
+ ///
+ public double Variance
+ {
+ get { return (1.0 - _p) / (_p * _p); }
+ }
+
+ ///
+ /// Gets the standard deviation of the distribution.
+ ///
+ public double StdDev
+ {
+ get { return Math.Sqrt(1.0 - _p) / _p; }
+ }
+
+ ///
+ /// Gets the entropy of the distribution.
+ ///
+ public double Entropy
+ {
+ get { return ((-_p * Math.Log(_p, 2.0)) - ((1.0 - _p) * Math.Log(1.0 - _p, 2.0))) / _p; }
+ }
+
+ ///
+ /// Gets the skewness of the distribution.
+ ///
+ /// Throws a not supported exception.
+ public double Skewness
+ {
+ get { return (2.0 - _p) / Math.Sqrt(1.0 - _p); }
+ }
+
+ ///
+ /// Gets the mode of the distribution.
+ ///
+ public int Mode
+ {
+ get { return 1; }
+ }
+
+ ///
+ /// Gets the median of the distribution.
+ ///
+ public double Median
+ {
+ get { return _p == 0.0 ? double.PositiveInfinity : _p == 1.0 ? 1.0 : Math.Ceiling(-Constants.Ln2 / Math.Log(1 - _p)); }
+ }
+
+ ///
+ /// Gets the smallest element in the domain of the distributions which can be represented by an integer.
+ ///
+ public int Minimum
+ {
+ get { return 1; }
+ }
+
+ ///
+ /// Gets the largest element in the domain of the distributions which can be represented by an integer.
+ ///
+ public int Maximum
+ {
+ get { return int.MaxValue; }
+ }
+
+ ///
+ /// Computes the probability mass (PMF) at k, i.e. P(X = k).
+ ///
+ /// The location in the domain where we want to evaluate the probability mass function.
+ /// the probability mass at location .
+ public double Probability(int k)
+ {
+ if (k <= 0)
+ {
+ return 0.0;
+ }
+
+ return Math.Pow(1.0 - _p, k - 1) * _p;
+ }
+
+ ///
+ /// Computes the log probability mass (lnPMF) at k, i.e. ln(P(X = k)).
+ ///
+ /// The location in the domain where we want to evaluate the log probability mass function.
+ /// the log probability mass at location .
+ public double ProbabilityLn(int k)
+ {
+ if (k <= 0)
+ {
+ return double.NegativeInfinity;
+ }
+
+ return ((k - 1) * Math.Log(1.0 - _p)) + Math.Log(_p);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ public double CumulativeDistribution(double x)
+ {
+ return 1.0 - Math.Pow(1.0 - _p, x);
+ }
+
+ ///
+ /// Computes the probability mass (PMF) at k, i.e. P(X = k).
+ ///
+ /// The location in the domain where we want to evaluate the probability mass function.
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ /// the probability mass at location .
+ public static double PMF(double p, int k)
+ {
+ if (!(p >= 0.0 && p <= 1.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (k <= 0)
+ {
+ return 0.0;
+ }
+
+ return Math.Pow(1.0 - p, k - 1) * p;
+ }
+
+ ///
+ /// Computes the log probability mass (lnPMF) at k, i.e. ln(P(X = k)).
+ ///
+ /// The location in the domain where we want to evaluate the log probability mass function.
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ /// the log probability mass at location .
+ public static double PMFLn(double p, int k)
+ {
+ if (!(p >= 0.0 && p <= 1.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (k <= 0)
+ {
+ return double.NegativeInfinity;
+ }
+
+ return ((k - 1) * Math.Log(1.0 - p)) + Math.Log(p);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(double p, double x)
+ {
+ if (!(p >= 0.0 && p <= 1.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return 1.0 - Math.Pow(1.0 - p, x);
+ }
+
+ ///
+ /// Returns one sample from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ /// One sample from the distribution implied by .
+ static int SampleUnchecked(System.Random rnd, double p)
+ {
+ return p == 1.0 ? 1 : (int)Math.Ceiling(Math.Log(1.0 - rnd.NextDouble(), 1.0 - p));
+ }
+
+ static void SamplesUnchecked(System.Random rnd, int[] values, double p)
+ {
+ if (p == 1.0)
+ {
+ CommonParallel.For(0, values.Length, 4096, (a, b) =>
+ {
+ for (int i = a; i < b; i++)
+ {
+ values[i] = 1;
+ }
+ });
+ return;
+ }
+
+ var uniform = rnd.NextDoubles(values.Length);
+ double rp = 1.0 - p;
+ CommonParallel.For(0, values.Length, 4096, (a, b) =>
+ {
+ for (int i = a; i < b; i++)
+ {
+ values[i] = (int)Math.Ceiling(Math.Log(1.0 - uniform[i], rp));
+ }
+ });
+ }
+
+ static IEnumerable SamplesUnchecked(System.Random rnd, double p)
+ {
+ if (p == 1.0)
+ {
+ return Generate.RepeatSequence(1);
+ }
+
+ double rp = 1.0 - p;
+ return rnd.NextDoubleSequence().Select(r => (int)Math.Ceiling(Math.Log(1.0 - r, rp)));
+ }
+
+ ///
+ /// Samples a Geometric distributed random variable.
+ ///
+ /// A sample from the Geometric distribution.
+ public int Sample()
+ {
+ return SampleUnchecked(_random, _p);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ public void Samples(int[] values)
+ {
+ SamplesUnchecked(_random, values, _p);
+ }
+
+ ///
+ /// Samples an array of Geometric distributed random variables.
+ ///
+ /// a sequence of samples from the distribution.
+ public IEnumerable Samples()
+ {
+ return SamplesUnchecked(_random, _p);
+ }
+
+ ///
+ /// Samples a random variable.
+ ///
+ /// The random number generator to use.
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ public static int Sample(System.Random rnd, double p)
+ {
+ if (!(p >= 0.0 && p <= 1.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(rnd, p);
+ }
+
+ ///
+ /// Samples a sequence of this random variable.
+ ///
+ /// The random number generator to use.
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ public static IEnumerable Samples(System.Random rnd, double p)
+ {
+ if (!(p >= 0.0 && p <= 1.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(rnd, p);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ public static void Samples(System.Random rnd, int[] values, double p)
+ {
+ if (!(p >= 0.0 && p <= 1.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(rnd, values, p);
+ }
+
+ ///
+ /// Samples a random variable.
+ ///
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ public static int Sample(double p)
+ {
+ if (!(p >= 0.0 && p <= 1.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(SystemRandomSource.Default, p);
+ }
+
+ ///
+ /// Samples a sequence of this random variable.
+ ///
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ public static IEnumerable Samples(double p)
+ {
+ if (!(p >= 0.0 && p <= 1.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(SystemRandomSource.Default, p);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// The probability (p) of generating one. Range: 0 ≤ p ≤ 1.
+ public static void Samples(int[] values, double p)
+ {
+ if (!(p >= 0.0 && p <= 1.0))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(SystemRandomSource.Default, values, p);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/Hypergeometric.cs b/MathNet.Numerics/Distributions/Hypergeometric.cs
new file mode 100644
index 0000000..9101e69
--- /dev/null
+++ b/MathNet.Numerics/Distributions/Hypergeometric.cs
@@ -0,0 +1,494 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2014 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+
+using System;
+using System.Collections.Generic;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Discrete Univariate Hypergeometric distribution.
+/// This distribution is a discrete probability distribution that describes the number of successes in a sequence
+/// of n draws from a finite population without replacement, just as the binomial distribution
+/// describes the number of successes for draws with replacement
+/// Wikipedia - Hypergeometric distribution.
+///
+public class Hypergeometric : IDiscreteDistribution
+{
+ System.Random _random;
+
+ readonly int _population;
+ readonly int _success;
+ readonly int _draws;
+
+ ///
+ /// Initializes a new instance of the Hypergeometric class.
+ ///
+ /// The size of the population (N).
+ /// The number successes within the population (K, M).
+ /// The number of draws without replacement (n).
+ public Hypergeometric(int population, int success, int draws)
+ {
+ if (!IsValidParameterSet(population, success, draws))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _population = population;
+ _success = success;
+ _draws = draws;
+ }
+
+ ///
+ /// Initializes a new instance of the Hypergeometric class.
+ ///
+ /// The size of the population (N).
+ /// The number successes within the population (K, M).
+ /// The number of draws without replacement (n).
+ /// The random number generator which is used to draw random samples.
+ public Hypergeometric(int population, int success, int draws, System.Random randomSource)
+ {
+ if (!IsValidParameterSet(population, success, draws))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _population = population;
+ _success = success;
+ _draws = draws;
+ }
+
+ ///
+ /// Returns a that represents this instance.
+ ///
+ ///
+ /// A that represents this instance.
+ ///
+ public override string ToString()
+ {
+ return "Hypergeometric(N = " + _population + ", M = " + _success + ", n = " + _draws + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The size of the population (N).
+ /// The number successes within the population (K, M).
+ /// The number of draws without replacement (n).
+ public static bool IsValidParameterSet(int population, int success, int draws)
+ {
+ return population >= 0 && success >= 0 && draws >= 0 && success <= population && draws <= population;
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the size of the population (N).
+ ///
+ public int Population
+ {
+ get { return _population; }
+ }
+
+ ///
+ /// Gets the number of draws without replacement (n).
+ ///
+ public int Draws
+ {
+ get { return _draws; }
+ }
+
+ ///
+ /// Gets the number successes within the population (K, M).
+ ///
+ public int Success
+ {
+ get { return _success; }
+ }
+
+ ///
+ /// Gets the mean of the distribution.
+ ///
+ public double Mean
+ {
+ get { return (double)_success * _draws / _population; }
+ }
+
+ ///
+ /// Gets the variance of the distribution.
+ ///
+ public double Variance
+ {
+ get { return _draws * _success * (_population - _draws) * (_population - _success) / (_population * _population * (_population - 1.0)); }
+ }
+
+ ///
+ /// Gets the standard deviation of the distribution.
+ ///
+ public double StdDev
+ {
+ get { return Math.Sqrt(Variance); }
+ }
+
+ ///
+ /// Gets the entropy of the distribution.
+ ///
+ public double Entropy
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the skewness of the distribution.
+ ///
+ public double Skewness
+ {
+ get { return (Math.Sqrt(_population - 1.0) * (_population - (2 * _draws)) * (_population - (2 * _success))) / (Math.Sqrt(_draws * _success * (_population - _success) * (_population - _draws)) * (_population - 2.0)); }
+ }
+
+ ///
+ /// Gets the mode of the distribution.
+ ///
+ public int Mode
+ {
+ get { return (_draws + 1) * (_success + 1) / (_population + 2); }
+ }
+
+ ///
+ /// Gets the median of the distribution.
+ ///
+ public double Median
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the minimum of the distribution.
+ ///
+ public int Minimum
+ {
+ get { return Math.Max(0, _draws + _success - _population); }
+ }
+
+ ///
+ /// Gets the maximum of the distribution.
+ ///
+ public int Maximum
+ {
+ get { return Math.Min(_success, _draws); }
+ }
+
+ ///
+ /// Computes the probability mass (PMF) at k, i.e. P(X = k).
+ ///
+ /// The location in the domain where we want to evaluate the probability mass function.
+ /// the probability mass at location .
+ public double Probability(int k)
+ {
+ return SpecialFunctions.Binomial(_success, k) * SpecialFunctions.Binomial(_population - _success, _draws - k) / SpecialFunctions.Binomial(_population, _draws);
+ }
+
+ ///
+ /// Computes the log probability mass (lnPMF) at k, i.e. ln(P(X = k)).
+ ///
+ /// The location in the domain where we want to evaluate the log probability mass function.
+ /// the log probability mass at location .
+ public double ProbabilityLn(int k)
+ {
+ return SpecialFunctions.BinomialLn(_success, k) + SpecialFunctions.BinomialLn(_population - _success, _draws - k) - SpecialFunctions.BinomialLn(_population, _draws);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ public double CumulativeDistribution(double x)
+ {
+ return CDF(_population, _success, _draws, x);
+ }
+
+ ///
+ /// Computes the probability mass (PMF) at k, i.e. P(X = k).
+ ///
+ /// The location in the domain where we want to evaluate the probability mass function.
+ /// The size of the population (N).
+ /// The number successes within the population (K, M).
+ /// The number of draws without replacement (n).
+ /// the probability mass at location .
+ public static double PMF(int population, int success, int draws, int k)
+ {
+ if (!(population >= 0 && success >= 0 && draws >= 0 && success <= population && draws <= population))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SpecialFunctions.Binomial(success, k) * SpecialFunctions.Binomial(population - success, draws - k) / SpecialFunctions.Binomial(population, draws);
+ }
+
+ ///
+ /// Computes the log probability mass (lnPMF) at k, i.e. ln(P(X = k)).
+ ///
+ /// The location in the domain where we want to evaluate the log probability mass function.
+ /// The size of the population (N).
+ /// The number successes within the population (K, M).
+ /// The number of draws without replacement (n).
+ /// the log probability mass at location .
+ public static double PMFLn(int population, int success, int draws, int k)
+ {
+ if (!(population >= 0 && success >= 0 && draws >= 0 && success <= population && draws <= population))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SpecialFunctions.BinomialLn(success, k) + SpecialFunctions.BinomialLn(population - success, draws - k) - SpecialFunctions.BinomialLn(population, draws);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// The size of the population (N).
+ /// The number successes within the population (K, M).
+ /// The number of draws without replacement (n).
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(int population, int success, int draws, double x)
+ {
+ if (!(population >= 0 && success >= 0 && draws >= 0 && success <= population && draws <= population))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (x < Math.Max(0, draws + success - population))
+ {
+ return 0.0;
+ }
+
+ if (x >= Math.Min(success, draws))
+ {
+ return 1.0;
+ }
+
+ var k = (int)Math.Floor(x);
+ var denominatorLn = SpecialFunctions.BinomialLn(population, draws);
+ var sum = 0.0;
+ for (var i = 0; i <= k; i++)
+ {
+ sum += Math.Exp(SpecialFunctions.BinomialLn(success, i) + SpecialFunctions.BinomialLn(population - success, draws - i) - denominatorLn);
+ }
+
+ return sum;
+ }
+
+ ///
+ /// Generates a sample from the Hypergeometric distribution without doing parameter checking.
+ ///
+ /// The random number generator to use.
+ /// The size of the population (N).
+ /// The number successes within the population (K, M).
+ /// The n parameter of the distribution.
+ /// a random number from the Hypergeometric distribution.
+ static int SampleUnchecked(System.Random rnd, int population, int success, int draws)
+ {
+ var x = 0;
+
+ do
+ {
+ var p = (double)success / population;
+ var r = rnd.NextDouble();
+ if (r < p)
+ {
+ x++;
+ success--;
+ }
+
+ population--;
+ draws--;
+ }
+ while (0 < draws);
+
+ return x;
+ }
+
+ static void SamplesUnchecked(System.Random rnd, int[] values, int population, int success, int draws)
+ {
+ for (int i = 0; i < values.Length; i++)
+ {
+ values[i] = SampleUnchecked(rnd, population, success, draws);
+ }
+ }
+
+ static IEnumerable SamplesUnchecked(System.Random rnd, int population, int success, int draws)
+ {
+ while (true)
+ {
+ yield return SampleUnchecked(rnd, population, success, draws);
+ }
+ }
+
+ ///
+ /// Samples a Hypergeometric distributed random variable.
+ ///
+ /// The number of successes in n trials.
+ public int Sample()
+ {
+ return SampleUnchecked(_random, _population, _success, _draws);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ public void Samples(int[] values)
+ {
+ SamplesUnchecked(_random, values, _population, _success, _draws);
+ }
+
+ ///
+ /// Samples an array of Hypergeometric distributed random variables.
+ ///
+ /// a sequence of successes in n trials.
+ public IEnumerable Samples()
+ {
+ return SamplesUnchecked(_random, _population, _success, _draws);
+ }
+
+ ///
+ /// Samples a random variable.
+ ///
+ /// The random number generator to use.
+ /// The size of the population (N).
+ /// The number successes within the population (K, M).
+ /// The number of draws without replacement (n).
+ public static int Sample(System.Random rnd, int population, int success, int draws)
+ {
+ if (!(population >= 0 && success >= 0 && draws >= 0 && success <= population && draws <= population))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(rnd, population, success, draws);
+ }
+
+ ///
+ /// Samples a sequence of this random variable.
+ ///
+ /// The random number generator to use.
+ /// The size of the population (N).
+ /// The number successes within the population (K, M).
+ /// The number of draws without replacement (n).
+ public static IEnumerable Samples(System.Random rnd, int population, int success, int draws)
+ {
+ if (!(population >= 0 && success >= 0 && draws >= 0 && success <= population && draws <= population))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(rnd, population, success, draws);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// The size of the population (N).
+ /// The number successes within the population (K, M).
+ /// The number of draws without replacement (n).
+ public static void Samples(System.Random rnd, int[] values, int population, int success, int draws)
+ {
+ if (!(population >= 0 && success >= 0 && draws >= 0 && success <= population && draws <= population))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(rnd, values, population, success, draws);
+ }
+
+ ///
+ /// Samples a random variable.
+ ///
+ /// The size of the population (N).
+ /// The number successes within the population (K, M).
+ /// The number of draws without replacement (n).
+ public static int Sample(int population, int success, int draws)
+ {
+ if (!(population >= 0 && success >= 0 && draws >= 0 && success <= population && draws <= population))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(SystemRandomSource.Default, population, success, draws);
+ }
+
+ ///
+ /// Samples a sequence of this random variable.
+ ///
+ /// The size of the population (N).
+ /// The number successes within the population (K, M).
+ /// The number of draws without replacement (n).
+ public static IEnumerable Samples(int population, int success, int draws)
+ {
+ if (!(population >= 0 && success >= 0 && draws >= 0 && success <= population && draws <= population))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(SystemRandomSource.Default, population, success, draws);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// The size of the population (N).
+ /// The number successes within the population (K, M).
+ /// The number of draws without replacement (n).
+ public static void Samples(int[] values, int population, int success, int draws)
+ {
+ if (!(population >= 0 && success >= 0 && draws >= 0 && success <= population && draws <= population))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(SystemRandomSource.Default, values, population, success, draws);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/IContinuousDistribution.cs b/MathNet.Numerics/Distributions/IContinuousDistribution.cs
new file mode 100644
index 0000000..9d3f171
--- /dev/null
+++ b/MathNet.Numerics/Distributions/IContinuousDistribution.cs
@@ -0,0 +1,85 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2014 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.Distributions;
+
+using System.Collections.Generic;
+
+///
+/// Continuous Univariate Probability Distribution.
+///
+///
+public interface IContinuousDistribution : IUnivariateDistribution
+{
+ ///
+ /// Gets the mode of the distribution.
+ ///
+ double Mode { get; }
+
+ ///
+ /// Gets the smallest element in the domain of the distribution which can be represented by a double.
+ ///
+ double Minimum { get; }
+
+ ///
+ /// Gets the largest element in the domain of the distribution which can be represented by a double.
+ ///
+ double Maximum { get; }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The location at which to compute the density.
+ /// the density at .
+ double Density(double x);
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The location at which to compute the log density.
+ /// the log density at .
+ double DensityLn(double x);
+
+ ///
+ /// Draws a random sample from the distribution.
+ ///
+ /// a sample from the distribution.
+ double Sample();
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ void Samples(double[] values);
+
+ ///
+ /// Draws a sequence of random samples from the distribution.
+ ///
+ /// an infinite sequence of samples from the distribution.
+ IEnumerable Samples();
+}
diff --git a/MathNet.Numerics/Distributions/IDiscreteDistribution.cs b/MathNet.Numerics/Distributions/IDiscreteDistribution.cs
new file mode 100644
index 0000000..1ce5a76
--- /dev/null
+++ b/MathNet.Numerics/Distributions/IDiscreteDistribution.cs
@@ -0,0 +1,85 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2014 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.Distributions;
+
+using System.Collections.Generic;
+
+///
+/// Discrete Univariate Probability Distribution.
+///
+///
+public interface IDiscreteDistribution : IUnivariateDistribution
+{
+ ///
+ /// Gets the mode of the distribution.
+ ///
+ int Mode { get; }
+
+ ///
+ /// Gets the smallest element in the domain of the distribution which can be represented by an integer.
+ ///
+ int Minimum { get; }
+
+ ///
+ /// Gets the largest element in the domain of the distribution which can be represented by an integer.
+ ///
+ int Maximum { get; }
+
+ ///
+ /// Computes the probability mass (PMF) at k, i.e. P(X = k).
+ ///
+ /// The location in the domain where we want to evaluate the probability mass function.
+ /// the probability mass at location .
+ double Probability(int k);
+
+ ///
+ /// Computes the log probability mass (lnPMF) at k, i.e. ln(P(X = k)).
+ ///
+ /// The location in the domain where we want to evaluate the log probability mass function.
+ /// the log probability mass at location .
+ double ProbabilityLn(int k);
+
+ ///
+ /// Draws a random sample from the distribution.
+ ///
+ /// a sample from the distribution.
+ int Sample();
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ void Samples(int[] values);
+
+ ///
+ /// Draws a sequence of random samples from the distribution.
+ ///
+ /// an infinite sequence of samples from the distribution.
+ IEnumerable Samples();
+}
diff --git a/MathNet.Numerics/Distributions/IDistribution.cs b/MathNet.Numerics/Distributions/IDistribution.cs
new file mode 100644
index 0000000..e6910d8
--- /dev/null
+++ b/MathNet.Numerics/Distributions/IDistribution.cs
@@ -0,0 +1,43 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2013 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Probability Distribution.
+///
+///
+///
+public interface IDistribution
+{
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ System.Random RandomSource { get; set; }
+}
diff --git a/MathNet.Numerics/Distributions/IUnivariateDistribution.cs b/MathNet.Numerics/Distributions/IUnivariateDistribution.cs
new file mode 100644
index 0000000..a637918
--- /dev/null
+++ b/MathNet.Numerics/Distributions/IUnivariateDistribution.cs
@@ -0,0 +1,75 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2013 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Univariate Probability Distribution.
+///
+///
+///
+public interface IUnivariateDistribution : IDistribution
+{
+ ///
+ /// Gets the mean of the distribution.
+ ///
+ double Mean { get; }
+
+ ///
+ /// Gets the variance of the distribution.
+ ///
+ double Variance { get; }
+
+ ///
+ /// Gets the standard deviation of the distribution.
+ ///
+ double StdDev { get; }
+
+ ///
+ /// Gets the entropy of the distribution.
+ ///
+ double Entropy { get; }
+
+ ///
+ /// Gets the skewness of the distribution.
+ ///
+ double Skewness { get; }
+
+ ///
+ /// Gets the median of the distribution.
+ ///
+ double Median { get; }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ double CumulativeDistribution(double x);
+}
diff --git a/MathNet.Numerics/Distributions/InverseGamma.cs b/MathNet.Numerics/Distributions/InverseGamma.cs
new file mode 100644
index 0000000..a2ed21c
--- /dev/null
+++ b/MathNet.Numerics/Distributions/InverseGamma.cs
@@ -0,0 +1,459 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2014 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+using MathNet.Numerics.Threading;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Continuous Univariate Inverse Gamma distribution.
+/// The inverse Gamma distribution is a distribution over the positive real numbers parameterized by
+/// two positive parameters.
+/// Wikipedia - InverseGamma distribution.
+///
+public class InverseGamma : IContinuousDistribution
+{
+ System.Random _random;
+
+ readonly double _shape;
+ readonly double _scale;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The shape (α) of the distribution. Range: α > 0.
+ /// The scale (β) of the distribution. Range: β > 0.
+ public InverseGamma(double shape, double scale)
+ {
+ if (!IsValidParameterSet(shape, scale))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _shape = shape;
+ _scale = scale;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The shape (α) of the distribution. Range: α > 0.
+ /// The scale (β) of the distribution. Range: β > 0.
+ /// The random number generator which is used to draw random samples.
+ public InverseGamma(double shape, double scale, System.Random randomSource)
+ {
+ if (!IsValidParameterSet(shape, scale))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _shape = shape;
+ _scale = scale;
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ /// a string representation of the distribution.
+ public override string ToString()
+ {
+ return "InverseGamma(α = " + _shape + ", β = " + _scale + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The shape (α) of the distribution. Range: α > 0.
+ /// The scale (β) of the distribution. Range: β > 0.
+ public static bool IsValidParameterSet(double shape, double scale)
+ {
+ return shape > 0.0 && scale > 0.0;
+ }
+
+ ///
+ /// Gets or sets the shape (α) parameter. Range: α > 0.
+ ///
+ public double Shape
+ {
+ get { return _shape; }
+ }
+
+ ///
+ /// Gets or sets The scale (β) parameter. Range: β > 0.
+ ///
+ public double Scale
+ {
+ get { return _scale; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the distribution.
+ ///
+ public double Mean
+ {
+ get
+ {
+ if (_shape <= 1)
+ {
+ throw new NotSupportedException();
+ }
+
+ return _scale / (_shape - 1.0);
+ }
+ }
+
+ ///
+ /// Gets the variance of the distribution.
+ ///
+ public double Variance
+ {
+ get
+ {
+ if (_shape <= 2)
+ {
+ throw new NotSupportedException();
+ }
+
+ return _scale * _scale / ((_shape - 1.0) * (_shape - 1.0) * (_shape - 2.0));
+ }
+ }
+
+ ///
+ /// Gets the standard deviation of the distribution.
+ ///
+ public double StdDev
+ {
+ get { return _scale / (Math.Abs(_shape - 1.0) * Math.Sqrt(_shape - 2.0)); }
+ }
+
+ ///
+ /// Gets the entropy of the distribution.
+ ///
+ public double Entropy
+ {
+ get { return _shape + Math.Log(_scale) + SpecialFunctions.GammaLn(_shape) - ((1 + _shape) * SpecialFunctions.DiGamma(_shape)); }
+ }
+
+ ///
+ /// Gets the skewness of the distribution.
+ ///
+ public double Skewness
+ {
+ get
+ {
+ if (_shape <= 3)
+ {
+ throw new NotSupportedException();
+ }
+
+ return (4 * Math.Sqrt(_shape - 2)) / (_shape - 3);
+ }
+ }
+
+ ///
+ /// Gets the mode of the distribution.
+ ///
+ public double Mode
+ {
+ get { return _scale / (_shape + 1.0); }
+ }
+
+ ///
+ /// Gets the median of the distribution.
+ ///
+ /// Throws .
+ public double Median
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets the minimum of the distribution.
+ ///
+ public double Minimum
+ {
+ get { return 0.0; }
+ }
+
+ ///
+ /// Gets the maximum of the distribution.
+ ///
+ public double Maximum
+ {
+ get { return double.PositiveInfinity; }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public double Density(double x)
+ {
+ return x < 0.0 ? 0.0 : Math.Pow(_scale, _shape) * Math.Pow(x, -_shape - 1.0) * Math.Exp(-_scale / x) / SpecialFunctions.Gamma(_shape);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The location at which to compute the log density.
+ /// the log density at .
+ ///
+ public double DensityLn(double x)
+ {
+ return Math.Log(Density(x));
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ ///
+ public double CumulativeDistribution(double x)
+ {
+ return SpecialFunctions.GammaUpperRegularized(_shape, _scale / x);
+ }
+
+ ///
+ /// Draws a random sample from the distribution.
+ ///
+ /// A random number from this distribution.
+ public double Sample()
+ {
+ return SampleUnchecked(_random, _shape, _scale);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ public void Samples(double[] values)
+ {
+ SamplesUnchecked(_random, values, _shape, _scale);
+ }
+
+ ///
+ /// Generates a sequence of samples from the Cauchy distribution.
+ ///
+ /// a sequence of samples from the distribution.
+ public IEnumerable Samples()
+ {
+ return SamplesUnchecked(_random, _shape, _scale);
+ }
+
+ static double SampleUnchecked(System.Random rnd, double shape, double scale)
+ {
+ return 1.0 / Gamma.SampleUnchecked(rnd, shape, scale);
+ }
+
+ static void SamplesUnchecked(System.Random rnd, double[] values, double shape, double scale)
+ {
+ Gamma.SamplesUnchecked(rnd, values, shape, scale);
+ CommonParallel.For(0, values.Length, 4096, (a, b) =>
+ {
+ for (int i = a; i < b; i++)
+ {
+ values[i] = 1.0 / values[i];
+ }
+ });
+ }
+
+ static IEnumerable SamplesUnchecked(System.Random rnd, double shape, double scale)
+ {
+ return Gamma.SamplesUnchecked(rnd, shape, scale).Select(z => 1.0 / z);
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The shape (α) of the distribution. Range: α > 0.
+ /// The scale (β) of the distribution. Range: β > 0.
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public static double PDF(double shape, double scale, double x)
+ {
+ if (shape <= 0.0 || scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return x < 0.0 ? 0.0 : Math.Pow(scale, shape) * Math.Pow(x, -shape - 1.0) * Math.Exp(-scale / x) / SpecialFunctions.Gamma(shape);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The shape (α) of the distribution. Range: α > 0.
+ /// The scale (β) of the distribution. Range: β > 0.
+ /// The location at which to compute the density.
+ /// the log density at .
+ ///
+ public static double PDFLn(double shape, double scale, double x)
+ {
+ return Math.Log(PDF(shape, scale, x));
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// The shape (α) of the distribution. Range: α > 0.
+ /// The scale (β) of the distribution. Range: β > 0.
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(double shape, double scale, double x)
+ {
+ if (shape <= 0.0 || scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SpecialFunctions.GammaUpperRegularized(shape, scale / x);
+ }
+
+ ///
+ /// Generates a sample from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The shape (α) of the distribution. Range: α > 0.
+ /// The scale (β) of the distribution. Range: β > 0.
+ /// a sample from the distribution.
+ public static double Sample(System.Random rnd, double shape, double scale)
+ {
+ if (shape <= 0.0 || scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(rnd, shape, scale);
+ }
+
+ ///
+ /// Generates a sequence of samples from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The shape (α) of the distribution. Range: α > 0.
+ /// The scale (β) of the distribution. Range: β > 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(System.Random rnd, double shape, double scale)
+ {
+ if (shape <= 0.0 || scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(rnd, shape, scale);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// The shape (α) of the distribution. Range: α > 0.
+ /// The scale (β) of the distribution. Range: β > 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(System.Random rnd, double[] values, double shape, double scale)
+ {
+ if (shape <= 0.0 || scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(rnd, values, shape, scale);
+ }
+
+ ///
+ /// Generates a sample from the distribution.
+ ///
+ /// The shape (α) of the distribution. Range: α > 0.
+ /// The scale (β) of the distribution. Range: β > 0.
+ /// a sample from the distribution.
+ public static double Sample(double shape, double scale)
+ {
+ if (shape <= 0.0 || scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(SystemRandomSource.Default, shape, scale);
+ }
+
+ ///
+ /// Generates a sequence of samples from the distribution.
+ ///
+ /// The shape (α) of the distribution. Range: α > 0.
+ /// The scale (β) of the distribution. Range: β > 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(double shape, double scale)
+ {
+ if (shape <= 0.0 || scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(SystemRandomSource.Default, shape, scale);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// The shape (α) of the distribution. Range: α > 0.
+ /// The scale (β) of the distribution. Range: β > 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(double[] values, double shape, double scale)
+ {
+ if (shape <= 0.0 || scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(SystemRandomSource.Default, values, shape, scale);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/InverseGaussian.cs b/MathNet.Numerics/Distributions/InverseGaussian.cs
new file mode 100644
index 0000000..5786e5b
--- /dev/null
+++ b/MathNet.Numerics/Distributions/InverseGaussian.cs
@@ -0,0 +1,458 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2019 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+using MathNet.Numerics.Statistics;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MathNet.Numerics.Distributions;
+
+public class InverseGaussian : IContinuousDistribution
+{
+ private System.Random _random;
+
+ ///
+ /// Gets the mean (μ) of the distribution. Range: μ > 0.
+ ///
+ public double Mu { get; }
+
+ ///
+ /// Gets the shape (λ) of the distribution. Range: λ > 0.
+ ///
+ public double Lambda { get; }
+
+ ///
+ /// Initializes a new instance of the InverseGaussian class.
+ ///
+ /// The mean (μ) of the distribution. Range: μ > 0.
+ /// The shape (λ) of the distribution. Range: λ > 0.
+ /// The random number generator which is used to draw random samples.
+ public InverseGaussian(double mu, double lambda, System.Random randomSource = null)
+ {
+ if (!IsValidParameterSet(mu, lambda))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ Mu = mu;
+ Lambda = lambda;
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ /// a string representation of the distribution.
+ public override string ToString()
+ {
+ return "InverseGaussian(μ = " + Mu + ", λ = " + Lambda + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The mean (μ) of the distribution. Range: μ > 0.
+ /// The shape (λ) of the distribution. Range: λ > 0.
+ public static bool IsValidParameterSet(double mu, double lambda)
+ {
+ var allFinite = mu.IsFinite() && lambda.IsFinite();
+ return allFinite && mu > 0.0 && lambda > 0.0;
+ }
+
+ ///
+ /// Gets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the Inverse Gaussian distribution.
+ ///
+ public double Mean
+ {
+ get
+ {
+ return Mu;
+ }
+ }
+
+ ///
+ /// Gets the variance of the Inverse Gaussian distribution.
+ ///
+ public double Variance
+ {
+ get
+ {
+ return Math.Pow(Mu, 3) / Lambda;
+ }
+ }
+
+ ///
+ /// Gets the standard deviation of the Inverse Gaussian distribution.
+ ///
+ public double StdDev
+ {
+ get
+ {
+ return Math.Sqrt(Variance);
+ }
+ }
+
+ ///
+ /// Gets the median of the Inverse Gaussian distribution.
+ /// No closed form analytical expression exists, so this value is approximated numerically and can throw an exception.
+ ///
+ public double Median
+ {
+ get { return InvCDF(0.5); }
+ }
+
+ ///
+ /// Gets the minimum of the Inverse Gaussian distribution.
+ ///
+ public double Minimum
+ {
+ get { return 0.0; }
+ }
+
+ ///
+ /// Gets the maximum of the Inverse Gaussian distribution.
+ ///
+ public double Maximum
+ {
+ get { return double.PositiveInfinity; }
+ }
+
+ ///
+ /// Gets the skewness of the Inverse Gaussian distribution.
+ ///
+ public double Skewness
+ {
+ get { return 3 * Math.Sqrt(Mu / Lambda); }
+ }
+
+ ///
+ /// Gets the kurtosis of the Inverse Gaussian distribution.
+ ///
+ public double Kurtosis
+ {
+ get { return 15 * Mu / Lambda; }
+ }
+
+ ///
+ /// Gets the mode of the Inverse Gaussian distribution.
+ ///
+ public double Mode
+ {
+ get { return Mu * (Math.Sqrt(1 + (9 * Mu * Mu) / (4 * Lambda * Lambda)) - (3 * Mu) / (2 * Lambda)); }
+ }
+
+ ///
+ /// Gets the entropy of the Inverse Gaussian distribution (currently not supported).
+ ///
+ public double Entropy
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Generates a sample from the inverse Gaussian distribution.
+ ///
+ /// a sample from the distribution.
+ public double Sample()
+ {
+ return SampleUnchecked(_random, Mu, Lambda);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ public void Samples(double[] values)
+ {
+ SamplesUnchecked(_random, values, Mu, Lambda);
+ }
+
+ ///
+ /// Generates a sequence of samples from the inverse Gaussian distribution.
+ ///
+ /// a sequence of samples from the distribution.
+ public IEnumerable Samples()
+ {
+ return SamplesUnchecked(_random, Mu, Lambda);
+ }
+
+ ///
+ /// Generates a sample from the inverse Gaussian distribution.
+ ///
+ /// The random number generator to use.
+ /// The mean (μ) of the distribution. Range: μ > 0.
+ /// The shape (λ) of the distribution. Range: λ > 0.
+ /// a sample from the distribution.
+ public static double Sample(System.Random rnd, double mu, double lambda)
+ {
+ if (!IsValidParameterSet(mu, lambda))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(rnd, mu, lambda);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// The mean (μ) of the distribution. Range: μ > 0.
+ /// The shape (λ) of the distribution. Range: λ > 0.
+ public static void Samples(System.Random rnd, double[] values, double mu, double lambda)
+ {
+ if (!IsValidParameterSet(mu, lambda))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(rnd, values, mu, lambda);
+ }
+
+ ///
+ /// Generates a sequence of samples from the Burr distribution.
+ ///
+ /// The random number generator to use.
+ /// The mean (μ) of the distribution. Range: μ > 0.
+ /// The shape (λ) of the distribution. Range: λ > 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(System.Random rnd, double mu, double lambda)
+ {
+ if (!IsValidParameterSet(mu, lambda))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(rnd, mu, lambda);
+ }
+
+ internal static double SampleUnchecked(System.Random rnd, double mu, double lambda)
+ {
+ double v = MathNet.Numerics.Distributions.Normal.Sample(rnd, 0, 1);
+ double test = rnd.NextDouble();
+ return InverseGaussianSampleImpl(mu, lambda, v, test);
+ }
+
+ internal static void SamplesUnchecked(System.Random rnd, double[] values, double mu, double lambda)
+ {
+ if (values.Length == 0)
+ {
+ return;
+ }
+
+ double[] v = new double[values.Length];
+ MathNet.Numerics.Distributions.Normal.Samples(rnd, v, 0, 1);
+ double[] test = rnd.NextDoubles(values.Length);
+ for (var j = 0; j < values.Length; ++j)
+ {
+ values[j] = InverseGaussianSampleImpl(mu, lambda, v[j], test[j]);
+ }
+ }
+
+ internal static IEnumerable SamplesUnchecked(System.Random rnd, double mu, double lambda)
+ {
+ while (true)
+ {
+ yield return SampleUnchecked(rnd, mu, lambda);
+ }
+ }
+
+ internal static double InverseGaussianSampleImpl(double mu, double lambda, double normalSample, double uniformSample)
+ {
+ double y = normalSample * normalSample;
+ double x = mu + (mu * mu * y) / (2 * lambda) - (mu / (2 * lambda)) * Math.Sqrt(4 * mu * lambda * y + mu * mu * y * y);
+ if (uniformSample <= mu / (mu + x))
+ return x;
+ else
+ return mu * mu / x;
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public double Density(double x)
+ {
+ return DensityImpl(Mu, Lambda, x);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The location at which to compute the log density.
+ /// the log density at .
+ ///
+ public double DensityLn(double x)
+ {
+ return DensityLnImpl(Mu, Lambda, x);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ ///
+ public double CumulativeDistribution(double x)
+ {
+ return CumulativeDistributionImpl(Mu, Lambda, x);
+ }
+
+ ///
+ /// Computes the inverse cumulative distribution (CDF) of the distribution at p, i.e. solving for P(X ≤ x) = p.
+ ///
+ /// The location at which to compute the inverse cumulative distribution function.
+ /// the inverse cumulative distribution at location .
+ public double InvCDF(double p)
+ {
+ Func equationToSolve = (x) => CumulativeDistribution(x) - p;
+ if (RootFinding.NewtonRaphson.TryFindRoot(equationToSolve, Density, Mode, 0, double.PositiveInfinity, 1e-8, 100, out double quantile))
+ return quantile;
+ else
+ throw new NonConvergenceException(Resources.NumericalEstimationFailed);
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The mean (μ) of the distribution. Range: μ > 0.
+ /// The shape (λ) of the distribution. Range: λ > 0.
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public static double PDF(double mu, double lambda, double x)
+ {
+ if (!IsValidParameterSet(mu, lambda))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return DensityImpl(mu, lambda, x);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The mean (μ) of the distribution. Range: μ > 0.
+ /// The shape (λ) of the distribution. Range: λ > 0.
+ /// The location at which to compute the log density.
+ /// the log density at .
+ ///
+ public static double PDFLn(double mu, double lambda, double x)
+ {
+ if (!IsValidParameterSet(mu, lambda))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return DensityLnImpl(mu, lambda, x);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The mean (μ) of the distribution. Range: μ > 0.
+ /// The shape (λ) of the distribution. Range: λ > 0.
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(double mu, double lambda, double x)
+ {
+ if (!IsValidParameterSet(mu, lambda))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return CumulativeDistributionImpl(mu, lambda, x);
+ }
+
+ ///
+ /// Computes the inverse cumulative distribution (CDF) of the distribution at p, i.e. solving for P(X ≤ x) = p.
+ ///
+ /// The mean (μ) of the distribution. Range: μ > 0.
+ /// The shape (λ) of the distribution. Range: λ > 0.
+ /// The location at which to compute the inverse cumulative distribution function.
+ /// the inverse cumulative distribution at location .
+ ///
+ public static double ICDF(double mu, double lambda, double p)
+ {
+ if (!IsValidParameterSet(mu, lambda))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ var igDist = new InverseGaussian(mu, lambda);
+ return igDist.InvCDF(p);
+ }
+
+ ///
+ /// Estimates the Inverse Gaussian parameters from sample data with maximum-likelihood.
+ ///
+ /// The samples to estimate the distribution parameters from.
+ /// The random number generator which is used to draw random samples. Optional, can be null.
+ /// An Inverse Gaussian distribution.
+ public static InverseGaussian Estimate(IEnumerable samples, System.Random randomSource = null)
+ {
+ var sampleVec = samples.ToArray();
+ var muHat = sampleVec.Mean();
+ var lambdahat = 1 / (1 / samples.HarmonicMean() - 1 / muHat);
+ return new InverseGaussian(muHat, lambdahat, randomSource);
+ }
+
+ internal static double DensityImpl(double mu, double lambda, double x)
+ {
+ return Math.Sqrt(lambda / (2 * Math.PI * Math.Pow(x, 3))) * Math.Exp(-((lambda * Math.Pow(x - mu, 2)) / (2 * mu * mu * x)));
+ }
+
+ internal static double DensityLnImpl(double mu, double lambda, double x)
+ {
+ return Math.Log(Math.Sqrt(lambda / (2 * Math.PI * Math.Pow(x, 3)))) - ((lambda * Math.Pow(x - mu, 2)) / (2 * mu * mu * x));
+ }
+
+ internal static double CumulativeDistributionImpl(double mu, double lambda, double x)
+ {
+ return Normal.CDF(0, 1, Math.Sqrt(lambda / x) * (x / mu - 1)) + Math.Exp(2 * lambda / mu) * Normal.CDF(0, 1, -Math.Sqrt(lambda / x) * (x / mu + 1));
+ }
+}
diff --git a/MathNet.Numerics/Distributions/InverseWishart.cs b/MathNet.Numerics/Distributions/InverseWishart.cs
new file mode 100644
index 0000000..c9b006d
--- /dev/null
+++ b/MathNet.Numerics/Distributions/InverseWishart.cs
@@ -0,0 +1,249 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2013 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.LinearAlgebra;
+using MathNet.Numerics.LinearAlgebra.Factorization;
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+
+using System;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Multivariate Inverse Wishart distribution. This distribution is
+/// parameterized by the degrees of freedom nu and the scale matrix S. The inverse Wishart distribution
+/// is the conjugate prior for the covariance matrix of a multivariate normal distribution.
+/// Wikipedia - Inverse-Wishart distribution.
+///
+public class InverseWishart : IDistribution
+{
+ System.Random _random;
+
+ readonly double _freedom;
+ readonly Matrix _scale;
+
+ ///
+ /// Caches the Cholesky factorization of the scale matrix.
+ ///
+ readonly Cholesky _chol;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The degree of freedom (ν) for the inverse Wishart distribution.
+ /// The scale matrix (Ψ) for the inverse Wishart distribution.
+ public InverseWishart(double degreesOfFreedom, Matrix scale)
+ {
+ if (Control.CheckDistributionParameters && !IsValidParameterSet(degreesOfFreedom, scale))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _freedom = degreesOfFreedom;
+ _scale = scale;
+ _chol = _scale.Cholesky();
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The degree of freedom (ν) for the inverse Wishart distribution.
+ /// The scale matrix (Ψ) for the inverse Wishart distribution.
+ /// The random number generator which is used to draw random samples.
+ public InverseWishart(double degreesOfFreedom, Matrix scale, System.Random randomSource)
+ {
+ if (Control.CheckDistributionParameters && !IsValidParameterSet(degreesOfFreedom, scale))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _freedom = degreesOfFreedom;
+ _scale = scale;
+ _chol = _scale.Cholesky();
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ /// a string representation of the distribution.
+ public override string ToString()
+ {
+ return "InverseWishart(ν = " + _freedom + ", Rows = " + _scale.RowCount + ", Columns = " + _scale.ColumnCount + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The degree of freedom (ν) for the inverse Wishart distribution.
+ /// The scale matrix (Ψ) for the inverse Wishart distribution.
+ public static bool IsValidParameterSet(double degreesOfFreedom, Matrix scale)
+ {
+ if (scale.RowCount != scale.ColumnCount)
+ {
+ return false;
+ }
+
+ for (var i = 0; i < scale.RowCount; i++)
+ {
+ if (scale.At(i, i) <= 0.0)
+ {
+ return false;
+ }
+ }
+
+ return degreesOfFreedom > 0.0;
+ }
+
+ ///
+ /// Gets or sets the degree of freedom (ν) for the inverse Wishart distribution.
+ ///
+ public double DegreesOfFreedom
+ {
+ get { return _freedom; }
+ }
+
+ ///
+ /// Gets or sets the scale matrix (Ψ) for the inverse Wishart distribution.
+ ///
+ public Matrix Scale
+ {
+ get { return _scale; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean.
+ ///
+ /// The mean of the distribution.
+ public Matrix Mean
+ {
+ get { return _scale * (1.0 / (_freedom - _scale.RowCount - 1.0)); }
+ }
+
+ ///
+ /// Gets the mode of the distribution.
+ ///
+ /// The mode of the distribution.
+ /// A. O'Hagan, and J. J. Forster (2004). Kendall's Advanced Theory of Statistics: Bayesian Inference. 2B (2 ed.). Arnold. ISBN 0-340-80752-0.
+ public Matrix Mode
+ {
+ get { return _scale * (1.0 / (_freedom + _scale.RowCount + 1.0)); }
+ }
+
+ ///
+ /// Gets the variance of the distribution.
+ ///
+ /// The variance of the distribution.
+ /// Kanti V. Mardia, J. T. Kent and J. M. Bibby (1979). Multivariate Analysis.
+ public Matrix Variance
+ {
+ get
+ {
+ return Matrix.Build.Dense(_scale.RowCount, _scale.ColumnCount, (i, j) =>
+ {
+ var num1 = ((_freedom - _scale.RowCount + 1) * _scale.At(i, j) * _scale.At(i, j)) + ((_freedom - _scale.RowCount - 1) * _scale.At(i, i) * _scale.At(j, j));
+ var num2 = (_freedom - _scale.RowCount) * (_freedom - _scale.RowCount - 1) * (_freedom - _scale.RowCount - 1) * (_freedom - _scale.RowCount - 3);
+ return num1 / num2;
+ });
+ }
+ }
+
+ ///
+ /// Evaluates the probability density function for the inverse Wishart distribution.
+ ///
+ /// The matrix at which to evaluate the density at.
+ /// If the argument does not have the same dimensions as the scale matrix.
+ /// the density at .
+ public double Density(Matrix x)
+ {
+ var p = _scale.RowCount;
+
+ if (x.RowCount != p || x.ColumnCount != p)
+ {
+ throw new ArgumentOutOfRangeException(nameof(x), Resources.ArgumentMatrixDimensions);
+ }
+
+ var chol = x.Cholesky();
+ var dX = chol.Determinant;
+ var sXi = chol.Solve(Scale);
+
+ // Compute the multivariate Gamma function.
+ var gp = Math.Pow(Constants.Pi, p * (p - 1.0) / 4.0);
+ for (var j = 1; j <= p; j++)
+ {
+ gp *= SpecialFunctions.Gamma((_freedom + 1.0 - j) / 2.0);
+ }
+
+ return Math.Pow(dX, -(_freedom + p + 1.0) / 2.0)
+ * Math.Exp(-0.5 * sXi.Trace())
+ * Math.Pow(_chol.Determinant, _freedom / 2.0)
+ / Math.Pow(2.0, _freedom * p / 2.0)
+ / gp;
+ }
+
+ ///
+ /// Samples an inverse Wishart distributed random variable by sampling
+ /// a Wishart random variable and inverting the matrix.
+ ///
+ /// a sample from the distribution.
+ public Matrix Sample()
+ {
+ return Sample(_random, _freedom, _scale);
+ }
+
+ ///
+ /// Samples an inverse Wishart distributed random variable by sampling
+ /// a Wishart random variable and inverting the matrix.
+ ///
+ /// The random number generator to use.
+ /// The degree of freedom (ν) for the inverse Wishart distribution.
+ /// The scale matrix (Ψ) for the inverse Wishart distribution.
+ /// a sample from the distribution.
+ public static Matrix Sample(System.Random rnd, double degreesOfFreedom, Matrix scale)
+ {
+ if (Control.CheckDistributionParameters && !IsValidParameterSet(degreesOfFreedom, scale))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ var r = Wishart.Sample(rnd, degreesOfFreedom, scale.Inverse());
+ return r.Inverse();
+ }
+}
diff --git a/MathNet.Numerics/Distributions/Laplace.cs b/MathNet.Numerics/Distributions/Laplace.cs
new file mode 100644
index 0000000..9d5232a
--- /dev/null
+++ b/MathNet.Numerics/Distributions/Laplace.cs
@@ -0,0 +1,454 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2014 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+using MathNet.Numerics.Threading;
+
+using System;
+using System.Collections.Generic;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Continuous Univariate Laplace distribution.
+/// The Laplace distribution is a distribution over the real numbers parameterized by a mean and
+/// scale parameter. The PDF is:
+/// p(x) = \frac{1}{2 * scale} \exp{- |x - mean| / scale}.
+/// Wikipedia - Laplace distribution.
+///
+public class Laplace : IContinuousDistribution
+{
+ System.Random _random;
+
+ readonly double _location;
+ readonly double _scale;
+
+ ///
+ /// Initializes a new instance of the class (location = 0, scale = 1).
+ ///
+ public Laplace()
+ : this(0.0, 1.0)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The location (μ) of the distribution.
+ /// The scale (b) of the distribution. Range: b > 0.
+ /// If is negative.
+ public Laplace(double location, double scale)
+ {
+ if (!IsValidParameterSet(location, scale))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _location = location;
+ _scale = scale;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The location (μ) of the distribution.
+ /// The scale (b) of the distribution. Range: b > 0.
+ /// The random number generator which is used to draw random samples.
+ /// If is negative.
+ public Laplace(double location, double scale, System.Random randomSource)
+ {
+ if (!IsValidParameterSet(location, scale))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _location = location;
+ _scale = scale;
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ /// a string representation of the distribution.
+ public override string ToString()
+ {
+ return "Laplace(μ = " + _location + ", b = " + _scale + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The location (μ) of the distribution.
+ /// The scale (b) of the distribution. Range: b > 0.
+ public static bool IsValidParameterSet(double location, double scale)
+ {
+ return scale > 0.0 && !double.IsNaN(location);
+ }
+
+ ///
+ /// Gets the location (μ) of the Laplace distribution.
+ ///
+ public double Location
+ {
+ get { return _location; }
+ }
+
+ ///
+ /// Gets the scale (b) of the Laplace distribution. Range: b > 0.
+ ///
+ public double Scale
+ {
+ get { return _scale; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the distribution.
+ ///
+ public double Mean
+ {
+ get { return _location; }
+ }
+
+ ///
+ /// Gets the variance of the distribution.
+ ///
+ public double Variance
+ {
+ get { return 2.0 * _scale * _scale; }
+ }
+
+ ///
+ /// Gets the standard deviation of the distribution.
+ ///
+ public double StdDev
+ {
+ get { return Constants.Sqrt2 * _scale; }
+ }
+
+ ///
+ /// Gets the entropy of the distribution.
+ ///
+ public double Entropy
+ {
+ get { return Math.Log(2.0 * Constants.E * _scale); }
+ }
+
+ ///
+ /// Gets the skewness of the distribution.
+ ///
+ public double Skewness
+ {
+ get { return 0.0; }
+ }
+
+ ///
+ /// Gets the mode of the distribution.
+ ///
+ public double Mode
+ {
+ get { return _location; }
+ }
+
+ ///
+ /// Gets the median of the distribution.
+ ///
+ public double Median
+ {
+ get { return _location; }
+ }
+
+ ///
+ /// Gets the minimum of the distribution.
+ ///
+ public double Minimum
+ {
+ get { return double.NegativeInfinity; }
+ }
+
+ ///
+ /// Gets the maximum of the distribution.
+ ///
+ public double Maximum
+ {
+ get { return double.PositiveInfinity; }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public double Density(double x)
+ {
+ return Math.Exp(-Math.Abs(x - _location) / _scale) / (2.0 * _scale);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The location at which to compute the log density.
+ /// the log density at .
+ ///
+ public double DensityLn(double x)
+ {
+ return -Math.Abs(x - _location) / _scale - Math.Log(2.0 * _scale);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ ///
+ public double CumulativeDistribution(double x)
+ {
+ return 0.5 * (1.0 + (Math.Sign(x - _location) * (1.0 - Math.Exp(-Math.Abs(x - _location) / _scale))));
+ }
+
+ ///
+ /// Samples a Laplace distributed random variable.
+ ///
+ /// a sample from the distribution.
+ public double Sample()
+ {
+ return SampleUnchecked(_random, _location, _scale);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ public void Samples(double[] values)
+ {
+ SamplesUnchecked(_random, values, _location, _scale);
+ }
+
+ ///
+ /// Generates a sample from the Laplace distribution.
+ ///
+ /// a sample from the distribution.
+ public IEnumerable Samples()
+ {
+ return SamplesUnchecked(_random, _location, _scale);
+ }
+
+ static double SampleUnchecked(System.Random rnd, double location, double scale)
+ {
+ var u = rnd.NextDouble() - 0.5;
+ return location - (scale * Math.Sign(u) * Math.Log(1.0 - (2.0 * Math.Abs(u))));
+ }
+
+ static void SamplesUnchecked(System.Random rnd, double[] values, double location, double scale)
+ {
+ rnd.NextDoubles(values);
+ CommonParallel.For(0, values.Length, 4096, (a, b) =>
+ {
+ for (int i = a; i < b; i++)
+ {
+ var u = values[i] - 0.5;
+ values[i] = location - (scale * Math.Sign(u) * Math.Log(1.0 - (2.0 * Math.Abs(u))));
+ }
+ });
+ }
+
+ static IEnumerable SamplesUnchecked(System.Random rnd, double location, double scale)
+ {
+ while (true)
+ {
+ yield return SampleUnchecked(rnd, location, scale);
+ }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The location (μ) of the distribution.
+ /// The scale (b) of the distribution. Range: b > 0.
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public static double PDF(double location, double scale, double x)
+ {
+ if (scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return Math.Exp(-Math.Abs(x - location) / scale) / (2.0 * scale);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The location (μ) of the distribution.
+ /// The scale (b) of the distribution. Range: b > 0.
+ /// The location at which to compute the density.
+ /// the log density at .
+ ///
+ public static double PDFLn(double location, double scale, double x)
+ {
+ if (scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return -Math.Abs(x - location) / scale - Math.Log(2.0 * scale);
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// The location (μ) of the distribution.
+ /// The scale (b) of the distribution. Range: b > 0.
+ /// the cumulative distribution at location .
+ ///
+ public static double CDF(double location, double scale, double x)
+ {
+ if (scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return 0.5 * (1.0 + (Math.Sign(x - location) * (1.0 - Math.Exp(-Math.Abs(x - location) / scale))));
+ }
+
+ ///
+ /// Generates a sample from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The location (μ) of the distribution.
+ /// The scale (b) of the distribution. Range: b > 0.
+ /// a sample from the distribution.
+ public static double Sample(System.Random rnd, double location, double scale)
+ {
+ if (scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(rnd, location, scale);
+ }
+
+ ///
+ /// Generates a sequence of samples from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The location (μ) of the distribution.
+ /// The scale (b) of the distribution. Range: b > 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(System.Random rnd, double location, double scale)
+ {
+ if (scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(rnd, location, scale);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// The location (μ) of the distribution.
+ /// The scale (b) of the distribution. Range: b > 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(System.Random rnd, double[] values, double location, double scale)
+ {
+ if (scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(rnd, values, location, scale);
+ }
+
+ ///
+ /// Generates a sample from the distribution.
+ ///
+ /// The location (μ) of the distribution.
+ /// The scale (b) of the distribution. Range: b > 0.
+ /// a sample from the distribution.
+ public static double Sample(double location, double scale)
+ {
+ if (scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(SystemRandomSource.Default, location, scale);
+ }
+
+ ///
+ /// Generates a sequence of samples from the distribution.
+ ///
+ /// The location (μ) of the distribution.
+ /// The scale (b) of the distribution. Range: b > 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(double location, double scale)
+ {
+ if (scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(SystemRandomSource.Default, location, scale);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// The location (μ) of the distribution.
+ /// The scale (b) of the distribution. Range: b > 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(double[] values, double location, double scale)
+ {
+ if (scale <= 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(SystemRandomSource.Default, values, location, scale);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/LogNormal.cs b/MathNet.Numerics/Distributions/LogNormal.cs
new file mode 100644
index 0000000..8440bb5
--- /dev/null
+++ b/MathNet.Numerics/Distributions/LogNormal.cs
@@ -0,0 +1,555 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2013 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+using MathNet.Numerics.Statistics;
+using MathNet.Numerics.Threading;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Continuous Univariate Log-Normal distribution.
+/// For details about this distribution, see
+/// Wikipedia - Log-Normal distribution.
+///
+public class LogNormal : IContinuousDistribution
+{
+ System.Random _random;
+
+ readonly double _mu;
+ readonly double _sigma;
+
+ ///
+ /// Initializes a new instance of the class.
+ /// The distribution will be initialized with the default
+ /// random number generator.
+ ///
+ /// The log-scale (μ) of the logarithm of the distribution.
+ /// The shape (σ) of the logarithm of the distribution. Range: σ ≥ 0.
+ public LogNormal(double mu, double sigma)
+ {
+ if (!IsValidParameterSet(mu, sigma))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _mu = mu;
+ _sigma = sigma;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ /// The distribution will be initialized with the default
+ /// random number generator.
+ ///
+ /// The log-scale (μ) of the distribution.
+ /// The shape (σ) of the distribution. Range: σ ≥ 0.
+ /// The random number generator which is used to draw random samples.
+ public LogNormal(double mu, double sigma, System.Random randomSource)
+ {
+ if (!IsValidParameterSet(mu, sigma))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _mu = mu;
+ _sigma = sigma;
+ }
+
+ ///
+ /// Constructs a log-normal distribution with the desired mu and sigma parameters.
+ ///
+ /// The log-scale (μ) of the distribution.
+ /// The shape (σ) of the distribution. Range: σ ≥ 0.
+ /// The random number generator which is used to draw random samples. Optional, can be null.
+ /// A log-normal distribution.
+ public static LogNormal WithMuSigma(double mu, double sigma, System.Random randomSource = null)
+ {
+ return new LogNormal(mu, sigma, randomSource);
+ }
+
+ ///
+ /// Constructs a log-normal distribution with the desired mean and variance.
+ ///
+ /// The mean of the log-normal distribution.
+ /// The variance of the log-normal distribution.
+ /// The random number generator which is used to draw random samples. Optional, can be null.
+ /// A log-normal distribution.
+ public static LogNormal WithMeanVariance(double mean, double var, System.Random randomSource = null)
+ {
+ var sigma2 = Math.Log(var / (mean * mean) + 1.0);
+ return new LogNormal(Math.Log(mean) - sigma2 / 2.0, Math.Sqrt(sigma2), randomSource);
+ }
+
+ ///
+ /// Estimates the log-normal distribution parameters from sample data with maximum-likelihood.
+ ///
+ /// The samples to estimate the distribution parameters from.
+ /// The random number generator which is used to draw random samples. Optional, can be null.
+ /// A log-normal distribution.
+ /// MATLAB: lognfit
+ public static LogNormal Estimate(IEnumerable samples, System.Random randomSource = null)
+ {
+ var muSigma = samples.Select(s => Math.Log(s)).MeanStandardDeviation();
+ return new LogNormal(muSigma.Item1, muSigma.Item2, randomSource);
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ /// a string representation of the distribution.
+ public override string ToString()
+ {
+ return "LogNormal(μ = " + _mu + ", σ = " + _sigma + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The log-scale (μ) of the distribution.
+ /// The shape (σ) of the distribution. Range: σ ≥ 0.
+ public static bool IsValidParameterSet(double mu, double sigma)
+ {
+ return sigma >= 0.0 && !double.IsNaN(mu);
+ }
+
+ ///
+ /// Gets the log-scale (μ) (mean of the logarithm) of the distribution.
+ ///
+ public double Mu
+ {
+ get { return _mu; }
+ }
+
+ ///
+ /// Gets the shape (σ) (standard deviation of the logarithm) of the distribution. Range: σ ≥ 0.
+ ///
+ public double Sigma
+ {
+ get { return _sigma; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mu of the log-normal distribution.
+ ///
+ public double Mean
+ {
+ get { return Math.Exp(_mu + (_sigma * _sigma / 2.0)); }
+ }
+
+ ///
+ /// Gets the variance of the log-normal distribution.
+ ///
+ public double Variance
+ {
+ get
+ {
+ var sigma2 = _sigma * _sigma;
+ return (Math.Exp(sigma2) - 1.0) * Math.Exp(_mu + _mu + sigma2);
+ }
+ }
+
+ ///
+ /// Gets the standard deviation of the log-normal distribution.
+ ///
+ public double StdDev
+ {
+ get
+ {
+ var sigma2 = _sigma * _sigma;
+ return Math.Sqrt((Math.Exp(sigma2) - 1.0) * Math.Exp(_mu + _mu + sigma2));
+ }
+ }
+
+ ///
+ /// Gets the entropy of the log-normal distribution.
+ ///
+ public double Entropy
+ {
+ get { return 0.5 + Math.Log(_sigma) + _mu + Constants.LogSqrt2Pi; }
+ }
+
+ ///
+ /// Gets the skewness of the log-normal distribution.
+ ///
+ public double Skewness
+ {
+ get
+ {
+ var expsigma2 = Math.Exp(_sigma * _sigma);
+ return (expsigma2 + 2.0) * Math.Sqrt(expsigma2 - 1);
+ }
+ }
+
+ ///
+ /// Gets the mode of the log-normal distribution.
+ ///
+ public double Mode
+ {
+ get { return Math.Exp(_mu - (_sigma * _sigma)); }
+ }
+
+ ///
+ /// Gets the median of the log-normal distribution.
+ ///
+ public double Median
+ {
+ get { return Math.Exp(_mu); }
+ }
+
+ ///
+ /// Gets the minimum of the log-normal distribution.
+ ///
+ public double Minimum
+ {
+ get { return 0.0; }
+ }
+
+ ///
+ /// Gets the maximum of the log-normal distribution.
+ ///
+ public double Maximum
+ {
+ get { return double.PositiveInfinity; }
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The location at which to compute the density.
+ /// the density at .
+ ///
+ public double Density(double x)
+ {
+ if (x < 0.0)
+ {
+ return 0.0;
+ }
+
+ var a = (Math.Log(x) - _mu) / _sigma;
+ return Math.Exp(-0.5 * a * a) / (x * _sigma * Constants.Sqrt2Pi);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The location at which to compute the log density.
+ /// the log density at .
+ ///
+ public double DensityLn(double x)
+ {
+ if (x < 0.0)
+ {
+ return double.NegativeInfinity;
+ }
+
+ var a = (Math.Log(x) - _mu) / _sigma;
+ return (-0.5 * a * a) - Math.Log(x * _sigma) - Constants.LogSqrt2Pi;
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// the cumulative distribution at location .
+ ///
+ public double CumulativeDistribution(double x)
+ {
+ return x < 0.0 ? 0.0
+ : 0.5 * SpecialFunctions.Erfc((_mu - Math.Log(x)) / (_sigma * Constants.Sqrt2));
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability. This is also known as the quantile or percent point function.
+ ///
+ /// The location at which to compute the inverse cumulative density.
+ /// the inverse cumulative density at .
+ ///
+ public double InverseCumulativeDistribution(double p)
+ {
+ return p <= 0.0 ? 0.0 : p >= 1.0 ? double.PositiveInfinity
+ : Math.Exp(_mu - _sigma * Constants.Sqrt2 * SpecialFunctions.ErfcInv(2.0 * p));
+ }
+
+ ///
+ /// Generates a sample from the log-normal distribution using the Box-Muller algorithm.
+ ///
+ /// a sample from the distribution.
+ public double Sample()
+ {
+ return SampleUnchecked(_random, _mu, _sigma);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ public void Samples(double[] values)
+ {
+ SamplesUnchecked(_random, values, _mu, _sigma);
+ }
+
+ ///
+ /// Generates a sequence of samples from the log-normal distribution using the Box-Muller algorithm.
+ ///
+ /// a sequence of samples from the distribution.
+ public IEnumerable Samples()
+ {
+ return SamplesUnchecked(_random, _mu, _sigma);
+ }
+
+ static double SampleUnchecked(System.Random rnd, double mu, double sigma)
+ {
+ return Math.Exp(Normal.SampleUnchecked(rnd, mu, sigma));
+ }
+
+ static IEnumerable SamplesUnchecked(System.Random rnd, double mu, double sigma)
+ {
+ return Normal.SamplesUnchecked(rnd, mu, sigma).Select(Math.Exp);
+ }
+
+ static void SamplesUnchecked(System.Random rnd, double[] values, double mu, double sigma)
+ {
+ Normal.SamplesUnchecked(rnd, values, mu, sigma);
+ CommonParallel.For(0, values.Length, 4096, (a, b) =>
+ {
+ for (int i = a; i < b; i++)
+ {
+ values[i] = Math.Exp(values[i]);
+ }
+ });
+ }
+
+ ///
+ /// Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
+ ///
+ /// The location at which to compute the density.
+ /// The log-scale (μ) of the distribution.
+ /// The shape (σ) of the distribution. Range: σ ≥ 0.
+ /// the density at .
+ ///
+ /// MATLAB: lognpdf
+ public static double PDF(double mu, double sigma, double x)
+ {
+ if (sigma < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (x < 0.0)
+ {
+ return 0.0;
+ }
+
+ var a = (Math.Log(x) - mu) / sigma;
+ return Math.Exp(-0.5 * a * a) / (x * sigma * Constants.Sqrt2Pi);
+ }
+
+ ///
+ /// Computes the log probability density of the distribution (lnPDF) at x, i.e. ln(∂P(X ≤ x)/∂x).
+ ///
+ /// The location at which to compute the density.
+ /// The log-scale (μ) of the distribution.
+ /// The shape (σ) of the distribution. Range: σ ≥ 0.
+ /// the log density at .
+ ///
+ public static double PDFLn(double mu, double sigma, double x)
+ {
+ if (sigma < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ if (x < 0.0)
+ {
+ return double.NegativeInfinity;
+ }
+
+ var a = (Math.Log(x) - mu) / sigma;
+ return (-0.5 * a * a) - Math.Log(x * sigma) - Constants.LogSqrt2Pi;
+ }
+
+ ///
+ /// Computes the cumulative distribution (CDF) of the distribution at x, i.e. P(X ≤ x).
+ ///
+ /// The location at which to compute the cumulative distribution function.
+ /// The log-scale (μ) of the distribution.
+ /// The shape (σ) of the distribution. Range: σ ≥ 0.
+ /// the cumulative distribution at location .
+ ///
+ /// MATLAB: logncdf
+ public static double CDF(double mu, double sigma, double x)
+ {
+ if (sigma < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return x < 0.0 ? 0.0
+ : 0.5 * (1.0 + SpecialFunctions.Erf((Math.Log(x) - mu) / (sigma * Constants.Sqrt2)));
+ }
+
+ ///
+ /// Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
+ /// at the given probability. This is also known as the quantile or percent point function.
+ ///
+ /// The location at which to compute the inverse cumulative density.
+ /// The log-scale (μ) of the distribution.
+ /// The shape (σ) of the distribution. Range: σ ≥ 0.
+ /// the inverse cumulative density at .
+ ///
+ /// MATLAB: logninv
+ public static double InvCDF(double mu, double sigma, double p)
+ {
+ if (sigma < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return p <= 0.0 ? 0.0 : p >= 1.0 ? double.PositiveInfinity
+ : Math.Exp(mu - sigma * Constants.Sqrt2 * SpecialFunctions.ErfcInv(2.0 * p));
+ }
+
+ ///
+ /// Generates a sample from the log-normal distribution using the Box-Muller algorithm.
+ ///
+ /// The random number generator to use.
+ /// The log-scale (μ) of the distribution.
+ /// The shape (σ) of the distribution. Range: σ ≥ 0.
+ /// a sample from the distribution.
+ public static double Sample(System.Random rnd, double mu, double sigma)
+ {
+ if (sigma < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(rnd, mu, sigma);
+ }
+
+ ///
+ /// Generates a sequence of samples from the log-normal distribution using the Box-Muller algorithm.
+ ///
+ /// The random number generator to use.
+ /// The log-scale (μ) of the distribution.
+ /// The shape (σ) of the distribution. Range: σ ≥ 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(System.Random rnd, double mu, double sigma)
+ {
+ if (sigma < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(rnd, mu, sigma);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The random number generator to use.
+ /// The array to fill with the samples.
+ /// The log-scale (μ) of the distribution.
+ /// The shape (σ) of the distribution. Range: σ ≥ 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(System.Random rnd, double[] values, double mu, double sigma)
+ {
+ if (sigma < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(rnd, values, mu, sigma);
+ }
+
+ ///
+ /// Generates a sample from the log-normal distribution using the Box-Muller algorithm.
+ ///
+ /// The log-scale (μ) of the distribution.
+ /// The shape (σ) of the distribution. Range: σ ≥ 0.
+ /// a sample from the distribution.
+ public static double Sample(double mu, double sigma)
+ {
+ if (sigma < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SampleUnchecked(SystemRandomSource.Default, mu, sigma);
+ }
+
+ ///
+ /// Generates a sequence of samples from the log-normal distribution using the Box-Muller algorithm.
+ ///
+ /// The log-scale (μ) of the distribution.
+ /// The shape (σ) of the distribution. Range: σ ≥ 0.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(double mu, double sigma)
+ {
+ if (sigma < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ return SamplesUnchecked(SystemRandomSource.Default, mu, sigma);
+ }
+
+ ///
+ /// Fills an array with samples generated from the distribution.
+ ///
+ /// The array to fill with the samples.
+ /// The log-scale (μ) of the distribution.
+ /// The shape (σ) of the distribution. Range: σ ≥ 0.
+ /// a sequence of samples from the distribution.
+ public static void Samples(double[] values, double mu, double sigma)
+ {
+ if (sigma < 0.0)
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ SamplesUnchecked(SystemRandomSource.Default, values, mu, sigma);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/MatrixNormal.cs b/MathNet.Numerics/Distributions/MatrixNormal.cs
new file mode 100644
index 0000000..a88421c
--- /dev/null
+++ b/MathNet.Numerics/Distributions/MatrixNormal.cs
@@ -0,0 +1,278 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2013 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.LinearAlgebra;
+using MathNet.Numerics.LinearAlgebra.Double;
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+
+using System;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Multivariate Matrix-valued Normal distributions. The distribution
+/// is parameterized by a mean matrix (M), a covariance matrix for the rows (V) and a covariance matrix
+/// for the columns (K). If the dimension of M is d-by-m then V is d-by-d and K is m-by-m.
+/// Wikipedia - MatrixNormal distribution.
+///
+public class MatrixNormal : IDistribution
+{
+ System.Random _random;
+
+ ///
+ /// The mean of the matrix normal distribution.
+ ///
+ readonly Matrix _m;
+
+ ///
+ /// The covariance matrix for the rows.
+ ///
+ readonly Matrix _v;
+
+ ///
+ /// The covariance matrix for the columns.
+ ///
+ readonly Matrix _k;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The mean of the matrix normal.
+ /// The covariance matrix for the rows.
+ /// The covariance matrix for the columns.
+ /// If the dimensions of the mean and two covariance matrices don't match.
+ public MatrixNormal(Matrix m, Matrix v, Matrix k)
+ {
+ if (Control.CheckDistributionParameters && !IsValidParameterSet(m, v, k))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _m = m;
+ _v = v;
+ _k = k;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The mean of the matrix normal.
+ /// The covariance matrix for the rows.
+ /// The covariance matrix for the columns.
+ /// The random number generator which is used to draw random samples.
+ /// If the dimensions of the mean and two covariance matrices don't match.
+ public MatrixNormal(Matrix m, Matrix v, Matrix k, System.Random randomSource)
+ {
+ if (Control.CheckDistributionParameters && !IsValidParameterSet(m, v, k))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _m = m;
+ _v = v;
+ _k = k;
+ }
+
+ ///
+ /// Returns a that represents this instance.
+ ///
+ ///
+ /// A that represents this instance.
+ ///
+ public override string ToString()
+ {
+ return "MatrixNormal(Rows = " + _m.RowCount + ", Columns = " + _m.ColumnCount + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// The mean of the matrix normal.
+ /// The covariance matrix for the rows.
+ /// The covariance matrix for the columns.
+ public static bool IsValidParameterSet(Matrix m, Matrix v, Matrix k)
+ {
+ var n = m.RowCount;
+ var p = m.ColumnCount;
+ if (v.ColumnCount != n || v.RowCount != n)
+ {
+ return false;
+ }
+
+ if (k.ColumnCount != p || k.RowCount != p)
+ {
+ return false;
+ }
+
+ for (var i = 0; i < v.RowCount; i++)
+ {
+ if (v.At(i, i) <= 0)
+ {
+ return false;
+ }
+ }
+
+ for (var i = 0; i < k.RowCount; i++)
+ {
+ if (k.At(i, i) <= 0)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ ///
+ /// Gets the mean. (M)
+ ///
+ /// The mean of the distribution.
+ public Matrix Mean
+ {
+ get { return _m; }
+ }
+
+ ///
+ /// Gets the row covariance. (V)
+ ///
+ /// The row covariance.
+ public Matrix RowCovariance
+ {
+ get { return _v; }
+ }
+
+ ///
+ /// Gets the column covariance. (K)
+ ///
+ /// The column covariance.
+ public Matrix ColumnCovariance
+ {
+ get { return _k; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Evaluates the probability density function for the matrix normal distribution.
+ ///
+ /// The matrix at which to evaluate the density at.
+ /// the density at
+ /// If the argument does not have the correct dimensions.
+ public double Density(Matrix x)
+ {
+ if (x.RowCount != _m.RowCount || x.ColumnCount != _m.ColumnCount)
+ {
+ throw Matrix.DimensionsDontMatch(x, _m, "x");
+ }
+
+ var a = x - _m;
+ var cholV = _v.Cholesky();
+ var cholK = _k.Cholesky();
+
+ return Math.Exp(-0.5 * cholK.Solve(a.Transpose() * cholV.Solve(a)).Trace())
+ / Math.Pow(2.0 * Constants.Pi, x.RowCount * x.ColumnCount / 2.0)
+ / Math.Pow(cholK.Determinant, x.RowCount / 2.0)
+ / Math.Pow(cholV.Determinant, x.ColumnCount / 2.0);
+ }
+
+ ///
+ /// Samples a matrix normal distributed random variable.
+ ///
+ /// A random number from this distribution.
+ public Matrix Sample()
+ {
+ return Sample(_random, _m, _v, _k);
+ }
+
+ ///
+ /// Samples a matrix normal distributed random variable.
+ ///
+ /// The random number generator to use.
+ /// The mean of the matrix normal.
+ /// The covariance matrix for the rows.
+ /// The covariance matrix for the columns.
+ /// If the dimensions of the mean and two covariance matrices don't match.
+ /// a sequence of samples from the distribution.
+ public static Matrix Sample(System.Random rnd, Matrix m, Matrix v, Matrix k)
+ {
+ if (Control.CheckDistributionParameters && !IsValidParameterSet(m, v, k))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ var n = m.RowCount;
+ var p = m.ColumnCount;
+
+ // Compute the Kronecker product of V and K, this is the covariance matrix for the stacked matrix.
+ var vki = v.KroneckerProduct(k.Inverse());
+
+ // Sample a vector valued random variable with VKi as the covariance.
+ var vector = SampleVectorNormal(rnd, new DenseVector(n * p), vki);
+
+ // Unstack the vector v and add the mean.
+ var r = m.Clone();
+ for (var i = 0; i < n; i++)
+ {
+ for (var j = 0; j < p; j++)
+ {
+ r.At(i, j, r.At(i, j) + vector[(j * n) + i]);
+ }
+ }
+
+ return r;
+ }
+
+ ///
+ /// Samples a vector normal distributed random variable.
+ ///
+ /// The random number generator to use.
+ /// The mean of the vector normal distribution.
+ /// The covariance matrix of the vector normal distribution.
+ /// a sequence of samples from defined distribution.
+ static Vector SampleVectorNormal(System.Random rnd, Vector mean, Matrix covariance)
+ {
+ var chol = covariance.Cholesky();
+
+ // Sample a standard normal variable.
+ var v = Vector.Build.Random(mean.Count, new Normal(rnd));
+
+ // Return the transformed variable.
+ return mean + (chol.Factor * v);
+ }
+}
diff --git a/MathNet.Numerics/Distributions/Multinomial.cs b/MathNet.Numerics/Distributions/Multinomial.cs
new file mode 100644
index 0000000..e67207c
--- /dev/null
+++ b/MathNet.Numerics/Distributions/Multinomial.cs
@@ -0,0 +1,391 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+//
+// Copyright (c) 2009-2013 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using MathNet.Numerics.LinearAlgebra;
+using MathNet.Numerics.LinearAlgebra.Double;
+using MathNet.Numerics.Properties;
+using MathNet.Numerics.Random;
+using MathNet.Numerics.Statistics;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MathNet.Numerics.Distributions;
+
+///
+/// Multivariate Multinomial distribution. For details about this distribution, see
+/// Wikipedia - Multinomial distribution.
+///
+///
+/// The distribution is parameterized by a vector of ratios: in other words, the parameter
+/// does not have to be normalized and sum to 1. The reason is that some vectors can't be exactly normalized
+/// to sum to 1 in floating point representation.
+///
+public class Multinomial : IDistribution
+{
+ System.Random _random;
+
+ ///
+ /// Stores the normalized multinomial probabilities.
+ ///
+ readonly double[] _p;
+
+ ///
+ /// The number of trials.
+ ///
+ readonly int _trials;
+
+ ///
+ /// Initializes a new instance of the Multinomial class.
+ ///
+ /// An array of nonnegative ratios: this array does not need to be normalized
+ /// as this is often impossible using floating point arithmetic.
+ /// The number of trials.
+ /// If any of the probabilities are negative or do not sum to one.
+ /// If is negative.
+ public Multinomial(double[] p, int n)
+ {
+ if (Control.CheckDistributionParameters && !IsValidParameterSet(p, n))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = SystemRandomSource.Default;
+ _p = (double[])p.Clone();
+ _trials = n;
+ }
+
+ ///
+ /// Initializes a new instance of the Multinomial class.
+ ///
+ /// An array of nonnegative ratios: this array does not need to be normalized
+ /// as this is often impossible using floating point arithmetic.
+ /// The number of trials.
+ /// The random number generator which is used to draw random samples.
+ /// If any of the probabilities are negative or do not sum to one.
+ /// If is negative.
+ public Multinomial(double[] p, int n, System.Random randomSource)
+ {
+ if (Control.CheckDistributionParameters && !IsValidParameterSet(p, n))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _random = randomSource ?? SystemRandomSource.Default;
+ _p = (double[])p.Clone();
+ _trials = n;
+ }
+
+ ///
+ /// Initializes a new instance of the Multinomial class from histogram . The distribution will
+ /// not be automatically updated when the histogram changes.
+ ///
+ /// Histogram instance
+ /// The number of trials.
+ /// If any of the probabilities are negative or do not sum to one.
+ /// If is negative.
+ public Multinomial(Histogram h, int n)
+ {
+ if (h == null)
+ {
+ throw new ArgumentNullException(nameof(h));
+ }
+
+ // The probability distribution vector.
+ var p = new double[h.BucketCount];
+
+ // Fill in the distribution vector.
+ for (var i = 0; i < h.BucketCount; i++)
+ {
+ p[i] = h[i].Count;
+ }
+
+ if (Control.CheckDistributionParameters && !IsValidParameterSet(p, n))
+ {
+ throw new ArgumentException(Resources.InvalidDistributionParameters);
+ }
+
+ _p = (double[])p.Clone();
+ _trials = n;
+ RandomSource = SystemRandomSource.Default;
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ /// a string representation of the distribution.
+ public override string ToString()
+ {
+ return "Multinomial(Dimension = " + _p.Length + ", Number of Trails = " + _trials + ")";
+ }
+
+ ///
+ /// Tests whether the provided values are valid parameters for this distribution.
+ ///
+ /// An array of nonnegative ratios: this array does not need to be normalized
+ /// as this is often impossible using floating point arithmetic.
+ /// The number of trials.
+ /// If any of the probabilities are negative returns false,
+ /// if the sum of parameters is 0.0, or if the number of trials is negative; otherwise true.
+ public static bool IsValidParameterSet(IEnumerable p, int n)
+ {
+ var sum = 0.0;
+ foreach (var t in p)
+ {
+ if (t < 0.0 || double.IsNaN(t))
+ {
+ return false;
+ }
+
+ sum += t;
+ }
+
+ if (sum == 0.0)
+ {
+ return false;
+ }
+
+ return n >= 0;
+ }
+
+ ///
+ /// Gets the proportion of ratios.
+ ///
+ public double[] P
+ {
+ get { return (double[])_p.Clone(); }
+ }
+
+ ///
+ /// Gets the number of trials.
+ ///
+ public int N
+ {
+ get { return _trials; }
+ }
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public System.Random RandomSource
+ {
+ get { return _random; }
+ set { _random = value ?? SystemRandomSource.Default; }
+ }
+
+ ///
+ /// Gets the mean of the distribution.
+ ///
+ public Vector Mean
+ {
+ get { return _trials * (DenseVector)P; }
+ }
+
+ ///