mirror of
https://github.com/ruvnet/RuVector.git
synced 2026-05-23 04:27:11 +00:00
style: cargo fmt on v0.3 module source files
Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
parent
5712eb9269
commit
aa009dff83
11 changed files with 143 additions and 121 deletions
|
|
@ -375,7 +375,9 @@ pub fn ruvector_linear_attention(
|
|||
// Linear attention: phi(q)^T * (sum phi(k_i) * v_i^T) / (phi(q)^T * sum phi(k_i))
|
||||
// Using ELU+1 as kernel feature map
|
||||
let phi = |x: &[f32]| -> Vec<f32> {
|
||||
x.iter().map(|&v| if v >= 0.0 { v + 1.0 } else { v.exp() }).collect()
|
||||
x.iter()
|
||||
.map(|&v| if v >= 0.0 { v + 1.0 } else { v.exp() })
|
||||
.collect()
|
||||
};
|
||||
|
||||
let phi_q = phi(&query);
|
||||
|
|
@ -480,7 +482,13 @@ pub fn ruvector_sliding_window_attention(
|
|||
|
||||
// Softmax
|
||||
let max_score = scores.iter().copied().fold(f32::NEG_INFINITY, f32::max);
|
||||
let exp_sum: f32 = scores.iter_mut().map(|s| { *s = (*s - max_score).exp(); *s }).sum();
|
||||
let exp_sum: f32 = scores
|
||||
.iter_mut()
|
||||
.map(|s| {
|
||||
*s = (*s - max_score).exp();
|
||||
*s
|
||||
})
|
||||
.sum();
|
||||
if exp_sum > 0.0 {
|
||||
for s in &mut scores {
|
||||
*s /= exp_sum;
|
||||
|
|
@ -612,7 +620,10 @@ pub fn ruvector_sparse_attention(
|
|||
let top = &scored[..k];
|
||||
|
||||
// Softmax on top-k scores
|
||||
let max_s = top.iter().map(|(_, s)| *s).fold(f32::NEG_INFINITY, f32::max);
|
||||
let max_s = top
|
||||
.iter()
|
||||
.map(|(_, s)| *s)
|
||||
.fold(f32::NEG_INFINITY, f32::max);
|
||||
let exps: Vec<f32> = top.iter().map(|(_, s)| (s - max_s).exp()).collect();
|
||||
let sum: f32 = exps.iter().sum();
|
||||
|
||||
|
|
@ -704,7 +715,10 @@ pub fn ruvector_moe_attention(
|
|||
|
||||
// Softmax on top-k expert scores
|
||||
let top_experts = &expert_scores[..k.min(expert_scores.len())];
|
||||
let max_s = top_experts.iter().map(|(_, s)| *s).fold(f32::NEG_INFINITY, f32::max);
|
||||
let max_s = top_experts
|
||||
.iter()
|
||||
.map(|(_, s)| *s)
|
||||
.fold(f32::NEG_INFINITY, f32::max);
|
||||
let exps: Vec<f32> = top_experts.iter().map(|(_, s)| (s - max_s).exp()).collect();
|
||||
let sum: f32 = exps.iter().sum();
|
||||
|
||||
|
|
@ -803,7 +817,13 @@ pub fn ruvector_hyperbolic_attention(
|
|||
|
||||
// Softmax
|
||||
let max_s = scores.iter().copied().fold(f32::NEG_INFINITY, f32::max);
|
||||
let exp_sum: f32 = scores.iter_mut().map(|s| { *s = (*s - max_s).exp(); *s }).sum();
|
||||
let exp_sum: f32 = scores
|
||||
.iter_mut()
|
||||
.map(|s| {
|
||||
*s = (*s - max_s).exp();
|
||||
*s
|
||||
})
|
||||
.sum();
|
||||
if exp_sum > 0.0 {
|
||||
for s in &mut scores {
|
||||
*s /= exp_sum;
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
pub mod operators;
|
||||
|
||||
use dashmap::DashMap;
|
||||
use std::sync::Arc;
|
||||
use parking_lot::RwLock;
|
||||
use ruvector_domain_expansion::DomainExpansionEngine;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Global domain expansion engine state.
|
||||
static DOMAIN_ENGINES: once_cell::sync::Lazy<DashMap<String, Arc<RwLock<DomainExpansionEngine>>>> =
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
use pgrx::prelude::*;
|
||||
use pgrx::JsonB;
|
||||
|
||||
use ruvector_domain_expansion::{DomainId, Solution, ContextBucket, ArmId};
|
||||
use ruvector_domain_expansion::{ArmId, ContextBucket, DomainId, Solution};
|
||||
|
||||
use super::get_or_create_engine;
|
||||
|
||||
|
|
|
|||
|
|
@ -441,9 +441,7 @@ unsafe fn metric_from_index(index: Relation) -> DistanceMetric {
|
|||
return DistanceMetric::Euclidean;
|
||||
}
|
||||
|
||||
let name = std::ffi::CStr::from_ptr(name_ptr)
|
||||
.to_str()
|
||||
.unwrap_or("");
|
||||
let name = std::ffi::CStr::from_ptr(name_ptr).to_str().unwrap_or("");
|
||||
|
||||
let metric = if name.contains("cosine") {
|
||||
DistanceMetric::Cosine
|
||||
|
|
@ -546,7 +544,9 @@ unsafe fn read_vector(
|
|||
if total_read_end > page_size {
|
||||
pgrx::warning!(
|
||||
"HNSW: Vector read would exceed page boundary ({} > {}), skipping block {}",
|
||||
total_read_end, page_size, block
|
||||
total_read_end,
|
||||
page_size,
|
||||
block
|
||||
);
|
||||
pg_sys::UnlockReleaseBuffer(buffer);
|
||||
return None;
|
||||
|
|
@ -608,7 +608,9 @@ unsafe fn read_neighbors(
|
|||
if total_read_end > page_size {
|
||||
pgrx::warning!(
|
||||
"HNSW: Neighbor read would exceed page boundary ({} > {}), skipping block {}",
|
||||
total_read_end, page_size, block
|
||||
total_read_end,
|
||||
page_size,
|
||||
block
|
||||
);
|
||||
pg_sys::UnlockReleaseBuffer(buffer);
|
||||
return Vec::new();
|
||||
|
|
@ -1353,8 +1355,7 @@ unsafe fn connect_node_to_neighbors(
|
|||
// Read current neighbor list for this layer
|
||||
let header_ptr = (page as *const u8).add(size_of::<PageHeaderData>());
|
||||
let node_header = &*(header_ptr as *const HnswNodePageHeader);
|
||||
let existing_count =
|
||||
node_header.neighbor_counts.get(layer).copied().unwrap_or(0) as usize;
|
||||
let existing_count = node_header.neighbor_counts.get(layer).copied().unwrap_or(0) as usize;
|
||||
|
||||
let vector_size = dimensions * size_of::<f32>();
|
||||
let neighbors_base = header_ptr
|
||||
|
|
@ -1587,12 +1588,9 @@ unsafe extern "C" fn hnsw_beginscan(
|
|||
// RelationGetIndexScan). See GiST's gistbeginscan for reference.
|
||||
if (*scan).numberOfOrderBys > 0 {
|
||||
let n = (*scan).numberOfOrderBys as usize;
|
||||
(*scan).xs_orderbyvals = pg_sys::palloc0(
|
||||
std::mem::size_of::<pg_sys::Datum>() * n,
|
||||
) as *mut pg_sys::Datum;
|
||||
(*scan).xs_orderbynulls = pg_sys::palloc(
|
||||
std::mem::size_of::<bool>() * n,
|
||||
) as *mut bool;
|
||||
(*scan).xs_orderbyvals =
|
||||
pg_sys::palloc0(std::mem::size_of::<pg_sys::Datum>() * n) as *mut pg_sys::Datum;
|
||||
(*scan).xs_orderbynulls = pg_sys::palloc(std::mem::size_of::<bool>() * n) as *mut bool;
|
||||
// Initialize all ORDER BY values as null (true = null)
|
||||
std::ptr::write_bytes((*scan).xs_orderbynulls, 1u8, n);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1503,12 +1503,9 @@ unsafe extern "C" fn ivfflat_ambeginscan(
|
|||
// RelationGetIndexScan). See GiST's gistbeginscan for reference.
|
||||
if (*scan).numberOfOrderBys > 0 {
|
||||
let n = (*scan).numberOfOrderBys as usize;
|
||||
(*scan).xs_orderbyvals = pg_sys::palloc0(
|
||||
std::mem::size_of::<pg_sys::Datum>() * n,
|
||||
) as *mut pg_sys::Datum;
|
||||
(*scan).xs_orderbynulls = pg_sys::palloc(
|
||||
std::mem::size_of::<bool>() * n,
|
||||
) as *mut bool;
|
||||
(*scan).xs_orderbyvals =
|
||||
pg_sys::palloc0(std::mem::size_of::<pg_sys::Datum>() * n) as *mut pg_sys::Datum;
|
||||
(*scan).xs_orderbynulls = pg_sys::palloc(std::mem::size_of::<bool>() * n) as *mut bool;
|
||||
std::ptr::write_bytes((*scan).xs_orderbynulls, 1u8, n);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@
|
|||
use pgrx::prelude::*;
|
||||
use pgrx::JsonB;
|
||||
|
||||
use ruvector_math::optimal_transport::{OptimalTransport, SlicedWasserstein, SinkhornSolver};
|
||||
use ruvector_math::product_manifold::ProductManifold;
|
||||
use ruvector_math::spherical::SphericalSpace;
|
||||
use ruvector_math::spectral::{SpectralClustering, ScaledLaplacian, GraphFilter, SpectralFilter};
|
||||
use ruvector_math::optimal_transport::GromovWasserstein;
|
||||
use ruvector_math::optimal_transport::{OptimalTransport, SinkhornSolver, SlicedWasserstein};
|
||||
use ruvector_math::product_manifold::ProductManifold;
|
||||
use ruvector_math::spectral::{GraphFilter, ScaledLaplacian, SpectralClustering, SpectralFilter};
|
||||
use ruvector_math::spherical::SphericalSpace;
|
||||
|
||||
/// Helper: parse a JsonB 2D array into Vec<Vec<f64>>.
|
||||
fn parse_points(json: &JsonB) -> Vec<Vec<f64>> {
|
||||
|
|
@ -16,11 +16,8 @@ fn parse_points(json: &JsonB) -> Vec<Vec<f64>> {
|
|||
.map(|arr| {
|
||||
arr.iter()
|
||||
.filter_map(|v| {
|
||||
v.as_array().map(|a| {
|
||||
a.iter()
|
||||
.filter_map(|x| x.as_f64())
|
||||
.collect()
|
||||
})
|
||||
v.as_array()
|
||||
.map(|a| a.iter().filter_map(|x| x.as_f64()).collect())
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
|
|
@ -48,11 +45,7 @@ fn flatten_adjacency(adj: &[Vec<f64>]) -> (Vec<f64>, usize) {
|
|||
|
||||
/// Compute Wasserstein (Earth Mover's) distance between two distributions.
|
||||
#[pg_extern(immutable, parallel_safe)]
|
||||
pub fn ruvector_wasserstein_distance(
|
||||
a: Vec<f32>,
|
||||
b: Vec<f32>,
|
||||
p: default!(i32, 1),
|
||||
) -> f32 {
|
||||
pub fn ruvector_wasserstein_distance(a: Vec<f32>, b: Vec<f32>, p: default!(i32, 1)) -> f32 {
|
||||
if a.len() != b.len() || a.is_empty() {
|
||||
pgrx::error!("Distributions must have same non-zero length");
|
||||
}
|
||||
|
|
@ -91,14 +84,12 @@ pub fn ruvector_sinkhorn_distance(
|
|||
|
||||
let solver = SinkhornSolver::new(reg as f64, 100);
|
||||
match solver.solve(&cost, &wa, &wb) {
|
||||
Ok(result) => {
|
||||
JsonB(serde_json::json!({
|
||||
"distance": result.cost,
|
||||
"converged": result.converged,
|
||||
"iterations": result.iterations,
|
||||
"transport_plan": result.plan,
|
||||
}))
|
||||
}
|
||||
Ok(result) => JsonB(serde_json::json!({
|
||||
"distance": result.cost,
|
||||
"converged": result.converged,
|
||||
"iterations": result.iterations,
|
||||
"transport_plan": result.plan,
|
||||
})),
|
||||
Err(e) => pgrx::error!("Sinkhorn failed: {}", e),
|
||||
}
|
||||
}
|
||||
|
|
@ -174,10 +165,7 @@ pub fn ruvector_jensen_shannon(p: Vec<f32>, q: Vec<f32>) -> f32 {
|
|||
|
||||
/// Compute Fisher information metric.
|
||||
#[pg_extern(immutable, parallel_safe)]
|
||||
pub fn ruvector_fisher_information(
|
||||
dist: Vec<f32>,
|
||||
tangent: Vec<f32>,
|
||||
) -> f32 {
|
||||
pub fn ruvector_fisher_information(dist: Vec<f32>, tangent: Vec<f32>) -> f32 {
|
||||
if dist.len() != tangent.len() || dist.is_empty() {
|
||||
pgrx::error!("Distribution and tangent must have same non-zero length");
|
||||
}
|
||||
|
|
@ -197,10 +185,7 @@ pub fn ruvector_fisher_information(
|
|||
|
||||
/// Spectral clustering on an adjacency matrix.
|
||||
#[pg_extern(immutable, parallel_safe)]
|
||||
pub fn ruvector_spectral_cluster(
|
||||
adj_json: JsonB,
|
||||
k: i32,
|
||||
) -> Vec<i32> {
|
||||
pub fn ruvector_spectral_cluster(adj_json: JsonB, k: i32) -> Vec<i32> {
|
||||
let adj = parse_matrix(&adj_json);
|
||||
if adj.is_empty() {
|
||||
return Vec::new();
|
||||
|
|
@ -307,10 +292,7 @@ pub fn ruvector_spherical_distance(a: Vec<f32>, b: Vec<f32>) -> f32 {
|
|||
|
||||
/// Compute Gromov-Wasserstein distance between two metric spaces.
|
||||
#[pg_extern(immutable, parallel_safe)]
|
||||
pub fn ruvector_gromov_wasserstein(
|
||||
pts_a_json: JsonB,
|
||||
pts_b_json: JsonB,
|
||||
) -> JsonB {
|
||||
pub fn ruvector_gromov_wasserstein(pts_a_json: JsonB, pts_b_json: JsonB) -> JsonB {
|
||||
let pts_a = parse_points(&pts_a_json);
|
||||
let pts_b = parse_points(&pts_b_json);
|
||||
|
||||
|
|
@ -320,13 +302,11 @@ pub fn ruvector_gromov_wasserstein(
|
|||
|
||||
let gw = GromovWasserstein::new(0.1);
|
||||
match gw.solve(&pts_a, &pts_b) {
|
||||
Ok(result) => {
|
||||
JsonB(serde_json::json!({
|
||||
"distance": result.loss.sqrt(),
|
||||
"converged": result.converged,
|
||||
"coupling": result.transport_plan,
|
||||
}))
|
||||
}
|
||||
Ok(result) => JsonB(serde_json::json!({
|
||||
"distance": result.loss.sqrt(),
|
||||
"converged": result.converged,
|
||||
"coupling": result.transport_plan,
|
||||
})),
|
||||
Err(e) => pgrx::error!("Gromov-Wasserstein failed: {}", e),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ pub fn edges_json_to_csr(json: &serde_json::Value) -> Result<CsrMatrix<f64>, Str
|
|||
.get("edges")
|
||||
.and_then(|e| e.as_array())
|
||||
.or_else(|| json.as_array())
|
||||
.ok_or_else(|| "Expected JSON object with 'edges' array or a JSON array of edges".to_string())?;
|
||||
.ok_or_else(|| {
|
||||
"Expected JSON object with 'edges' array or a JSON array of edges".to_string()
|
||||
})?;
|
||||
|
||||
if edges.is_empty() {
|
||||
return Err("Edge list is empty".to_string());
|
||||
|
|
@ -47,14 +49,24 @@ pub fn edges_json_to_csr(json: &serde_json::Value) -> Result<CsrMatrix<f64>, Str
|
|||
pub fn matrix_json_to_csr(json: &serde_json::Value) -> Result<CsrMatrix<f64>, String> {
|
||||
// Structured format with rows/cols
|
||||
if let Some(entries) = json.get("entries").and_then(|e| e.as_array()) {
|
||||
let rows = json.get("rows").and_then(|r| r.as_u64()).ok_or("Missing 'rows'")? as usize;
|
||||
let cols = json.get("cols").and_then(|c| c.as_u64()).ok_or("Missing 'cols'")? as usize;
|
||||
let rows = json
|
||||
.get("rows")
|
||||
.and_then(|r| r.as_u64())
|
||||
.ok_or("Missing 'rows'")? as usize;
|
||||
let cols = json
|
||||
.get("cols")
|
||||
.and_then(|c| c.as_u64())
|
||||
.ok_or("Missing 'cols'")? as usize;
|
||||
|
||||
let coo: Vec<(usize, usize, f64)> = entries
|
||||
.iter()
|
||||
.filter_map(|e| {
|
||||
let a = e.as_array()?;
|
||||
Some((a[0].as_u64()? as usize, a[1].as_u64()? as usize, a[2].as_f64()?))
|
||||
Some((
|
||||
a[0].as_u64()? as usize,
|
||||
a[1].as_u64()? as usize,
|
||||
a[2].as_f64()?,
|
||||
))
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
|
|||
|
|
@ -165,10 +165,7 @@ pub fn ruvector_solve_sparse(
|
|||
|
||||
/// Solve a graph Laplacian system Lx=b.
|
||||
#[pg_extern(immutable, parallel_safe)]
|
||||
pub fn ruvector_solve_laplacian(
|
||||
laplacian_json: JsonB,
|
||||
rhs: Vec<f32>,
|
||||
) -> JsonB {
|
||||
pub fn ruvector_solve_laplacian(laplacian_json: JsonB, rhs: Vec<f32>) -> JsonB {
|
||||
let csr = match matrix_json_to_csr(&laplacian_json.0) {
|
||||
Ok(m) => m,
|
||||
Err(e) => pgrx::error!("Laplacian solve: {}", e),
|
||||
|
|
@ -192,11 +189,7 @@ pub fn ruvector_solve_laplacian(
|
|||
|
||||
/// Compute effective resistance between two nodes.
|
||||
#[pg_extern(immutable, parallel_safe)]
|
||||
pub fn ruvector_effective_resistance(
|
||||
laplacian_json: JsonB,
|
||||
source: i32,
|
||||
target: i32,
|
||||
) -> f32 {
|
||||
pub fn ruvector_effective_resistance(laplacian_json: JsonB, source: i32, target: i32) -> f32 {
|
||||
let csr = match matrix_json_to_csr(&laplacian_json.0) {
|
||||
Ok(m) => m,
|
||||
Err(e) => pgrx::error!("Effective resistance: {}", e),
|
||||
|
|
@ -219,8 +212,16 @@ pub fn ruvector_effective_resistance(
|
|||
Ok(res) => {
|
||||
let s = source as usize;
|
||||
let t = target as usize;
|
||||
let x_s = if s < res.solution.len() { res.solution[s] as f64 } else { 0.0 };
|
||||
let x_t = if t < res.solution.len() { res.solution[t] as f64 } else { 0.0 };
|
||||
let x_s = if s < res.solution.len() {
|
||||
res.solution[s] as f64
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let x_t = if t < res.solution.len() {
|
||||
res.solution[t] as f64
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
(x_s - x_t) as f32
|
||||
}
|
||||
Err(e) => pgrx::error!("Effective resistance failed: {}", e),
|
||||
|
|
@ -307,18 +308,48 @@ pub fn ruvector_solver_info() -> TableIterator<
|
|||
),
|
||||
> {
|
||||
let algos = vec![
|
||||
("neumann", "Jacobi-preconditioned Neumann series", "O(nnz * log(1/eps))"),
|
||||
("cg", "Conjugate Gradient for SPD systems", "O(n * sqrt(kappa))"),
|
||||
("forward-push", "Andersen-Chung-Lang PageRank", "O(1/epsilon)"),
|
||||
("backward-push", "Backward Push for target PPR", "O(1/epsilon)"),
|
||||
("hybrid-random-walk", "Push + Monte Carlo sampling", "O(sqrt(n/epsilon))"),
|
||||
("bmssp", "Block MSS preconditioned solver", "O(n * nnz_per_row)"),
|
||||
("true-solver", "Topology-aware batch solver", "O(batch * nnz)"),
|
||||
(
|
||||
"neumann",
|
||||
"Jacobi-preconditioned Neumann series",
|
||||
"O(nnz * log(1/eps))",
|
||||
),
|
||||
(
|
||||
"cg",
|
||||
"Conjugate Gradient for SPD systems",
|
||||
"O(n * sqrt(kappa))",
|
||||
),
|
||||
(
|
||||
"forward-push",
|
||||
"Andersen-Chung-Lang PageRank",
|
||||
"O(1/epsilon)",
|
||||
),
|
||||
(
|
||||
"backward-push",
|
||||
"Backward Push for target PPR",
|
||||
"O(1/epsilon)",
|
||||
),
|
||||
(
|
||||
"hybrid-random-walk",
|
||||
"Push + Monte Carlo sampling",
|
||||
"O(sqrt(n/epsilon))",
|
||||
),
|
||||
(
|
||||
"bmssp",
|
||||
"Block MSS preconditioned solver",
|
||||
"O(n * nnz_per_row)",
|
||||
),
|
||||
(
|
||||
"true-solver",
|
||||
"Topology-aware batch solver",
|
||||
"O(batch * nnz)",
|
||||
),
|
||||
];
|
||||
|
||||
TableIterator::new(algos.into_iter().map(|(a, d, c)| {
|
||||
(a.to_string(), d.to_string(), c.to_string())
|
||||
}))
|
||||
TableIterator::new(
|
||||
algos
|
||||
.into_iter()
|
||||
.map(|(a, d, c)| (a.to_string(), d.to_string(), c.to_string())),
|
||||
)
|
||||
}
|
||||
|
||||
/// Analyze matrix sparsity profile.
|
||||
|
|
@ -384,7 +415,8 @@ pub fn ruvector_conjugate_gradient(
|
|||
..Default::default()
|
||||
};
|
||||
|
||||
let solver = ruvector_solver::cg::ConjugateGradientSolver::new(tol as f64, max_iter as usize, true);
|
||||
let solver =
|
||||
ruvector_solver::cg::ConjugateGradientSolver::new(tol as f64, max_iter as usize, true);
|
||||
|
||||
match solver.solve(&csr, &rhs_f64, &budget) {
|
||||
Ok(res) => JsonB(serde_json::json!({
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
pub mod operators;
|
||||
|
||||
use dashmap::DashMap;
|
||||
use ruvector_sona::{SonaConfig, SonaEngine};
|
||||
use std::sync::Arc;
|
||||
use ruvector_sona::{SonaEngine, SonaConfig};
|
||||
|
||||
/// Global Sona engine state per table.
|
||||
static SONA_ENGINES: once_cell::sync::Lazy<DashMap<String, Arc<SonaEngine>>> =
|
||||
|
|
|
|||
|
|
@ -7,10 +7,7 @@ use super::get_or_create_engine;
|
|||
|
||||
/// Record a learning trajectory for a table (Micro-LoRA).
|
||||
#[pg_extern]
|
||||
pub fn ruvector_sona_learn(
|
||||
table_name: &str,
|
||||
trajectory_json: JsonB,
|
||||
) -> JsonB {
|
||||
pub fn ruvector_sona_learn(table_name: &str, trajectory_json: JsonB) -> JsonB {
|
||||
let engine = get_or_create_engine(table_name);
|
||||
|
||||
// Parse trajectory: {"initial": [f32...], "steps": [{"embedding": [f32...], "actions": [...], "reward": f32}]}
|
||||
|
|
@ -56,10 +53,7 @@ pub fn ruvector_sona_learn(
|
|||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let reward = step
|
||||
.get("reward")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.0) as f32;
|
||||
let reward = step.get("reward").and_then(|v| v.as_f64()).unwrap_or(0.0) as f32;
|
||||
|
||||
builder.add_step(embedding, attention_weights, reward);
|
||||
}
|
||||
|
|
@ -82,10 +76,7 @@ pub fn ruvector_sona_learn(
|
|||
|
||||
/// Apply learned LoRA transformation to an embedding.
|
||||
#[pg_extern(immutable, parallel_safe)]
|
||||
pub fn ruvector_sona_apply(
|
||||
table_name: &str,
|
||||
embedding: Vec<f32>,
|
||||
) -> Vec<f32> {
|
||||
pub fn ruvector_sona_apply(table_name: &str, embedding: Vec<f32>) -> Vec<f32> {
|
||||
let engine = get_or_create_engine(table_name);
|
||||
|
||||
let mut output = vec![0.0f32; embedding.len()];
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ use pgrx::prelude::*;
|
|||
use pgrx::JsonB;
|
||||
|
||||
use ruvector_math::homology::{
|
||||
BottleneckDistance, PersistenceDiagram, PersistentHomology, VietorisRips, WassersteinDistance,
|
||||
BirthDeathPair, PointCloud, Point,
|
||||
BirthDeathPair, BottleneckDistance, PersistenceDiagram, PersistentHomology, Point, PointCloud,
|
||||
VietorisRips, WassersteinDistance,
|
||||
};
|
||||
|
||||
/// Helper: parse a JsonB array of points into a PointCloud.
|
||||
|
|
@ -118,10 +118,7 @@ pub fn ruvector_betti_numbers(
|
|||
|
||||
/// Compute bottleneck distance between two persistence diagrams.
|
||||
#[pg_extern(immutable, parallel_safe)]
|
||||
pub fn ruvector_bottleneck_distance(
|
||||
diag_a_json: JsonB,
|
||||
diag_b_json: JsonB,
|
||||
) -> f32 {
|
||||
pub fn ruvector_bottleneck_distance(diag_a_json: JsonB, diag_b_json: JsonB) -> f32 {
|
||||
let diag_a = parse_diagram(&diag_a_json);
|
||||
let diag_b = parse_diagram(&diag_b_json);
|
||||
|
||||
|
|
@ -144,10 +141,7 @@ pub fn ruvector_persistence_wasserstein(
|
|||
|
||||
/// Compute topological summary (Betti numbers + persistence statistics + entropy).
|
||||
#[pg_extern(immutable, parallel_safe)]
|
||||
pub fn ruvector_topological_summary(
|
||||
points_json: JsonB,
|
||||
max_dim: default!(i32, 1),
|
||||
) -> JsonB {
|
||||
pub fn ruvector_topological_summary(points_json: JsonB, max_dim: default!(i32, 1)) -> JsonB {
|
||||
let cloud = parse_point_cloud(&points_json);
|
||||
if cloud.is_empty() {
|
||||
return JsonB(serde_json::json!({}));
|
||||
|
|
@ -210,10 +204,7 @@ pub fn ruvector_topological_summary(
|
|||
|
||||
/// Detect topological drift between old and new embeddings.
|
||||
#[pg_extern(immutable, parallel_safe)]
|
||||
pub fn ruvector_embedding_drift(
|
||||
old_json: JsonB,
|
||||
new_json: JsonB,
|
||||
) -> JsonB {
|
||||
pub fn ruvector_embedding_drift(old_json: JsonB, new_json: JsonB) -> JsonB {
|
||||
let old_cloud = parse_point_cloud(&old_json);
|
||||
let new_cloud = parse_point_cloud(&new_json);
|
||||
|
||||
|
|
@ -281,7 +272,8 @@ pub fn ruvector_vietoris_rips(
|
|||
})
|
||||
.collect();
|
||||
|
||||
let mut simplex_counts: std::collections::HashMap<usize, usize> = std::collections::HashMap::new();
|
||||
let mut simplex_counts: std::collections::HashMap<usize, usize> =
|
||||
std::collections::HashMap::new();
|
||||
for fs in &filtration.simplices {
|
||||
*simplex_counts.entry(fs.simplex.dim()).or_insert(0) += 1;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue