tract-tensorflow 0.2.1

Tiny, no-nonsense, self contained, TensorFlow and ONNX inference
Documentation
use tract_core::ops as tractops;
use tract_core::ops::prelude::*;

use crate::ops::OpRegister;
use crate::tfpb::node_def::NodeDef;

pub fn register_all_ops(reg: &mut OpRegister) {
    reg.insert("Less", with_T!(tractops::logic::Lesser::Bin));
    reg.insert("Merge", merge);
    reg.insert("Switch", |_| Ok(Box::new(Switch)));
}

#[derive(Debug, Clone)]
pub struct Switch;

impl Op for Switch {
    fn name(&self) -> Cow<str> {
        "tf.Switch".into()
    }

    fn noutputs(&self) -> usize {
        2
    }
}

impl StatelessOp for Switch {
    fn eval(&self, mut inputs: TVec<SharedTensor>) -> TractResult<TVec<SharedTensor>> {
        let (input, pred) = args_2!(inputs);
        let null = unsafe { Tensor::null_dt(input.datum_type(), input.shape())? };
        if pred.to_scalar::<bool>()? {
            Ok(tvec!(null.into(), input))
        } else {
            Ok(tvec!(input, null.into()))
        }
    }
}

impl InferenceRulesOp for Switch {
    fn rules<'r, 'p: 'r, 's: 'r>(
        &'s self,
        s: &mut Solver<'r>,
        inputs: &'p SharedTensorsProxy,
        outputs: &'p SharedTensorsProxy,
    ) -> InferenceResult {
        s.equals(&inputs.len, 2)?;
        s.equals(&inputs[1].datum_type, DatumType::Bool)?;
        s.equals(&inputs[1].shape, shapefact!())?;
        s.given(&outputs.len, move |s, len| {
            for i in 0..(len as usize) {
                s.equals(&inputs[0].datum_type, &outputs[i].datum_type)?;
                s.equals(&inputs[0].shape, &outputs[i].shape)?;
            }
            Ok(())
        })
    }
}

fn merge(pb: &crate::tfpb::node_def::NodeDef) -> TractResult<Box<Op>> {
    let inputs = pb.get_attr_int::<i32>("N")?;
    Ok(Box::new(Merge::new(inputs as usize)))
}

#[derive(Debug, Clone, new)]
pub struct Merge {
    n: usize,
}

impl Op for Merge {
    fn name(&self) -> Cow<str> {
        "tf.Merge".into()
    }

    fn noutputs(&self) -> usize {
        2
    }
}

impl StatelessOp for Merge {
    fn eval(&self, mut inputs: TVec<SharedTensor>) -> TractResult<TVec<SharedTensor>> {
        let index = inputs
            .iter()
            .position(|t| !t.is_null())
            .ok_or("No tensor received in merge")?;
        Ok(tvec!(
            inputs.remove(index),
            Tensor::from(index as i32).into()
        ))
    }
}

impl InferenceRulesOp for Merge {
    fn rules<'r, 'p: 'r, 's: 'r>(
        &'s self,
        s: &mut Solver<'r>,
        inputs: &'p SharedTensorsProxy,
        outputs: &'p SharedTensorsProxy,
    ) -> InferenceResult {
        s.equals(&inputs.len, self.n as i32)?;
        s.equals(&outputs.len, 1)?;
        for i in 1..self.n {
            s.equals(&inputs[0].datum_type, &inputs[i].datum_type)?;
            s.equals(&inputs[0].shape, &inputs[i].shape)?;
        }
        s.equals(&inputs[0].datum_type, &outputs[0].datum_type)?;
        s.equals(&inputs[0].shape, &outputs[0].shape)?;
        Ok(())
    }
}