parent
a2ce71161e
commit
335f5dbc82
34
FinLib.NET/FinLib.Bench/Benchmarks/HistoricalVAR.cs
Normal file
34
FinLib.NET/FinLib.Bench/Benchmarks/HistoricalVAR.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
using System.Security.Cryptography;
|
||||||
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using FinLib.Bench.Comparisons;
|
||||||
|
|
||||||
|
namespace FinLib.Bench.Benchmarks;
|
||||||
|
|
||||||
|
public class HistoricalVar
|
||||||
|
{
|
||||||
|
private const int N = 10000;
|
||||||
|
|
||||||
|
// [Params(0.05d, 0.1d)]
|
||||||
|
public double confidence = 0.05;
|
||||||
|
[Params(10, 1000, 10000)]
|
||||||
|
public int length;
|
||||||
|
private double[] data;
|
||||||
|
|
||||||
|
[GlobalSetup]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
data = new double[length];
|
||||||
|
var random = new Random(42);
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
data[i] = random.NextDouble();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public double FinLibDotnet() => HistoricalVARDotnet.ValueAtRisk(data.ToList(), confidence);
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public double FinLibRust() => FinLib.Risk.ValueAtRisk.Historical(data, confidence);
|
||||||
|
}
|
37
FinLib.NET/FinLib.Bench/Comparisons/HistoricalVARDotnet.cs
Normal file
37
FinLib.NET/FinLib.Bench/Comparisons/HistoricalVARDotnet.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
namespace FinLib.Bench.Comparisons;
|
||||||
|
|
||||||
|
public static class HistoricalVARDotnet
|
||||||
|
{
|
||||||
|
public static IEnumerable<(T, T)> Pairwise<T>(this IEnumerable<T> enumerable)
|
||||||
|
{
|
||||||
|
var previous = default(T);
|
||||||
|
|
||||||
|
using (var e = enumerable.GetEnumerator())
|
||||||
|
{
|
||||||
|
if (e.MoveNext())
|
||||||
|
previous = e.Current;
|
||||||
|
|
||||||
|
while (e.MoveNext())
|
||||||
|
{
|
||||||
|
previous = e.Current;
|
||||||
|
yield return (previous, e.Current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<double> RatesOfChange(this IEnumerable<double> values)
|
||||||
|
{
|
||||||
|
foreach (var value in values.Pairwise())
|
||||||
|
{
|
||||||
|
yield return (value.Item2 - value.Item1) / value.Item1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double ValueAtRisk(List<double> values, double confidence)
|
||||||
|
{
|
||||||
|
var roc = values.RatesOfChange().Order().ToArray();
|
||||||
|
var threshold = (int) Math.Floor(confidence * roc.Length);
|
||||||
|
|
||||||
|
return roc[threshold];
|
||||||
|
}
|
||||||
|
}
|
18
FinLib.NET/FinLib.Bench/FinLib.Bench.csproj
Normal file
18
FinLib.NET/FinLib.Bench/FinLib.Bench.csproj
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\FinLib\FinLib.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
13
FinLib.NET/FinLib.Bench/Program.cs
Normal file
13
FinLib.NET/FinLib.Bench/Program.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using BenchmarkDotNet.Running;
|
||||||
|
using FinLib.Bench.Benchmarks;
|
||||||
|
|
||||||
|
namespace FinLib.Bench
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
var summary = BenchmarkRunner.Run<HistoricalVar>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,24 +11,24 @@ public class Tests
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestCompoundInterest()
|
public void TestCompoundInterest()
|
||||||
{
|
{
|
||||||
FinLib.CompoundInterest(100, 0.05, 1, 1).Should().Be(105);
|
Interest.Interest.Compound(100, 0.05, 1, 1).Should().Be(105);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCompoundInterestMonthly()
|
public void TestCompoundInterestMonthly()
|
||||||
{
|
{
|
||||||
Math.Round(FinLib.CompoundInterest(100, 0.05, 1, 12), 2).Should().Be(105.12);
|
Math.Round(Interest.Interest.Compound(100, 0.05, 1, 12), 2).Should().Be(105.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Covariance()
|
public void Covariance()
|
||||||
{
|
{
|
||||||
FinLib.Covariance([1d, 2d, 3d, 4], [1d, 2, 3, 4]).Should().Be(1.6666666666666667);
|
Stats.Stats.Covariance([1d, 2d, 3d, 4], [1d, 2, 3, 4]).Should().Be(1.6666666666666667);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void CovarianceBreaking()
|
public void CovarianceBreaking()
|
||||||
{
|
{
|
||||||
FinLib.Covariance([1d, 2d, 3d, 4], [1d]).Should().BeNull();
|
Stats.Stats.Covariance([1d, 2d, 3d, 4], [1d]).Should().BeNull();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,6 +4,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FinLib", "FinLib\FinLib.csp
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FinLib.Test", "FinLib.Test\FinLib.Test.csproj", "{148E024C-07D0-4DC0-A3E2-A146E709897B}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FinLib.Test", "FinLib.Test\FinLib.Test.csproj", "{148E024C-07D0-4DC0-A3E2-A146E709897B}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FinLib.Bench", "FinLib.Bench\FinLib.Bench.csproj", "{F4171146-0A63-4611-972B-BAFB9A090D2B}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -18,5 +20,9 @@ Global
|
|||||||
{148E024C-07D0-4DC0-A3E2-A146E709897B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{148E024C-07D0-4DC0-A3E2-A146E709897B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{148E024C-07D0-4DC0-A3E2-A146E709897B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{148E024C-07D0-4DC0-A3E2-A146E709897B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{148E024C-07D0-4DC0-A3E2-A146E709897B}.Release|Any CPU.Build.0 = Release|Any CPU
|
{148E024C-07D0-4DC0-A3E2-A146E709897B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{F4171146-0A63-4611-972B-BAFB9A090D2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{F4171146-0A63-4611-972B-BAFB9A090D2B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{F4171146-0A63-4611-972B-BAFB9A090D2B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{F4171146-0A63-4611-972B-BAFB9A090D2B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
14
FinLib.NET/FinLib/FinLib.Interest.cs
Normal file
14
FinLib.NET/FinLib/FinLib.Interest.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FinLib.Interest;
|
||||||
|
|
||||||
|
public static class Interest
|
||||||
|
{
|
||||||
|
public static double Compound(double principal, double rate, double time, double n)
|
||||||
|
{
|
||||||
|
return NativeMethods.interest_compound(principal, rate, time, n);
|
||||||
|
}
|
||||||
|
}
|
21
FinLib.NET/FinLib/FinLib.Risk.cs
Normal file
21
FinLib.NET/FinLib/FinLib.Risk.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FinLib.Risk;
|
||||||
|
|
||||||
|
public static class ValueAtRisk
|
||||||
|
{
|
||||||
|
public static double Historical(IEnumerable<double> values, double confidence)
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
var valueArr = values.ToArray();
|
||||||
|
fixed (double* ptrOne = valueArr) {
|
||||||
|
var ret = NativeMethods.value_at_risk(ptrOne, (UIntPtr)valueArr.Length, confidence);
|
||||||
|
|
||||||
|
return *ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,15 +3,10 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace FinLib;
|
namespace FinLib.Stats;
|
||||||
|
|
||||||
public static class FinLib
|
public static class Stats
|
||||||
{
|
{
|
||||||
public static double CompoundInterest(double principal, double rate, double time, double n)
|
|
||||||
{
|
|
||||||
return NativeMethods.interest_compound(principal, rate, time, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double? Covariance(IEnumerable<double> valuesOne, IEnumerable<double> valuesTwo)
|
public static double? Covariance(IEnumerable<double> valuesOne, IEnumerable<double> valuesTwo)
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -22,6 +22,9 @@ namespace FinLib
|
|||||||
[DllImport(__DllName, EntryPoint = "covariance", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
[DllImport(__DllName, EntryPoint = "covariance", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||||
internal static extern double* covariance(double* arr, nuint len, double* arr_two, nuint len_two);
|
internal static extern double* covariance(double* arr, nuint len, double* arr_two, nuint len_two);
|
||||||
|
|
||||||
|
[DllImport(__DllName, EntryPoint = "value_at_risk", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||||
|
internal static extern double* value_at_risk(double* arr, nuint len, double confidence);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,8 @@ const double *covariance(const double *arr, size_t len, const double *arr_two, s
|
|||||||
|
|
||||||
double interest_compound(double principal, double rate, double time, double n);
|
double interest_compound(double principal, double rate, double time, double n);
|
||||||
|
|
||||||
|
const double *value_at_risk(const double *arr, size_t len, double confidence);
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
||||||
} // namespace finlib
|
} // namespace finlib
|
@ -14,7 +14,7 @@ fn main() {
|
|||||||
.with_config(config)
|
.with_config(config)
|
||||||
.generate()
|
.generate()
|
||||||
.expect("Unable to generate bindings")
|
.expect("Unable to generate bindings")
|
||||||
.write_to_file("../finlib-cpp/finlib-native.h");
|
.write_to_file("../finlib-cpp/include/finlib-native.h");
|
||||||
|
|
||||||
csbindgen::Builder::default()
|
csbindgen::Builder::default()
|
||||||
.input_extern_file("src/lib.rs")
|
.input_extern_file("src/lib.rs")
|
||||||
|
@ -23,4 +23,14 @@ pub unsafe extern "C" fn covariance(arr: *const f64, len: usize, arr_two: *const
|
|||||||
Some(v) => Box::into_raw(Box::new(v))
|
Some(v) => Box::into_raw(Box::new(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn value_at_risk(arr: *const f64, len: usize, confidence: f64) -> *const f64 {
|
||||||
|
let input_array = unsafe {
|
||||||
|
assert!(!arr.is_null());
|
||||||
|
slice::from_raw_parts(arr, len)
|
||||||
|
};
|
||||||
|
|
||||||
|
Box::into_raw(Box::new(finlib::risk::var::historical::value_at_risk(input_array, confidence)))
|
||||||
}
|
}
|
@ -6,7 +6,8 @@ edition.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
pyo3 = { workspace = true, optional = true }
|
pyo3 = { workspace = true, optional = true }
|
||||||
rayon = { workspace = true }
|
rayon = { workspace = true, optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
py = ["dep:pyo3"]
|
py = ["dep:pyo3"]
|
||||||
|
parallel = ["dep::rayon"]
|
@ -1,2 +1,4 @@
|
|||||||
pub mod interest;
|
pub mod interest;
|
||||||
pub mod stats;
|
pub mod stats;
|
||||||
|
pub mod util;
|
||||||
|
pub mod risk;
|
1
finlib/src/risk/mod.rs
Normal file
1
finlib/src/risk/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod var;
|
27
finlib/src/risk/var/historical.rs
Normal file
27
finlib/src/risk/var/historical.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
use crate::util::roc::rates_of_change;
|
||||||
|
|
||||||
|
#[cfg(feature = "parallel")]
|
||||||
|
use rayon::prelude::*;
|
||||||
|
|
||||||
|
// https://www.simtrade.fr/blog_simtrade/historical-method-var-calculation/
|
||||||
|
|
||||||
|
pub fn value_at_risk(values: &[f64], confidence: f64) -> f64 {
|
||||||
|
let mut roc = rates_of_change(values).collect::<Vec<_>>();
|
||||||
|
// roc.par_sort_by(|x, y| x.partial_cmp(y).unwrap());
|
||||||
|
roc.sort_by(|x, y| x.partial_cmp(y).unwrap());
|
||||||
|
|
||||||
|
let threshold = (confidence * roc.len() as f64).floor() as usize;
|
||||||
|
|
||||||
|
roc[threshold]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn var_test() {
|
||||||
|
let result = value_at_risk(&[1f64, 2f64, 4f64, 5f64], 0.01f64);
|
||||||
|
assert_eq!(result, 0.25f64);
|
||||||
|
}
|
||||||
|
}
|
1
finlib/src/risk/var/mod.rs
Normal file
1
finlib/src/risk/var/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod historical;
|
@ -1,3 +1,4 @@
|
|||||||
|
#[cfg(feature = "parallel")]
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use super::mean;
|
use super::mean;
|
||||||
|
|
||||||
@ -8,7 +9,13 @@ pub fn covariance(slice: &[f64], slice_two: &[f64]) -> Option<f64>
|
|||||||
let mean_1 = mean(slice);
|
let mean_1 = mean(slice);
|
||||||
let mean_2 = mean(slice_two);
|
let mean_2 = mean(slice_two);
|
||||||
|
|
||||||
Some(slice.par_iter().zip(slice_two.par_iter())
|
Some(slice
|
||||||
|
// .par_iter()
|
||||||
|
.iter()
|
||||||
|
.zip(slice_two
|
||||||
|
// .par_iter()
|
||||||
|
.iter()
|
||||||
|
)
|
||||||
.map(|(x, y)| (x - mean_1) * (y - mean_2))
|
.map(|(x, y)| (x - mean_1) * (y - mean_2))
|
||||||
.sum::<f64>()
|
.sum::<f64>()
|
||||||
/ ((slice.len() - 1) as f64))
|
/ ((slice.len() - 1) as f64))
|
||||||
|
@ -1,29 +1,37 @@
|
|||||||
mod covariance;
|
mod covariance;
|
||||||
pub use covariance::*;
|
pub use covariance::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "parallel")]
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
|
|
||||||
pub fn mean(slice: &[f64]) -> f64
|
pub fn mean(slice: &[f64]) -> f64
|
||||||
{
|
{
|
||||||
slice.par_iter().sum::<f64>() / slice.len() as f64
|
slice
|
||||||
|
// .par_iter()
|
||||||
|
.iter()
|
||||||
|
.sum::<f64>() / slice.len() as f64
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn population_variance(slice: &[f64]) -> f64
|
pub fn population_variance(slice: &[f64]) -> f64
|
||||||
{
|
{
|
||||||
let mean = mean(slice);
|
let mean = mean(slice);
|
||||||
slice.par_iter()
|
slice
|
||||||
|
// .par_iter()
|
||||||
|
.iter()
|
||||||
.map(|x| f64::powi(x - mean, 2))
|
.map(|x| f64::powi(x - mean, 2))
|
||||||
.sum::<f64>()
|
.sum::<f64>()
|
||||||
/ slice.len() as f64
|
/ slice.len() as f64
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sample_variance(slice: &[f64]) -> f64
|
pub fn sample_variance(slice: &[f64]) -> f64
|
||||||
{
|
{
|
||||||
let mean = mean(slice);
|
let mean = mean(slice);
|
||||||
slice.par_iter()
|
slice
|
||||||
|
// .par_iter()
|
||||||
|
.iter()
|
||||||
.map(|x| f64::powi(x - mean, 2))
|
.map(|x| f64::powi(x - mean, 2))
|
||||||
.sum::<f64>()
|
.sum::<f64>()
|
||||||
/ ((slice.len() - 1) as f64)
|
/ ((slice.len() - 1) as f64)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn population_std_dev(slice: &[f64]) -> f64
|
pub fn population_std_dev(slice: &[f64]) -> f64
|
||||||
|
1
finlib/src/util/mod.rs
Normal file
1
finlib/src/util/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod roc;
|
33
finlib/src/util/roc.rs
Normal file
33
finlib/src/util/roc.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#[cfg(feature = "parallel")]
|
||||||
|
use rayon::prelude::*;
|
||||||
|
|
||||||
|
pub fn changes(values: &[f64]) -> impl Iterator<Item = f64> + use<'_> {
|
||||||
|
values
|
||||||
|
.windows(2)
|
||||||
|
// .par_windows(2)
|
||||||
|
.map(|x| x[1] - x[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rates_of_change(values: &[f64]) -> impl Iterator<Item = f64> + use<'_> {
|
||||||
|
values
|
||||||
|
.windows(2)
|
||||||
|
// .par_windows(2)
|
||||||
|
.map(|x| (x[1] - x[0])/x[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn change_test() {
|
||||||
|
let result = changes(&[1f64, 2f64, 4f64, 5f64]).collect::<Vec<_>>();
|
||||||
|
assert_eq!(result, vec![1f64, 2f64, 1f64]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn roc_test() {
|
||||||
|
let result = rates_of_change(&[1f64, 2f64, 4f64, 5f64]).collect::<Vec<_>>();
|
||||||
|
assert_eq!(result, vec![1f64, 1f64, 0.25f64]);
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,19 @@ pub fn covariance(slice: Vec<f64>, slice_two: Vec<f64>) -> PyResult<Option<f64>>
|
|||||||
|
|
||||||
#[pymodule]
|
#[pymodule]
|
||||||
fn pyfinlib(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
fn pyfinlib(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||||
m.add_function(wrap_pyfunction!(compound, m)?)?;
|
register_interest_module(m);
|
||||||
m.add_function(wrap_pyfunction!(covariance, m)?)?;
|
register_stats_module(m);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_interest_module(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||||
|
let child_module = PyModule::new(parent_module.py(), "interest")?;
|
||||||
|
child_module.add_function(wrap_pyfunction!(compound, &child_module)?)?;
|
||||||
|
parent_module.add_submodule(&child_module)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_stats_module(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||||
|
let child_module = PyModule::new(parent_module.py(), "stats")?;
|
||||||
|
child_module.add_function(wrap_pyfunction!(covariance, &child_module)?)?;
|
||||||
|
parent_module.add_submodule(&child_module)
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user