3

I want to use Unity to do math kind of like MATLAB, but also science type things in chemistry, physics, and engineering.

Just wanted to ask if these functions sound right for computing derivatives and partial derivatives numerically, and how I might go about doing 2nd partial derivatives and Laplace operator like in formulas like the Schrodinger's Equation, the Heat Equation, and so on?

I'm still learning Differential Equations, but wanted to relate it to numerical computation in C# for calculation.

public double Derivative(Func<double, double> function, double x, double h)
{
    return (function(x + h) - function(x)) / h;
}

public double SecondDerivative(Func<double, double> function, double x, double h)
{
    return (function(x + h) - 2 * function(x) + function(x - h)) / (h * h);
}

public double ThirdDerivative(Func<double, double> function, double x, double h)
{
    return (function(x + 3 * h) - 3 * function(x + 2 * h) + 3 * function(x + h) - function(x)) / (h * h * h);
}

public double PartialDerivativeX(Func<double, double, double> function, double x, double y, double h)
{
    return (function(x + h, y) - function(x, y)) / h;
}

public double PartialDerivativeY(Func<double, double, double> function, double x, double y, double h)
{
    return (function(x, y + h) - function(x, y)) / h;
}
John Ernest
  • 785
  • 1
  • 8
  • 20
  • 2
    It's a good approximation; however, since derivative is a *limit* and `h` is a *finite* value you always going to have some *error*. Do not forget to include *validation* (what if I pass `null` as function?); is it possible for your routine to *compute* reasonable `h`? E.g. `double result = Derivative(x => Math.Sin(x), Math.PI / 2.0);`: say, I want a derivative of *sine* at *pi / 2* and have no idea what is the best `h` for computing it. – Dmitry Bychenko Oct 03 '18 at 07:40
  • 2
    Another suggestion is to return *function*, e.g. `Func`: `public Func Derivative(Func function)` so we can put `var d_sin = Derivative(x => Math.Sin(x)); ... double result = d_sin(Math.PI / 2.0);` – Dmitry Bychenko Oct 03 '18 at 07:44
  • 2
    Be careful with `h`: what if I put `var result = Derivative(x => Math.Sin(x), Math.PI / 2, 0);`? `var result Derivative(x => Math.Abs(x), 0, 0.1);`? `var result Derivative(x => x / x, 0, 0.1);`? – Dmitry Bychenko Oct 03 '18 at 07:52
  • I wanted to give some ability to parameterize h, but yes, it should be within some reasonable range. I can fix that up fairly quickly. I like your suggestion of returning a function as well. – John Ernest Oct 03 '18 at 16:47
  • you can try providing default value for `h`: `Derivative(Func function, double x, double h = 0.0)` if `h` is `0` (default value) you routine computes the best `h` suitable; if `h` provided the routine uses it. – Dmitry Bychenko Oct 04 '18 at 06:52

2 Answers2

2

You probably have a little way to go.

As a first step, what you should do / must do is

familiarize yourself with all the existing libraries, which approach the challenge you are interested in.

This is the basic pipeline of Unity development (and really all software in our era).

If you search the web, you will find the libraries which handle issues such as derivatives, transcendental functions, etc etc

you could start with this QA, a number of packages are mentioned!

https://stackoverflow.com/a/34208687/294884

By totally familiarizing yourself with existing packages, you will to begin with learn how to package such a thing, API, etc, which is the first thing you have to get comfortable with when you make your own!

halfer
  • 19,824
  • 17
  • 99
  • 186
Fattie
  • 27,874
  • 70
  • 431
  • 719
  • I had looked over Math.NET, but it had a weird looking way of trying to compute derivatives. You can see the docs here: https://numerics.mathdotnet.com/api/MathNet.Numerics.Differentiation/NumericalDerivative.htm It has an "order" parameter and a "stepsize", I am not sure how these correlate exactly, and the docs as you see don't explain that much. Do you have familiarity with it perhaps? – John Ernest Oct 04 '18 at 17:06
  • hi @JohnErnest ! To work in the field, you will have to (1) absolutely, totally, completely familiarize yourself with **ALL** the math / calculus / derivatives libraries. (2) Within each of those libraries, you will have to become utterly expert. {For example, when someone who works for me has to do this in some new field, for a project, we allow **say 200 or so hours** (3 weeks) for them to do it. Only then can you even begin to work in that field set.} Note that **by all means** you can, and should, ask as many new questions, as often as you want, about that or any library, right here on SO. – Fattie Oct 04 '18 at 19:20
  • (You will see me asking huge numbers of very basic questions on here, about exactly such things, when I am working in a new API or field! Enjoy!} – Fattie Oct 04 '18 at 19:21
  • I don't mind reinventing the wheel Fattie, I'm on medical disability after being a programmer building apps for corporate for over a decade that never involved once Calculus, it was most just web apis for databases and the like, boring stuff. I'm building a 3d graphing calculator in Unity3D right now for some things, you see with Math.Net most of their Calculus is in Symbolic libraries, which I don't need or want for what I'm doing, it's relating Calculus numerically to graphing functions in 3D. Plus I've already built most of the parser anyway. I was simply wondering if you knew that library. – John Ernest Oct 05 '18 at 04:26
  • Is it better by the way to ask math related programming questions here on SO, or on Math Stackexchange? That seems like a gray area. – John Ernest Oct 05 '18 at 04:28
  • I'd go with here, @JohnErnest PS as a new user be sure to vote up / tick answers, as it gives you points so you can ask more questions! – Fattie Oct 05 '18 at 09:23
1

Your implementation is a good approximation (since derivative is a limit and h is a finite value). However, I suggest some different code:

public static class MyMath {
  // static: we don't want "this"
  // Func<double, double> return value: derivative is a function, not a value. 
  //   If we want a point - double - let's name the method as DerivativeAt 
  // No h - we can't provide correct h for all possible x
  public static Func<double, double> Derivative(Func<double, double> function) {
    //DONE: Validate public methods arguments
    if (null == function)
      throw new ArgumentNullException("function");

    return new Func<double, double>((x) => {
      // Let's compute h for given x 
      // Easiest, but not the best 
      double h = Math.Abs(x) < 1e-10 ? 1e-16 : x / 1.0e6;

      // "Central" derivative is often a better choice then right one ((f(x + h) - f(x))/h)
      return (function(x + h) - function(x - h)) / (2.0 * h);
    });
  }

  // h = 0.0: be nice and let user has no idea what step is reasonable   
  public static double DerivativeAt(Func<double, double> function, 
                                    double x, 
                                    double h = 0.0) {
    //DONE: Validate public methods arguments
    if (null == function)
      throw new ArgumentNullException("function");

    // If user don't want to provide h, let's compute it 
    if (0 == h) 
      h = Math.Abs(x) < 1e-10 ? 1e-16 : x / 1.0e6; // Easiest, but not the best 

    // "Central" derivative is often a better choice then right one ((f(x + h) - f(x))/h)
    return (function(x + h) - function(x - h)) / (2.0 * h);
  }
}

If you frequently use Derivative you can try declaring it as an extension method:

public static Func<double, double> Derivative(this Func<double, double> function) {...}

public static double DerivativeAt(this Func<double, double> function, 
                                  double x, 
                                  double h = 0.0) { ... }

Demo: let's find out maximum error when x within [0 .. 2 * PI) range for Sin function

// We don't want to repeat pesky "MyMath" in "MyMath.Derivative" 
using static MyNamespace.MyMath;

...

// Derivative of Sin (expected to be Cos) 
var d_sin = Derivative(x => Math.Sin(x));

double maxError = Enumerable
  .Range(0, 1000)
  .Select(i => 2.0 * Math.PI * i / 1000.0)
  .Select(x => Math.Abs(d_sin(x) - Math.Cos(x))) // d(sin(x)) / dx == cos(x) 
  .Max();

Console.WriteLine(maxError);

Outcome:

1.64271596325705E-10

Edit: "Central" derivative.

As we know, derivative is a limit

df/dx == lim (f(x + h) - f(x)) / h
         h -> 0

however we can ask: how h tend to 0. We have a lot of ways in case of complex numbers (h can, say, spiral down to 0 or goes along a strait line); in case of real numbers h can be either positive (right semi-derivative) or negative (left semi-derivative). Usually (standard definition) we require left semi-deivative be equal to right one in order to have derivative:

d+f(x) == d-f(x) == df/dx

However, sometime we use lenient definition ("central" derivative):

df/dx == (d+f(x) + d-f(x)) / 2  

For instance, d(abs(x))/dx at x = 0

d-abs(x)      = -1
d+abs(x)      =  1
d abs(x) / dx    doesn't exist (standard definition)
d abs(x) / dx =  0 "central", lenient definition.

Please, note that you current code computes in fact right semi-derivative; in case of Abs(x) you'll get wrong 1. 0 is a better answer in the context if not in calculus but in, say, engineering (imagine a moving car which does have a velocity). Another issue is that when computing derivative at x we don't need f exist at x. For instance

f(x) = x / abs(x) which can be put as

       -1 when x < 0
f(x) =    doesn't exist when x = 0
       +1 when x > 0 

Please, note that derivative df/dx at x = 0 exists (it's positive infinity). That's why when computing derivative we should avoid computing f(x). You current code will return

 (h / h + 0 / 0) / h == (1 + NaN) / h == NaN 

double.NaN - derivative doesn't exists (and that is wrong); "central" derivative will return

 (h/h - -h/h) / (2 * h) == 1 / h == some huge number (approximation of +Inf)
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • I like this solution Dmitry! I have a question though, you mention in the code using a central derivative. I looked through my Calculus text and did not see a central derivative mentioned, only the limit method as you say the right one. Could you please share how you came to the usage of this central derivative if it is not too much trouble? – John Ernest Oct 04 '18 at 17:12
  • 1
    @John Ernest: "central" derivative is a (computation, engineering) *trick*; when we define derivative as an average of left and right semi-derivatives. There are two reasons: 1. if derivative doesn't exist but we want some reasonable value. 2. When function doesn't exist at `x` but derivative does. I've edited the asnwer – Dmitry Bychenko Oct 04 '18 at 20:27