// 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()); }
// N-nary zip operation, e.g. for TernaryZip for clip() // 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::ValidateNaryZip(bool isFinalValidationPass, bool allowBroadcast, size_t numInputs) { assert(m_inputs.size() == numInputs); ComputationNodeBase::Validate(isFinalValidationPass); InferMBLayoutFromInputsForStandardCase(isFinalValidationPass); ValidateInferNaryInputDims(numInputs); // check minibatch layout consistency for all possible pairs (n choose 2) if (isFinalValidationPass) for (size_t i = 0; i < numInputs; i++) for (size_t j = i + 1; j < numInputs; j++) ValidateMBLayout(Input(i), Input(j)); // result has tensor shape with dimensions being the max over all inputs let shape0 = GetInputSampleLayout(0); // dims is max over all inputs size_t maxRank = shape0.GetRank(); for (size_t i = 1; i < numInputs; i++) { let shape = GetInputSampleLayout(i); if (shape.GetRank() > maxRank) maxRank = shape.GetRank(); } SmallVector<size_t> dims = shape0.GetDims(); dims.resize(maxRank, 1); // pad with 1 // first check for invalid dimensions for (size_t k = 0; k < maxRank; k++) { size_t maxDim = 0; TensorShape maxShape = shape0; // arbitrary; this is just used for the error message for (size_t i = 0; i < numInputs; i++) { let currentShape = GetInputSampleLayout(i); size_t currentRank = currentShape.GetRank(); // make sure that the rank of this input is bigger than the current index (otherwise, these are implied singleton dimensions that do not need to be checked) if (currentRank > k) { size_t currentDim = currentShape[k]; if (currentDim > 1 && maxDim != currentDim && maxDim > 1) // 1=broadcasting, 0=not known yet, meant to be inferred { InvalidArgument("%ls: Input dimensions [%s] and [%s] are not compatible.", NodeDescription().c_str(), string(maxShape).c_str(), string(currentShape).c_str()); } else if (currentDim > maxDim) { maxDim = currentDim; maxShape = currentShape; } } } } // now set up the right dims for (size_t k = 0; k < maxRank; k++) { for (size_t i = 0; i < numInputs; i++) { let shape = GetInputSampleLayout(i); if (shape.GetRank() > k) { size_t dim = shape[k]; if (dims[k] <= 1 && dim != 0) dims[k] = dim; } } } SetDims(TensorShape(dims), HasMBLayout()); }