use std::ops::{Add, Div, Mul, Sub};
use thiserror::Error;
use crate::dim_expr::DimExpr;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SymDim(pub(crate) RawSymDim);
#[derive(Clone, Debug, PartialEq, Eq, Error)]
#[error("unknown symbolic tensor id {tensor_id}")]
pub struct SymDimConversionError {
pub tensor_id: u64,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum RawSymDim {
Const(usize),
TensorAxis { tensor_id: u64, axis: usize },
Add(Box<RawSymDim>, Box<RawSymDim>),
Sub(Box<RawSymDim>, Box<RawSymDim>),
Mul(Box<RawSymDim>, Box<RawSymDim>),
FloorDiv(Box<RawSymDim>, Box<RawSymDim>),
Min(Box<RawSymDim>, Box<RawSymDim>),
Max(Box<RawSymDim>, Box<RawSymDim>),
}
impl SymDim {
pub fn min(self, other: impl Into<SymDim>) -> Self {
let other = other.into();
binary(self, other, |lhs, rhs| {
RawSymDim::Min(Box::new(lhs), Box::new(rhs))
})
}
pub fn max(self, other: impl Into<SymDim>) -> Self {
let other = other.into();
binary(self, other, |lhs, rhs| {
RawSymDim::Max(Box::new(lhs), Box::new(rhs))
})
}
#[doc(hidden)]
pub fn tensor_axis(tensor_id: u64, axis: usize) -> Self {
Self(RawSymDim::TensorAxis { tensor_id, axis })
}
#[doc(hidden)]
pub fn constant_value(&self) -> Option<usize> {
raw_constant_value(&self.0)
}
#[doc(hidden)]
pub fn from_dim_expr(expr: &DimExpr, input_shapes: &[&[SymDim]]) -> Self {
match expr {
DimExpr::Const(value) => Self::from(*value),
DimExpr::InputDim { input_idx, axis } => input_shapes[*input_idx][*axis].clone(),
DimExpr::Add(lhs, rhs) => {
Self::from_dim_expr(lhs, input_shapes) + Self::from_dim_expr(rhs, input_shapes)
}
DimExpr::Sub(lhs, rhs) => {
Self::from_dim_expr(lhs, input_shapes) - Self::from_dim_expr(rhs, input_shapes)
}
DimExpr::Mul(lhs, rhs) => {
Self::from_dim_expr(lhs, input_shapes) * Self::from_dim_expr(rhs, input_shapes)
}
DimExpr::FloorDiv(lhs, rhs) => {
Self::from_dim_expr(lhs, input_shapes) / Self::from_dim_expr(rhs, input_shapes)
}
DimExpr::Min(lhs, rhs) => {
Self::from_dim_expr(lhs, input_shapes).min(Self::from_dim_expr(rhs, input_shapes))
}
DimExpr::Max(lhs, rhs) => {
Self::from_dim_expr(lhs, input_shapes).max(Self::from_dim_expr(rhs, input_shapes))
}
}
}
#[doc(hidden)]
pub fn to_dim_expr(
&self,
tensor_map: &[(u64, usize)],
) -> std::result::Result<DimExpr, SymDimConversionError> {
raw_to_dim_expr(&self.0, tensor_map)
}
pub fn referenced_tensor_ids(&self) -> Vec<u64> {
let mut ids = Vec::new();
collect_tensor_ids(&self.0, &mut ids);
ids
}
}
fn collect_tensor_ids(raw: &RawSymDim, ids: &mut Vec<u64>) {
match raw {
RawSymDim::Const(_) => {}
RawSymDim::TensorAxis { tensor_id, .. } => {
if !ids.contains(tensor_id) {
ids.push(*tensor_id);
}
}
RawSymDim::Add(lhs, rhs)
| RawSymDim::Sub(lhs, rhs)
| RawSymDim::Mul(lhs, rhs)
| RawSymDim::FloorDiv(lhs, rhs)
| RawSymDim::Min(lhs, rhs)
| RawSymDim::Max(lhs, rhs) => {
collect_tensor_ids(lhs, ids);
collect_tensor_ids(rhs, ids);
}
}
}
fn raw_constant_value(raw: &RawSymDim) -> Option<usize> {
match raw {
RawSymDim::Const(value) => Some(*value),
RawSymDim::TensorAxis { .. } => None,
RawSymDim::Add(lhs, rhs) => raw_constant_value(lhs)?.checked_add(raw_constant_value(rhs)?),
RawSymDim::Sub(lhs, rhs) => raw_constant_value(lhs)?.checked_sub(raw_constant_value(rhs)?),
RawSymDim::Mul(lhs, rhs) => raw_constant_value(lhs)?.checked_mul(raw_constant_value(rhs)?),
RawSymDim::FloorDiv(lhs, rhs) => {
let rhs = raw_constant_value(rhs)?;
raw_constant_value(lhs)?.checked_div(rhs)
}
RawSymDim::Min(lhs, rhs) => Some(raw_constant_value(lhs)?.min(raw_constant_value(rhs)?)),
RawSymDim::Max(lhs, rhs) => Some(raw_constant_value(lhs)?.max(raw_constant_value(rhs)?)),
}
}
impl From<usize> for SymDim {
fn from(value: usize) -> Self {
Self(RawSymDim::Const(value))
}
}
fn raw_to_dim_expr(
raw: &RawSymDim,
tensor_map: &[(u64, usize)],
) -> std::result::Result<DimExpr, SymDimConversionError> {
Ok(match raw {
RawSymDim::Const(value) => DimExpr::Const(*value),
RawSymDim::TensorAxis { tensor_id, axis } => {
let input_idx = tensor_map
.iter()
.find_map(|(candidate_id, input_idx)| {
(candidate_id == tensor_id).then_some(*input_idx)
})
.ok_or(SymDimConversionError {
tensor_id: *tensor_id,
})?;
DimExpr::InputDim {
input_idx,
axis: *axis,
}
}
RawSymDim::Add(lhs, rhs) => DimExpr::add(
raw_to_dim_expr(lhs, tensor_map)?,
raw_to_dim_expr(rhs, tensor_map)?,
),
RawSymDim::Sub(lhs, rhs) => DimExpr::sub(
raw_to_dim_expr(lhs, tensor_map)?,
raw_to_dim_expr(rhs, tensor_map)?,
),
RawSymDim::Mul(lhs, rhs) => DimExpr::mul(
raw_to_dim_expr(lhs, tensor_map)?,
raw_to_dim_expr(rhs, tensor_map)?,
),
RawSymDim::FloorDiv(lhs, rhs) => DimExpr::floor_div(
raw_to_dim_expr(lhs, tensor_map)?,
raw_to_dim_expr(rhs, tensor_map)?,
),
RawSymDim::Min(lhs, rhs) => DimExpr::min(
raw_to_dim_expr(lhs, tensor_map)?,
raw_to_dim_expr(rhs, tensor_map)?,
),
RawSymDim::Max(lhs, rhs) => DimExpr::max(
raw_to_dim_expr(lhs, tensor_map)?,
raw_to_dim_expr(rhs, tensor_map)?,
),
})
}
fn binary(lhs: SymDim, rhs: SymDim, f: impl FnOnce(RawSymDim, RawSymDim) -> RawSymDim) -> SymDim {
SymDim(f(lhs.0, rhs.0))
}
impl Add for SymDim {
type Output = SymDim;
fn add(self, rhs: SymDim) -> Self::Output {
binary(self, rhs, |lhs, rhs| {
RawSymDim::Add(Box::new(lhs), Box::new(rhs))
})
}
}
impl Add<usize> for SymDim {
type Output = SymDim;
fn add(self, rhs: usize) -> Self::Output {
self + SymDim::from(rhs)
}
}
impl Add<SymDim> for usize {
type Output = SymDim;
fn add(self, rhs: SymDim) -> Self::Output {
SymDim::from(self) + rhs
}
}
impl Sub for SymDim {
type Output = SymDim;
fn sub(self, rhs: SymDim) -> Self::Output {
binary(self, rhs, |lhs, rhs| {
RawSymDim::Sub(Box::new(lhs), Box::new(rhs))
})
}
}
impl Sub<usize> for SymDim {
type Output = SymDim;
fn sub(self, rhs: usize) -> Self::Output {
self - SymDim::from(rhs)
}
}
impl Sub<SymDim> for usize {
type Output = SymDim;
fn sub(self, rhs: SymDim) -> Self::Output {
SymDim::from(self) - rhs
}
}
impl Mul for SymDim {
type Output = SymDim;
fn mul(self, rhs: SymDim) -> Self::Output {
binary(self, rhs, |lhs, rhs| {
RawSymDim::Mul(Box::new(lhs), Box::new(rhs))
})
}
}
impl Mul<usize> for SymDim {
type Output = SymDim;
fn mul(self, rhs: usize) -> Self::Output {
self * SymDim::from(rhs)
}
}
impl Mul<SymDim> for usize {
type Output = SymDim;
fn mul(self, rhs: SymDim) -> Self::Output {
SymDim::from(self) * rhs
}
}
impl Div for SymDim {
type Output = SymDim;
fn div(self, rhs: SymDim) -> Self::Output {
binary(self, rhs, |lhs, rhs| {
RawSymDim::FloorDiv(Box::new(lhs), Box::new(rhs))
})
}
}
impl Div<usize> for SymDim {
type Output = SymDim;
fn div(self, rhs: usize) -> Self::Output {
self / SymDim::from(rhs)
}
}
impl Div<SymDim> for usize {
type Output = SymDim;
fn div(self, rhs: SymDim) -> Self::Output {
SymDim::from(self) / rhs
}
}