Expand description
§lookup-tables
High performance & compile-time customizable lookup tables
§Features
§Tables
LookupTable1D- Approximatef(x)givenxLookupTable2D- Approximatef(x, y)givenx,y
§Out-of-bounds behavior
Clamp- Clamp at the bounds and do not extrapolate outside the tableInterp- Interpolate freely outside bounds
§Searching Methods
Linear- Lineary search to find bounding indicies. Typically faster for small (< 20) values in a tableBinary- Binary search for bounding indices. Useful for large datasetsCachedLinearCell- Linear searching with a cached last-used index. Effective for large datasets with slowly changing lookup valuesRuntimeSearch- 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. Typicallyf32orf64Search- search method implementing theSearchtraitLowerBound- bounding behavior at the lower bound of the axis implementing theBoundtraitUpperBound- bounding behavior at the higher bound of the axis implementing theBoundtrait
§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)
- Cached
Linear Cell - 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.
- Lookup
Table1D - Lookup
Table2D
Enums§
- Error
- Runtime
Search - Determine search method dynamically at runtime