From 05590e5405aea1453269d9181131ca5961e88d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=BCleyman=20Kaya?= <111174999+suleyman-kaya@users.noreply.github.com> Date: Sun, 28 Jul 2024 01:47:26 +0300 Subject: [PATCH] Add partial derivatives and tests (#209) * Add partial derivatives and tests * Update README.md * Update README for deriv module * Reformat deriv_test.v * Remove unnecessary lines * Shorten README * reformat deriv.v * Update Readme * Update deriv/deriv.v Co-authored-by: Ulises Jeremias * Update deriv_test.v * Update deriv.v * run `v fmt -w deriv/deriv.v` --------- Co-authored-by: Ulises Jeremias Co-authored-by: Delyan Angelov --- deriv/README.md | 12 ++++++++++++ deriv/deriv.v | 30 ++++++++++++++++++++++++++++++ deriv/deriv_test.v | 24 ++++++++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/deriv/README.md b/deriv/README.md index 096613e8d..b66312a63 100644 --- a/deriv/README.md +++ b/deriv/README.md @@ -63,6 +63,18 @@ for values greater than `x`. 📉 This function is equivalent to calling `deriv.forward` with a negative step-size. +### `partial` + +```v ignore +pub fn partial(f fn ([]f64) f64, x []f64, variable int, h f64) (f64, f64) +``` + +This function computes the partial derivative of the function `f` with respect to +a specified variable at point `x` using step-size `h`. It returns the derivative +in `result` and an error estimate in `abserr`. The function `f` should take an array +of coordinates and return a single value. This method provides both the derivative +and its error estimate. + ## References and Further Reading This work is a spiritual descendent of the Differentiation module in [GSL](https://github.com/ampl/gsl). 📖 diff --git a/deriv/deriv.v b/deriv/deriv.v index 47a2b33cd..eb53c0758 100644 --- a/deriv/deriv.v +++ b/deriv/deriv.v @@ -1,6 +1,7 @@ module deriv import vsl.func +import vsl.errors import vsl.internal.prec import math @@ -113,3 +114,32 @@ pub fn forward(f func.Fn, x f64, h f64) (f64, f64) { pub fn backward(f func.Fn, x f64, h f64) (f64, f64) { return forward(f, x, -h) } + +/** +* partial is a function that computes the partial derivative of a multivariable function with respect to a specified variable. +* +* @param f The multivariable function for which the partial derivative is to be computed. +* @param x The point at which the partial derivative is to be computed, represented as an array of coordinates. +* @param variable The index of the variable with respect to which the partial derivative is to be computed. +* @param h The step size to be used in the central difference method. +* +* @return A tuple containing the value of the partial derivative and the estimated error. +*/ +pub fn partial(f fn ([]f64) f64, x []f64, variable int, h f64) (f64, f64) { + if variable < 0 || variable >= x.len { + errors.vsl_panic('Invalid variable index', .efailed) + } + + // Define a helper function that converts the multivariate function + // to a univariate function for the specified variable + partial_helper := func.Fn{ + f: fn [f, x, variable] (t f64, _ []f64) f64 { + mut x_new := x.clone() + x_new[variable] = t + return f(x_new) + } + } + + // Use the central difference method to compute the partial derivative + return central(partial_helper, x[variable], h) +} diff --git a/deriv/deriv_test.v b/deriv/deriv_test.v index f25a5573f..041c53d79 100644 --- a/deriv/deriv_test.v +++ b/deriv/deriv_test.v @@ -68,6 +68,18 @@ fn df6(x f64, _ []f64) f64 { return -1.0 / (x * x) } +fn f_multi(x []f64) f64 { + return x[0] * x[0] + x[1] * x[1] // f(x,y) = x^2 + y^2 +} + +fn df_multi_dx(x []f64) f64 { + return 2 * x[0] // ∂f/∂x = 2x +} + +fn df_multi_dy(x []f64) f64 { + return 2 * x[1] // ∂f/∂y = 2y +} + fn test_deriv() { f1_ := func.Fn.new(f: f1) df1_ := func.Fn.new(f: df1) @@ -100,6 +112,18 @@ fn test_deriv() { assert deriv_test('central', f6_, df6_, 10.0) assert deriv_test('forward', f6_, df6_, 10.0) assert deriv_test('backward', f6_, df6_, 10.0) + + // Partial derivative test + x := [2.0, 3.0] + h := 1e-5 + + // Partial derivative with respect to x + dx, _ := partial(f_multi, x, 0, h) + assert float64.tolerance(dx, df_multi_dx(x), 1e-5) + + // Partial derivative with respect to y + dy, _ := partial(f_multi, x, 1, h) + assert float64.tolerance(dy, df_multi_dy(x), 1e-5) } fn deriv_test(deriv_method string, f func.Fn, df func.Fn, x f64) bool {