use std::rc::Rc;
use co::backend::IBackend;
use co::tensor::SharedTensor;
use coblas::transpose::Transpose;
use coblas::plugin::*;
use layer::*;
use util::{ArcLock, native_scalar, LayerOps};
use weight::FillerType;
use leaf_capnp::linear_config as capnp_config;
use capnp_util::*;
#[derive(Debug)]
pub struct Linear {
output_size: usize,
one: SharedTensor<f32>,
zero: SharedTensor<f32>,
}
impl Linear {
pub fn from_config(config: &LinearConfig) -> Linear {
let one = native_scalar(1f32);
let zero = native_scalar(0f32);
Linear {
output_size: config.output_size,
one: one,
zero: zero,
}
}
fn calculate_input_size(input_shape: &[usize]) -> usize {
input_shape.iter().skip(1).fold(1, |prod, i| prod * i)
}
fn calculate_output_shape(&self, input_shape: &[usize]) -> Vec<usize> {
let n = input_shape[0]; vec![n, self.output_size]
}
fn calculate_weight_shape(&self, input_shape: &[usize]) -> Vec<usize> {
let m = Self::calculate_input_size(input_shape);
vec![self.output_size, m]
}
}
impl<B: IBackend + LayerOps<f32>> ILayer<B> for Linear {
impl_ilayer_common!();
fn auto_weight_blobs(&self) -> bool {
true
}
fn init(&mut self, backend: Rc<B>) {
let device = <B as IBackend>::device(&backend);
let _ = self.one.add_device(device);
self.one.sync(device).unwrap();
let _ = self.zero.add_device(device);
self.zero.sync(device).unwrap();
}
fn reshape(&mut self,
backend: ::std::rc::Rc<B>,
input_data: &mut Vec<ArcLock<SharedTensor<f32>>>,
input_gradient: &mut Vec<ArcLock<SharedTensor<f32>>>,
weights_data: &mut Vec<ArcLock<SharedTensor<f32>>>,
weights_gradient: &mut Vec<ArcLock<SharedTensor<f32>>>,
output_data: &mut Vec<ArcLock<SharedTensor<f32>>>,
output_gradient: &mut Vec<ArcLock<SharedTensor<f32>>>) {
let input = input_data[0].read().unwrap();
let output_shape = self.calculate_output_shape(input.desc());
output_data[0].write().unwrap().resize(&output_shape).unwrap();
output_gradient[0].write().unwrap().resize(&output_shape).unwrap();
let weight_shape = self.calculate_weight_shape(input.desc());
if let Some(weight) = weights_data.get(0) {
weight.write().unwrap().resize(&weight_shape).unwrap();
let filler = FillerType::Glorot {
input_size: Self::calculate_input_size(input.desc()),
output_size: self.output_size,
};
filler.fill(&mut weight.write().unwrap());
let native_backend = ::util::native_backend();
let bound_weight = weight.read().unwrap();
let native_output = bound_weight.get(native_backend.device()).unwrap().as_native().unwrap();
}
if let Some(weight) = weights_gradient.get(0) {
weight.write().unwrap().resize(&weight_shape).unwrap();
}
}
}
impl<B: IBackend + LayerOps<f32>> ComputeOutput<f32, B> for Linear {
fn compute_output(&self,
backend: &B,
weights: &[&SharedTensor<f32>],
input_data: &[&SharedTensor<f32>],
output_data: &mut [&mut SharedTensor<f32>]) {
backend.gemm_plain(&self.one, Transpose::NoTrans, input_data[0], Transpose::Trans, weights[0], &self.zero, output_data[0]).unwrap();
let has_bias_term = false; if has_bias_term {
let bias_multiplier = unimplemented!();
let bias_data = unimplemented!();
backend.gemm_plain(&self.one, Transpose::NoTrans, bias_multiplier, Transpose::NoTrans, bias_data, &self.one, output_data[0]).unwrap();
}
}
}
impl<B: IBackend + LayerOps<f32>> ComputeInputGradient<f32, B> for Linear {
fn compute_input_gradient(&self,
backend: &B,
weights_data: &[&SharedTensor<f32>],
output_data: &[&SharedTensor<f32>],
output_gradients: &[&SharedTensor<f32>],
input_data: &[&SharedTensor<f32>],
input_gradients: &mut [&mut SharedTensor<f32>]) {
backend.gemm_plain(&self.one, Transpose::NoTrans, output_gradients[0], Transpose::NoTrans, weights_data[0], &self.zero, input_gradients[0]).unwrap();
}
}
impl<B: IBackend + LayerOps<f32>> ComputeParametersGradient<f32, B> for Linear {
fn compute_parameters_gradient(&self,
backend: &B,
output_data: &[&SharedTensor<f32>],
output_gradients: &[&SharedTensor<f32>],
input_data: &[&SharedTensor<f32>],
parameters_gradients: &mut [&mut SharedTensor<f32>]) {
backend.gemm_plain(&self.one, Transpose::Trans, output_gradients[0], Transpose::NoTrans, input_data[0], &self.zero, parameters_gradients[0]).unwrap();
}
}
impl ::std::default::Default for Linear {
fn default() -> Linear {
let config = LinearConfig {
output_size: 10,
};
Self::from_config(&config)
}
}
#[derive(Debug, Clone)]
#[allow(missing_copy_implementations)]
pub struct LinearConfig {
pub output_size: usize,
}
impl<'a> CapnpWrite<'a> for LinearConfig {
type Builder = capnp_config::Builder<'a>;
fn write_capnp(&self, builder: &mut Self::Builder) {
builder.borrow().set_output_size(self.output_size as u64);
}
}
impl<'a> CapnpRead<'a> for LinearConfig {
type Reader = capnp_config::Reader<'a>;
fn read_capnp(reader: Self::Reader) -> Self {
let output_size = reader.get_output_size() as usize;
LinearConfig {
output_size: output_size
}
}
}
impl Into<LayerType> for LinearConfig {
fn into(self) -> LayerType {
LayerType::Linear(self)
}
}