Skip to content

Provides an implementation for jets and some jet-based utilities

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
Notifications You must be signed in to change notification settings

jakoschiko/wtfisjet

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

58 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

wtfisjet

Provides an implementation for jet and some jet-based utilities.

Status of this crate

The author does not consider this crate as stable yet. However, it is well documented and tested, so give it a try!

Features

These features are available:

  • Implementation for jets that can be used for AD.
  • Multiple implementation for the infinitesimal part of the jet with different performance characteristics.
  • Implementation for B-splines that uses jets as control points.
  • Implementation for Newton's method that uses jets for calculating the derivative.
  • Many feature flags that allows to include only the features you need.

WTF is jet?

A jet is a magic number. If you use jets for your calculation instead of ordinary numbers, you get not only the result but also the derivatives with respect to the inputs. This is called automatic differentiation (AD). There are different ways of accomplishing AD, dual numbers are one of them and a jet is an n-dimensional dual number. The ceres library has a good explanation of how all of this works.

This library implements jet with the struct Jet. The most important thing you need to know about Jet is that it consists of two parts:

  • The real part is the actual result.
  • The infinitesimal part is the magic part that calculates the derivatives. It's represented by the trait Infinitesimal. This crate provides different implementations with different performance characteristics.

So, how can you use Jet? Instead of writing this:

fn foo(x: f32) -> f32 {
    x * x - 1.0
}

let result = foo(2.0);
println!("{}", result);
// Output: 3

You write this:

use wtfisjet::{DenseInfinitesimal, Dim, Infinitesimal, Jet};

fn foo<I: Infinitesimal<f32>>(x: Jet<f32, I>) -> Jet<f32, I> {
    x.clone() * x - 1.0
}

type MyJet = Jet<f32, DenseInfinitesimal<f32>>;
let dim = Dim(1);

let result = foo(MyJet::variable(2.0, 0, dim));
println!("{}", result);
// Output: (3 + [4]h)
//          ^    ^
//          |    |
//          |   derivative of foo with respect to x
//          |
//         result of foo

As mentioned before, jets are n-dimensional. That means you can calculate the derivatives with respect to multiple inputs:

use wtfisjet::{DenseInfinitesimal, Dim, Infinitesimal, Jet};

fn bar<I: Infinitesimal<f32>>(x: Jet<f32, I>, y: Jet<f32, I>) -> Jet<f32, I> {
    (x.clone() * x - 1.0) / y
}

type MyJet = Jet<f32, DenseInfinitesimal<f32>>;
let dim = Dim(2); // Because we have now two variables, we need at least 2 dimensions

let result = bar(
    // The different variables uses different indices
    MyJet::variable(2.0, 0, dim),
    MyJet::variable(4.0, 1, dim),
);
println!("{}", result);
// Output: (0.75 + [1, -0.1875]h)
//          ^^^^    ^  ^^^^^^^
//           |      |     |
//           |      |    derivative of bar with respect to y
//           |      |
//           |     derivative of bar with respect to x
//           |
//         result of bar

Please note that functions foo and bar in the previous examples don't hide Jet behind a type parameter. Of course you could do this:

use std::ops::Mul;
use wtfisjet::{DenseInfinitesimal, Dim, Infinitesimal, Jet};

// Note: Not recommended!
fn baz<N: Clone + Mul<Output=N>>(x: N) -> N {
    x.clone() * x
}

type MyJet = Jet<f32, DenseInfinitesimal<f32>>;
let dim = Dim(1);

let result = baz(MyJet::variable(2.0, 0, dim));

However, this is not recommended. Jet has many pitfalls and it's helpful to see that your calculation is dealing with jets and not ordinary numbers. Additionally you can often improve the performance by using special functions that reduce the number of necessary Infinitesimal operations (e.g. you can use x.square() instead of x.clone() * x).

If you're not interested in the derivatives but want to use a Jet based function, you can use NoInfinitesimal:

use wtfisjet::{Dim, Infinitesimal, Jet, NoInfinitesimal};

fn foo<I: Infinitesimal<f32>>(x: Jet<f32, I>) -> Jet<f32, I> {
    x.clone() * x - 1.0
}

type MyJet = Jet<f32, NoInfinitesimal>; // NoInfinitesimal is a zero sized type (ZST)
let dim = Dim(0); // NoInfinitesimal requires zero dimensions

let result = foo(MyJet::constant(2.0, dim)).real;
println!("{}", result);
// Output: 3

Assuming that Rust's zero-overhead principle is not a lie, this will be as fast as using a f32 based function.

Example: Curve fitting

What is curve fitting? Imagine you have a tool that takes some variables and produces a curve. You want to produce a curve that adheres some constraints. How do you choose the variables? Curve fitting is the process of finding those variables.

This library provides utilities for simple curve fitting:

  • You can use B-splines as a tool for constructing curves. The variables are the control points of the B-spline. The constraints are points that should be matched by the B-spline curve.
  • You can use Newton's method to approximate a solution for the variables.
  • You can use jets to calculate the derivative that is necessary for Newton's method.

The example curve_fitting.rs shows how this works. It produces the following curve:

curve fitting result

Feature flags

These feature flags can be used to customize the crate.

big-rational-number (enabled by default)

If enabled, the type num-rational::BigRational will implement Number. This is very useful for writing tests with arbitrary precision. It will require the dependency num-rational.

sparse-infinitesimal (enabled by default)

If enabled, the type wtfisjet::SparseInfinitesimal is available. It will require the dependency intmap.

const-infinitesimal (enabled by default)

If enabled, the type wtfisjet::ConstInfinitesimal is available. It will require the dependency array-init.

b-spline (enabled by default)

If enabled, the module wtfisjet::bspline is available.

newton (enabled by default)

If enabled, the module wtfisjet::newton is available. It will require the dependency rulinalg.

dice (disabled by default)

If enabled, the module wtfisjet::dice is available. It provides generators for random test data. It will require the dependency dicetest.

asserts (disabled by default)

If enabled, the module wtfisjet::asserts is available. It provides assertions for tests. It will require the dependency dicetest and diceprop.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

About

Provides an implementation for jets and some jet-based utilities

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published