/* NiuTrans.Tensor - an open-source tensor library * Copyright (C) 2017, Natural Language Processing Lab, Northestern University. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * $Created by: XIAO Tong (email: xiaotong@mail.neu.edu.cn) 2018-04-24 */ #include "../../XTensor.h" #include "../../XName.h" #include "Multiply.h" #include "Multiply.cuh" #include "MultiplyDim.h" namespace nts { // namespace nts(NiuTrans.Tensor) /* element-wise product of two tensors c(i) = a(i)*b(i) + \alpha * c(i) where i is the index of the item >> a - tensor a >> b - tensor b >> c - result tensor >> alpha - the coefficient >> leadingDim - the dimension along which we perform broadcasting */ void _Multiply(const XTensor * a, const XTensor * b, XTensor * c, DTYPE alpha, int leadingDim) { int leadingDimRDI = a->order - leadingDim - 1; CheckNTErrors((a->unitNum <= c->unitNum && b->unitNum <= c->unitNum), "Unmatched tensors in multiplication!"); CheckNTErrors((a->order == b->order && a->order == c->order), "Unmatched tensors!"); #ifdef USE_CUDA if (a->devID >= 0 || b->devID >= 0 || c->devID >= 0) { _CudaMultiply(a, b, c, alpha, leadingDim); return; } #endif int stride = 1; int blockSizeA = 1; int blockSizeB = 1; int blockSizeC = 1; int blockNum = 1; int dimensionSizeA = a->dimSizeRDI[leadingDimRDI]; int dimensionSizeB = b->dimSizeRDI[leadingDimRDI]; int dimensionSizeC = c->dimSizeRDI[leadingDimRDI]; for (int i = 0; i < a->order; i++) { if (i != leadingDimRDI) { CheckNTErrors((a->dimSizeRDI[i] == b->dimSizeRDI[i] && a->dimSizeRDI[i] == c->dimSizeRDI[i]), "Unmatched tensors!"); } if (i < leadingDimRDI) stride *= a->dimSizeRDI[i]; } blockSizeA = stride * dimensionSizeA; blockSizeB = stride * dimensionSizeB; blockSizeC = stride * dimensionSizeC; blockNum = a->unitNum / blockSizeA; if (!a->isSparse && !b->isSparse) { if (a->dataType == DEFAULT_DTYPE && b->dataType == DEFAULT_DTYPE) { if (a->unitNum == c->unitNum && b->unitNum == c->unitNum) { int size = a->unitNum; DTYPE * ap = (DTYPE*)a->data; DTYPE * bp = (DTYPE*)b->data; DTYPE * cp = (DTYPE*)c->data; if (alpha == 0) { for (int i = 0; i < size; i++) cp[i] = ap[i] * bp[i]; } else { for (int i = 0; i < size; i++) cp[i] = ap[i] * bp[i] + alpha * cp[i]; } } else { for (int k = 0; k < blockNum; k++) { for (int ci = 0, ai = 0, bi = 0; ci < dimensionSizeC; ci++, ai++, bi++) { if (ai >= dimensionSizeA) ai = 0; if (bi >= dimensionSizeB) bi = 0; DTYPE * ap = (DTYPE*)a->data + k * blockSizeA + ai * stride; DTYPE * bp = (DTYPE*)b->data + k * blockSizeB + bi * stride; DTYPE * cp = (DTYPE*)c->data + k * blockSizeC + ci * stride; for (int j = 0; j < stride; j++) cp[j] = ap[j] * bp[j] + cp[j] * alpha; } } } } else { // TODO!! ShowNTErrors("TODO!"); } } else { // TODO!! ShowNTErrors("TODO!"); } } /* element-wise product of two tensors (do it on site) keep the result in the input tensor a and return nothing a(i) = a(i)*b(i) + \alpha * a(i) where i is the index of the item >> a - tensor a (where keep the result) >> b - tensor b >> alpha - the coefficient >> leadingDim - the dimension along which we perform broadcasting */ void _MultiplyMe(XTensor * a, const XTensor * b, DTYPE alpha, int leadingDim) { _Multiply(a, b, a, alpha, leadingDim); } /* return a dimension if the multiplication is performed as MultiplyDim (in more details in MultiplyDim.h) >> a - a tensor >> b - another tensor for multiplication */ int GetMultiplyDimIndex(const XTensor &a, const XTensor &b) { if(a.order < b.order) return -1; if(XTensor::IsSameShaped(&a, &b)) return -1; int hitCount = 0; int hitDim = -1; for(int i = 0; i < b.order; i++){ if(b.dimSize[b.order - 1 - i] == 1) continue; else if(b.dimSize[b.order - 1 - i] == a.dimSize[a.order - 1 - i]){ hitCount++; hitDim = a.order - b.order + i; } } if(hitCount == 1) return hitDim; else return -1; } /* element-wise product of two tensors (return a XTensor structure) make a new tensor c to keep the result and return it c(i) = a(i)*b(i) where i is the index of the item >> a - tensor a >> b - tensor b >> leadingDim - the dimension along which we perform broadcasting << return - the product of the tensors */ XTensor Multiply(const XTensor &a, const XTensor &b, DTYPE alpha, int leadingDim) { XTensor c(&a); c.SetTMPFlag(); int n = GetMultiplyDimIndex(a, b); if(n == -1){ CheckNTErrors(a.dimSize[leadingDim] == b.dimSize[leadingDim], "TODO!"); /* call _Multiply function */ _Multiply(&a, &b, &c, 0, leadingDim); /* tensor connections */ XLink::MakeLink(&a, &b, &c, MATH_MULTIPLY); XLink::AddParamToHead(&c, alpha); XLink::AddParamToHeadInt(&c, leadingDim); } else if(n >= 0 && n < a.order){ /* call _MultiplyDim function */ _MultiplyDim(&a, &b, &c, n, alpha); /* tensor connections */ XLink::MakeLink(&a, &b, &c, MATH_MULTIPLYDIM); XLink::AddParamToHeadInt(&c, n); XLink::AddParamToHead(&c, alpha); } else{ ShowNTErrors("Something is wrong!"); } return c; } } // namespace nts(NiuTrans.Tensor)