pub(crate) use super::*;
use logic_tests_embedding_learning::apply_nonlinearity_with_temp;
#[test]
fn k1_logical_join_computes_grandparent() {
let parent = vec![
vec![0.0, 1.0, 0.0],
vec![0.0, 0.0, 1.0],
vec![0.0, 0.0, 0.0],
];
let grandparent = logical_join(&parent, &parent, LogicMode::Boolean);
assert_eq!(
grandparent[0][2], 1.0,
"Alice should be grandparent of Charlie"
);
assert_eq!(grandparent[0][0], 0.0, "Alice is not her own grandparent");
assert_eq!(grandparent[1][2], 0.0, "Bob is not grandparent of Charlie");
}
#[test]
fn k1_logical_join_continuous_mode() {
let a = vec![vec![0.5, 0.3], vec![0.2, 0.8]];
let b = vec![vec![0.4, 0.6], vec![0.7, 0.1]];
let result = logical_join(&a, &b, LogicMode::Continuous);
let expected_00 = 0.5 * 0.4 + 0.3 * 0.7; assert!((result[0][0] - expected_00).abs() < 1e-6);
}
#[test]
fn k2_logical_project_boolean_existential() {
let parent = vec![
vec![0.0, 1.0, 0.0], vec![0.0, 0.0, 1.0], vec![0.0, 0.0, 0.0], ];
let has_child = logical_project(&parent, 1, LogicMode::Boolean);
assert_eq!(has_child[0], 1.0, "Alice has child");
assert_eq!(has_child[1], 1.0, "Bob has child");
assert_eq!(has_child[2], 0.0, "Charlie has no child");
}
#[test]
fn k3_logical_project_continuous_sum() {
let tensor = vec![vec![0.2, 0.3, 0.5], vec![0.1, 0.4, 0.2]];
let projected = logical_project(&tensor, 1, LogicMode::Continuous);
assert!((projected[0] - 1.0).abs() < 1e-6);
assert!((projected[1] - 0.7).abs() < 1e-6);
}
#[test]
fn k4_logical_union_boolean_max() {
let a = vec![vec![1.0, 0.0, 1.0]];
let b = vec![vec![0.0, 1.0, 1.0]];
let result = logical_union(&a, &b, LogicMode::Boolean);
assert_eq!(result[0][0], 1.0);
assert_eq!(result[0][1], 1.0);
assert_eq!(result[0][2], 1.0);
}
#[test]
fn k4_logical_union_continuous_probabilistic() {
let a = vec![vec![0.3, 0.5]];
let b = vec![vec![0.4, 0.6]];
let result = logical_union(&a, &b, LogicMode::Continuous);
let expected_0 = 0.3 + 0.4 - 0.3 * 0.4; let expected_1 = 0.5 + 0.6 - 0.5 * 0.6; assert!((result[0][0] - expected_0).abs() < 1e-6);
assert!((result[0][1] - expected_1).abs() < 1e-6);
}
#[test]
fn k5_logical_negation() {
let tensor = vec![vec![1.0, 0.0, 0.7]];
let negated = logical_negation(&tensor, LogicMode::Boolean);
assert_eq!(negated[0][0], 0.0);
assert_eq!(negated[0][1], 1.0);
assert_eq!(negated[0][2], 0.0);
let negated_cont = logical_negation(&tensor, LogicMode::Continuous);
assert!((negated_cont[0][2] - 0.3).abs() < 1e-6); }
#[test]
fn k6_logical_select() {
let tensor = vec![vec![0.8, 0.6, 0.9]];
let condition = vec![vec![1.0, 0.0, 1.0]];
let selected = logical_select(&tensor, &condition, LogicMode::Boolean);
assert_eq!(selected[0][0], 0.8);
assert_eq!(selected[0][1], 0.0); assert_eq!(selected[0][2], 0.9);
}
#[test]
fn k7_boolean_mode_binary_output() {
let a = vec![vec![0.3, 0.7, 0.5]];
let b = vec![vec![0.6, 0.4, 0.5]];
let result = logical_union(&a, &b, LogicMode::Boolean);
for row in &result {
for &val in row {
assert!(val == 0.0 || val == 1.0, "Boolean mode must produce 0 or 1");
}
}
}
#[test]
fn k8_continuous_mode_preserves_values() {
let tensor = vec![vec![0.3, 0.7]];
let negated = logical_negation(&tensor, LogicMode::Continuous);
assert!((negated[0][0] - 0.7).abs() < 1e-6);
assert!((negated[0][1] - 0.3).abs() < 1e-6);
}
#[test]
fn k9_tensor_program_forward_chaining() {
let mut program = ProgramBuilder::new(LogicMode::Boolean)
.add_fact(
"parent",
vec![
vec![0.0, 1.0, 0.0],
vec![0.0, 0.0, 1.0],
vec![0.0, 0.0, 0.0],
],
)
.add_rule(
"grandparent",
Equation::Join("parent".into(), "parent".into()),
)
.build();
let results = program.forward();
let grandparent = results
.get("grandparent")
.expect("grandparent should exist");
assert_eq!(grandparent[0][2], 1.0);
}
#[test]
fn k10_tensor_program_query() {
let mut program = ProgramBuilder::new(LogicMode::Boolean)
.add_fact(
"parent",
vec![
vec![0.0, 1.0, 0.0],
vec![0.0, 0.0, 1.0],
vec![0.0, 0.0, 0.0],
],
)
.add_rule(
"grandparent",
Equation::Join("parent".into(), "parent".into()),
)
.build();
let result = program.query("grandparent");
assert!(result.is_some());
}
#[test]
fn k11_embedding_bilinear_scoring() {
let space = EmbeddingSpace::new(3, 4);
let score = space.score(0, "knows", 1);
assert!(score.is_finite());
}
#[test]
fn k12_relation_matrices_learnable() {
let mut space = EmbeddingSpace::new(3, 4);
space.add_relation("knows");
let matrix = space.get_relation_matrix("knows");
assert!(matrix.is_some());
assert_eq!(matrix.unwrap().len(), 4); }
#[test]
fn k13_multi_hop_composition() {
let mut space = EmbeddingSpace::new(3, 4);
space.add_relation("parent");
let composed = space.compose_relations(&["parent", "parent"]);
assert_eq!(composed.len(), 4); }
#[test]
fn k14_rescal_factorization() {
let factorizer = RescalFactorizer::new(3, 4, 2);
let triples = vec![
(0, 0, 1), (1, 0, 2), ];
let result = factorizer.factorize(&triples, 10);
assert!(result.entity_embeddings.len() == 3);
}
#[test]
fn k15_boolean_attention_argmax() {
let scores = vec![vec![0.1, 0.8, 0.05, 0.05]];
let weights = apply_nonlinearity(&scores, Nonlinearity::BooleanAttention);
assert_eq!(weights[0][0], 0.0);
assert_eq!(weights[0][1], 1.0); assert_eq!(weights[0][2], 0.0);
assert_eq!(weights[0][3], 0.0);
}
#[test]
fn k16_continuous_attention_softmax() {
let scores = vec![vec![1.0, 2.0, 3.0]];
let weights = apply_nonlinearity(&scores, Nonlinearity::Softmax);
let sum: f64 = weights[0].iter().sum();
assert!((sum - 1.0).abs() < 1e-6);
assert!(weights[0].iter().all(|&x| x > 0.0));
}
#[test]
fn k17_attention_mask() {
let scores = vec![vec![1.0, 2.0, 3.0, 4.0]];
let mask = vec![vec![false, false, true, true]];
let weights = apply_nonlinearity_with_mask(&scores, Nonlinearity::Softmax, Some(&mask));
assert!(weights[0][2] < 1e-6);
assert!(weights[0][3] < 1e-6);
let unmasked_sum = weights[0][0] + weights[0][1];
assert!((unmasked_sum - 1.0).abs() < 1e-6);
}
#[test]
fn k18_forward_chain_multiple_antecedents() {
let mut program = ProgramBuilder::new(LogicMode::Boolean)
.add_fact("a", vec![vec![1.0, 0.0], vec![0.0, 1.0]])
.add_fact("b", vec![vec![0.0, 1.0], vec![1.0, 0.0]])
.add_rule("c", Equation::JoinMultiple(vec!["a".into(), "b".into()]))
.build();
let results = program.forward();
assert!(results.contains_key("c"));
}
#[test]
fn k19_temperature_sharpness() {
let scores = vec![vec![1.0, 2.0]];
let weights_hot = apply_nonlinearity_with_temp(&scores, Nonlinearity::Softmax, 0.1);
let weights_cold = apply_nonlinearity_with_temp(&scores, Nonlinearity::Softmax, 10.0);
assert!(weights_hot[0][1] > weights_cold[0][1]);
}
#[test]
fn k20_trueno_simd_acceleration() {
use std::time::Instant;
let size = 16;
let iterations = 10;
let a: Vec<Vec<f64>> = (0..size)
.map(|i| (0..size).map(|j| ((i + j) % 10) as f64 / 10.0).collect())
.collect();
let b: Vec<Vec<f64>> = (0..size)
.map(|i| {
(0..size)
.map(|j| ((i * j + 1) % 10) as f64 / 10.0)
.collect()
})
.collect();
let start_simd = Instant::now();
for _ in 0..iterations {
let _ = logical_join(&a, &b, LogicMode::Continuous);
}
let simd_time = start_simd.elapsed();
let start_scalar = Instant::now();
for _ in 0..iterations {
let _ = naive_matrix_multiply_scalar(&a, &b);
}
let scalar_time = start_scalar.elapsed();
let simd_ns = simd_time.as_nanos() as f64;
let scalar_ns = scalar_time.as_nanos() as f64;
println!(
"K20 Benchmark: SIMD-layout={:.2}ms, Scalar={:.2}ms, Ratio={:.2}x",
simd_ns / 1_000_000.0,
scalar_ns / 1_000_000.0,
scalar_ns / simd_ns
);
let result = logical_join(&a, &b, LogicMode::Continuous);
assert!(!result.is_empty());
assert_eq!(result.len(), size);
assert_eq!(result[0].len(), size);
for row in &result {
for &val in row {
assert!(val.is_finite(), "SIMD result must be numerically stable");
}
}
}
pub(super) fn naive_matrix_multiply_scalar(a: &[Vec<f64>], b: &[Vec<f64>]) -> Vec<Vec<f64>> {
let rows = a.len();
let cols = if b.is_empty() { 0 } else { b[0].len() };
let inner = if a.is_empty() { 0 } else { a[0].len() };
let mut result = vec![vec![0.0; cols]; rows];
for j in 0..cols {
for i in 0..rows {
let mut sum = 0.0;
for k in 0..inner {
sum += a[i][k] * b[k][j];
}
result[i][j] = sum;
}
}
result
}
#[path = "logic_tests_embedding_learning.rs"]
mod logic_tests_embedding_learning;