/*virtual*/ string ComputationNodeBase::FormatOperationPrototype(const string& extraArgs) const { string prototype; prototype += msra::strfun::strprintf("%ls = %ls", NodeName().c_str(), OperationName().c_str()); // arguments of operation if (IsLeaf()) prototype += "()"; else { prototype += " ("; for (size_t i = 0; i < GetNumInputs(); i++) { const auto& child = m_inputs[i]; if (i > 0) prototype += ", "; if (child) prototype += msra::strfun::strprintf("%ls", child->NodeName().c_str()); else prototype += "NULL"; } prototype += extraArgs; prototype += ")"; } // type (tensor dimensions) of operation prototype += " : "; if (!IsLeaf()) { //prototype += "("; for (size_t i = 0; i < GetNumInputs(); i++) { const auto& child = m_inputs[i]; if (i > 0) prototype += ", "; if (child == nullptr) { prototype += "NULL"; continue; } prototype += child->ShapeDescription().c_str(); } prototype += extraArgs; //prototype += ")"; } prototype += msra::strfun::strprintf(" -> %s", ShapeDescription().c_str()); return prototype; }
/*virtual*/ void ComputationNode<ElemType>::DumpNodeInfo(const bool /*printValues*/, const bool printMetadata, File& fstream) const { if (printMetadata) { fstream << L"\n" + NodeName() + L"=" + OperationName(); if (!IsLeaf()) { fstream << wstring(L"("); for (size_t i = 0; i < GetNumInputs(); i++) { if (i > 0) fstream << wstring(L","); fstream << (Input(i) ? Input(i)->NodeName() : L"NULL"); } fstream << wstring(L")"); } } }
xml_schema::Qname SubscribeTraits::ResponseType() { return xml_schema::Qname(WS_NS_EVENTING, OperationName() + "Response"); }
xml_schema::Qname SubscribeTraits::RequestType() { return xml_schema::Qname(WS_NS_EVENTING, OperationName()); }
xml_schema::Qname GetMetadataTraits::RequestType() { return xml_schema::Qname(WS_NS_METADATA_EXCHANGE, OperationName()); }
/*virtual*/ void ScatterPackedNode<ElemType>::Validate(bool isFinalValidationPass) /*override*/ { ComputationNodeBase::Validate(isFinalValidationPass); // inherit MBLayout from layoutData (that's the only thing we use it for) m_pMBLayout = Input(LAYOUTDATA)->GetMBLayout(); if (isFinalValidationPass && (!Input(LAYOUTDATA)->HasMBLayout() || !Input(INDEXDATA)->HasMBLayout() || !Input(SOURCEDATA)->HasMBLayout())) LogicError("%ls %ls operation requires all inputs to be minibatch data (must have MBLayouts).", NodeName().c_str(), OperationName().c_str()); if (isFinalValidationPass && Input(INDEXDATA)->GetSampleLayout().GetNumElements() != 1) InvalidArgument("%ls %ls operation requires the second argument (indexData) to be a scalar sequence.", NodeName().c_str(), OperationName().c_str()); // TODO: We also know that indexData and sourceData must have the same MBLayout. But that is checked at runtime. // inherit tensor dimension from sourceData SetDims(Input(SOURCEDATA)->GetSampleLayout(), HasMBLayout()); }
/*virtual*/ void ScatterPackedNode<ElemType>::ForwardPropNonLooping() /*override*/ { if (*Input(INDEXDATA)->GetMBLayout() != *Input(SOURCEDATA)->GetMBLayout()) InvalidArgument("%ls %ls operation requires the minibatch layout of index and source data to be the same.", NodeName().c_str(), OperationName().c_str()); Input(INDEXDATA)->MaskMissingValueColumnsTo(FrameRange(Input(INDEXDATA)->GetMBLayout()), -1); // indicates an invalid column to Gather/Scatter let& index = Input(INDEXDATA)->Value(); // column indices to copy from let& source = Input(SOURCEDATA)->Value(); // source data to copy auto& output = Value(); // output goes here output.DoScatterColumnsOf(/*beta=*/0, index, source, /*alpha=*/1); }
/*virtual*/ void PackedIndexNode<ElemType>::Validate(bool isFinalValidationPass) /*override*/ { ComputationNodeBase::Validate(isFinalValidationPass); // inherit both MBLayout and sample dimension (scalar) from indexData // Because we map (per-seq) index sequence to (packed) index sequence. Target is only for index calculation. m_pMBLayout = Input(INDEXDATA)->GetMBLayout(); if (isFinalValidationPass && (!Input(INDEXDATA)->HasMBLayout() || !Input(SOURCEDATA)->HasMBLayout())) LogicError("%ls %ls operation requires both inputs to be minibatch data (must have MBLayouts).", NodeName().c_str(), OperationName().c_str()); if (isFinalValidationPass && Input(INDEXDATA)->GetSampleLayout().GetNumElements() != 1) InvalidArgument("%ls %ls operation requires the second argument (indexData) to be a scalar sequence.", NodeName().c_str(), OperationName().c_str()); SetDims(Input(INDEXDATA)->GetSampleLayout(), HasMBLayout()); }
/*virtual*/ void WhereNode<ElemType>::Validate(bool isFinalValidationPass) /*override*/ { ComputationNodeBase::Validate(isFinalValidationPass); // we generate its own MBLayout if (isFinalValidationPass && !Input(0)->HasMBLayout()) InvalidArgument("%ls %ls operation can only operate on minibatch data (which have a layout).", NodeName().c_str(), OperationName().c_str()); if (!m_pMBLayout) { m_pMBLayout = make_shared<MBLayout>(); // this generates a new layout m_pMBLayout->SetUniqueAxisName(L"WhereNodeAxis"); } // we map scalars to scalars if (isFinalValidationPass && Input(0)->GetSampleLayout().GetNumElements() != 1) InvalidArgument("%ls %ls operation can only operate on scalar input.", NodeName().c_str(), OperationName().c_str()); SetDims(TensorShape(1), true); }
/*virtual*/ void GatherPackedNode<ElemType>::Validate(bool isFinalValidationPass) /*override*/ { ComputationNodeBase::Validate(isFinalValidationPass); // inherit MBLayout from indexData m_pMBLayout = Input(INDEXDATA)->GetMBLayout(); if (isFinalValidationPass && (!Input(INDEXDATA)->HasMBLayout() || !Input(SOURCEDATA)->HasMBLayout())) LogicError("%ls %ls operation requires both inputs to be minibatch data (must have MBLayouts).", NodeName().c_str(), OperationName().c_str()); if (isFinalValidationPass && Input(INDEXDATA)->GetSampleLayout().GetNumElements() != 1) InvalidArgument("%ls %ls operation requires the first argument (indexData) to be a scalar sequence.", NodeName().c_str(), OperationName().c_str()); // inherit tensor dimension from sourceData SetDims(Input(SOURCEDATA)->GetSampleLayout(), HasMBLayout()); }
void LearnableParameter<ElemType>::ReviseFromFile(const std::wstring& reviseFromFilePath) { try { InitFromFile(reviseFromFilePath); } catch (const std::exception & e) { RuntimeError("ReviseFromFile: Failed to reload %ls %ls operation from file %ls: %s", NodeName().c_str(), OperationName().c_str(), reviseFromFilePath.c_str(), e.what()); } }
void LearnableParameter<ElemType>::InitFromArray(const std::vector<ElemType>& array, size_t numRows, size_t numCols) { // infer tensor dimensions from input file if not set // Note: The mapping of dimensions of the input matrix to tensor dimensions are somewhat confusing. // The file contains a 2D matrix (one row per text line) that is saved into our column-major representation. // That representation is then reshaped into a column-major tensor. if (GetSampleLayout().GetNumElements() == 0) // at least one dimension is 0 { auto dims = GetSampleLayout().GetDims(); // infer rank if (dims.size() == 0) dims.push_back(0); if (dims.size() == 1 && numCols != 1) dims.push_back(0); // infer #rows if (dims[0] == 0) // infer row dimension as input matrix row dimension dims[0] = numRows; // (if already set, then mismatch will be caught in VerifyDataSize() below) // infer #cols: product of all dimensions but the first must match matrix #cols; if there is a single 0 position, we infer it size_t zeroDim = 0; // 0 means not found size_t prod = 1; for (size_t k = 1; k < dims.size(); k++) { auto dim = dims[k]; if (dim != 0) prod *= dim; else if (zeroDim == 0) zeroDim = k; else InvalidArgument("%ls %ls operation's specified shape [%s] cannot be inferred: Too many unknown dimensions.", NodeName().c_str(), OperationName().c_str(), string(GetSampleLayout()).c_str()); } if (zeroDim != 0) // we found a zero { dims[zeroDim] = numCols / prod; if (prod * dims[zeroDim] != numCols) InvalidArgument("%ls %ls operation's specified shape [%s] cannot be inferred: Tensor shape cannot hold a [%d x %d] matrix.", NodeName().c_str(), OperationName().c_str(), string(GetSampleLayout()).c_str(), (int)numRows, (int)numCols); } SetDims(TensorShape(dims), false); } // BUGBUG: We should allow to read an arbitrary tensor from a single-column file. // Currently, this would cause a matrix/tensor dimension mismatch. --TODO: Is this comment up-to-date? Value().SetValue(numRows, numCols, m_deviceId, const_cast<ElemType*>(array.data()), matrixFlagNormal); // TODO: Get rid of that const_cast, as soon as after Ryan's Matrix-lib refactoring separated out SetValue() from external vs. from deep copy VerifyDataSize(Value()); // sanity check }