Skip to main content

Crate lookup_tables

Crate lookup_tables 

Source
Expand description

§lookup-tables

High performance & compile-time customizable lookup tables

§Features

§Tables

§Out-of-bounds behavior

  • Clamp - Clamp at the bounds and do not extrapolate outside the table
  • Interp - Interpolate freely outside bounds

§Searching Methods

  • Linear - Lineary search to find bounding indicies. Typically faster for small (< 20) values in a table
  • Binary - Binary search for bounding indices. Useful for large datasets
  • CachedLinearCell - Linear searching with a cached last-used index. Effective for large datasets with slowly changing lookup values
  • RuntimeSearch - Use any search method, configured at runtime

§Axis Customization

Interpolation search and bounding is configured on a per Axis basis. An axis consists of

  • Indep - type of the independent variable. Typically f32 or f64
  • Search - search method implementing the Search trait
  • LowerBound - bounding behavior at the lower bound of the axis implementing the Bound trait
  • UpperBound - bounding behavior at the higher bound of the axis implementing the Bound trait

§Examples

§Lookup Table 1D With Clamping on Lower Bound

use lookup_tables::{Axis, Linear, Clamp, Interp, LookupTable1D};
use std::f64::consts::PI;

let height_data  = vec![0., 1., 2., 3., 4., 5.];
// experimentally measured volume an irregular object at the above heights
let volume_data  = vec![0., 3., 5., 10., 12., 13.];

// height data will be f64 and searched with a linear method. The lower bound will be clamped as we cannot have
// a height / volume less than 0, but the upper bound will interpolate unbounded
type MyAxis = Axis<f64, Linear, Clamp, Interp>;

// lookup table will search through the first independent variable (height) with parameters from `MyAxis`. Dependent
// variable (volume) returned will be `f64`
type MyTable = LookupTable1D<MyAxis, f64>;

let table = MyTable::new(height_data, Linear::default(), volume_data).unwrap();

let interpolated_volume = table.lookup(2.5);
assert!(interpolated_volume == 7.5);

// negative height measurement clamps the results to the lowest volume
let interpolated_volume = table.lookup(-1.0);
assert!(interpolated_volume == 0.0);

// out of bounds height interpolates volume linearly
let interpolated_volume = table.lookup(10.);
assert!(interpolated_volume == 18.0);

§Lookup Table 1D With Vector-Valued Dependent Variable

Instead of a table to convert a height measurement to a volume reading, what if we wanted to compute multiple properties of our system simultaneously using the same height data? We could construct multiple tables or we could change the dependent variable to an array type meeting the bounds of LookupTable1D::lookup

use lookup_tables::{Axis, Linear, Clamp, Interp, LookupTable1D};
use nalgebra::Vector2;
use std::f64::consts::PI;

let height_data  = vec![0., 1., 2., 3., 4., 5.];
// experimentally measured property data of an irregular object at the above heights.
// the first entry in each index is the same as our volume data above
let property_data = vec![
    Vector2::new(0., 0.), 
    Vector2::new(3., 1.),
    Vector2::new(5., 2.), 
    Vector2::new(10.,3.),
    Vector2::new(12.,4.),
    Vector2::new(13.,5.)
];

// Same axis type as above. Contains `f64` data, searched with a linear method, clamped at
// the lower bound, interpolated at the higher bound.
type MyAxis = Axis<f64, Linear, Clamp, Interp>;

// lookup table will search through the first independent variable (height) with parameters from `MyAxis`. 
// Dependent variable returned will be `Vector2`
type MyTable = LookupTable1D<MyAxis, Vector2<f64>>;

let table = MyTable::new(height_data, Linear::default(), property_data).unwrap();

let interpolated_properties = table.lookup(2.5);
assert!(interpolated_properties == Vector2::new(7.5, 2.5));

§Lookup Table 2D With Clamped Bounds

If we want to approximate f(x,y) we can instead use LookupTable2D

use lookup_tables::{Axis, Binary, Clamp, LookupTable2D};
use std::f64::consts::PI;
use ndarray::{Array1, Array2};

let n = 10;

fn analytical_f(x: f64, y: f64) -> f64 {
    x*y + y
}

let x : Vec<f64> = ndarray::Array1::linspace(0.0, 5.0, n).to_vec();
let y : Vec<f64> = x.clone();

let mut f = ndarray::Array2::zeros((n, n));

// populate `f` matrix with evaluations of `analytical_f`
for i in 0..n {
    for j in 0..n {
        f[[i,j]] = analytical_f(x[i], y[j]);
    }
}


// Clamp at both ends of the axis, binary search for our values
type MyAxis = Axis<f64, Binary, Clamp, Clamp>;
// 2D lookup table has two axis. Dependent variable result is `f64`, what `analytical_f` 
// outputs.
type MyTable = LookupTable2D<MyAxis, MyAxis, f64>;

let table = MyTable::new(x, Binary::default(), y, Binary::default(), f).unwrap();

let x_0 = 1.2;
let y_0 = 4.5;
let output = table.lookup(x_0, y_0);
float_eq::assert_float_eq!(output, analytical_f(x_0, y_0), abs <= 1e-10);

Structs§

Axis
Binary
Binary search to find bounding indices. Useful for large datasets (>20)
CachedLinearCell
Store the last set of bounding indices and learly search from the last known match. Effective for tables with slowly changing values.
Clamp
Interp
Linear
Linear search to find the bounding indices. Typically faster for small (<20) values in the table.
LookupTable1D
LookupTable2D

Enums§

Error
RuntimeSearch
Determine search method dynamically at runtime

Traits§

Bound
Search