extern crate ndarray;
use ndarray_ext::{ArrRng, NdArray};
use rand::Rng;
use tensor::{ArrayLike, Tensor};
mod activation_ops;
mod array_ops;
mod basic_source_ops;
mod binary_ops;
mod const_gen_ops;
mod conv_ops;
mod dot_ops;
pub mod gradient_descent_ops;
mod gradient_ops;
mod math_ops;
mod random_ops;
mod reduction_ops;
mod xent_ops;
impl Tensor {
pub fn get(&self, i: isize) -> Tensor {
let op = array_ops::IndexOp { index: i };
Tensor::builder().set_input(self).build(op)
}
}
pub fn grad(ys: &[&Tensor], xs: &[&Tensor]) -> Vec<Tensor> {
::gradient::symbolic_gradients(ys, xs, &vec![None; ys.len()])
}
pub fn grad_with_default(ys: &[&Tensor], xs: &[&Tensor], output_grads: &[&Tensor]) -> Vec<Tensor> {
::gradient::symbolic_gradients(
ys,
xs,
output_grads
.into_iter()
.map(|a| Some(a.as_ref()))
.collect::<Vec<_>>()
.as_slice(),
)
}
pub fn jacobians(y: &Tensor, xs: &[&Tensor], objective_len: usize) -> Vec<Tensor> {
let vec_vec = (0..objective_len as isize)
.map(|i| {
::gradient::symbolic_gradients(&[&y.get(i)], xs, &[None])
})
.collect::<Vec<Vec<_>>>();
(0..xs.len())
.map(|i| {
let jac = (0..objective_len)
.map(|j| expand_dims(&flatten(&vec_vec[j][i]), &[0]))
.collect::<Vec<_>>();
concat(jac.iter().map(|a| a).collect::<Vec<_>>().as_slice(), 0)
})
.collect::<Vec<_>>()
}
pub fn _hessian_vector_product(ys: &[&Tensor], xs: &[&Tensor], vectors: &[&Tensor]) -> Vec<Tensor> {
let grads =
::gradient::symbolic_gradients(ys, xs, &xs.iter().map(|_| None).collect::<Vec<_>>());
let products = grads
.iter()
.zip(vectors)
.map(|(g, &v)| g * v)
.collect::<Vec<_>>();
let products = products.iter().map(|a| a).collect::<Vec<_>>();
::gradient::symbolic_gradients(products.as_slice(), xs, &[None])
}
pub fn stop_gradient<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.set_has_gradient(false)
.build(gradient_ops::StopGradient)
}
#[inline]
pub fn variable<T: ndarray::Dimension>(arr: ndarray::Array<f32, T>) -> Tensor {
let arr = arr.into_dyn();
Tensor::builder()
.set_shape(convert_to_tensor(::ndarray_ext::shape_of(&arr)))
.set_variable_array(arr)
.build(basic_source_ops::Variable)
}
#[inline]
pub fn placeholder(shape_: &[isize]) -> Tensor {
let b = Tensor::builder().set_is_placeholder(true);
let rank = shape_.len();
let b = if rank == 0 || -1 != shape_[0] {
b.set_shape(convert_to_tensor(
NdArray::from_shape_vec(
ndarray::IxDyn(&[rank]),
shape_.iter().map(|&x| x as f32).collect::<Vec<_>>(),
).unwrap(),
))
} else {
b
};
b.build(basic_source_ops::Placeholder)
}
#[inline]
pub fn constant<T>(arr: ndarray::Array<f32, T>) -> Tensor
where
T: ndarray::Dimension,
{
let arr = arr.into_dyn();
Tensor::builder()
.set_shape(convert_to_tensor(::ndarray_ext::shape_of(&arr)))
.set_constant_array(arr)
.build(basic_source_ops::Const)
}
pub fn shape<A: AsRef<Tensor>>(x: A) -> Tensor {
if let Some(ref inner) = x.as_ref().shape {
inner.clone()
} else {
Tensor::builder()
.set_input(x.as_ref())
.set_has_gradient(false)
.build(array_ops::Shape)
}
}
pub fn size<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.set_has_gradient(false)
.build(array_ops::Size)
}
pub fn rank<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.set_has_gradient(false)
.build(array_ops::Rank)
}
pub fn sin<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.set_shape(x.as_ref().shape())
.build(math_ops::Sin)
}
pub fn cos<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.set_shape(x.as_ref().shape())
.build(math_ops::Cos)
}
pub fn tan<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.set_shape(x.as_ref().shape())
.build(math_ops::Tan)
}
pub fn asin<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.set_shape(x.as_ref().shape())
.build(math_ops::Asin)
}
pub fn acos<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.set_shape(x.as_ref().shape())
.build(math_ops::Acos)
}
pub fn atan<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.set_shape(x.as_ref().shape())
.build(math_ops::Atan)
}
pub fn sinh<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.set_shape(x.as_ref().shape())
.build(math_ops::Sinh)
}
pub fn cosh<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.set_shape(x.as_ref().shape())
.build(math_ops::Cosh)
}
pub fn tanh<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.set_shape(x.as_ref().shape())
.build(math_ops::Tanh)
}
pub fn asinh<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.set_shape(x.as_ref().shape())
.build(math_ops::Asinh)
}
pub fn acosh<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.set_shape(x.as_ref().shape())
.build(math_ops::Acosh)
}
pub fn atanh<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.set_shape(x.as_ref().shape())
.build(math_ops::Atanh)
}
#[doc(hidden)]
pub fn nth_tensor<A>(x: A, n: usize) -> Tensor
where
A: AsRef<Tensor>,
{
Tensor::builder()
.set_input(x.as_ref())
.set_input_indices(vec![n])
.build(activation_ops::Identity)
}
pub fn identity<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.set_shape(x.as_ref().shape())
.build(activation_ops::Identity)
}
#[inline]
fn infer_bin_op_shape<T: AsRef<Tensor>, A: AsRef<Tensor>>(shape_a: T, shape_b: A) -> Tensor {
Tensor::builder()
.set_inputs(vec![shape_a.as_ref(), shape_b.as_ref()])
.build(array_ops::InferBinOpShape)
}
#[inline]
fn bin_op_helper<A: AsRef<Tensor>, B: AsRef<Tensor>, T: ::op::Op + 'static>(
a: A,
b: B,
op: T,
) -> Tensor {
let a_shape = a.as_ref().shape();
let b_shape = b.as_ref().shape();
Tensor::builder()
.set_shape(infer_bin_op_shape(&a_shape, &b_shape))
.set_inputs(vec![a.as_ref(), b.as_ref()])
.build(op)
}
pub fn add<A: AsRef<Tensor>, B: AsRef<Tensor>>(a: A, b: B) -> Tensor {
bin_op_helper(a, b, binary_ops::AddOp)
}
pub fn sub<A: AsRef<Tensor>, B: AsRef<Tensor>>(a: A, b: B) -> Tensor {
bin_op_helper(a, b, binary_ops::SubOp)
}
pub fn mul<A: AsRef<Tensor>, B: AsRef<Tensor>>(a: A, b: B) -> Tensor {
bin_op_helper(a, b, binary_ops::MulOp)
}
pub fn div<A: AsRef<Tensor>, B: AsRef<Tensor>>(a: A, b: B) -> Tensor {
bin_op_helper(a, b, binary_ops::DivOp)
}
#[doc(hidden)]
pub fn mul_inplace<A: AsRef<Tensor>>(a: Tensor, b: A) -> Tensor {
Tensor::builder()
.set_inputs(vec![&a, b.as_ref()])
.set_shape(a.shape())
.build(binary_ops::InplaceMulOp)
}
#[doc(hidden)]
pub fn div_inplace<A: AsRef<Tensor>>(a: Tensor, b: A) -> Tensor {
Tensor::builder()
.set_inputs(vec![&a, b.as_ref()])
.set_shape(a.shape())
.build(binary_ops::InplaceDivOp)
}
pub fn add_inplace<A: AsRef<Tensor>>(a: Tensor, b: A) -> Tensor {
Tensor::builder()
.set_inputs(vec![&a, b.as_ref()])
.set_shape(a.shape())
.build(binary_ops::InplaceAddOp)
}
pub fn sub_inplace<A: AsRef<Tensor>>(a: Tensor, b: A) -> Tensor {
Tensor::builder()
.set_inputs(vec![&a, b.as_ref()])
.set_shape(a.shape())
.build(binary_ops::InplaceSubOp)
}
pub fn sqrt<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.set_shape(x.as_ref().shape())
.build(math_ops::Sqrt)
}
pub fn pow<A: AsRef<Tensor>>(x: A, a: f32) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.set_shape(x.as_ref().shape())
.build(math_ops::Pow { a })
}
pub fn log<A: AsRef<Tensor>>(x: A, a: f32) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.set_shape(x.as_ref().shape())
.build(math_ops::Log { a })
}
pub fn exp<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.set_shape(x.as_ref().shape())
.build(math_ops::Exp)
}
pub fn maximum<A: AsRef<Tensor>, B: AsRef<Tensor>>(a: A, b: B) -> Tensor {
Tensor::builder()
.set_inputs(vec![a.as_ref(), b.as_ref()])
.build(math_ops::Maximum)
}
pub fn minimum<A: AsRef<Tensor>, B: AsRef<Tensor>>(a: A, b: B) -> Tensor {
Tensor::builder()
.set_inputs(vec![a.as_ref(), b.as_ref()])
.build(math_ops::Minimum)
}
pub fn add_n(xs: &[&Tensor]) -> Tensor {
let len = xs.len();
assert_ne!(len, 0);
if len == 1 {
xs[0].clone()
} else {
Tensor::builder()
.set_inputs(xs.to_vec())
.set_shape(xs[0].shape())
.build(array_ops::AddN)
}
}
pub fn equal<A: AsRef<Tensor>, B: AsRef<Tensor>>(a: A, b: B) -> Tensor {
Tensor::builder()
.set_inputs(vec![a.as_ref(), b.as_ref()])
.build(math_ops::Equal)
}
pub fn not_equal<A: AsRef<Tensor>, B: AsRef<Tensor>>(a: A, b: B) -> Tensor {
Tensor::builder()
.set_inputs(vec![a.as_ref(), b.as_ref()])
.build(math_ops::NotEqual)
}
pub fn argmax<A: AsRef<Tensor>>(x: A, axis: isize, keep_dim: bool) -> Tensor {
let op = reduction_ops::ArgMax { axis, keep_dim };
Tensor::builder().set_input(x.as_ref()).build(op)
}
pub fn expand_dims<A: AsRef<Tensor>, T: ArrayLike>(x: A, axes: &T) -> Tensor {
let op = array_ops::ExpandDims;
Tensor::builder()
.set_inputs(vec![x.as_ref(), &axes.as_tensor()])
.build(op)
}
pub fn squeeze<A: AsRef<Tensor>, T: ArrayLike>(x: A, axes: &T) -> Tensor {
let op = array_ops::Squeeze;
Tensor::builder()
.set_inputs(vec![x.as_ref(), &axes.as_tensor()])
.build(op)
}
pub fn tile<A: AsRef<Tensor>>(x: A, axis: isize, num: usize) -> Tensor {
let op = array_ops::Tile { axis, num };
Tensor::builder().set_input(x.as_ref()).build(op)
}
pub fn clip<A: AsRef<Tensor>>(x: A, min: f32, max: f32) -> Tensor {
let op = array_ops::Clip { min, max };
Tensor::builder().set_input(x.as_ref()).build(op)
}
pub fn reduce_max<T: ArrayLike, A: AsRef<Tensor>>(x: A, axes: &T, keep_dims: bool) -> Tensor {
let op = reduction_ops::ReduceMax {
keep_dims,
sparse_axes: false,
};
Tensor::builder()
.set_inputs(vec![x.as_ref(), &axes.as_tensor()])
.build(op)
}
pub fn reduce_min<T: ArrayLike, A: AsRef<Tensor>>(x: A, axes: &T, keep_dims: bool) -> Tensor {
let op = reduction_ops::ReduceMin {
keep_dims,
sparse_axes: false,
};
Tensor::builder()
.set_inputs(vec![x.as_ref(), &axes.as_tensor()])
.build(op)
}
pub fn reduce_sum<T: ArrayLike, A: AsRef<Tensor>>(x: A, axes: &T, keep_dims: bool) -> Tensor {
let op = reduction_ops::ReduceSum {
keep_dims,
sparse_axes: false,
};
Tensor::builder()
.set_inputs(vec![x.as_ref(), &axes.as_tensor()])
.build(op)
}
pub fn reduce_mean<T: ArrayLike, A: AsRef<Tensor>>(x: A, axes: &T, keep_dims: bool) -> Tensor {
let op = reduction_ops::ReduceMean {
keep_dims,
sparse_axes: false,
};
Tensor::builder()
.set_inputs(vec![x.as_ref(), &axes.as_tensor()])
.build(op)
}
pub fn reduce_prod<T: ArrayLike, A: AsRef<Tensor>>(x: A, axes: &T, keep_dims: bool) -> Tensor {
let op = reduction_ops::ReduceProd {
keep_dims,
sparse_axes: false,
};
Tensor::builder()
.set_inputs(vec![x.as_ref(), &axes.as_tensor()])
.build(op)
}
pub fn reshape<T: ArrayLike, A: AsRef<Tensor>>(x: A, shape: &T) -> Tensor {
Tensor::builder()
.set_inputs(vec![x.as_ref(), &shape.as_tensor()])
.build(array_ops::Reshape)
}
pub fn flatten<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_inputs(vec![x.as_ref(), &scalar(-1.)])
.set_shape(x.as_ref().shape())
.build(array_ops::Reshape)
}
pub fn sign<A: AsRef<Tensor>>(a: A) -> Tensor {
Tensor::builder()
.set_shape(a.as_ref().shape())
.set_input(a.as_ref())
.build(math_ops::Sign)
}
pub fn abs<A: AsRef<Tensor>>(a: A) -> Tensor {
Tensor::builder()
.set_shape(a.as_ref().shape())
.set_input(a.as_ref())
.build(math_ops::Abs)
}
pub fn floor<A: AsRef<Tensor>>(a: A) -> Tensor {
Tensor::builder()
.set_shape(a.as_ref().shape())
.set_input(a.as_ref())
.build(math_ops::Floor)
}
pub fn neg<A: AsRef<Tensor>>(a: A) -> Tensor {
Tensor::builder()
.set_shape(a.as_ref().shape())
.set_input(a.as_ref())
.build(math_ops::NegOp)
}
pub fn square<A: AsRef<Tensor>>(a: A) -> Tensor {
Tensor::builder()
.set_shape(a.as_ref().shape())
.set_input(a.as_ref())
.build(math_ops::Square)
}
pub fn reciprocal<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_shape(x.as_ref().shape())
.set_input(x.as_ref())
.build(math_ops::Reciprocal)
}
pub fn ceil<A: AsRef<Tensor>>(a: A) -> Tensor {
Tensor::builder()
.set_shape(a.as_ref().shape())
.set_input(a.as_ref())
.build(math_ops::Ceil)
}
pub fn greater<A: AsRef<Tensor>, B: AsRef<Tensor>>(a: A, b: B) -> Tensor {
Tensor::builder()
.set_inputs(vec![a.as_ref(), b.as_ref()])
.build(math_ops::Greater)
}
pub fn greater_equal<A: AsRef<Tensor>, B: AsRef<Tensor>>(a: A, b: B) -> Tensor {
Tensor::builder()
.set_inputs(vec![a.as_ref(), b.as_ref()])
.build(math_ops::GreaterEqual)
}
pub fn lesser<A: AsRef<Tensor>, B: AsRef<Tensor>>(a: A, b: B) -> Tensor {
Tensor::builder()
.set_inputs(vec![a.as_ref(), b.as_ref()])
.build(math_ops::Lesser)
}
pub fn lesser_equal<A: AsRef<Tensor>, B: AsRef<Tensor>>(a: A, b: B) -> Tensor {
Tensor::builder()
.set_inputs(vec![a.as_ref(), b.as_ref()])
.build(math_ops::LesserEqual)
}
pub fn sigmoid<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_shape(x.as_ref().shape())
.set_input(x.as_ref())
.build(activation_ops::Sigmoid)
}
pub fn elu<A: AsRef<Tensor>>(x: A, alpha: f32) -> Tensor {
Tensor::builder()
.set_shape(x.as_ref().shape())
.set_input(x.as_ref())
.build(activation_ops::ELU { alpha })
}
pub fn relu<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_shape(x.as_ref().shape())
.set_input(x.as_ref())
.build(activation_ops::ReLU)
}
pub fn leaky_relu<A: AsRef<Tensor>>(x: A, alpha: f32) -> Tensor {
maximum(&x, alpha * x.as_ref())
}
pub fn softplus<A: AsRef<Tensor>>(x: A) -> Tensor {
Tensor::builder()
.set_shape(x.as_ref().shape())
.set_input(x.as_ref())
.build(activation_ops::Softplus)
}
pub fn reduce_logsumexp<A: AsRef<Tensor>>(x: A, axis: isize, keep_dims: bool) -> Tensor {
let op = math_ops::LogSumExp { axis, keep_dims };
Tensor::builder().set_input(x.as_ref()).build(op)
}
pub fn log_softmax<A: AsRef<Tensor>>(x: A, axis: isize) -> Tensor {
Tensor::builder()
.set_shape(x.as_ref().shape())
.set_input(x.as_ref())
.build(xent_ops::LogSoftmax { axis })
}
pub fn softmax<A: AsRef<Tensor>>(x: A, axis: isize) -> Tensor {
let op = activation_ops::Softmax { axis };
Tensor::builder().set_input(x.as_ref()).build(op)
}
pub fn sigmoid_cross_entropy<A: AsRef<Tensor>, B: AsRef<Tensor>>(y: A, t: B) -> Tensor {
let op = xent_ops::SigmoidCrossEntropy;
Tensor::builder()
.set_shape(y.as_ref().shape())
.set_inputs(vec![y.as_ref(), t.as_ref()])
.build(op)
}
pub fn softmax_cross_entropy<A: AsRef<Tensor>, B: AsRef<Tensor>>(y: A, t: B) -> Tensor {
let op = xent_ops::SoftmaxCrossEntropy;
Tensor::builder()
.set_inputs(vec![y.as_ref(), t.as_ref()])
.build(op)
}
pub fn sparse_softmax_cross_entropy<A: AsRef<Tensor>, B: AsRef<Tensor>>(y: A, t: B) -> Tensor {
let op = xent_ops::SparseSoftmaxCrossEntropy;
Tensor::builder()
.set_inputs(vec![y.as_ref(), t.as_ref()])
.build(op)
}
pub fn matmul<A: AsRef<Tensor>, B: AsRef<Tensor>>(a: A, b: B) -> Tensor {
let op = dot_ops::MatMul {
transpose_a: false,
transpose_b: false,
};
Tensor::builder()
.set_inputs(vec![a.as_ref(), b.as_ref()])
.build(op)
}
pub fn matmul_t<A: AsRef<Tensor>, B: AsRef<Tensor>>(
a: A,
b: B,
transpose_a: bool,
transpose_b: bool,
) -> Tensor {
let op = dot_ops::MatMul {
transpose_a,
transpose_b,
};
Tensor::builder()
.set_inputs(vec![a.as_ref(), b.as_ref()])
.build(op)
}
pub fn tensordot<A, B, T>(a: A, b: B, a_axes: &T, b_axes: &T) -> Tensor
where
T: ArrayLike,
A: AsRef<Tensor>,
B: AsRef<Tensor>,
{
fn normalize_negative_axes(axes: &Tensor, x_rank: &Tensor) -> Tensor {
let ref zero = zeros(&axes.shape());
let ge = greater_equal(axes, zero);
let lt = lesser(axes, zero);
add_inplace(mul_inplace(ge, axes), &mul_inplace(lt, &(axes + x_rank)))
}
fn preprocess<T: ArrayLike>(x: &Tensor, axes: &T, flip: bool) -> (Tensor, Tensor) {
let ref x_shape = x.shape();
let ref x_rank = x.rank();
let ref axes = normalize_negative_axes(&axes.as_tensor(), x_rank);
let ref free = setdiff1d(&range(&scalar(0.), x_rank, &scalar(1.)), axes);
let free_dims = gather(x_shape, free, 0);
let ref axes_dims = gather(x_shape, axes, 0);
let ref prod_free_dims = reduce_prod(&free_dims, &[0], true);
let ref prod_axes_dims = reduce_prod(axes_dims, &[0], true);
let (perm, new_shape) = if flip {
(
concat(&[axes, free], 0),
concat(&[prod_axes_dims, prod_free_dims], 0),
)
} else {
(
concat(&[free, axes], 0),
concat(&[prod_free_dims, prod_axes_dims], 0),
)
};
(reshape(&transpose(x, &perm), &new_shape), free_dims)
}
let ((a_reshaped, a_free_dims), (b_reshaped, b_free_dims)) = (
preprocess(a.as_ref(), a_axes, false),
preprocess(b.as_ref(), b_axes, true),
);
let ref mm = matmul(&a_reshaped, &b_reshaped);
let final_shape = concat(&[&a_free_dims, &b_free_dims], 0);
reshape(mm, &final_shape)
}
pub fn batch_matmul<A: AsRef<Tensor>, B: AsRef<Tensor>>(a: A, b: B) -> Tensor {
let op = dot_ops::BatchMatMul {
transpose_a: false,
transpose_b: false,
};
Tensor::builder()
.set_inputs(vec![a.as_ref(), b.as_ref()])
.build(op)
}
pub fn setdiff1d<A: AsRef<Tensor>, B: AsRef<Tensor>>(a: A, b: B) -> Tensor {
let op = array_ops::SetDiff1D;
Tensor::builder()
.set_inputs(vec![a.as_ref(), b.as_ref()])
.build(op)
}
pub fn transpose<T: ArrayLike, A: AsRef<Tensor>>(x: A, perm: &T) -> Tensor {
let op = math_ops::Transpose { zip: true };
Tensor::builder()
.set_inputs(vec![x.as_ref(), &perm.as_tensor()])
.build(op)
}
pub fn split<A: AsRef<Tensor>>(x: A, sizes: &[usize], axis: isize) -> Vec<Tensor> {
(0..sizes.len())
.map(|i| {
let op = array_ops::Split {
sizes: sizes.to_vec(),
index: i,
axis,
};
Tensor::builder().set_input(x.as_ref()).build(op)
})
.collect::<Vec<_>>()
}
pub fn slice<A: AsRef<Tensor>>(x: A, starts: &[isize], ends: &[isize]) -> Tensor {
assert_eq!(starts.len(), ends.len());
let starts_ends = starts.iter().zip(ends.iter());
let indices = starts_ends
.map(|(s, e)| ndarray::Si(*s, if *e == -1 { None } else { Some(*e) }, 1))
.collect::<Vec<ndarray::Si>>();
let op = array_ops::Slice {
indices: indices.into_boxed_slice(),
};
Tensor::builder().set_input(x.as_ref()).build(op)
}
pub fn concat(tensors: &[&Tensor], axis: isize) -> Tensor {
let op = array_ops::Concat { axis };
Tensor::builder().set_inputs(tensors.to_vec()).build(op)
}
pub fn gather_common<T: ArrayLike, A: AsRef<Tensor>>(param: A, indices: &T, axis: isize) -> Tensor {
let op = array_ops::Gather {
axis,
should_normalize_negative_indices: true,
};
Tensor::builder()
.set_inputs(vec![&indices.as_tensor(), param.as_ref()])
.build(op)
}
pub fn gather<T: ArrayLike, A: AsRef<Tensor>>(param: A, indices: &T, axis: isize) -> Tensor {
let op = array_ops::Gather {
axis,
should_normalize_negative_indices: false,
};
Tensor::builder()
.set_inputs(vec![&indices.as_tensor(), param.as_ref()])
.build(op)
}
pub fn normalize<T: ArrayLike, A: AsRef<Tensor>>(x: A, axes: &T) -> Tensor {
let x = x.as_ref();
let axes = axes.as_tensor();
let ref mean = reduce_mean(x, &axes, true);
let ref centered = x - mean;
let ref variance = reduce_mean(square(centered), &axes, true);
(x - mean) / sqrt(&(variance + 1e-5))
}
pub fn batch_norm<A, B, C>(x: A, scale: B, shift: C) -> Tensor
where
A: AsRef<Tensor>,
B: AsRef<Tensor>,
C: AsRef<Tensor>,
{
normalize(x, &[0]) * scale.as_ref() + shift.as_ref()
}
pub fn scalar(val: f32) -> Tensor {
let op = const_gen_ops::Scalar { val };
Tensor::builder()
.set_shape(convert_to_tensor(::ndarray_ext::scalar_shape()))
.build(op)
}
pub fn random_normal<T: ArrayLike>(shape: &T, mean: f64, stddev: f64) -> Tensor {
random_normal_rng(Default::default(), shape, mean, stddev)
}
pub fn random_normal_rng<T: ArrayLike, R: Rng + 'static>(
arr_rng: ArrRng<R>,
shape: &T,
mean: f64,
stddev: f64,
) -> Tensor {
let shape = shape.as_tensor();
Tensor::builder()
.set_input(&shape)
.set_shape(shape)
.build(random_ops::RandomNormal::new(arr_rng, mean, stddev))
}
pub fn random_uniform<T: ArrayLike>(shape: &T, min: f64, max: f64) -> Tensor {
random_uniform_rng(Default::default(), shape, min, max)
}
pub fn random_uniform_rng<T: ArrayLike, R: Rng + 'static>(
arr_rng: ArrRng<R>,
shape: &T,
min: f64,
max: f64,
) -> Tensor {
let shape = shape.as_tensor();
Tensor::builder()
.set_input(&shape)
.set_shape(shape)
.build(random_ops::RandomUniform::new(arr_rng, min, max))
}
pub fn standard_normal<T: ArrayLike>(shape: &T) -> Tensor {
standard_normal_rng(Default::default(), shape)
}
pub fn standard_normal_rng<T: ArrayLike, R: Rng + 'static>(
arr_rng: ArrRng<R>,
shape: &T,
) -> Tensor {
let shape = shape.as_tensor();
Tensor::builder()
.set_input(&shape)
.set_shape(shape)
.build(random_ops::StandardNormal::new(arr_rng))
}
pub fn standard_uniform<T: ArrayLike>(shape: &T) -> Tensor {
standard_uniform_rng(Default::default(), shape)
}
pub fn standard_uniform_rng<T: ArrayLike, R: Rng + 'static>(
arr_rng: ArrRng<R>,
shape: &T,
) -> Tensor {
let shape = shape.as_tensor();
Tensor::builder()
.set_input(&shape)
.set_shape(shape)
.build(random_ops::StandardUniform::new(arr_rng))
}
pub fn bernoulli<T: ArrayLike>(shape: &T, p: f64) -> Tensor {
bernoulli_rng(Default::default(), shape, p)
}
pub fn bernoulli_rng<T: ArrayLike, R: Rng + 'static>(
arr_rng: ArrRng<R>,
shape: &T,
p: f64,
) -> Tensor {
let shape = shape.as_tensor();
Tensor::builder()
.set_input(&shape)
.set_shape(shape)
.build(random_ops::Bernoulli::new(arr_rng, p))
}
pub fn random_exp<T: ArrayLike>(shape: &T, lambda: f64) -> Tensor {
random_exp_rng(Default::default(), shape, lambda)
}
pub fn random_exp_rng<T: ArrayLike, R: Rng + 'static>(
arr_rng: ArrRng<R>,
shape: &T,
lambda: f64,
) -> Tensor {
let shape = shape.as_tensor();
Tensor::builder()
.set_input(&shape)
.set_shape(shape)
.build(random_ops::Exponential::new(arr_rng, lambda))
}
pub fn random_gamma<T: ArrayLike>(shape: &T, shape_param: f64, scale: f64) -> Tensor {
random_gamma_rng(Default::default(), shape, shape_param, scale)
}
pub fn random_gamma_rng<T: ArrayLike, R: Rng + 'static>(
arr_rng: ArrRng<R>,
shape: &T,
shape_param: f64,
scale: f64,
) -> Tensor {
let shape = shape.as_tensor();
Tensor::builder()
.set_input(&shape)
.set_shape(shape)
.build(random_ops::Gamma::new(arr_rng, shape_param, scale))
}
pub fn log_normal<T: ArrayLike>(shape: &T, mean: f64, stddev: f64) -> Tensor {
log_normal_rng(Default::default(), shape, mean, stddev)
}
pub fn log_normal_rng<T: ArrayLike, R: Rng + 'static>(
arr_rng: ArrRng<R>,
shape: &T,
mean: f64,
stddev: f64,
) -> Tensor {
let shape = shape.as_tensor();
Tensor::builder()
.set_input(&shape)
.set_shape(shape)
.build(random_ops::LogNormal::new(arr_rng, mean, stddev))
}
pub fn convert_to_tensor<T>(arr: ndarray::Array<f32, T>) -> Tensor
where
T: ndarray::Dimension,
{
let arr = arr.into_dyn();
let shape = {
let op = const_gen_ops::ConvertToTensor {
arr: ::ndarray_ext::shape_of(&arr),
};
Tensor::builder().build(op)
};
Tensor::builder()
.set_shape(shape)
.build(const_gen_ops::ConvertToTensor { arr })
}
pub fn zeros<T: ArrayLike>(shape: &T) -> Tensor {
Tensor::builder()
.set_input(&shape.as_tensor())
.build(const_gen_ops::Zeros)
}
pub fn ones<T: ArrayLike>(shape: &T) -> Tensor {
Tensor::builder()
.set_input(&shape.as_tensor())
.build(const_gen_ops::Ones)
}
pub fn range<T: ArrayLike>(start: &T, end: &T, step: &T) -> Tensor {
Tensor::builder()
.set_inputs(vec![
&start.as_tensor(),
&end.as_tensor(),
&step.as_tensor(),
])
.build(const_gen_ops::Range)
}
pub fn conv2d<A, B>(x: A, w: B, pad: usize, stride: usize) -> Tensor
where
A: AsRef<Tensor>,
B: AsRef<Tensor>,
{
Tensor::builder()
.set_inputs(vec![x.as_ref(), w.as_ref()])
.build(conv_ops::conv2d::Conv2D {
pad,
stride,
dilation: 1,
})
}
pub fn dilated_conv2d<A, B>(x: A, w: B, pad: usize, stride: usize, dilate: usize) -> Tensor
where
A: AsRef<Tensor>,
B: AsRef<Tensor>,
{
Tensor::builder()
.set_inputs(vec![x.as_ref(), w.as_ref()])
.build(conv_ops::conv2d::Conv2D {
pad,
stride,
dilation: dilate,
})
}
pub fn conv2d_transpose<A, B>(x: A, w: B, pad: usize, stride: usize) -> Tensor
where
A: AsRef<Tensor>,
B: AsRef<Tensor>,
{
Tensor::builder()
.set_inputs(vec![x.as_ref(), w.as_ref()])
.build(conv_ops::conv2d_transpose::Conv2DTranspose {
pad,
stride,
dilation: 1,
cols: None,
})
}
pub fn dilated_conv2d_transpose<A, B>(
x: A,
w: B,
pad: usize,
stride: usize,
dilate: usize,
) -> Tensor
where
A: AsRef<Tensor>,
B: AsRef<Tensor>,
{
Tensor::builder()
.set_inputs(vec![x.as_ref(), w.as_ref()])
.build(conv_ops::conv2d_transpose::Conv2DTranspose {
pad,
stride,
dilation: dilate,
cols: None,
})
}
pub fn max_pool2d<A: AsRef<Tensor>>(x: A, pool_size: usize, pad: usize, stride: usize) -> Tensor {
Tensor::builder()
.set_input(x.as_ref())
.build(conv_ops::max_pool2d::MaxPool2D {
pad,
stride,
size: pool_size,
})
}