use crate::api::constraint_set::IncrementalConstraint;
use crate::constraint::relational::OperatorTerminal;
use crate::stream::collection_extract::{source, ChangeSource};
use crate::stream::relational::operator::{GroupRightKeys, UnitKeys};
use crate::stream::relational::view_plan::EntityKey;
use crate::stream::relational::{
operator::{CollectionNode, ExistenceNode, Operator},
Leaf,
};
use solverforge_core::{score::SoftScore, ConstraintRef, ImpactType};
#[derive(Clone, Debug)]
struct Entity {
key: u32,
weight: i64,
}
struct Model {
left: Vec<Entity>,
right: Vec<Entity>,
}
fn left(m: &Model) -> &[Entity] {
&m.left
}
fn right(m: &Model) -> &[Entity] {
&m.right
}
fn left_key(e: &Entity) -> u32 {
e.key
}
fn right_key(e: &Entity) -> u32 {
e.key
}
fn weight(_: &Model, v: &Leaf<'_, Entity>) -> SoftScore {
SoftScore::of(v.entity.weight)
}
use crate::stream::relational::RowHandle;
#[test]
fn semi_and_anti_joins_publish_only_zero_crossings_and_preserve_left_identity() {
let mut rm: Vec<RowHandle> = Vec::new();
let mut ins: Vec<RowHandle> = Vec::new();
for exists in [true, false] {
let l = CollectionNode::new(
source(left as fn(&Model) -> &[Entity], ChangeSource::Descriptor(0)),
0,
);
let r = CollectionNode::new(
source(
right as fn(&Model) -> &[Entity],
ChangeSource::Descriptor(1),
),
1,
);
let mut tree = ExistenceNode::new(
l,
r,
EntityKey::new(left_key),
UnitKeys::new(EntityKey::new(right_key)),
exists,
);
let mut m = Model {
left: vec![Entity { key: 1, weight: 5 }],
right: vec![Entity { key: 1, weight: 1 }, Entity { key: 1, weight: 2 }],
};
tree.initialize(&m);
let initial = tree.handles();
tree.retract(&m, 1, 0, &mut rm, &mut ins);
assert!(rm.is_empty() && ins.is_empty()); let mut rm_delta: Vec<RowHandle> = Vec::new();
let mut ins_delta: Vec<RowHandle> = Vec::new();
tree.retract(&m, 1, 1, &mut rm_delta, &mut ins_delta); assert_eq!(rm_delta.len() + ins_delta.len(), 1);
if exists {
assert_eq!(rm_delta, initial);
} else {
assert_eq!(ins_delta, tree.handles());
}
tree.insert(&m, 1, 0, &mut rm_delta, &mut ins_delta); assert_eq!(rm_delta.len() + ins_delta.len(), 1);
tree.insert(&m, 1, 1, &mut rm, &mut ins);
assert!(rm.is_empty() && ins.is_empty()); assert_eq!(tree.handles(), initial);
let mut c = OperatorTerminal::new(
ConstraintRef::new("test", "exists"),
ImpactType::Reward,
tree,
weight,
false,
);
let mut score = c.initialize(&m);
let oracle = |m: &Model| {
SoftScore::of(
m.left
.iter()
.filter(|l| m.right.iter().any(|r| l.key == r.key) == exists)
.map(|e| e.weight)
.sum(),
)
};
for step in 0..90 {
let descriptor = step % 2;
let index = if descriptor == 0 { 0 } else { (step / 2) % 2 };
score = score + c.on_retract(&m, index, descriptor);
let values = if descriptor == 0 {
&mut m.left
} else {
&mut m.right
};
values[index].key = (step % 3) as u32;
values[index].weight = step as i64 - 20;
score = score + c.on_insert(&m, index, descriptor);
assert_eq!(score, oracle(&m));
assert_eq!(c.evaluate(&m), score);
assert_eq!(c.get_matches(&m).len(), c.match_count(&m));
}
c.reset();
assert_eq!(c.initialize(&m), oracle(&m));
}
}
#[test]
fn grouped_right_replacement_changes_semi_join_membership_during_retraction() {
use crate::stream::collector::{count, CountAccumulator};
use crate::stream::relational::operator::{GroupNode, GroupView};
fn aggregate<O: Operator<Model>>(
v: &GroupView<'_, Model, O, u32, CountAccumulator, (), usize>,
) -> u32 {
v.with_result(|count| *count as u32)
}
let l = CollectionNode::new(
source(left as fn(&Model) -> &[Entity], ChangeSource::Descriptor(0)),
0,
);
let r = CollectionNode::new(
source(
right as fn(&Model) -> &[Entity],
ChangeSource::Descriptor(1),
),
1,
);
let grouped = GroupNode::new(r, |e: &Leaf<'_, Entity>| e.entity.key, count());
let tree = ExistenceNode::new(
l,
grouped,
EntityKey::new(left_key),
GroupRightKeys::new(aggregate),
true,
);
let mut c = OperatorTerminal::new(
ConstraintRef::new("test", "grouped exists"),
ImpactType::Reward,
tree,
weight,
false,
);
let mut m = Model {
left: vec![Entity { key: 1, weight: 5 }, Entity { key: 2, weight: 9 }],
right: vec![Entity { key: 1, weight: 1 }, Entity { key: 1, weight: 2 }],
};
assert_eq!(c.evaluate(&m), SoftScore::of(9));
let mut score = c.initialize(&m);
score = score + c.on_retract(&m, 0, 1);
assert_eq!(score, SoftScore::of(5));
m.right[0].key = 2;
assert_eq!(c.evaluate(&m), SoftScore::of(5));
score = score + c.on_insert(&m, 0, 1);
assert_eq!(score, SoftScore::of(5));
assert_eq!(c.match_count(&m), 1);
assert_eq!(c.get_matches(&m)[0].justification.entities.len(), 1);
}