use std::ops::{AddAssign, Mul, Neg};
use num::Zero;
use crate::quiver_algebra::SemiRing;
use super::algebra::ClusterAlgebra;
pub struct PoissonClusterAlgebra<const N: usize, VertexLabel, EdgeLabel, Coeffs, BaseRing>
where
VertexLabel: std::hash::Hash + Eq + Clone,
EdgeLabel: Eq + std::hash::Hash + Clone,
Coeffs: SemiRing,
BaseRing: Zero
+ Neg<Output = BaseRing>
+ PartialEq
+ Clone
+ AddAssign<BaseRing>
+ Mul<i64, Output = BaseRing>,
{
algebra: ClusterAlgebra<N, VertexLabel, EdgeLabel, Coeffs>,
lambda: [[BaseRing; N]; N],
}
impl<const N: usize, VertexLabel, EdgeLabel, Coeffs: SemiRing, BaseRing>
From<ClusterAlgebra<N, VertexLabel, EdgeLabel, Coeffs>>
for PoissonClusterAlgebra<N, VertexLabel, EdgeLabel, Coeffs, BaseRing>
where
VertexLabel: std::hash::Hash + Eq + Clone,
EdgeLabel: Eq + std::hash::Hash + Clone,
BaseRing: Zero
+ Neg<Output = BaseRing>
+ PartialEq
+ Clone
+ AddAssign<BaseRing>
+ Mul<i64, Output = BaseRing>,
{
fn from(algebra: ClusterAlgebra<N, VertexLabel, EdgeLabel, Coeffs>) -> Self {
let lambda = core::array::from_fn(|_| core::array::from_fn(|_| BaseRing::zero()));
Self { algebra, lambda }
}
}
impl<const N: usize, VertexLabel, EdgeLabel, Coeffs: SemiRing, BaseRing>
PoissonClusterAlgebra<N, VertexLabel, EdgeLabel, Coeffs, BaseRing>
where
VertexLabel: std::hash::Hash + Eq + Clone,
EdgeLabel: Eq + std::hash::Hash + Clone,
BaseRing: Zero
+ Neg<Output = BaseRing>
+ PartialEq
+ Clone
+ AddAssign<BaseRing>
+ Mul<i64, Output = BaseRing>,
{
pub fn new(
algebra: ClusterAlgebra<N, VertexLabel, EdgeLabel, Coeffs>,
lambda: [[BaseRing; N]; N],
) -> Self {
#[allow(clippy::needless_range_loop)]
for i in 0..N {
for j in 0..N {
assert!(
lambda[i][j] == -lambda[j][i].clone(),
"lambda must be skew-symmetric"
);
}
}
Self { algebra, lambda }
}
pub fn poisson_coefficient(&self, v1: &VertexLabel, v2: &VertexLabel) -> Option<&BaseRing> {
let i = self.algebra.vertex_index(v1)?;
let j = self.algebra.vertex_index(v2)?;
Some(&self.lambda[i][j])
}
pub fn set_bracket(&mut self, v1: &VertexLabel, v2: &VertexLabel, value: BaseRing) {
let Some(i) = self.algebra.vertex_index(v1) else {
return;
};
let Some(j) = self.algebra.vertex_index(v2) else {
return;
};
self.lambda[i][j] = value.clone();
self.lambda[j][i] = -value;
}
pub fn freeze(&mut self, vertex: VertexLabel) {
self.algebra.freeze(vertex);
}
pub fn unfreeze(&mut self, vertex: &VertexLabel) {
self.algebra.unfreeze(vertex);
}
pub fn is_frozen(&self, vertex: &VertexLabel) -> bool {
self.algebra.is_frozen(vertex)
}
pub fn view_cluster<const M: usize>(&self, vertex: [VertexLabel; M]) -> [(Coeffs, Coeffs); M] {
self.algebra.view_cluster(vertex)
}
pub fn mutate(&mut self, vertex: &VertexLabel) {
if self.algebra.is_frozen(vertex) {
return;
}
let Some(k) = self.algebra.vertex_index(vertex) else {
return;
};
let mut b_col = [0; N];
for (_, src) in self.algebra.quiver().predecessors(vertex) {
if let Some(i) = self.algebra.vertex_index(&src) {
b_col[i] += 1;
}
}
for (_, tgt) in self.algebra.quiver().successors(vertex) {
if let Some(i) = self.algebra.vertex_index(&tgt) {
b_col[i] -= 1;
}
}
let old_lambda = self.lambda.clone();
#[allow(clippy::needless_range_loop)]
for j in 0..N {
if j == k {
continue;
}
let mut new_kj = -old_lambda[k][j].clone();
for i in 0..N {
if b_col[i] > 0 {
new_kj += old_lambda[i][j].clone() * b_col[i];
}
}
self.lambda[k][j] = new_kj.clone();
self.lambda[j][k] = -new_kj;
}
self.algebra.mutate(vertex);
}
}
#[cfg(test)]
mod test {
use super::PoissonClusterAlgebra;
use crate::cluster_algebra::ClusterAlgebra;
use crate::quiver_algebra::make_a2_quiver;
fn a2_string_quiver() -> crate::quiver_algebra::Quiver<&'static str, String> {
make_a2_quiver().map_labels(|v| v, |e| e.to_string())
}
#[test]
fn a2_poisson() {
let alg = ClusterAlgebra::<2, _, _, i32>::new(
a2_string_quiver(),
[("alpha", 5), ("beta", 7)].into_iter().collect(),
|n| format!("__gen_{n}"),
)
.expect("valid quiver and seed");
let mut palg = PoissonClusterAlgebra::from(alg);
palg.set_bracket(&"alpha", &"beta", 1);
assert_eq!(
palg.poisson_coefficient(&"alpha", &"beta").copied(),
Some(1)
);
assert_eq!(
palg.poisson_coefficient(&"beta", &"alpha").copied(),
Some(-1)
);
palg.mutate(&"alpha");
assert_eq!(palg.view_cluster(["alpha"]), [(8, 5)]);
assert_eq!(palg.view_cluster(["beta"]), [(7, 1)]);
assert_eq!(
palg.poisson_coefficient(&"alpha", &"beta").copied(),
Some(-1)
);
assert_eq!(
palg.poisson_coefficient(&"beta", &"alpha").copied(),
Some(1)
);
}
#[test]
fn a3_poisson_nontrivial() {
use crate::quiver_algebra::Quiver;
let mut quiver: Quiver<&str, String> = Quiver::new();
quiver.add_edge("v1", "v2", "a".to_string());
quiver.add_edge("v2", "v3", "b".to_string());
let alg = ClusterAlgebra::<3, _, _, i32>::new(
quiver,
[("v1", 2), ("v2", 3), ("v3", 5)].into_iter().collect(),
|n| format!("__gen_{n}"),
)
.expect("valid quiver and seed");
let mut palg = PoissonClusterAlgebra::from(alg);
palg.set_bracket(&"v1", &"v2", 1i64);
palg.set_bracket(&"v2", &"v3", 1);
palg.set_bracket(&"v1", &"v3", 3);
palg.mutate(&"v2");
assert_eq!(palg.poisson_coefficient(&"v1", &"v2").copied(), Some(-1));
assert_eq!(palg.poisson_coefficient(&"v2", &"v3").copied(), Some(2));
assert_eq!(palg.poisson_coefficient(&"v1", &"v3").copied(), Some(3));
}
}