Skip to content

Commit

Permalink
Make it work with latest published simd_aligned.
Browse files Browse the repository at this point in the history
  • Loading branch information
ralfbiedert committed Dec 14, 2024
1 parent 3cf3e5c commit e613152
Show file tree
Hide file tree
Showing 11 changed files with 115 additions and 78 deletions.
46 changes: 39 additions & 7 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,50 @@ name: Rust

on:
push:
branches: [ master ]
branches: [ '**' ]
pull_request:
branches: [ master ]
branches: [ '**' ]

env:
CARGO_TERM_COLOR: always

jobs:
test:
name: Test
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target:
- x86_64-unknown-linux-gnu
- aarch64-apple-darwin
include:
- target: x86_64-unknown-linux-gnu
runs-on: ubuntu-latest
style: true
test: true
- target: aarch64-apple-darwin
runs-on: macos-latest
test: true
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@nightly
- run: cargo test
- name: Checkout
uses: actions/checkout@v4
with:
lfs: true
- name: Rust - Install
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt,clippy
targets: ${{ matrix.target }}
- name: Rust - Version
run: rustc -Vv
- name: Rust - Build
run: cargo build --verbose --target=${{ matrix.target }} --tests --all-features
- name: Rust - Style
if: matrix.style == true
run: cargo fmt --check
- name: Rust - Clippy
if: matrix.style == true
run: cargo clippy -- -D warnings
- name: Rust - Test
if: matrix.test == true
run: cargo test --verbose --target=${{ matrix.target }} --all-features -- --test-threads=1 --nocapture
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ path = "src/lib.rs"
crate-type = ["rlib"]

[dependencies]
#simd_aligned = "0.5"
simd_aligned = { path = "../simd_aligned" }
simd_aligned = "0.6.1"
#simd_aligned = { path = "../simd_aligned" }

[dev-dependencies]
rand = "0.8.5"
Expand Down
26 changes: 14 additions & 12 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! [![Latest Version]][crates.io]
//! [![Rust](https://github.com/ralfbiedert/ffsvm-rust/actions/workflows/rust.yml/badge.svg?branch=master)](https://github.com/ralfbiedert/ffsvm-rust/actions/workflows/rust.yml)
//! [![deps.svg]][deps]
//! [![docs]][docs.rs]
//! ![MIT]
//! [![crates.io-badge]][crates.io-url]
//! [![docs.rs-badge]][docs.rs-url]
//! ![license-badge]
//! [![rust-version-badge]][rust-version-url]
//! [![rust-build-badge]][rust-build-url]
//!
//! # In One Sentence
//!
Expand Down Expand Up @@ -80,13 +80,15 @@
//!
//! [See here for details.](https://github.com/ralfbiedert/ffsvm-rust/blob/master/docs/FAQ.md)
//!
//! [Latest Version]: https://img.shields.io/crates/v/ffsvm.svg
//! [crates.io]: https://crates.io/crates/ffsvm
//! [MIT]: https://img.shields.io/badge/license-MIT-blue.svg
//! [docs]: https://docs.rs/ffsvm/badge.svg
//! [docs.rs]: https://docs.rs/ffsvm/
//! [deps]: https://deps.rs/repo/github/ralfbiedert/ffsvm-rust
//! [deps.svg]: https://deps.rs/repo/github/ralfbiedert/ffsvm-rust/status.svg
//! [crates.io-badge]: https://img.shields.io/crates/v/ffsvm.svg
//! [crates.io-url]: https://crates.io/crates/ffsvm
//! [license-badge]: https://img.shields.io/badge/license-BSD2-blue.svg
//! [docs.rs-badge]: https://docs.rs/ffsvm/badge.svg
//! [docs.rs-url]: https://docs.rs/ffsvm/
//! [rust-version-badge]: https://img.shields.io/badge/rust-1.83%2B-blue.svg?maxAge=3600
//! [rust-version-url]: https://github.com/ralfbiedert/ffsvm
//! [rust-build-badge]: https://github.com/ralfbiedert/ffsvm/actions/workflows/rust.yml/badge.svg
//! [rust-build-url]: https://github.com/ralfbiedert/ffsvm/actions/workflows/rust.yml
#![warn(clippy::all)] // Enable ALL the warnings ...
#![warn(clippy::nursery)]
#![warn(clippy::pedantic)]
Expand Down
18 changes: 10 additions & 8 deletions src/svm/class.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use crate::{sparse::SparseMatrix};
use simd_aligned::{f32x8, f64x4, MatD, Rows};
use crate::sparse::SparseMatrix;
use simd_aligned::{
arch::{f32x8, f64x4},
MatSimd, Rows,
};

/// Represents one class of the SVM model.
#[derive(Clone, Debug)]
Expand All @@ -10,21 +13,20 @@ pub(crate) struct Class<M32> {

// /// The number of support vectors in this class
// pub(crate) num_support_vectors: usize,

/// Coefficients between this class and n-1 other classes.
pub(crate) coefficients: MatD<f64x4, Rows>,
pub(crate) coefficients: MatSimd<f64x4, Rows>,

/// All support vectors in this class.
pub(crate) support_vectors: M32,
}

impl Class<MatD<f32x8, Rows>> {
impl Class<MatSimd<f32x8, Rows>> {
/// Creates a new class with the given parameters.
pub fn with_parameters(classes: usize, support_vectors: usize, attributes: usize, label: i32) -> Self {
Self {
label,
coefficients: MatD::with_dimension(classes - 1, support_vectors),
support_vectors: MatD::with_dimension(support_vectors, attributes),
coefficients: MatSimd::with_dimension(classes - 1, support_vectors),
support_vectors: MatSimd::with_dimension(support_vectors, attributes),
}
}
}
Expand All @@ -34,7 +36,7 @@ impl Class<SparseMatrix<f32>> {
pub fn with_parameters(classes: usize, support_vectors: usize, _attributes: usize, label: i32) -> Self {
Self {
label,
coefficients: MatD::with_dimension(classes - 1, support_vectors),
coefficients: MatSimd::with_dimension(classes - 1, support_vectors),
support_vectors: SparseMatrix::with(support_vectors),
}
}
Expand Down
25 changes: 13 additions & 12 deletions src/svm/core/dense.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
use simd_aligned::{f32x8, traits::Simd, MatD, Rows, VecD};
use std::convert::TryFrom;

use crate::{
errors::Error,
parser::ModelFile,
Expand All @@ -14,6 +11,8 @@ use crate::{
util::{find_max_index, set_all, sigmoid_predict},
vectors::Triangular,
};
use simd_aligned::{arch::f32x8, traits::Simd, MatSimd, Rows, VecSimd};
use std::convert::TryFrom;

/// An SVM using [SIMD](https://en.wikipedia.org/wiki/SIMD) intrinsics optimized for speed.
///
Expand Down Expand Up @@ -44,7 +43,7 @@ pub struct DenseSVM {
pub(crate) kernel: Box<dyn KernelDense>,

/// All classes
pub(crate) classes: Vec<Class<MatD<f32x8, Rows>>>,
pub(crate) classes: Vec<Class<MatSimd<f32x8, Rows>>>,
}

impl DenseSVM {
Expand Down Expand Up @@ -94,7 +93,7 @@ impl DenseSVM {
}

/// Computes the kernel values for this problem
pub(crate) fn compute_kernel_values(&self, problem: &mut FeatureVector<VecD<f32x8>>) {
pub(crate) fn compute_kernel_values(&self, problem: &mut FeatureVector<VecSimd<f32x8>>) {
// Get current problem and decision values array
let features = &problem.features;
let kernel_values = &mut problem.kernel_values;
Expand All @@ -112,13 +111,15 @@ impl DenseSVM {
// based on Method 2 from the paper "Probability Estimates for Multi-class
// Classification by Pairwise Coupling", Journal of Machine Learning Research 5 (2004) 975-1005,
// by Ting-Fan Wu, Chih-Jen Lin and Ruby C. Weng.
pub(crate) fn compute_multiclass_probabilities(&self, problem: &mut FeatureVector<VecD<f32x8>>) -> Result<(), Error> { compute_multiclass_probabilities_impl!(self, problem) }
pub(crate) fn compute_multiclass_probabilities(&self, problem: &mut FeatureVector<VecSimd<f32x8>>) -> Result<(), Error> {
compute_multiclass_probabilities_impl!(self, problem)
}

/// Based on kernel values, computes the decision values for this problem.
pub(crate) fn compute_classification_values(&self, problem: &mut FeatureVector<VecD<f32x8>>) { compute_classification_values_impl!(self, problem) }
pub(crate) fn compute_classification_values(&self, problem: &mut FeatureVector<VecSimd<f32x8>>) { compute_classification_values_impl!(self, problem) }

/// Based on kernel values, computes the decision values for this problem.
pub(crate) fn compute_regression_values(&self, problem: &mut FeatureVector<VecD<f32x8>>) {
pub(crate) fn compute_regression_values(&self, problem: &mut FeatureVector<VecSimd<f32x8>>) {
let class = &self.classes[0];
let coef = class.coefficients.row(0);
let kvalues = problem.kernel_values.row(0);
Expand All @@ -137,9 +138,9 @@ impl DenseSVM {
pub fn classes(&self) -> usize { self.classes.len() }
}

impl Predict<VecD<f32x8>> for DenseSVM {
impl Predict<VecSimd<f32x8>> for DenseSVM {
// Predict the value for one problem.
fn predict_value(&self, fv: &mut FeatureVector<VecD<f32x8>>) -> Result<(), Error> {
fn predict_value(&self, fv: &mut FeatureVector<VecSimd<f32x8>>) -> Result<(), Error> {
match self.svm_type {
SVMType::CSvc | SVMType::NuSvc => {
// Compute kernel, decision values and eventually the label
Expand All @@ -160,7 +161,7 @@ impl Predict<VecD<f32x8>> for DenseSVM {
}
}

fn predict_probability(&self, problem: &mut FeatureVector<VecD<f32x8>>) -> Result<(), Error> { predict_probability_impl!(self, problem) }
fn predict_probability(&self, problem: &mut FeatureVector<VecSimd<f32x8>>) -> Result<(), Error> { predict_probability_impl!(self, problem) }
}

impl<'a> TryFrom<&'a str> for DenseSVM {
Expand All @@ -176,7 +177,7 @@ impl<'a, 'b> TryFrom<&'a ModelFile<'b>> for DenseSVM {
type Error = Error;

fn try_from(raw_model: &'a ModelFile<'_>) -> Result<Self, Error> {
let (mut svm, nr_sv) = prepare_svm!(raw_model, dyn KernelDense, MatD<f32x8, Rows>, Self);
let (mut svm, nr_sv) = prepare_svm!(raw_model, dyn KernelDense, MatSimd<f32x8, Rows>, Self);

let vectors = &raw_model.vectors();

Expand Down
35 changes: 19 additions & 16 deletions src/svm/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ use crate::{
vectors::Triangular,
};

use simd_aligned::{f32x8, f64x4, MatD, Rows, VecD};
use simd_aligned::{
arch::{f32x8, f64x4},
MatSimd, Rows, VecSimd,
};

/// Feature vectors produced for [`DenseSVM`]s.
///
/// Also see [`FeatureVector`] for more methods for this type.
pub type DenseFeatures = FeatureVector<VecD<f32x8>>;
pub type DenseFeatures = FeatureVector<VecSimd<f32x8>>;

/// Feature vectors produced for [`SparseSVM`]s.
///
Expand Down Expand Up @@ -69,7 +72,7 @@ pub struct FeatureVector<T> {
pub(crate) features: T,

/// KernelDense values. A vector for each class.
pub(crate) kernel_values: MatD<f64x4, Rows>,
pub(crate) kernel_values: MatSimd<f64x4, Rows>,

/// All votes for a given class label.
pub(crate) vote: Vec<u32>,
Expand All @@ -78,17 +81,17 @@ pub struct FeatureVector<T> {
pub(crate) decision_values: Triangular<f64>,

/// Pairwise probabilities
pub(crate) pairwise: MatD<f64x4, Rows>,
pub(crate) pairwise: MatSimd<f64x4, Rows>,

/// Needed for multi-class probability estimates replicating libSVM.
pub(crate) q: MatD<f64x4, Rows>,
pub(crate) q: MatSimd<f64x4, Rows>,

/// Needed for multi-class probability estimates replicating libSVM.
pub(crate) qp: Vec<f64>,

/// Probability estimates that will be updated after this problem was processed
/// by `predict_probability`.
pub(crate) probabilities: VecD<f64x4>,
pub(crate) probabilities: VecSimd<f64x4>,

/// Computed label that will be updated after this problem was processed.
pub(crate) result: Label,
Expand All @@ -102,7 +105,7 @@ impl<T> FeatureVector<T> {
pub fn probabilities(&self) -> &[f64] { self.probabilities.flat() }
}

impl FeatureVector<VecD<f32x8>> {
impl FeatureVector<VecSimd<f32x8>> {
/// Returns the features. You must set them first and classify the problem before you can get a solution.
pub fn features(&mut self) -> &mut [f32] { self.features.flat_mut() }
}
Expand All @@ -116,14 +119,14 @@ impl DenseFeatures {
/// Creates a new problem with the given parameters.
pub(crate) fn with_dimension(total_sv: usize, num_classes: usize, num_attributes: usize) -> Self {
Self {
features: VecD::with(0.0, num_attributes),
kernel_values: MatD::with_dimension(num_classes, total_sv),
pairwise: MatD::with_dimension(num_classes, num_classes),
q: MatD::with_dimension(num_classes, num_classes),
features: VecSimd::with(0.0, num_attributes),
kernel_values: MatSimd::with_dimension(num_classes, total_sv),
pairwise: MatSimd::with_dimension(num_classes, num_classes),
q: MatSimd::with_dimension(num_classes, num_classes),
qp: vec![Default::default(); num_classes],
decision_values: Triangular::with_dimension(num_classes, Default::default()),
vote: vec![Default::default(); num_classes],
probabilities: VecD::with(0.0, num_classes),
probabilities: VecSimd::with(0.0, num_classes),
result: Label::None,
}
}
Expand All @@ -137,13 +140,13 @@ impl SparseFeatures {
pub(crate) fn with_dimension(total_sv: usize, num_classes: usize, _num_attributes: usize) -> Self {
Self {
features: SparseVector::new(),
kernel_values: MatD::with_dimension(num_classes, total_sv),
pairwise: MatD::with_dimension(num_classes, num_classes),
q: MatD::with_dimension(num_classes, num_classes),
kernel_values: MatSimd::with_dimension(num_classes, total_sv),
pairwise: MatSimd::with_dimension(num_classes, num_classes),
q: MatSimd::with_dimension(num_classes, num_classes),
qp: vec![Default::default(); num_classes],
decision_values: Triangular::with_dimension(num_classes, Default::default()),
vote: vec![Default::default(); num_classes],
probabilities: VecD::with(0.0, num_classes),
probabilities: VecSimd::with(0.0, num_classes),
result: Label::None,
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/svm/kernel/linear.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ use crate::{
sparse::{SparseMatrix, SparseVector},
};

use simd_aligned::{f32x8, MatD, Rows, VecD, traits::Simd};
use simd_aligned::{arch::f32x8, traits::Simd, MatSimd, Rows, VecSimd};

#[derive(Copy, Clone, Debug, Default)]
#[doc(hidden)]
pub struct Linear {}

impl KernelDense for Linear {
fn compute(&self, vectors: &MatD<f32x8, Rows>, feature: &VecD<f32x8>, output: &mut [f64]) {
fn compute(&self, vectors: &MatSimd<f32x8, Rows>, feature: &VecSimd<f32x8>, output: &mut [f64]) {
for (i, sv) in vectors.row_iter().enumerate() {
let mut sum = f32x8::splat(0.0);
let feature: &[f32x8] = feature;
Expand Down
9 changes: 3 additions & 6 deletions src/svm/kernel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,17 @@ mod poly;
mod rbf;
mod sigmoid;

use crate::{
sparse::{SparseMatrix, SparseVector},
};
use simd_aligned::{f32x8, MatD, Rows, VecD};

pub use self::{linear::*, poly::*, rbf::*, sigmoid::*};
use crate::sparse::{SparseMatrix, SparseVector};
use simd_aligned::{arch::f32x8, MatSimd, Rows, VecSimd};

/// Base trait for kernels
#[doc(hidden)]
pub trait KernelDense
where
Self: Send + Sync,
{
fn compute(&self, vectors: &MatD<f32x8, Rows>, feature: &VecD<f32x8>, output: &mut [f64]);
fn compute(&self, vectors: &MatSimd<f32x8, Rows>, feature: &VecSimd<f32x8>, output: &mut [f64]);
}

/// Base trait for kernels
Expand Down
4 changes: 2 additions & 2 deletions src/svm/kernel/poly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
sparse::{SparseMatrix, SparseVector},
};

use simd_aligned::{f32x8, traits::Simd, MatD, Rows, VecD};
use simd_aligned::{arch::f32x8, traits::Simd, MatSimd, Rows, VecSimd};

#[derive(Copy, Clone, Debug, Default)]
#[doc(hidden)]
Expand All @@ -18,7 +18,7 @@ pub struct Poly {
}

impl KernelDense for Poly {
fn compute(&self, vectors: &MatD<f32x8, Rows>, feature: &VecD<f32x8>, output: &mut [f64]) {
fn compute(&self, vectors: &MatSimd<f32x8, Rows>, feature: &VecSimd<f32x8>, output: &mut [f64]) {
for (i, sv) in vectors.row_iter().enumerate() {
let mut sum = f32x8::splat(0.0);
let feature: &[f32x8] = feature;
Expand Down
Loading

0 comments on commit e613152

Please sign in to comment.