// binary zip operation, e.g. Plus // If allowBroadcast then one can be a sub-dimension of the other (if layout then only for rows, otherwise for cols, too). // This also helpfully resizes the children if not yet sized. void ComputationNodeBase::ValidateBinaryZip(bool isFinalValidationPass, bool allowBroadcast) { assert(m_inputs.size() == 2); ComputationNodeBase::Validate(isFinalValidationPass); InferMBLayoutFromInputsForStandardCase(isFinalValidationPass); ValidateInferBinaryInputDims(); if (isFinalValidationPass) ValidateMBLayout(Input(0), Input(1)); // result has tensor shape with dimensions being the max over both let shape0 = GetInputSampleLayout(0); let shape1 = GetInputSampleLayout(1); SmallVector<size_t> dims = shape0.GetDims(); if (shape1.GetRank() > dims.size()) dims.resize(shape1.GetRank(), 1); // pad with ones // If rank of [0] is higher than we only need to take max over rank [1]. // If rank of [1] is higher then we have padded to equal lentgh. for (size_t k = 0; k < shape1.GetRank(); k++) { size_t dim1 = shape1[k]; // BUGBUG: We must consider the allowBroadcast flag here. if (dims[k] <= 1 && dim1 != 0) // is [0] broadcasting (1) or unspecified (0)? dims[k] = dim1; // then use dimension we broadcast to else if (dim1 <= 1 && dims[k] != 0) // if [1] is broadcasting or unspecified ; // then dims is already correct else if (isFinalValidationPass && dim1 != dims[k]) // no broadcasting or unspecified: they must match InvalidArgument("%ls: Input dimensions [%s] and [%s] are not compatible.", NodeDescription().c_str(), string(shape0).c_str(), string(shape1).c_str()); } SetDims(TensorShape(dims), HasMBLayout()); }
// binary zip operation, e.g. Plus // If allowBroadcast then one can be a sub-dimension of the other (if layout then only for rows, otherwise for cols, too). // This also helpfully resizes the children if not yet sized. void ComputationNodeBase::ValidateBinaryZip(bool isFinalValidationPass, bool allowBroadcast) { assert(m_inputs.size() == 2); ComputationNodeBase::Validate(isFinalValidationPass); InferMBLayoutFromInputsForStandardCase(isFinalValidationPass); ValidateInferBinaryInputDims(); if (isFinalValidationPass && Input(0)->GetMBLayout() != Input(1)->GetMBLayout() && Input(0)->HasMBLayout() && Input(1)->HasMBLayout()) { LogicError("%ls: Minibatch layouts are not the same between arguments and might get out of sync during runtime. If this is by design, use ReconcileDynamicAxis() to forward layouts between nodes.", NodeDescription().c_str()); } // result has tensor shape with dimensions being the max over both let shape0 = GetInputSampleLayout(0); let shape1 = GetInputSampleLayout(1); SmallVector<size_t> dims = shape0.GetDims(); if (shape1.GetRank() > dims.size()) dims.resize(shape1.GetRank(), 1); // pad with ones // If rank of [0] is higher than we only need to take max over rank [1]. // If rank of [1] is higher then we have padded to equal lentgh. for (size_t k = 0; k < shape1.GetRank(); k++) { size_t dim1 = shape1[k]; // BUGBUG: We must consider the allowBroadcast flag here. if (dims[k] == 1) // is [0] broadcasting? dims[k] = dim1; // then use dimension we broadcast to else if (dim1 == 1) // if [1] is broadcasting ; // dims is already correct else if (isFinalValidationPass && dim1 != dims[k]) // no broadcasting: they must match InvalidArgument("%ls: Input dimensions [%s] and [%s] are not compatible.", NodeDescription().c_str(), string(shape0).c_str(), string(shape1).c_str()); } SetDims(TensorShape(dims), HasMBLayout()); }
// binary reduce-to-(1,1) operation, e.g. CrossEntropyWithSoftmaxNode // Currently only called by criterion nodes. // This function also infers child LearnableParameters. In case you wonder why this is needed for criterion nodes, there are edge cases, e.g. a // learnable parameter being regularized by a criterion node, where the learnable parameter is fed both into that criterion node and other places. void ComputationNodeBase::ValidateBinaryReduce(bool isFinalValidationPass) { ComputationNodeBase::Validate(isFinalValidationPass); m_pMBLayout = nullptr; // this node does not hold mini-batch data ValidateInferBinaryInputDims(); if (isFinalValidationPass) { if (!(Input(0)->GetSampleLayout().IsElementwiseCompatibleWith(Input(1)->GetSampleLayout()))) { string s1 = Input(0)->GetSampleLayout(); string s2 = Input(1)->GetSampleLayout(); // BUGBUG: Allow broadcasting? LogicError("%ls: The tensor dimensions in the inputs do not match. %s != %s", NodeDescription().c_str(), s1.c_str(), s2.c_str()); } else if (!(Input(0)->HasMBLayout())) LogicError("%ls: Expected MBLayout in Input 0.", NodeDescription().c_str()); else if (!(Input(1)->HasMBLayout())) LogicError("%ls: Expected MBLayout in Input 1.", NodeDescription().c_str()); // Shape of the MBLayouts is checked at runtime. } SetDims(TensorShape(1), false); }