use anyhow::anyhow;
use graph::{NAdd, NMul, NetworkEdge, NetworkGraph, NetworkLeaf, NetworkNode, NetworkOp};
use linnet::half_edge::NodeIndex;
use serde::{Deserialize, Serialize};
use library::{Library, LibraryError};
use crate::algebra::algebraic_traits::RefOne;
use crate::contraction::Contract;
use crate::network::library::{DummyKey, FunctionLibrary, FunctionLibraryError, LibraryTensor};
use crate::structure::abstract_index::AbstractIndex;
use crate::structure::permuted::PermuteTensor;
use crate::structure::representation::LibrarySlot;
use crate::structure::slot::{AbsInd, IsAbstractSlot};
use crate::structure::{HasName, PermutedStructure, StructureError};
use std::borrow::Cow;
use std::fmt::Display;
use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign};
use store::{NetworkStore, TensorScalarStore, TensorScalarStoreMapping};
use thiserror::Error;
use crate::{
contraction::ContractionError,
structure::{CastStructure, HasStructure, ScalarTensor, TensorStructure},
};
use std::{convert::Infallible, fmt::Debug};
#[derive(
Debug,
Clone,
Serialize,
Deserialize,
bincode_trait_derive::Encode,
bincode_trait_derive::Decode,
PartialEq,
Eq,
)]
#[cfg_attr(
feature = "shadowing",
trait_decode(trait = symbolica::state::HasStateMap),
)]
pub struct Network<S, LibKey, FunKey, Aind = AbstractIndex> {
pub graph: NetworkGraph<LibKey, FunKey, Aind>,
pub store: S,
pub state: NetworkState,
}
#[derive(
Debug,
Clone,
Serialize,
Deserialize,
bincode_trait_derive::Encode,
bincode_trait_derive::Decode,
PartialEq,
Eq,
Copy,
)]
pub enum NetworkState {
PureScalar,
Tensor,
SelfDualTensor,
Scalar,
}
impl NetworkState {
pub fn is_scalar(&self) -> bool {
matches!(self, NetworkState::Scalar | NetworkState::PureScalar)
}
pub fn is_tensor(&self) -> bool {
matches!(self, NetworkState::Tensor | NetworkState::SelfDualTensor)
}
pub fn pow(self, pow: i8) -> Self {
match self {
NetworkState::PureScalar => NetworkState::PureScalar,
NetworkState::Scalar => NetworkState::Scalar,
NetworkState::SelfDualTensor => {
if pow % 2 == 0 {
NetworkState::Scalar
} else {
NetworkState::SelfDualTensor
}
}
NetworkState::Tensor => panic!("Cannot have integer power of non-self dual tensor"),
}
}
pub fn is_compatible(&self, other: &Self) -> bool {
matches!(
(self, other),
(NetworkState::Tensor, NetworkState::Tensor)
| (NetworkState::SelfDualTensor, NetworkState::SelfDualTensor)
| (NetworkState::Scalar, NetworkState::Scalar)
| (NetworkState::PureScalar, NetworkState::Scalar)
| (NetworkState::Scalar, NetworkState::PureScalar)
| (NetworkState::PureScalar, NetworkState::PureScalar)
)
}
}
impl MulAssign for NetworkState {
fn mul_assign(&mut self, rhs: Self) {
*self = match (*self, rhs) {
(NetworkState::PureScalar, NetworkState::PureScalar) => NetworkState::PureScalar,
(NetworkState::PureScalar, NetworkState::Scalar) => NetworkState::Scalar,
(NetworkState::Tensor, _) => NetworkState::Tensor,
(NetworkState::Scalar, NetworkState::PureScalar) => NetworkState::Scalar,
(_, NetworkState::Tensor) => NetworkState::Tensor,
(NetworkState::SelfDualTensor, _) => NetworkState::SelfDualTensor,
(_, NetworkState::SelfDualTensor) => NetworkState::SelfDualTensor,
(NetworkState::Scalar, NetworkState::Scalar) => NetworkState::Scalar,
}
}
}
impl AddAssign for NetworkState {
fn add_assign(&mut self, rhs: Self) {
*self = match (*self, rhs) {
(NetworkState::PureScalar, NetworkState::PureScalar) => NetworkState::PureScalar,
(NetworkState::PureScalar, NetworkState::Scalar) => NetworkState::Scalar,
(a, b) => {
assert_eq!(a, b, "Cannot add incompatible network states:{a:?} + {b:?}");
a
}
}
}
}
pub mod graph;
pub mod library;
pub mod set;
pub mod store;
impl<S: TensorScalarStoreMapping, K: Clone, FK: Clone, Aind: AbsInd> TensorScalarStoreMapping
for Network<S, K, FK, Aind>
{
type Store<U, V> = Network<S::Store<U, V>, K, FK, Aind>;
type Scalar = S::Scalar;
type Tensor = S::Tensor;
fn iter_scalars(&self) -> impl Iterator<Item = &Self::Scalar> {
self.store.iter_scalars()
}
fn iter_tensors(&self) -> impl Iterator<Item = &Self::Tensor> {
self.store.iter_tensors()
}
fn iter_scalars_mut(&mut self) -> impl Iterator<Item = &mut Self::Scalar> {
self.store.iter_scalars_mut()
}
fn iter_tensors_mut(&mut self) -> impl Iterator<Item = &mut Self::Tensor> {
self.store.iter_tensors_mut()
}
fn map<U, V>(
self,
scalar_map: impl FnMut(Self::Scalar) -> U,
tensor_map: impl FnMut(Self::Tensor) -> V,
) -> Self::Store<V, U> {
Network {
store: self.store.map(scalar_map, tensor_map),
graph: self.graph,
state: self.state,
}
}
fn map_result<U, V, Er>(
self,
scalar_map: impl FnMut(Self::Scalar) -> Result<U, Er>,
tensor_map: impl FnMut(Self::Tensor) -> Result<V, Er>,
) -> Result<Self::Store<V, U>, Er> {
Ok(Network {
store: self.store.map_result(scalar_map, tensor_map)?,
graph: self.graph,
state: self.state,
})
}
fn map_ref<'a, U, V>(
&'a self,
scalar_map: impl FnMut(&'a Self::Scalar) -> U,
tensor_map: impl FnMut(&'a Self::Tensor) -> V,
) -> Self::Store<V, U> {
Network {
store: self.store.map_ref(scalar_map, tensor_map),
graph: self.graph.clone(),
state: self.state,
}
}
fn map_ref_result<U, V, Er>(
&self,
scalar_map: impl FnMut(&Self::Scalar) -> Result<U, Er>,
tensor_map: impl FnMut(&Self::Tensor) -> Result<V, Er>,
) -> Result<Self::Store<V, U>, Er> {
Ok(Network {
store: self.store.map_ref_result(scalar_map, tensor_map)?,
graph: self.graph.clone(),
state: self.state,
})
}
fn map_ref_enumerate<U, V>(
&self,
scalar_map: impl FnMut((usize, &Self::Scalar)) -> U,
tensor_map: impl FnMut((usize, &Self::Tensor)) -> V,
) -> Self::Store<V, U> {
Network {
store: self.store.map_ref_enumerate(scalar_map, tensor_map),
graph: self.graph.clone(),
state: self.state,
}
}
fn map_ref_result_enumerate<U, V, Er>(
&self,
scalar_map: impl FnMut((usize, &Self::Scalar)) -> Result<U, Er>,
tensor_map: impl FnMut((usize, &Self::Tensor)) -> Result<V, Er>,
) -> Result<Self::Store<V, U>, Er> {
Ok(Network {
store: self
.store
.map_ref_result_enumerate(scalar_map, tensor_map)?,
graph: self.graph.clone(),
state: self.state,
})
}
fn map_ref_mut<U, V>(
&mut self,
scalar_map: impl FnMut(&mut Self::Scalar) -> U,
tensor_map: impl FnMut(&mut Self::Tensor) -> V,
) -> Self::Store<V, U> {
Network {
store: self.store.map_ref_mut(scalar_map, tensor_map),
graph: self.graph.clone(),
state: self.state,
}
}
fn map_ref_mut_result<U, V, Er>(
&mut self,
scalar_map: impl FnMut(&mut Self::Scalar) -> Result<U, Er>,
tensor_map: impl FnMut(&mut Self::Tensor) -> Result<V, Er>,
) -> Result<Self::Store<V, U>, Er> {
Ok(Network {
store: self.store.map_ref_mut_result(scalar_map, tensor_map)?,
graph: self.graph.clone(),
state: self.state,
})
}
fn map_ref_mut_enumerate<U, V>(
&mut self,
scalar_map: impl FnMut((usize, &mut Self::Scalar)) -> U,
tensor_map: impl FnMut((usize, &mut Self::Tensor)) -> V,
) -> Self::Store<V, U> {
Network {
store: self.store.map_ref_mut_enumerate(scalar_map, tensor_map),
graph: self.graph.clone(),
state: self.state,
}
}
fn map_ref_mut_result_enumerate<U, V, Er>(
&mut self,
scalar_map: impl FnMut((usize, &mut Self::Scalar)) -> Result<U, Er>,
tensor_map: impl FnMut((usize, &mut Self::Tensor)) -> Result<V, Er>,
) -> Result<Self::Store<V, U>, Er> {
Ok(Network {
store: self
.store
.map_ref_mut_result_enumerate(scalar_map, tensor_map)?,
graph: self.graph.clone(),
state: self.state,
})
}
}
impl<S: TensorScalarStore, FK: Debug, K: Debug, Aind: AbsInd> Default for Network<S, K, FK, Aind> {
fn default() -> Self {
Self::one()
}
}
impl<S: TensorScalarStore, FK: Debug, K: Debug, Aind: AbsInd> NMul for Network<S, K, FK, Aind> {
type Output = Self;
fn n_mul<I: IntoIterator<Item = Self>>(self, iter: I) -> Self::Output {
let mut store = self.store;
let mut state = self.state;
let items = iter.into_iter().map(|mut a| {
a.graph.shift_scalars(store.n_scalars());
a.graph.shift_tensors(store.n_tensors());
store.extend(a.store);
state *= a.state;
a.graph
});
let graph = self.graph.n_mul(items);
if state.is_tensor() && graph.dangling_indices().is_empty() {
state = NetworkState::Scalar;
}
Network {
graph,
store,
state,
}
}
}
impl<S: TensorScalarStore, FK: Debug, K: Debug, Aind: AbsInd> Mul for Network<S, K, FK, Aind> {
type Output = Self;
fn mul(mut self, mut other: Self) -> Self::Output {
let mut store = self.store;
other.graph.shift_scalars(store.n_scalars());
other.graph.shift_tensors(store.n_tensors());
store.extend(other.store);
self.state *= other.state;
Network {
graph: self.graph * other.graph,
store,
state: self.state,
}
}
}
impl<S: TensorScalarStore, FK: Debug, K: Debug, Aind: AbsInd> MulAssign
for Network<S, K, FK, Aind>
{
fn mul_assign(&mut self, mut rhs: Self) {
rhs.graph.shift_scalars(self.store.n_scalars());
rhs.graph.shift_tensors(self.store.n_tensors());
self.store.extend(rhs.store);
self.state *= rhs.state;
self.graph *= rhs.graph;
}
}
impl<T: TensorStructure, S, FK: Debug, K: Debug, Aind: AbsInd> MulAssign<T>
for Network<NetworkStore<T, S>, K, FK, Aind>
where
T::Slot: IsAbstractSlot<Aind = Aind>,
{
fn mul_assign(&mut self, rhs: T) {
*self *= Network::from_tensor(rhs);
}
}
impl<T: TensorStructure, S, FK: Debug, K: Debug, Aind: AbsInd> Mul<T>
for Network<NetworkStore<T, S>, K, FK, Aind>
where
T::Slot: IsAbstractSlot<Aind = Aind>,
{
type Output = Self;
fn mul(self, other: T) -> Self::Output {
let mut store = self.store;
let mut other = Network::from_tensor(other);
other.graph.shift_scalars(store.n_scalars());
other.graph.shift_tensors(store.n_tensors());
store.extend(other.store);
Network {
graph: self.graph * other.graph,
store,
state: self.state,
}
}
}
impl<S: TensorScalarStore, FK: Debug, K: Debug, Aind: AbsInd> Add for Network<S, K, FK, Aind> {
type Output = Self;
fn add(self, mut other: Self) -> Self::Output {
let mut store = self.store;
other.graph.shift_scalars(store.n_scalars());
other.graph.shift_tensors(store.n_tensors());
store.extend(other.store);
Network {
graph: self.graph + other.graph,
store,
state: self.state,
}
}
}
impl<S: TensorScalarStore, FK: Debug, K: Debug, Aind: AbsInd> AddAssign
for Network<S, K, FK, Aind>
{
fn add_assign(&mut self, mut rhs: Self) {
rhs.graph.shift_scalars(self.store.n_scalars());
rhs.graph.shift_tensors(self.store.n_tensors());
self.store.extend(rhs.store);
self.graph += rhs.graph;
}
}
impl<T: TensorStructure, S, FK: Debug, K: Debug, Aind: AbsInd> AddAssign<T>
for Network<NetworkStore<T, S>, K, FK, Aind>
where
T::Slot: IsAbstractSlot<Aind = Aind>,
{
fn add_assign(&mut self, rhs: T) {
*self += Network::from_tensor(rhs);
}
}
impl<T: TensorStructure, S, FK: Debug, K: Debug, Aind: AbsInd> Add<T>
for Network<NetworkStore<T, S>, K, FK, Aind>
where
T::Slot: IsAbstractSlot<Aind = Aind>,
{
type Output = Self;
fn add(mut self, other: T) -> Self::Output {
self += other;
self
}
}
impl<T: TensorStructure, FK: Debug, K: Debug, Aind: AbsInd> Add<i8>
for Network<NetworkStore<T, i8>, K, FK, Aind>
where
T::Slot: IsAbstractSlot<Aind = Aind>,
{
type Output = Self;
fn add(mut self, other: i8) -> Self::Output {
let mut other = Network::from_scalar(other);
other.graph.shift_tensors(self.store.n_tensors());
other.graph.shift_tensors(self.store.n_tensors());
self.store.extend(other.store);
Network {
graph: self.graph + other.graph,
store: self.store,
state: self.state,
}
}
}
impl<S: TensorScalarStore, FK: Debug, K: Debug, Aind: AbsInd> NAdd for Network<S, K, FK, Aind> {
type Output = Self;
fn n_add<I: IntoIterator<Item = Self>>(self, iter: I) -> Self::Output {
let mut store = self.store;
let mut state = self.state;
let items = iter.into_iter().map(|mut a| {
a.graph.shift_scalars(store.n_scalars());
a.graph.shift_tensors(store.n_tensors());
store.extend(a.store);
state += a.state;
a.graph
});
Network {
graph: self.graph.n_add(items),
store,
state,
}
}
}
impl<S: TensorScalarStore, K: Clone + Debug, FK: Clone + Debug, Aind: AbsInd> Neg
for Network<S, K, FK, Aind>
{
type Output = Self;
fn neg(self) -> Self::Output {
Self {
store: self.store,
graph: self.graph.neg(),
state: self.state,
}
}
}
impl<S: TensorScalarStore, K: Clone + Debug, FK: Clone + Debug, Aind: AbsInd> Sub
for Network<S, K, FK, Aind>
{
type Output = Self;
fn sub(mut self, rhs: Self) -> Self::Output {
self -= rhs;
self
}
}
impl<S: TensorScalarStore, K: Clone + Debug, FK: Clone + Debug, Aind: AbsInd> SubAssign
for Network<S, K, FK, Aind>
{
fn sub_assign(&mut self, mut rhs: Self) {
rhs.graph.shift_scalars(self.store.n_scalars());
rhs.graph.shift_tensors(self.store.n_tensors());
self.store.extend(rhs.store);
self.graph -= rhs.graph
}
}
impl<T: TensorStructure, S, K: Clone + Debug, FK: Clone + Debug, Aind: AbsInd> SubAssign<T>
for Network<NetworkStore<T, S>, K, FK, Aind>
where
T::Slot: IsAbstractSlot<Aind = Aind>,
{
fn sub_assign(&mut self, rhs: T) {
*self -= Network::from_tensor(rhs)
}
}
impl<T: TensorStructure, S, K: Clone + Debug, FK: Clone + Debug, Aind: AbsInd> Sub<T>
for Network<NetworkStore<T, S>, K, FK, Aind>
where
T::Slot: IsAbstractSlot<Aind = Aind>,
{
type Output = Self;
fn sub(mut self, other: T) -> Self::Output {
self -= other;
self
}
}
impl<S: TensorScalarStore, FK: Debug, K: Debug, Aind: AbsInd> Network<S, K, FK, Aind> {
pub fn pow(self, pow: i8) -> Self {
Self {
store: self.store,
graph: self.graph.pow(pow),
state: self.state.pow(pow),
}
}
pub fn fun(self, key: FK) -> Self {
let graph = self.graph.function(key);
Self {
store: self.store,
state: graph.state(),
graph,
}
}
pub fn from_scalar(scalar: S::Scalar) -> Self {
let mut store = S::default();
let id = store.add_scalar(scalar);
Network {
graph: NetworkGraph::scalar(id),
store,
state: NetworkState::PureScalar,
}
}
pub fn merge_ops(&mut self)
where
K: Clone,
{
self.graph.merge_ops();
}
pub fn from_tensor(tensor: S::Tensor) -> Self
where
S::Tensor: TensorStructure,
<S::Tensor as TensorStructure>::Slot: IsAbstractSlot<Aind = Aind>,
{
let mut store = S::default();
let state = if tensor.is_scalar() {
NetworkState::Scalar
} else if tensor.is_fully_self_dual() {
NetworkState::SelfDualTensor
} else {
NetworkState::Tensor
};
let id = store.add_tensor(tensor);
Network {
graph: NetworkGraph::tensor(store.get_tensor(id), NetworkLeaf::LocalTensor(id)),
store,
state,
}
}
pub fn library_tensor<T>(tensor: &T, key: PermutedStructure<K>) -> Self
where
T: TensorStructure,
T::Slot: IsAbstractSlot<Aind = Aind>,
{
let state = if tensor.is_scalar() {
NetworkState::Scalar
} else if tensor.is_fully_self_dual() {
NetworkState::SelfDualTensor
} else {
NetworkState::Tensor
};
Network {
graph: NetworkGraph::tensor(tensor, NetworkLeaf::LibraryKey(key)),
store: S::default(),
state,
}
}
pub fn one() -> Self {
Network {
graph: NetworkGraph::one(),
store: S::default(),
state: NetworkState::PureScalar,
}
}
pub fn zero() -> Self {
Network {
graph: NetworkGraph::zero(),
store: S::default(),
state: NetworkState::PureScalar,
}
}
}
#[derive(Error, Debug)]
pub enum TensorNetworkError<K: Display, FK: Display> {
#[error("Slot edge to prod node")]
SlotEdgeToProdNode,
#[error("Slot edge to scalar node")]
SlotEdgeToScalarNode,
#[error("More than one neg")]
MoreThanOneNeg,
#[error("Childless neg")]
ChildlessNeg,
#[error("Contraction Error:{0}")]
ContractionError(#[from] ContractionError),
#[error("Scalar connected by a slot edge")]
ScalarSlotEdge,
#[error("Structure Error:{0}")]
StructErr(#[from] StructureError),
#[error("LibraryError:{0}")]
LibErr(#[from] LibraryError<K>),
#[error("FunctionLibraryError:{0}")]
FunLibErr(#[from] FunctionLibraryError<FK>),
#[error("Non tensor node still present")]
NonTensorNodePresent,
#[error("Negative non-even power on non-scalar node:{0}")]
NegativeExponentNonScalar(String),
#[error("Too many arguments for function:{0}")]
TooManyArgsFunction(String),
#[error("Non self-dual tensor power{0}")]
NonSelfDualTensorPower(String),
#[error("invalid resulting node{0}")]
InvalidResultNode(NetworkNode<DummyKey, FK>),
#[error("internal edge still present, contract it first")]
InternalEdgePresent,
#[error("uncontracted scalar")]
UncontractedScalar,
#[error("Cannot contract edge between {0} and {1}")]
CannotContractEdgeBetween(NetworkNode<K, FK>, NetworkNode<K, FK>),
#[error("no nodes in the graph")]
NoNodes,
#[error("no scalar present")]
NoScalar,
#[error("more than one node in the graph")]
MoreThanOneNode,
#[error("is not scalar output")]
NotScalarOutput,
#[error("failed scalar multiplication")]
FailedScalarMul,
#[error("scalar field is empty")]
ScalarFieldEmpty,
#[error("not all scalars: {0}")]
NotAllScalars(String),
#[error("try to sum scalar with library tensor: {0}")]
ScalarLibSum(String),
#[error("try to sum scalar with a tensor: {0}")]
SumScalarTensor(String),
#[error("Incompatible summands: {0}")]
IncompatibleSummand(String),
#[error("failed to contract")]
FailedContract(ContractionError),
#[error("negative exponent not yet supported")]
NegativeExponent,
#[error("failed to contract: {0}")]
FailedContractMsg(String),
#[error(transparent)]
Other(#[from] anyhow::Error),
#[error("Io error")]
InOut(#[from] std::io::Error),
#[error("Infallible")]
Infallible,
}
impl<K: Display, FK: Display> From<Infallible> for TensorNetworkError<K, FK> {
fn from(_: Infallible) -> Self {
TensorNetworkError::Infallible
}
}
pub enum TensorOrScalarOrKey<T, S, K, Aind> {
Tensor {
tensor: T,
graph_slots: Vec<LibrarySlot<Aind>>,
},
Scalar(S),
Key {
key: K,
nodeid: NodeIndex,
},
}
pub enum ExecutionResult<T> {
One,
Zero,
Val(T),
}
impl<T: Display> Display for ExecutionResult<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ExecutionResult::One => write!(f, "One"),
ExecutionResult::Zero => write!(f, "Zero"),
ExecutionResult::Val(val) => write!(f, "{}", val),
}
}
}
impl<
T: TensorStructure,
S,
K: Display + Debug,
FK: Display + Debug,
Str: TensorScalarStore<Tensor = T, Scalar = S>,
Aind: AbsInd,
> Network<Str, K, FK, Aind>
where
T::Slot: IsAbstractSlot<Aind = Aind>,
{
pub fn validate(&self)
where
K: TensorStructure,
{
for (n, _neigh, v) in self.graph.graph.iter_nodes() {
match v {
NetworkNode::Leaf(NetworkLeaf::LibraryKey(k)) => {
let reps = self
.graph
.slots(n)
.into_iter()
.map(|s| s.rep())
.collect::<Vec<_>>();
let n_reps = k
.structure
.external_reps_iter()
.map(|r| r.to_lib())
.collect::<Vec<_>>();
assert_eq!(n_reps, reps);
}
NetworkNode::Leaf(NetworkLeaf::LocalTensor(k)) => {
let reps = self
.graph
.slots(n)
.into_iter()
.map(|s| s.rep())
.collect::<Vec<_>>();
let n_reps = self
.store
.get_tensor(*k)
.external_reps_iter()
.map(|r| r.to_lib())
.collect::<Vec<_>>();
assert_eq!(n_reps, reps);
}
_ => {}
}
}
}
#[allow(clippy::result_large_err, clippy::type_complexity)]
pub fn result(
&self,
) -> Result<
ExecutionResult<TensorOrScalarOrKey<&T, &S, &PermutedStructure<K>, Aind>>,
TensorNetworkError<K, FK>,
>
where
FK: Clone,
{
let (node, nid, graph_slots) = self.graph.result()?;
match node {
NetworkNode::Leaf(l) => match l {
NetworkLeaf::LibraryKey(k) => Ok(ExecutionResult::Val(TensorOrScalarOrKey::Key {
key: k,
nodeid: nid,
})),
NetworkLeaf::LocalTensor(t) => {
Ok(ExecutionResult::Val(TensorOrScalarOrKey::Tensor {
tensor: self.store.get_tensor(*t),
graph_slots,
}))
}
NetworkLeaf::Scalar(t) => Ok(ExecutionResult::Val(TensorOrScalarOrKey::Scalar(
self.store.get_scalar(*t),
))),
},
NetworkNode::Op(o) => match o {
NetworkOp::Product => Ok(ExecutionResult::One),
NetworkOp::Sum => Ok(ExecutionResult::Zero),
o => Err(TensorNetworkError::InvalidResultNode(NetworkNode::Op(
o.clone(),
))),
},
}
}
#[allow(clippy::result_large_err)]
pub fn result_tensor<'a, LT, L: Library<T::Structure, Key = K, Value = PermutedStructure<LT>>>(
&'a self,
lib: &L,
) -> Result<ExecutionResult<Cow<'a, T>>, TensorNetworkError<K, FK>>
where
S: 'a,
T: Clone + ScalarTensor + HasStructure,
K: Display + Debug,
FK: Display + Debug + Clone,
LT: TensorStructure<Indexed = T> + Clone + LibraryTensor<WithIndices = T>,
T: PermuteTensor<Permuted = T>,
for<'b> &'b S: Into<T::Scalar>,
<<LT::WithIndices as HasStructure>::Structure as TensorStructure>::Slot:
IsAbstractSlot<Aind = Aind>,
{
Ok(match self.result()? {
ExecutionResult::One => ExecutionResult::One,
ExecutionResult::Zero => ExecutionResult::Zero,
ExecutionResult::Val(v) => ExecutionResult::Val(match v {
TensorOrScalarOrKey::Tensor { tensor, .. } => Cow::Borrowed(tensor),
TensorOrScalarOrKey::Scalar(s) => Cow::Owned(T::new_scalar(s.into())),
TensorOrScalarOrKey::Key { nodeid, .. } => {
let less = self.graph.get_lib_data(lib, nodeid).unwrap();
Cow::Owned(less)
}
}),
})
}
#[allow(clippy::result_large_err)]
pub fn result_scalar<'a>(
&'a self,
) -> Result<ExecutionResult<Cow<'a, S>>, TensorNetworkError<K, FK>>
where
T: Clone + ScalarTensor + 'a,
T::Scalar: Into<S>,
K: Display,
FK: Display + Clone,
S: Clone,
{
Ok(match self.result()? {
ExecutionResult::One => ExecutionResult::One,
ExecutionResult::Zero => ExecutionResult::Zero,
ExecutionResult::Val(v) => ExecutionResult::Val(match v {
TensorOrScalarOrKey::Tensor { tensor: t, .. } => Cow::Owned(
t.clone()
.scalar()
.ok_or(TensorNetworkError::NoScalar)?
.into(),
),
TensorOrScalarOrKey::Scalar(s) => Cow::Borrowed(s),
TensorOrScalarOrKey::Key { .. } => return Err(TensorNetworkError::NoScalar),
}),
})
}
pub fn cast<U>(self) -> Network<Str::Store<U, S>, K, FK, Aind>
where
K: Clone,
FK: Clone,
T: CastStructure<U> + HasStructure,
T::Structure: TensorStructure,
U: HasStructure,
U::Structure: From<T::Structure> + TensorStructure<Slot = T::Slot>,
{
self.map(|a| a, |t| t.cast_structure())
}
}
pub trait StructureLessDisplay {
fn display(&self) -> String {
String::new()
}
}
impl<S: HasName> StructureLessDisplay for S
where
S::Args: StructureLessDisplay,
S::Name: Display,
{
fn display(&self) -> String {
format!(
"{}({})",
self.name().map(|t| t.to_string()).unwrap_or_default(),
self.args().map(|t| t.display()).unwrap_or_default()
)
}
}
impl<S, K: Display + Debug, FK: Display + Debug, Aind: AbsInd> Network<S, K, FK, Aind> {
pub fn dot(&self) -> std::string::String {
self.graph.dot()
}
pub fn dot_pretty(&self) -> std::string::String
where
S: TensorScalarStore,
S::Scalar: Display,
K: StructureLessDisplay,
S::Tensor: StructureLessDisplay,
{
self.graph.dot_impl(
|i| {
let ss = &self.store.get_scalar(i);
format!("{}:{}", i, ss)
},
|k| k.display(),
|t| {
let tt = &self.store.get_tensor(t);
tt.display()
},
|fk| fk.to_string(),
)
}
}
impl<T, S, FK: Debug, K: Debug, Aind: AbsInd> Network<NetworkStore<T, S>, K, FK, Aind> {
pub fn dot_display_impl(
&self,
scalar_disp: impl Fn(&S) -> String,
library_disp: impl Fn(&K) -> Option<String>,
tensor_disp: impl Fn(&T) -> String,
function_disp: impl Fn(&FK) -> String,
) -> std::string::String {
self.graph.graph.dot_impl(
&self.graph.graph.full_filter(),
"",
&|_| None,
&|e| {
if let NetworkEdge::Slot(s) = e {
Some(format!("label=\"{s}\""))
} else {
None
}
},
&|n| match n {
NetworkNode::Leaf(l) => match l {
NetworkLeaf::LibraryKey(l) => {
Some(format!("label = \"L:{}\"", library_disp(&l.structure)?))
}
NetworkLeaf::LocalTensor(l) => Some(format!(
"label = \"T:{}\"",
tensor_disp(self.store.get_tensor(*l))
)),
NetworkLeaf::Scalar(s) => Some(format!(
"label = \"S:{}\"",
scalar_disp(self.store.get_scalar(*s))
)),
},
NetworkNode::Op(o) => {
Some(format!("label = \"{}\"", o.display_with(&function_disp)))
}
},
)
}
}
#[cfg(feature = "shadowing")]
pub mod parsing;
pub mod contract;
pub use contract::{
ContractScalars, ContractionStrategy, SingleSmallestDegree, SmallestDegree, SmallestDegreeIter,
};
pub trait ExecutionStrategy<E, FL, L, K, FK, Aind>
where
E: ExecuteOp<FL, L, K, FK, Aind>,
{
#[allow(clippy::result_large_err)]
fn execute_all<C: ContractionStrategy<E, L, K, FK, Aind>>(
executor: &mut E,
graph: &mut NetworkGraph<K, FK, Aind>,
lib: &L,
fnlib: &FL,
) -> Result<(), TensorNetworkError<K, FK>>
where
K: Display,
FK: Display;
}
pub struct Sequential;
pub struct Steps<const N: usize> {}
pub struct StepsDebug<const N: usize> {}
impl<const N: usize, E, L, FL, FK: Debug, K: Debug, Aind: AbsInd>
ExecutionStrategy<E, FL, L, K, FK, Aind> for StepsDebug<N>
where
E: ExecuteOp<FL, L, K, FK, Aind>,
K: Clone,
FK: Clone,
{
fn execute_all<C: ContractionStrategy<E, L, K, FK, Aind>>(
executor: &mut E,
graph: &mut NetworkGraph<K, FK, Aind>,
lib: &L,
fnlib: &FL,
) -> Result<(), TensorNetworkError<K, FK>>
where
K: Display,
FK: Display,
{
for _ in 0..N {
if let Some((extracted_graph, op)) = graph.extract_next_ready_op() {
println!(
"Extracted_graph: {}",
extracted_graph.dot_impl(
|s| s.to_string(),
|_| "".to_string(),
|s| s.to_string(),
|_| "".to_string()
)
);
println!(
"Graph: {}",
graph.dot_impl(
|s| s.to_string(),
|_| "".to_string(),
|s| s.to_string(),
|_| "".to_string()
)
);
let replacement = executor.execute::<C>(extracted_graph, lib, fnlib, op)?;
println!(
"Replacement Graph: {}",
replacement.dot_impl(
|s| s.to_string(),
|_| "".to_string(),
|s| s.to_string(),
|_| "".to_string()
)
);
graph.splice_descendents_of(replacement);
}
}
Ok(())
}
}
impl<const N: usize, E, FL, L, K, FK, Aind: AbsInd> ExecutionStrategy<E, FL, L, K, FK, Aind>
for Steps<N>
where
E: ExecuteOp<FL, L, K, FK, Aind>,
K: Clone + Debug,
FK: Clone + Debug,
{
fn execute_all<C: ContractionStrategy<E, L, K, FK, Aind>>(
executor: &mut E,
graph: &mut NetworkGraph<K, FK, Aind>,
lib: &L,
fnlib: &FL,
) -> Result<(), TensorNetworkError<K, FK>>
where
K: Display,
FK: Display,
{
for _ in 0..N {
if let Some((extracted_graph, op)) = graph.extract_next_ready_op() {
let replacement = executor.execute::<C>(extracted_graph, lib, fnlib, op)?;
graph.splice_descendents_of(replacement);
}
}
Ok(())
}
}
impl<E, L, FL, K, FK, Aind: AbsInd> ExecutionStrategy<E, FL, L, K, FK, Aind> for Sequential
where
E: ExecuteOp<FL, L, K, FK, Aind>,
FK: Clone + Debug,
K: Clone + Debug,
{
fn execute_all<C: ContractionStrategy<E, L, K, FK, Aind>>(
executor: &mut E,
graph: &mut NetworkGraph<K, FK, Aind>,
lib: &L,
fnlib: &FL,
) -> Result<(), TensorNetworkError<K, FK>>
where
K: Display,
FK: Display,
{
while {
if let Some((extracted_graph, op)) = graph.extract_next_ready_op() {
let replacement = executor.execute::<C>(extracted_graph, lib, fnlib, op)?;
graph.splice_descendents_of(replacement);
true
} else {
false
}
} {}
Ok(())
}
}
pub trait ExecuteOp<FL, L, K, FK, Aind>: Sized {
#[allow(clippy::result_large_err)]
fn execute<C: ContractionStrategy<Self, L, K, FK, Aind>>(
&mut self,
graph: NetworkGraph<K, FK, Aind>,
lib: &L,
fn_lib: &FL,
op: NetworkOp<FK>,
) -> Result<NetworkGraph<K, FK, Aind>, TensorNetworkError<K, FK>>
where
K: Display,
FK: Display;
}
impl<S, Store: TensorScalarStore, K, FK, Aind: AbsInd> Network<Store, K, FK, Aind>
where
Store::Tensor: HasStructure<Structure = S>,
{
#[allow(clippy::result_large_err)]
pub fn execute<
Strat: ExecutionStrategy<Store, FL, L, K, FK, Aind>,
C: ContractionStrategy<Store, L, K, FK, Aind>,
LT,
L,
FL,
>(
&mut self,
lib: &L,
fn_lib: &FL,
) -> Result<(), TensorNetworkError<K, FK>>
where
K: Display + Clone + Debug,
FK: Display + Clone + Debug,
L: Library<S, Key = K, Value = PermutedStructure<LT>> + Sync,
FL: FunctionLibrary<Store::Tensor, Store::Scalar, Key = FK>,
LT: LibraryTensor<WithIndices = Store::Tensor>,
Store: ExecuteOp<FL, L, K, FK, Aind>,
{
self.merge_ops();
Strat::execute_all::<C>(&mut self.store, &mut self.graph, lib, fn_lib)
}
}
impl<
LT: LibraryTensor + Clone,
T: HasStructure
+ TensorStructure
+ Neg<Output = T>
+ Clone
+ Ref
+ Contract<LCM = T>
+ for<'a> AddAssign<T::Ref<'a>>
+ for<'a> AddAssign<LT::WithIndices>
+ From<LT::WithIndices>,
L: Library<T::Structure, Key = K, Value = PermutedStructure<LT>>,
Sc: Neg<Output = Sc>
+ RefOne
+ Div<Output = Sc>
+ for<'a> AddAssign<Sc::Ref<'a>>
+ Clone
+ for<'a> AddAssign<T::ScalarRef<'a>>
+ From<T::Scalar>
+ Ref
+ for<'a> MulAssign<Sc::Ref<'a>>,
K: Display + Debug + Clone,
FK: Display + Debug,
FL: FunctionLibrary<T, Sc, Key = FK>,
Aind: AbsInd,
> ExecuteOp<FL, L, K, FK, Aind> for NetworkStore<T, Sc>
where
LT::WithIndices: PermuteTensor<Permuted = LT::WithIndices>,
<<LT::WithIndices as HasStructure>::Structure as TensorStructure>::Slot:
IsAbstractSlot<Aind = Aind>,
{
fn execute<C: ContractionStrategy<Self, L, K, FK, Aind>>(
&mut self,
mut graph: NetworkGraph<K, FK, Aind>,
lib: &L,
fn_lib: &FL,
op: NetworkOp<FK>,
) -> Result<NetworkGraph<K, FK, Aind>, TensorNetworkError<K, FK>> {
graph.sync_order();
match op {
NetworkOp::Neg => {
let ops = graph
.graph
.iter_nodes()
.find(|(_, _, d)| matches!(d, NetworkNode::Op(NetworkOp::Neg)));
let (opid, children, _) = ops.unwrap();
let mut child = None;
for c in children {
if let Some(id) = graph.graph.involved_node_id(c) {
if let NetworkNode::Leaf(l) = &graph.graph[id] {
if child.is_some() {
return Err(TensorNetworkError::MoreThanOneNeg);
} else {
child = Some((id, l));
}
}
}
}
if let Some((child_id, leaf)) = child {
let new_node = match leaf {
NetworkLeaf::Scalar(s) => {
let s = self.scalar[*s].clone().neg();
let pos = self.scalar.len();
self.scalar.push(s);
NetworkLeaf::Scalar(pos)
}
NetworkLeaf::LibraryKey(_) => {
let inds = graph.get_lib_data(lib, child_id).unwrap();
let t = T::from(inds).neg();
let pos = self.tensors.len();
self.tensors.push(t);
NetworkLeaf::LocalTensor(pos)
}
NetworkLeaf::LocalTensor(t) => {
let t = self.tensors[*t].clone().neg();
let pos = self.tensors.len();
self.tensors.push(t);
NetworkLeaf::LocalTensor(pos)
}
};
graph.identify_nodes_without_self_edges(
&[child_id, opid],
NetworkNode::Leaf(new_node),
);
Ok(graph)
} else {
Err(TensorNetworkError::ChildlessNeg)
}
}
NetworkOp::Product => {
let (graph, _) = C::contract(self, graph, lib)?;
Ok(graph)
}
NetworkOp::Sum => {
let mut targets = Vec::new();
let mut all_nodes = Vec::new();
for (n, _, v) in graph.graph.iter_nodes() {
all_nodes.push(n);
if let NetworkNode::Leaf(l) = &v {
targets.push((n, l));
}
}
let (nf, first) = &targets[0];
let new_node = match first {
NetworkLeaf::Scalar(s) => {
let mut accumulator = self.scalar[*s].clone();
for (_, t) in &targets[1..] {
match t {
NetworkLeaf::Scalar(s) => {
accumulator += self.scalar[*s].refer();
}
NetworkLeaf::LocalTensor(t) => {
if let Some(s) = self.tensors[*t].scalar_ref() {
accumulator += s;
} else {
return Err(TensorNetworkError::NotAllScalars(
"".to_string(),
));
}
}
NetworkLeaf::LibraryKey { .. } => {
return Err(TensorNetworkError::ScalarLibSum("".to_string()));
}
}
}
let pos = self.scalar.len();
self.scalar.push(accumulator);
NetworkLeaf::Scalar(pos)
}
NetworkLeaf::LocalTensor(t) => {
let mut accumulator = self.tensors[*t].clone();
if accumulator.is_scalar() {
let mut accumulator = Sc::from(accumulator.scalar().unwrap());
for (_, t) in &targets[1..] {
match t {
NetworkLeaf::Scalar(s) => {
accumulator += self.scalar[*s].refer();
}
NetworkLeaf::LocalTensor(t) => {
if let Some(s) = self.tensors[*t].scalar_ref() {
accumulator += s;
} else {
return Err(TensorNetworkError::NotAllScalars(
"".to_string(),
));
}
}
NetworkLeaf::LibraryKey { .. } => {
return Err(TensorNetworkError::ScalarLibSum(
"".to_string(),
));
}
}
}
let pos = self.scalar.len();
self.scalar.push(accumulator);
NetworkLeaf::Scalar(pos)
} else {
for (nid, t) in &targets[1..] {
match t {
NetworkLeaf::Scalar(_) => {
return Err(TensorNetworkError::SumScalarTensor(
"".to_string(),
));
}
NetworkLeaf::LocalTensor(t) => {
accumulator += self.tensors[*t].refer();
}
NetworkLeaf::LibraryKey(_) => {
let with_index = graph.get_lib_data(lib, *nid).unwrap();
accumulator += with_index;
}
}
}
let pos = self.tensors.len();
self.tensors.push(accumulator);
NetworkLeaf::LocalTensor(pos)
}
}
NetworkLeaf::LibraryKey(_) => {
let inds = graph.get_lib_data(lib, *nf).unwrap();
let mut accumulator = T::from(inds);
for (nid, t) in &targets[1..] {
match t {
NetworkLeaf::Scalar(_) => {
return Err(TensorNetworkError::SumScalarTensor(
"".to_string(),
));
}
NetworkLeaf::LocalTensor(t) => {
accumulator += self.tensors[*t].refer();
}
NetworkLeaf::LibraryKey(_) => {
let with = graph.get_lib_data(lib, *nid).unwrap();
accumulator += with;
}
}
}
let pos = self.tensors.len();
self.tensors.push(accumulator);
NetworkLeaf::LocalTensor(pos)
}
};
graph.identify_nodes_without_self_edges(&all_nodes, NetworkNode::Leaf(new_node));
Ok(graph)
}
NetworkOp::Function(f) => {
let ops = graph
.graph
.iter_nodes()
.find(|(_, _, d)| matches!(d, NetworkNode::Op(NetworkOp::Function(_))));
let (opid, children, _) = ops.unwrap();
let mut child = None;
for c in children {
if let Some(id) = graph.graph.involved_node_id(c) {
if let NetworkNode::Leaf(l) = &graph.graph[id] {
if let Some((nid, _)) = child {
if nid != id {
return Err(TensorNetworkError::Other(anyhow!(
"Cannot have more than one tensor argument to function"
)));
}
} else {
child = Some((id, l));
}
}
}
}
if let Some((child_id, leaf)) = child {
let new_node = match leaf {
NetworkLeaf::Scalar(s) => {
let s = self.scalar[*s].clone();
let pos = self.scalar.len();
let s = fn_lib.apply_scalar(&f, s)?;
self.scalar.push(s);
NetworkLeaf::Scalar(pos)
}
NetworkLeaf::LibraryKey(_) => {
let inds = graph.get_lib_data(lib, child_id).unwrap();
let t = fn_lib.apply(&f, T::from(inds))?;
let pos = self.tensors.len();
self.tensors.push(t);
NetworkLeaf::LocalTensor(pos)
}
NetworkLeaf::LocalTensor(t) => {
let t = self.tensors[*t].clone();
let t = fn_lib.apply(&f, t)?;
let pos = self.tensors.len();
self.tensors.push(t);
NetworkLeaf::LocalTensor(pos)
}
};
graph.identify_nodes_without_self_edges(
&[child_id, opid],
NetworkNode::Leaf(new_node),
);
Ok(graph)
} else {
Err(TensorNetworkError::ChildlessNeg)
}
}
NetworkOp::Power(_) => {
let mut pow = 0;
let ops = graph.graph.iter_nodes().find(|(_, _, d)| {
if let NetworkNode::Op(NetworkOp::Power(i)) = d {
pow = *i;
true
} else {
false
}
});
let (opid, children, _) = ops.unwrap();
let mut child = None;
for c in children {
if let Some(id) = graph.graph.involved_node_id(c) {
if let NetworkNode::Leaf(l) = &graph.graph[id] {
if let Some((nid, _)) = child {
if nid != id {
return Err(TensorNetworkError::Other(anyhow!(
"Cannot have more than one tensor argument to power:{}",
graph.dot()
)));
}
} else {
child = Some((id, l));
}
}
}
}
let n = pow.abs();
if let Some((child_id, leaf)) = child {
let new_node = match leaf {
NetworkLeaf::Scalar(si) => {
if n == 0 {
NetworkLeaf::Scalar(*si)
} else {
let mut s = self.scalar[*si].clone();
for _ in 1..n {
s *= self.scalar[*si].refer();
}
if pow < 0 {
s = s.ref_one() / s;
}
let pos = self.scalar.len();
self.scalar.push(s);
NetworkLeaf::Scalar(pos)
}
}
NetworkLeaf::LibraryKey(a) => {
let inds = graph.get_lib_data(lib, child_id).unwrap();
let mut t = T::from(inds);
match pow {
0 => {
let pos = self.scalar.len();
let one = self.scalar[0].ref_one();
self.scalar.push(one);
NetworkLeaf::Scalar(pos)
}
1 => NetworkLeaf::LibraryKey(a.clone()),
_ => {
let squares = n / 2;
let mut square = t.contract(&t)?;
if n % 2 == 1 {
if n != 1 {
for _ in 0..squares {
square = square.contract(&square)?;
}
t = square.contract(&t)?;
}
if pow < 0 {
if !t.is_scalar() {
return Err(
TensorNetworkError::NegativeExponentNonScalar(
"".to_string(),
),
);
} else {
let mut s = Sc::from(t.scalar().unwrap());
let pos = self.scalar.len();
s = s.ref_one() / s;
self.scalar.push(s);
NetworkLeaf::Scalar(pos)
}
} else {
let pos = self.tensors.len();
self.tensors.push(t);
NetworkLeaf::LocalTensor(pos)
}
} else {
let mut s = Sc::from(square.scalar().unwrap());
let sc = s.clone();
for _ in 1..squares {
s *= sc.refer();
}
let pos = self.scalar.len();
if pow < 0 {
s = s.ref_one() / s;
}
self.scalar.push(s);
NetworkLeaf::Scalar(pos)
}
}
}
}
NetworkLeaf::LocalTensor(ti) => {
let mut t = self.tensors[*ti].clone();
match pow {
0 => {
let pos = self.scalar.len();
let one = self.scalar[0].ref_one();
self.scalar.push(one);
NetworkLeaf::Scalar(pos)
}
1 => NetworkLeaf::LocalTensor(*ti),
_ => {
let squares = n / 2;
let mut square = t.contract(&t)?;
if n % 2 == 1 {
if n != 1 {
for _ in 0..squares {
square = square.contract(&square)?;
}
t = square.contract(&t)?;
}
if pow < 0 {
if !t.is_scalar() {
return Err(
TensorNetworkError::NegativeExponentNonScalar(
"".to_string(),
),
);
} else {
let mut s = Sc::from(t.scalar().unwrap());
let pos = self.scalar.len();
s = s.ref_one() / s;
self.scalar.push(s);
NetworkLeaf::Scalar(pos)
}
} else {
let pos = self.tensors.len();
self.tensors.push(t);
NetworkLeaf::LocalTensor(pos)
}
} else {
let mut s = Sc::from(square.scalar().unwrap());
let sc = s.clone();
for _ in 1..squares {
s *= sc.refer();
}
let pos = self.scalar.len();
if pow < 0 {
s = s.ref_one() / s;
}
self.scalar.push(s);
NetworkLeaf::Scalar(pos)
}
}
}
}
};
graph.identify_nodes_without_self_edges(
&[child_id, opid],
NetworkNode::Leaf(new_node),
);
Ok(graph)
} else {
Err(TensorNetworkError::ChildlessNeg)
}
}
}
}
}
pub trait Ref {
type Ref<'a>
where
Self: 'a;
fn refer(&self) -> Self::Ref<'_>;
}
impl Ref for f64 {
type Ref<'a>
= &'a f64
where
Self: 'a;
fn refer(&self) -> Self::Ref<'_> {
self
}
}
#[cfg(feature = "shadowing")]
pub mod symbolica_interop;
#[cfg(test)]
mod tests;