Crate vectora[−][src]
Expand description
A vector computation library
Contents
- About
- Safety Guarantee
- Versioning
- Minimum Rust Version Compatibility Policy
- Source
- Issues
- Contributing
- License
- Getting Started
About
Vectora is a library for n-dimensional vector computation. It currently supports scalar integer and floating point data.
The main library entry point is the Vector struct. Please see the Gettting Started guide
for a detailed library overview with examples.
Safety Guarantee
The current default distribution does not contain unsafe code blocks.
Versioning
This project uses semantic versioning and is currently in a pre-v1.0 stage of development. The public API should not be considered stable across release versions at this time.
Minimum Rust Version Compatibility Policy
This project parameterizes generics by constants and relies on the constant generics feature support
that was stabilized in Rust v1.51. The minimum
supported rustc version is believed to be v1.51.0.
Source
The source files are available at https://github.com/chrissimpkins/vectora.
Issues
The issue tracker is available on the GitHub repository. Don’t be shy. Please report any issues that you identify so that we can address them.
Contributing
Contributions are welcomed. Developer documentation is available in the source repository README.
Submit your source or documentation changes as a GitHub pull request on the source repository.
License
Vectora is released under the Apache License v2.0. Please review the full text of the license for details.
Getting Started
See the Vector page for detailed API documentation of the main library
entry point.
The following section provides an overview of common tasks, and will get you up and running with the library quickly.
Add Vectora to Your Project
Import the vectora library in the [dependencies] section
of your Cargo.toml file:
[dependencies]
vectora = "0.1.3"The examples below assume the following Vector struct import in
your Rust source files:
use vectora::Vector;Initialization
A Vector can have mutable values, but it cannot grow in length. The
dimension length is fixed at instantiation and all fields are initialized
at instantiation.
Zero Vector
Use the Vector::zero method to initialize a Vector with zero values
of the respective numeric type:
let v_zero_int: Vector<i32, 3> = Vector::zero();
let v_zero_float: Vector<f64, 2> = Vector::zero();With Predefined Data in Other Types
The recommended approach is to use Vector::from with an array of
ordered data when possible:
// example three dimensional f64 Vector
let v1: Vector<f64, 3> = Vector::from([1.0, 2.0, 3.0]);
// example two dimensional i32 Vector
let v2: Vector<i32, 2> = Vector::from([4, -5]);
// with a library type alias
use vectora::types::vector::Vector3dF64;
let v3: Vector3dF64 = Vector::from([1.0, 2.0, 3.0]);or use one of the alternate initialization approaches with data
in iterator, array, slice, or Vec types:
// from an iterator over an array or Vec with collect
let v4: Vector<i32, 3> = [1, 2, 3].into_iter().collect();
let v5: Vector<f64, 2> = vec![1.0, 2.0].into_iter().collect();
// from a slice with try_from
let arr = [1, 2, 3];
let vec = vec![1.0, 2.0, 3.0];
let v6: Vector<i32, 3> = Vector::try_from(&arr[..]).unwrap();
let v7: Vector<f64, 3> = Vector::try_from(&vec[..]).unwrap();
// from a Vec with try_from
let vec = vec![1, 2, 3];
let v8: Vector<i32, 3> = Vector::try_from(&vec).unwrap();Please see the API docs for the approach to overflows and underflows with the
FromIterator
implementation that supports the collect approach.
Access and Assignment with Indexing
Use zero-based indices for access and assignment:
Access
let v1: Vector<f64, 3> = Vector::from([1.0, 2.0, 3.0]);
let x = v1[0];
let y = v1[1];
let z = v1[2];Attempts to access items beyond the length of the Vector panics:
// panics!
let _ = v1[10];Assignment
let mut v1_m: Vector<f64, 3> = Vector::from([1.0, 2.0, 3.0]);
v1_m[0] = 10.0;
v1_m[1] = 20.0;
v1_m[2] = 30.0;Attempts to assign to items beyond the length of the Vector panics:
// panics!
v1_m[10] = 100.0;See the Vector::get and Vector::get_mut method documentation
for getters that perform bounds checks and do not panic.
Slicing
Coerce to a read-only slice of the Vector:
let v = Vector::<i32, 3>::from([1, 2, 3]);
let v_slice = &v[0..2];
assert_eq!(v_slice, [1, 2]);Partial Equivalence Testing
Partial equivalence comparison support is available for integer and
float numeric types with the == operator.
Integer types
let a: Vector<i32, 3> = Vector::from([10, 50, 100]);
let b: Vector<i32, 3> = Vector::from([5*2, 25+25, 10_i32.pow(2)]);
assert!(a == b);Float types
Float comparisons use the approx crate relative epsilon float equivalence relation implementation.
Why handle these differently than the standard library implementation?
Some floating point numbers can be defined as different due to floating point precision:
// panics!
assert!(0.15_f64 + 0.15_f64 == 0.1_f64 + 0.2_f64);You likely mean for these float sums to compare as approximately equivalent.
With the Vector type, they do:
let a: Vector<f64, 1> = Vector::from([0.15 + 0.15]);
let b: Vector<f64, 1> = Vector::from([0.1 + 0.2]);
assert!(a == b);assert_eq! and assert_ne! macro assertions use the same
partial equivalence testing approach as you’ll note throughout these docs.
You can implement the same equivalence relation approach for float types that
are not contained in a Vector with the approx crate
relative_eq!, relative_ne!, assert_relative_eq!, and assert_relative_ne!
macros.
Iteration and Loops
Over immutable scalar component references
let v: Vector<i32, 3> = Vector::from([-1, 2, 3]);
let mut iter = v.iter();
assert_eq!(iter.next(), Some(&-1));
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), Some(&3));
assert_eq!(iter.next(), None);Syntax for a loop over this type:
for x in &v {
// do things
}Over mutable scalar component references
let mut v: Vector<i32, 3> = Vector::from([-1, 2, 3]);
let mut iter = v.iter_mut();
assert_eq!(iter.next(), Some(&mut -1));
assert_eq!(iter.next(), Some(&mut 2));
assert_eq!(iter.next(), Some(&mut 3));
assert_eq!(iter.next(), None);Syntax for a loop over this type:
for x in &mut v {
// do things
}Over mutable scalar component values
let v: Vector<i32, 3> = Vector::from([-1, 2, 3]);
let mut iter = v.into_iter();
assert_eq!(iter.next(), Some(-1));
assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), Some(3));
assert_eq!(iter.next(), None);Syntax for a loop over this type:
for x in v {
// do things
}Vector Arithmetic
Use operator overloads for vector arithmetic:
Vector Addition
let v1: Vector<f64, 3> = Vector::from([1.0, 2.0, 3.0]);
let v2: Vector<f64, 3> = Vector::from([4.0, 5.0, 6.0]);
let v3 = v1 + v2;
assert_eq!(v3, Vector::from([5.0, 7.0, 9.0]));Vector Subtraction
let v1: Vector<f64, 3> = Vector::from([1.0, 2.0, 3.0]);
let v2: Vector<f64, 3> = Vector::from([4.0, 5.0, 6.0]);
let v3 = v2 - v1;
assert_eq!(v3, Vector::from([3.0, 3.0, 3.0]));Scalar Multiplication
let v1: Vector<f64, 3> = Vector::from([1.0, 2.0, 3.0]);
let v2: Vector<f64, 3> = Vector::from([4.0, 5.0, 6.0]);
let v3 = v1 * 10.0;
let v4 = v2 * -1.0;
assert_eq!(v3, Vector::from([10.0, 20.0, 30.0]));
assert_eq!(v4, Vector::from([-4.0, -5.0, -6.0]));Please note that overflowing integer arithmetic uses the default Rust standard library approach of panics in debug builds and twos complement wrapping in release builds. You will not encounter undefined behavior with either build type, but this approach may not be what you want. Avoid these operator overloads if your use case requires support for integer overflows/underflows and you prefer to handle it differently.
Methods for Vector Operations
Method support is available for other common vector calculations. Examples of some commonly used operations are shown below:
Dot product
use approx::assert_relative_eq;
let v1: Vector<f64, 3> = Vector::from([1.0, 2.0, 3.0]);
let v2: Vector<f64, 3> = Vector::from([4.0, 5.0, 6.0]);
let dot_prod = v1.dot(&v2);
assert_relative_eq!(dot_prod, 32.0);[ API docs ]
Vector Magnitude
use approx::assert_relative_eq;
let v1: Vector<f64, 3> = Vector::from([1.0, 2.0, 3.0]);
let m = v1.magnitude();
assert_relative_eq!(m, 3.7416573867739413);[ API docs ]
Vector Distance
use approx::assert_relative_eq;
let v1: Vector<f64, 2> = Vector::from([2.0, 2.0]);
let v2: Vector<f64, 2> = Vector::from([4.0, 4.0]);
assert_relative_eq!(v1.distance(&v2), 8.0_f64.sqrt());
assert_relative_eq!(v1.distance(&v1), 0.0_f64);[ API docs ]
Opposite Vector
use approx::assert_relative_eq;
let v: Vector<f64, 3> = Vector::from([2.0, 2.0, 2.0]);
assert_eq!(v.opposite(), Vector::from([-2.0, -2.0, -2.0]));
assert_relative_eq!(v.opposite().magnitude(), v.magnitude());[ API docs ]
Normalization
use approx::assert_relative_eq;
let v1: Vector<f64, 3> = Vector::from([1.0, 2.0, 3.0]);
let unit_vector = v1.normalize();
assert_relative_eq!(unit_vector.magnitude(), 1.0);[ API docs ]
Linear Interpolation
let v1: Vector<f64, 3> = Vector::from([1.0, 2.0, 3.0]);
let v2: Vector<f64, 3> = Vector::from([4.0, 5.0, 6.0]);
let v3 = v1.lerp(&v2, 0.5).unwrap();
assert_eq!(v3, Vector::from([2.5, 3.5, 4.5]));[ API docs ]
Closure Mapping
let v1: Vector<f64, 3> = Vector::from([-1.0, 2.0, 3.0]);
let v3 = v1.map_closure(|x| x.powi(2));
assert_eq!(v3, Vector::from([1.0, 4.0, 9.0]));[ API docs ]
Function Mapping
let v1: Vector<f64, 3> = Vector::from([-1.0, 2.0, 3.0]);
fn square(x: f64) -> f64 {
x.powi(2)
}
let v3 = v1.map_fn(square);
assert_eq!(v3, Vector::from([1.0, 4.0, 9.0]));[ API docs ]
Many of these methods have mutable alternates that edit the Vector data
in place instead of allocating a new Vector. The mutable methods are
prefixed with mut_*.
See the Vector method implementations docs
for the complete list of supported methods and additional examples.
Working with Rust Standard Library Types
Casting a Vector to a number of commonly used Rust standard library data collection
types is straightforward. Note that some of these type casts support mutable Vector
owned data references, allowing you to use standard library type operations to change the
Vector data.
array Representations
Immutable:
let v: Vector<i32, 3> = Vector::from([-1, 2, 3]);
assert_eq!(v.as_array(), &[-1, 2, 3]);
assert_eq!(v.to_array(), [-1, 2, 3]);Mutable:
let mut v: Vector<i32, 3> = Vector::from([-1, 2, 3]);
let m_arr = v.as_mut_array();
assert_eq!(m_arr, &mut [-1, 2, 3]);
m_arr[0] = -10;
assert_eq!(m_arr, &mut [-10, 2, 3]);
assert_eq!(v, Vector::from([-10, 2, 3]));slice Representations
Immutable:
let v: Vector<i32, 3> = Vector::from([-1, 2, 3]);
assert_eq!(v.as_slice(), &[-1, 2, 3][..]);Mutable:
let mut v: Vector<i32, 3> = Vector::from([-1, 2, 3]);
let m_sli = v.as_mut_slice();
assert_eq!(m_sli, &mut [-1, 2, 3][..]);
m_sli[0] = -10;
assert_eq!(m_sli, &mut [-10, 2, 3]);
assert_eq!(v, Vector::from([-10, 2, 3]));Vec Representations
This always allocates a new Vec with copied data and
does not support mutation of the original Vector data.
let v: Vector<i32, 3> = Vector::from([-1, 2, 3]);
assert_eq!(v.to_vec(), vec![-1, 2, 3]);See the Initialization section for details on how
to instantiate a Vector from a standard library Vec type.
Re-exports
pub use types::vector::Vector;