#[cfg(any(feature = "cuda", feature = "avx2", feature = "vulkan"))]
use std::sync::Arc;
use zer_core::{
comparison::{ComparisonBatch, ComparisonVector},
record::Record,
record_pool::RecordPool,
schema::Schema,
scoring::{ModelParams, ScoredPair},
traits::{Comparator as ComparatorTrait, Result as ZerResult, Scorer as ScorerTrait},
};
enum BackendInner {
Cpu,
#[cfg(any(feature = "cuda", feature = "avx2", feature = "vulkan"))]
Gpu(Arc<zer_compute::DeviceBackend>),
}
pub struct Backend {
inner: BackendInner,
name: &'static str,
}
impl Backend {
pub fn auto_detect() -> Self {
match std::env::args()
.find_map(|a| a.strip_prefix("--target=").map(str::to_owned))
.as_deref()
{
Some(t) => Self::from_target(t),
None => Self::cpu(),
}
}
pub fn cpu() -> Self {
Self { inner: BackendInner::Cpu, name: "cpu" }
}
pub fn from_target(target: &str) -> Self {
if target == "cpu" {
return Self::cpu();
}
#[cfg(any(feature = "cuda", feature = "avx2", feature = "vulkan"))]
{
let pref = match target {
"auto" => zer_compute::BackendPreference::Auto,
"cuda" => zer_compute::BackendPreference::Cuda,
"vulkan" => zer_compute::BackendPreference::Vulkan,
"avx2" => zer_compute::BackendPreference::Avx2,
other => {
tracing::error!(target = other, "unknown --target; valid: auto, cpu, avx2, cuda, vulkan");
std::process::exit(1);
}
};
return match zer_compute::DeviceBackend::from_preference(pref) {
Ok(dev) => {
let name = dev.name();
if dev.is_accelerated() {
Self { inner: BackendInner::Gpu(Arc::new(dev)), name }
} else {
Self { inner: BackendInner::Cpu, name: "cpu" }
}
}
Err(e) => {
tracing::error!(target, error = %e, "--target unavailable");
std::process::exit(1);
}
};
}
#[allow(unreachable_code)]
{
if target == "auto" {
return Self::cpu();
}
tracing::error!(target, "unknown --target; valid values when built without GPU features: auto, cpu");
std::process::exit(1);
}
}
pub fn name(&self) -> &'static str {
self.name
}
pub fn is_gpu(&self) -> bool {
!matches!(self.inner, BackendInner::Cpu)
}
}
enum ComparatorInner {
Cpu(zer_compare::FieldComparator),
#[cfg(any(feature = "cuda", feature = "avx2", feature = "vulkan"))]
Gpu(zer_compute::DeviceComparator),
}
pub struct Comparator {
inner: ComparatorInner,
}
impl Comparator {
pub fn from_cpu(fc: zer_compare::FieldComparator) -> Self {
Self { inner: ComparatorInner::Cpu(fc) }
}
pub fn new(schema: &Schema, backend: &Backend) -> Self {
match &backend.inner {
BackendInner::Cpu => Self {
inner: ComparatorInner::Cpu(
zer_compare::FieldComparator::from_schema(schema),
),
},
#[cfg(any(feature = "cuda", feature = "avx2", feature = "vulkan"))]
BackendInner::Gpu(dev) => Self {
inner: ComparatorInner::Gpu(
zer_compute::DeviceComparator::new(Arc::clone(dev), schema).unwrap(),
),
},
}
}
pub fn backend_name(&self) -> &'static str {
match &self.inner {
ComparatorInner::Cpu(_) => "cpu",
#[cfg(any(feature = "cuda", feature = "avx2", feature = "vulkan"))]
ComparatorInner::Gpu(c) => c.backend_name(),
}
}
pub fn compare_batch_from_pool(
&self,
pool: &RecordPool,
pair_indices: &[(usize, usize)],
schema: &Schema,
) -> ComparisonBatch {
match &self.inner {
ComparatorInner::Cpu(c) => c.compare_batch_from_pool(pool, pair_indices, schema),
#[cfg(any(feature = "cuda", feature = "avx2", feature = "vulkan"))]
ComparatorInner::Gpu(c) => c.compare_batch_from_pool(pool, pair_indices, schema),
}
}
pub fn compare_batch_indexed(
&self,
records: &[Record],
pair_indices: &[(usize, usize)],
schema: &Schema,
) -> ComparisonBatch {
let pool = RecordPool::from_records(records, schema);
self.compare_batch_from_pool(&pool, pair_indices, schema)
}
}
impl ComparatorTrait for Comparator {
fn compare(&self, a: &Record, b: &Record, schema: &Schema) -> ComparisonVector {
match &self.inner {
ComparatorInner::Cpu(c) => c.compare(a, b, schema),
#[cfg(any(feature = "cuda", feature = "avx2", feature = "vulkan"))]
ComparatorInner::Gpu(c) => c.compare(a, b, schema),
}
}
fn compare_batch_from_pool(
&self,
pool: &RecordPool,
indices: &[(usize, usize)],
schema: &Schema,
) -> ComparisonBatch {
self.compare_batch_from_pool(pool, indices, schema)
}
}
enum ScorerInner {
Cpu(zer_compare::FellegiSunterScorer),
#[cfg(any(feature = "cuda", feature = "avx2", feature = "vulkan"))]
Gpu(zer_compute::DeviceScorer),
}
pub struct Scorer {
inner: ScorerInner,
}
impl Scorer {
pub fn new(backend: &Backend) -> Self {
match &backend.inner {
BackendInner::Cpu => Self {
inner: ScorerInner::Cpu(zer_compare::FellegiSunterScorer),
},
#[cfg(any(feature = "cuda", feature = "avx2", feature = "vulkan"))]
BackendInner::Gpu(dev) => Self {
inner: ScorerInner::Gpu(zer_compute::DeviceScorer::new(Arc::clone(dev))),
},
}
}
pub fn backend_name(&self) -> &'static str {
match &self.inner {
ScorerInner::Cpu(_) => "cpu",
#[cfg(any(feature = "cuda", feature = "avx2", feature = "vulkan"))]
ScorerInner::Gpu(s) => s.backend_name(),
}
}
}
impl ScorerTrait for Scorer {
fn score(&self, vector: &ComparisonVector, params: &ModelParams) -> ScoredPair {
match &self.inner {
ScorerInner::Cpu(s) => s.score(vector, params),
#[cfg(any(feature = "cuda", feature = "avx2", feature = "vulkan"))]
ScorerInner::Gpu(s) => s.score(vector, params),
}
}
fn score_batch(
&self,
batch: &ComparisonBatch,
params: &ModelParams,
) -> Vec<ScoredPair> {
match &self.inner {
ScorerInner::Cpu(s) => s.score_batch(batch, params),
#[cfg(any(feature = "cuda", feature = "avx2", feature = "vulkan"))]
ScorerInner::Gpu(s) => s.score_batch(batch, params),
}
}
fn estimate_params(
&self,
batch: &ComparisonBatch,
init: Option<ModelParams>,
max_iter: usize,
) -> ZerResult<ModelParams> {
match &self.inner {
ScorerInner::Cpu(s) => s.estimate_params(batch, init, max_iter),
#[cfg(any(feature = "cuda", feature = "avx2", feature = "vulkan"))]
ScorerInner::Gpu(s) => s.estimate_params(batch, init, max_iter),
}
}
}
#[cfg(any(feature = "cuda", feature = "avx2", feature = "vulkan"))]
pub mod kernel {
pub use zer_compute::{
backend::DeviceBackend,
error::GpuError,
kernel::{Kernel, KernelDispatch},
};
}
pub use zer_blocking as blocking;
pub use zer_compare as compare;
pub use zer_core as core;
pub use zer_schema as schema;
pub use zer_cluster as cluster;
#[cfg(feature = "pipeline")]
pub use zer_pipeline as pipeline;
#[cfg(any(feature = "cuda", feature = "avx2", feature = "vulkan"))]
pub use zer_compute as compute;
pub mod prelude {
pub use crate::{Backend, Comparator, Scorer};
pub use zer_core::{
comparison::{ComparisonBatch, ComparisonLevel, ComparisonVector},
entity::{Entity, EntityId, EntityMember, ResolutionMethod},
error::ZerError,
record::{FieldValue, Record, RecordId},
record_pool::RecordPool,
schema::{FieldKind, Schema, SchemaBuilder},
scoring::{MatchBand, ModelParams, ScoredPair},
traits::{
BlockIndex, Blocker, Clusterer, EntityStore, Judge, JudgeVerdict, RecordStore,
Comparator as ComparatorTrait,
Scorer as ScorerTrait,
},
VecRecordStore,
};
pub use zer_blocking::{
BlockerFactory, CompositeBlocker, InvertedIndex, SchemaCategory,
keys::{
AddressInitialKey, AliasPhoneticKey, CameraTimeWindowKey, DateFragmentKey,
DateGranularity, DocumentDigitSuffixKey, DocumentSuffixKey, ExactFieldKey,
FuzzyYearKey, GeoGridKey, LicensePlateNormKey, PhoneticAlgo, PhoneticNameDobKey,
PlateOCRFuzzyKey, SuffixKey, TransliteratedPhoneticKey,
},
};
pub use zer_compare::{
FellegiSunterScorer, FieldComparator, LevelThresholds, SimilarityFn,
JaroWinklerSimilarity, PhoneticEqualitySimilarity, TokenOverlapSimilarity,
AddressTokenOverlap, StreetNumberEditDistance,
};
pub use zer_schema::{ModelArtifact, SchemaFingerprint, SchemaInferrer, SchemaRegistry, StartupMode};
pub use zer_cluster::{ClusterConfig, ConnectedComponentsClusterer, ZalEntityStore};
#[cfg(feature = "pipeline")]
pub use zer_pipeline::{
BatchReport, ClusterIter, ClusterView, IngestResult, Ingester,
Pipeline, PipelineBuilder, PipelineConfig, RateConfig,
};
}