use std::{collections::HashMap, ops::MulAssign, sync::Arc};
use crate::arithmetic_utils::{ChainMultiplyable, CheckedAdd, CheckedAddAssign, Ring};
use crate::quiver_algebra::{
path_algebra::PathAlgebra,
quiver::{BasisElt, Quiver},
quiver_rep::QuiverRep,
};
#[must_use]
pub struct QuiverWithRelations<VertexLabel, EdgeLabel, Coeffs, const OP_ALG: bool>
where
VertexLabel: std::hash::Hash + Eq + Clone,
EdgeLabel: Eq + std::hash::Hash + Clone,
Coeffs: Ring,
{
quiver: Arc<Quiver<VertexLabel, EdgeLabel>>,
relations: Vec<PathAlgebra<VertexLabel, EdgeLabel, Coeffs, OP_ALG>>,
}
impl<VertexLabel, EdgeLabel, Coeffs, const OP_ALG: bool>
QuiverWithRelations<VertexLabel, EdgeLabel, Coeffs, OP_ALG>
where
VertexLabel: std::hash::Hash + Eq + Clone,
EdgeLabel: Eq + std::hash::Hash + Clone,
Coeffs: Ring,
{
pub fn new(
quiver: Arc<Quiver<VertexLabel, EdgeLabel>>,
mut relations: Vec<PathAlgebra<VertexLabel, EdgeLabel, Coeffs, OP_ALG>>,
is_zero: Option<fn(&Coeffs) -> bool>,
) -> Self {
if let Some(is_zero) = is_zero {
for rel in &mut relations {
rel.simplify(is_zero);
}
}
relations.retain(PathAlgebra::might_be_nonzero);
for rel in &relations {
assert!(Arc::ptr_eq(&quiver, rel.quiver()));
assert!(rel.all_parallel().is_ok());
}
Self { quiver, relations }
}
pub fn from_quiver_no_relations(quiver: Arc<Quiver<VertexLabel, EdgeLabel>>) -> Self {
Self::new(quiver, vec![], None)
}
pub fn from_quiver_and_w(
quiver: Arc<Quiver<VertexLabel, EdgeLabel>>,
w_function: &PathAlgebra<VertexLabel, EdgeLabel, Coeffs, OP_ALG>,
is_zero: Option<fn(&Coeffs) -> bool>,
) -> Self {
let mut relations = Vec::new();
for arrow in quiver.edge_labels() {
let mut cur_cyclic_derivative = w_function.clone();
cur_cyclic_derivative.cyclic_derivative(arrow);
relations.push(cur_cyclic_derivative);
}
Self::new(quiver, relations, is_zero)
}
#[allow(clippy::must_use_candidate)]
pub fn quiver(&self) -> &Arc<Quiver<VertexLabel, EdgeLabel>> {
&self.quiver
}
pub fn relations(
&self,
) -> impl Iterator<Item = &PathAlgebra<VertexLabel, EdgeLabel, Coeffs, OP_ALG>> {
self.relations.iter()
}
pub fn rep_descends<MatrixType>(
&self,
quiver_rep: &QuiverRep<VertexLabel, EdgeLabel, MatrixType, OP_ALG>,
mut matrix_is_zero: impl FnMut(&MatrixType) -> bool,
) -> bool
where
MatrixType: CheckedAdd + CheckedAddAssign + ChainMultiplyable + Clone + MulAssign<Coeffs>,
{
for rel in &self.relations {
if !rel.might_be_nonzero() {
continue;
}
if let Ok(mat_this_rel) = quiver_rep.mat_from_path_algebra(rel.clone()) {
if !matrix_is_zero(&mat_this_rel) {
return false;
}
} else {
return false;
}
}
true
}
}
impl<VertexLabel, EdgeLabel> Quiver<VertexLabel, EdgeLabel>
where
VertexLabel: std::hash::Hash + Eq + Clone,
EdgeLabel: Eq + std::hash::Hash + Clone,
{
#[allow(clippy::missing_panics_doc)]
pub fn preprojective_algebra<Coeffs: Ring, const OP_ALG: bool>(
self,
dagger: impl Fn(&EdgeLabel) -> EdgeLabel,
one_coeffs: &Coeffs,
) -> (
Arc<Self>,
QuiverWithRelations<VertexLabel, EdgeLabel, Coeffs, OP_ALG>,
) {
let (doubled, adjoint_pairs) = self.double(dagger);
let doubled_arc = Arc::new(doubled);
let mut vertex_relations: HashMap<
VertexLabel,
PathAlgebra<VertexLabel, EdgeLabel, Coeffs, OP_ALG>,
> = HashMap::new();
for (a, a_star) in &adjoint_pairs {
let (src, tgt) = doubled_arc
.edge_endpoint_labels(a)
.expect("original arrow is in the doubled quiver");
let aa_star = PathAlgebra::singleton(
doubled_arc.clone(),
BasisElt::Path(nonempty::nonempty![a.clone(), a_star.clone()]),
one_coeffs.clone(),
);
vertex_relations
.entry(src)
.and_modify(|rel| *rel += aa_star.clone())
.or_insert(aa_star);
let a_star_a = PathAlgebra::singleton(
doubled_arc.clone(),
BasisElt::Path(nonempty::nonempty![a_star.clone(), a.clone()]),
one_coeffs.clone(),
);
vertex_relations
.entry(tgt)
.and_modify(|rel| *rel -= a_star_a.clone())
.or_insert(-a_star_a);
}
let relations: Vec<_> = vertex_relations.into_values().collect();
let qwr = QuiverWithRelations::new(doubled_arc.clone(), relations, None);
(doubled_arc, qwr)
}
}
#[cfg(test)]
mod tests {
use crate::quiver_algebra;
use super::*;
#[test]
fn a2_preprojective() {
let a2 =
quiver_algebra::quiver::tests::make_a2_quiver().map_labels(|v| v, |e| e.to_string());
let (_doubled_arc, preprojective) =
a2.preprojective_algebra::<_, true>(|e| format!("{e}*"), &1i64);
assert_eq!(preprojective.relations().count(), 2);
for rel in preprojective.relations() {
assert!(rel.is_homogeneous_of_degree(2));
}
}
#[test]
fn test_ginzburg() {
use super::PathAlgebra;
use crate::quiver_algebra::quiver::BasisElt;
use std::sync::Arc;
let (ginzburg_quiver, _adjoint_pairs, _self_loops) =
quiver_algebra::quiver::tests::make_ginzburg_quiver();
let ginzburg_quiver = Arc::new(ginzburg_quiver);
let x_omega = BasisElt::Path(nonempty::nonempty!["Omega0".to_string()]);
let x_a = PathAlgebra::singleton(
ginzburg_quiver.clone(),
BasisElt::Path(nonempty::nonempty!["A".to_string()]),
1.0,
);
let x_adag = PathAlgebra::singleton(
ginzburg_quiver.clone(),
BasisElt::Path(nonempty::nonempty!["ADagger".to_string()]),
1.0,
);
let ginz_cubic = (x_a.clone() * x_adag.clone() - x_adag.clone() * x_a.clone()) * x_omega;
let _ginz_with_rels = QuiverWithRelations::<_, _, _, true>::new(
ginzburg_quiver.clone(),
vec![ginz_cubic],
Some(|x: &f64| *x == 0.0),
);
}
}