// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE.md file in the project root for full license information. // #include "stdafx.h" #include "CNTKLibrary.h" #include "Utils.h" #include "TensorView.h" #include "Matrix.h" #include "CPUSparseMatrix.h" #include "GPUSparseMatrix.h" #include #include "TensorShape.h" using namespace Microsoft::MSR::CNTK; namespace CNTK { template static TensorView* AllocateTensorView(const NDShape& viewShape, const DeviceDescriptor& device, void* dataBuffer, size_t bufferSizeInBytes) { if (dataBuffer == nullptr) InvalidArgument("Cannot create a NDArrayView over a null data buffer."); if (bufferSizeInBytes < (viewShape.TotalSize() * sizeof(ElementType))) InvalidArgument("Size (%d) of the specified buffer for creating the NDArrayView is smaller than the specified view shape '%S'.", (int)bufferSizeInBytes, viewShape.AsString().c_str()); auto matrixDims = GetMatrixDimensions(viewShape); std::shared_ptr> matrix = std::make_shared>(matrixDims.first, matrixDims.second, (ElementType*)dataBuffer, AsCNTKImplDeviceId(device), matrixFlagDontOwnBuffer); return new TensorView(matrix, AsTensorViewShape(viewShape)); } static void* AllocateTensorView(CNTK::DataType dataType, const NDShape& viewShape, const DeviceDescriptor& device, void* dataBuffer, size_t bufferSizeInBytes) { switch (dataType) { case DataType::Float: return AllocateTensorView(viewShape, device, dataBuffer, bufferSizeInBytes); case DataType::Double: return AllocateTensorView(viewShape, device, dataBuffer, bufferSizeInBytes); default: LogicError("Unsupported DataType %s", DataTypeName(dataType)); break; } } template static TensorView* AllocateTensorView(const NDShape& viewShape, CNTK::StorageFormat storageType, const DeviceDescriptor& device, size_t numNonZeroValues = 0) { auto matrixDims = GetMatrixDimensions(viewShape); std::shared_ptr> matrix = std::make_shared>(matrixDims.first, matrixDims.second, AsCNTKImplDeviceId(device), IsSparseStorageFormat(storageType) ? MatrixType::SPARSE : MatrixType::DENSE, AsCNTKImplMatrixFormat(storageType), numNonZeroValues); return new TensorView(matrix, AsTensorViewShape(viewShape)); } static void* AllocateTensorView(CNTK::DataType dataType, CNTK::StorageFormat storageType, const NDShape& viewShape, const DeviceDescriptor& device, size_t numNonZeroValues = 0) { switch (dataType) { case DataType::Float: return AllocateTensorView(viewShape, storageType, device, numNonZeroValues); case DataType::Double: return AllocateTensorView(viewShape, storageType, device, numNonZeroValues); default: LogicError("Unsupported DataType %s", DataTypeName(dataType)); break; } } NDArrayView::NDArrayView(CNTK::DataType dataType, const NDShape& viewShape, void* dataBuffer, size_t bufferSizeInBytes, const DeviceDescriptor& device, bool readOnly/* = false*/) : NDArrayView(dataType, device, StorageFormat::Dense, viewShape, readOnly, AllocateTensorView(dataType, viewShape, device, dataBuffer, bufferSizeInBytes)) { } template NDArrayView::NDArrayView(const NDShape& viewShape, const SparseIndexType* colStarts, const SparseIndexType* rowIndices, const ElementType* nonZeroValues, size_t numNonZeroValues, const DeviceDescriptor& device, bool readOnly/* = false*/) : NDArrayView(AsDataType(), device, StorageFormat::SparseCSC, viewShape, false, AllocateTensorView(viewShape, StorageFormat::SparseCSC, device, numNonZeroValues)) { if ((colStarts == nullptr) || (rowIndices == nullptr) || (nonZeroValues == nullptr) || (numNonZeroValues == 0) || (numNonZeroValues > viewShape.TotalSize())) InvalidArgument("Invalid sparse CSC format data specified for construction of NDArrayView with shape '%S'; " "either one of the specified buffers is null or the count (%d) of non-zero values is invalid.", viewShape.AsString().c_str(), (int)numNonZeroValues); auto sparseMatrix = GetWritableMatrix(1); sparseMatrix->SetMatrixFromCSCFormat(colStarts, rowIndices, nonZeroValues, numNonZeroValues, sparseMatrix->GetNumRows(), sparseMatrix->GetNumCols()); m_isReadOnly = readOnly; } NDArrayView::NDArrayView(CNTK::DataType dataType, const DeviceDescriptor& device, CNTK::StorageFormat storageType, const NDShape& viewShape, bool readOnly, void* tensorView) : m_dataType(dataType), m_device(device), m_storageFormat(storageType), m_viewShape(viewShape), m_isReadOnly(readOnly) { m_tensorView = std::shared_ptr(tensorView, [this](void*) { switch (m_dataType) { case DataType::Float: delete GetTensorView(); break; case DataType::Double: delete GetTensorView(); break; default: LogicError("Unsupported DataType %s", DataTypeName(m_dataType)); break; } }); } NDArrayView::NDArrayView(CNTK::DataType dataType, CNTK::StorageFormat storageType, const NDShape& viewShape, const DeviceDescriptor& device) : NDArrayView(dataType, device, storageType, viewShape, false, AllocateTensorView(dataType, storageType, viewShape, device)) {} NDArrayView::~NDArrayView() {} void NDArrayView::SetValue(float value) { if (GetDataType() == DataType::Double) SetValue((double)value); else { if (IsSparse()) LogicError("NDArrayView::SetValue: Setting a NDArrayView contents to a scalar is only allowed for objects with dense storage format."); GetWritableMatrix()->SetValue(value); } } void NDArrayView::SetValue(double value) { if (IsSparse()) LogicError("NDArrayView::SetValue: Setting a NDArrayView contents to a scalar is only allowed for objects with dense storage format."); GetWritableMatrix()->SetValue(value); } template /*static*/ std::shared_ptr> NDArrayView::GetMatrixImpl(const TensorView* tensorView, size_t rowColSplitPoint) { auto tensorShape = tensorView->GetShape(); // we should always reshape for rank-0, so that batch and sequence axis goes to columns if (tensorShape.GetRank() <= 1 && rowColSplitPoint != 0) return tensorView->AsMatrix(); size_t splitPoint = rowColSplitPoint; if (splitPoint == NDArrayView::AutoSelectRowColSplitPoint) { // Determine the split point by determining which of the axes can be // folded and selecting the non-foldable axis as the split point std::vector dimsToDrop(tensorShape.GetRank(), false); for (size_t k = 1; k < tensorShape.GetRank(); ++k) if (tensorShape.CanFlatten(k)) dimsToDrop[k - 1] = true; // There should be at most 2 dims we cannot drop auto numDimsThatCannotBeDropped = std::count_if(dimsToDrop.begin(), dimsToDrop.end(), [](const bool& val) { return !val; }); if (numDimsThatCannotBeDropped > 2) LogicError("The TensorView (shape = %s) underlying this NDArrayView cannot be flattened to a Matrix.", ((std::string)tensorShape).c_str()); // If we can fold the entire tensor down to a vector so any of the axes can be a valid split point, // let's pick the split point to be 1 splitPoint = 1; if (numDimsThatCannotBeDropped > 1) { while (dimsToDrop[splitPoint - 1]) splitPoint++; } } tensorShape.FlattenTo2DInPlace(splitPoint, "NDArrayView::GetMatrix"); return tensorView->Reshaped(tensorShape).AsMatrix(); } template std::shared_ptr> NDArrayView::GetMatrix(size_t rowColSplitPoint/* = AutoSelectRowColSplitPoint*/) const { return GetMatrixImpl(GetTensorView(), rowColSplitPoint); } template std::shared_ptr> NDArrayView::GetWritableMatrix(size_t rowColSplitPoint/* = AutoSelectRowColSplitPoint*/) { return GetMatrixImpl(GetWritableTensorView(), rowColSplitPoint); } template const TensorView* NDArrayView::GetTensorView() const { if (AsDataType() != m_dataType) LogicError("NDArrayView::GetTensorView: The specified ElementType %s does not match the DataType %s", typeid(ElementType).name(), DataTypeName(m_dataType)); return (const TensorView*)(m_tensorView.get()); } template TensorView* NDArrayView::GetWritableTensorView() { if (IsReadOnly()) InvalidArgument("NDArrayView::GetWritableTensorView: Cannot get a writable TensorView from a read-only NDArrayView."); return const_cast*>(GetTensorView()); } NDArrayViewPtr NDArrayView::DeepClone(const DeviceDescriptor& device, bool readOnly/* = false*/) const { NDArrayViewPtr newView = MakeSharedObject(this->GetDataType(), this->GetStorageFormat(), this->Shape(), device); switch (m_dataType) { case DataType::Float: { auto newMatrix = newView->GetWritableMatrix(); auto thisMatrix = GetMatrix(); newMatrix->AssignValuesOf(*thisMatrix); break; } case DataType::Double: { auto newMatrix = newView->GetWritableMatrix(); auto thisMatrix = GetMatrix(); newMatrix->AssignValuesOf(*thisMatrix); break; } default: LogicError("NDArrayView::DeepClone: Unsupported DataType %s", DataTypeName(m_dataType)); break; } newView->m_isReadOnly = readOnly; return newView; } void NDArrayView::CopyFrom(const NDArrayView& source) { if ((source.Shape() != Shape()) && (AsTensorShape(source.Shape()) != AsTensorShape(Shape()))) InvalidArgument("NDArrayView::CopyFrom: The source view shape '%S' is not same as the shape '%S' of this NDArrayView.", source.Shape().AsString().c_str(), Shape().AsString().c_str()); if (IsReadOnly()) RuntimeError("NDArrayView::CopyFrom: Cannot modify contents of a readonly NDArrayView."); switch (m_dataType) { case DataType::Float: { auto sourceMatrix = source.GetMatrix(); auto destMatrix = GetWritableMatrix(); destMatrix->AssignValuesOf(*sourceMatrix); break; } case DataType::Double: { auto sourceMatrix = source.GetMatrix(); auto destMatrix = GetWritableMatrix(); destMatrix->AssignValuesOf(*sourceMatrix); break; } default: LogicError("NDArrayView::CopyFrom: Unsupported DataType %s", DataTypeName(m_dataType)); break; } } NDArrayViewPtr NDArrayView::Alias(bool readOnly/* = false*/) const { void* tensorView = nullptr; switch (m_dataType) { case DataType::Float: tensorView = new TensorView(*(GetTensorView())); break; case DataType::Double: tensorView = new TensorView(*(GetTensorView())); break; default: LogicError("NDArrayView::Alias: Unsupported DataType %s", DataTypeName(m_dataType)); break; } return MakeSharedObject(GetDataType(), Device(), GetStorageFormat(), Shape(), IsReadOnly() || readOnly, tensorView); } NDArrayViewPtr NDArrayView::SliceView(const std::vector& startOffset, const std::vector& extent, bool readOnly) const { auto rank = Shape().Rank(); if (startOffset.size() != rank) InvalidArgument("NDArrayView::SliceView: Rank (%d) of the NDArrayView does not match the dimensionality (%d) of the specified slice offset.", (int)rank, (int)startOffset.size()); if (extent.size() > rank) InvalidArgument("NDArrayView::SliceView: Dimensionality (%d) of the specified slice extent exceeds the rank (%d) of this NDArrayView.", (int)extent.size(), (int)rank); if (std::find(extent.begin(), extent.end(), 0) != extent.end()) InvalidArgument("NDArrayView::SliceView: Specified slice extent is zero along at least one of the axes."); bool anyPrevAxisSliced = false; NDShape sliceViewShape(extent); std::vector endOffset(rank); for (size_t i = 0; i < rank; ++i) { if ((i < sliceViewShape.Rank()) && (sliceViewShape[i] == NDShape::InferredDimension)) sliceViewShape[i] = Shape()[i] - startOffset[i]; endOffset[i] = startOffset[i] + ((i < sliceViewShape.Rank()) ? sliceViewShape[i] : 1); if (anyPrevAxisSliced && ((endOffset[i] - startOffset[i]) != 1)) InvalidArgument("NDArrayView::SliceView: Cannot create a slice which is not contiguous in memory. " "This NDArrayView shape = %S, slice offset = %S, slice extent = %S.", Shape().AsString().c_str(), NDShape(startOffset).AsString().c_str(), NDShape(extent).AsString().c_str()); bool isCurrentAxisSliced = (startOffset[i] != 0) || (endOffset[i] != Shape()[i]); anyPrevAxisSliced = anyPrevAxisSliced || isCurrentAxisSliced; } auto flatBufferOffset = AsTensorShape(Shape()).Locate(startOffset); auto sliceViewMatrixDims = GetMatrixDimensions(sliceViewShape); assert((flatBufferOffset % sliceViewMatrixDims.first) == 0); auto sliceMatrixColumnOffset = flatBufferOffset / sliceViewMatrixDims.first; void* tensorView = nullptr; switch (m_dataType) { case DataType::Float: { auto currentMatrix = GetMatrix(); std::pair currentMatrixDims = { currentMatrix->GetNumRows(), currentMatrix->GetNumCols() }; std::shared_ptr> slicedMatrixView; if (sliceViewMatrixDims.first != currentMatrixDims.first) slicedMatrixView = make_shared>(currentMatrix->Reshaped(1, currentMatrix->GetNumElements()).ColumnSlice(flatBufferOffset, sliceViewShape.TotalSize())); else slicedMatrixView = make_shared>(currentMatrix->ColumnSlice(sliceMatrixColumnOffset, sliceViewMatrixDims.second)); tensorView = new TensorView(slicedMatrixView, AsTensorViewShape(sliceViewShape)); break; } case DataType::Double: { auto currentMatrix = GetMatrix(); std::pair currentMatrixDims = { currentMatrix->GetNumRows(), currentMatrix->GetNumCols() }; std::shared_ptr> slicedMatrixView; if (sliceViewMatrixDims.first != currentMatrixDims.first) slicedMatrixView = make_shared>(currentMatrix->Reshaped(1, currentMatrix->GetNumElements()).ColumnSlice(flatBufferOffset, sliceViewShape.TotalSize())); else slicedMatrixView = make_shared>(currentMatrix->ColumnSlice(sliceMatrixColumnOffset, sliceViewMatrixDims.second)); tensorView = new TensorView(slicedMatrixView, AsTensorViewShape(sliceViewShape)); break; } default: LogicError("NDArrayView::SliceView: Unsupported DataType %s", DataTypeName(m_dataType)); break; } return MakeSharedObject(GetDataType(), Device(), GetStorageFormat(), sliceViewShape, IsReadOnly() || readOnly, tensorView); } NDArrayViewPtr NDArrayView::AsShape(const NDShape& newShape) const { if (newShape.TotalSize() != Shape().TotalSize()) { InvalidArgument("NDArrayView::AsShape: The total size (%d) of this view's shape '%S' must be same as the size (%d) of the newShape '%S'.", (int)Shape().TotalSize(), Shape().AsString().c_str(), (int)newShape.TotalSize(), newShape.AsString().c_str()); } auto newTensorShape = AsTensorViewShape(newShape); void* tensorView = nullptr; switch (m_dataType) { case DataType::Float: tensorView = new TensorView(*(GetTensorView()), newTensorShape); break; case DataType::Double: tensorView = new TensorView(*(GetTensorView()), newTensorShape); break; default: LogicError("NDArrayView::AsShape: Unsupported DataType %s", DataTypeName(m_dataType)); break; } return MakeSharedObject(GetDataType(), Device(), GetStorageFormat(), newShape, IsReadOnly(), tensorView); } // TODO: This could actually be strided? template ElementType* NDArrayView::WritableDataBuffer() { if (IsReadOnly()) InvalidArgument("NDArrayView::WritableDataBuffer: Cannot get writable data buffer from a read-only NDArrayView."); return const_cast(DataBuffer()); } // TODO: This could actually be strided? template const ElementType* NDArrayView::DataBuffer() const { if (AsDataType() != m_dataType) InvalidArgument("NDArrayView::DataBuffer: The specified ElementType '%s' does not match this NDArrayView's DataType '%s'.", typeid(ElementType).name(), DataTypeName(m_dataType)); if (IsSparse()) InvalidArgument("The stroage format of 'this' NDArrayView is sparse. Please use SparseDataBuffers()."); // First make sure that the underlying matrix is on the right device auto matrix = GetMatrix(); matrix->TransferToDeviceIfNotThere(AsCNTKImplDeviceId(m_device), true); return matrix->Data(); } template std::tuple NDArrayView::SparseCSCDataBuffers() const { if (AsDataType() != m_dataType) InvalidArgument("NDArrayView::SparseDataBuffers: The specified ElementType '%s' does not match this NDArrayView's DataType '%s'.", typeid(ElementType).name(), DataTypeName(m_dataType)); if (!IsSparse()) RuntimeError("The storage format of 'this' NDArrayView is dense. Please use another DataBuffer()."); if(GetStorageFormat() != StorageFormat::SparseCSC) RuntimeError("The SparseCSCDataBuffers() method only supports CSC sparse format."); std::shared_ptr> matrix = GetMatrix(); auto matrixDims = GetMatrixDimensions(Shape()); if (matrix->GetNumRows() != matrixDims.first) LogicError("The number of rows of the underlying matrix does not match the shape."); if (matrix->GetNumCols() != matrixDims.second) LogicError("The number of columns of the underlying matrix does not match the shape."); matrix->TransferToDeviceIfNotThere(AsCNTKImplDeviceId(m_device), true); if ((matrix->GetMatrixType() != Microsoft::MSR::CNTK::MatrixType::SPARSE) || (matrix->GetFormat() != Microsoft::MSR::CNTK::MatrixFormat::matrixFormatSparseCSC)) RuntimeError("NDArrayView::SparseDataBuffers: The underlying matrix of 'this' NDArrayView is not in the CSC sparse format."); size_t numNonZeroValues; ElementType* nonZeroValues; SparseIndexType* colStarts; SparseIndexType* rowIndices; if (m_device.Type() == DeviceKind::CPU) { if (sizeof(CPUSPARSE_INDEX_TYPE) != sizeof(SparseIndexType)) LogicError("Inconsistent data type for sparse index in 'this' Value and the underlying matrix on CPU."); std::shared_ptr> sparseMatrix = matrix->m_CPUSparseMatrix; numNonZeroValues = sparseMatrix->NzCount(); nonZeroValues = static_cast(sparseMatrix->NzValues()); colStarts = static_cast(sparseMatrix->ColLocation()); rowIndices = static_cast(sparseMatrix->RowLocation()); } else if (m_device.Type() == DeviceKind::GPU) { if (sizeof(GPUSPARSE_INDEX_TYPE) != sizeof(SparseIndexType)) LogicError("Inconsistent data type for sparse index in 'this' Value and the underlying matrix on GPU."); std::shared_ptr> sparseMatrix = matrix->m_GPUSparseMatrix; numNonZeroValues = sparseMatrix->NzCount(); nonZeroValues = static_cast(sparseMatrix->NzValues()); colStarts = static_cast(sparseMatrix->ColLocation()); rowIndices = static_cast(sparseMatrix->RowLocation()); } else { RuntimeError("NDArrayView::SparseDataBuffers: The device %S is currently not supported.",DeviceKindName(m_device.Type())); } return std::tuple(nonZeroValues, colStarts, rowIndices, numNonZeroValues); } template std::tuple NDArrayView::SparseBlockColumnDataBuffers() const { if (AsDataType() != m_dataType) InvalidArgument("NDArrayView::SparseBlockColumnDataBuffers: The specified ElementType '%s' does not match this NDArrayView's DataType '%s'.", typeid(ElementType).name(), DataTypeName(m_dataType)); if (!IsSparse()) RuntimeError("The storage format of 'this' NDArrayView is dense. Please use another DataBuffer()."); if (GetStorageFormat() != StorageFormat::SparseBlockCol) RuntimeError("The SparseBlockColumnDataBuffers() method only supports sparse block column format."); std::shared_ptr> matrix = GetMatrix(); size_t numBlocks; size_t numRows; size_t numCols; ElementType* blockValues; SparseIndexType* blockId2Col; SparseIndexType* col2BlockId; if (m_device.Type() == DeviceKind::GPU) { if (sizeof(GPUSPARSE_INDEX_TYPE) != sizeof(SparseIndexType)) LogicError("Inconsistent data type for sparse index in 'this' Value and the underlying matrix on GPU."); std::shared_ptr> sparseMatrix = matrix->m_GPUSparseMatrix; numBlocks = sparseMatrix->GetBlockSize(); numRows = sparseMatrix->GetNumRows(); numCols = sparseMatrix->GetNumCols(); blockValues = static_cast(sparseMatrix->NzValues()); blockId2Col = static_cast(sparseMatrix->BlockId2ColOrRow()); col2BlockId = static_cast(sparseMatrix->ColOrRow2BlockId()); } else { // CPU sparse block column is not yet supported, as the index format is different from GPU sparse block column RuntimeError("NDArrayView::SparseBlockColumnDataBuffers: The device %S is currently not supported.", DeviceKindName(m_device.Type())); } return std::tuple(blockValues, blockId2Col, col2BlockId, numBlocks, numRows, numCols); } void NDArrayView::AdjustSparseBlockColumn(const SparseIndexType* cpuCol2BlockId, size_t numBlocks, bool useBlockId2Col) { switch (m_dataType) { case DataType::Float: { auto matrix = GetWritableMatrix(); matrix->AdjustSparseBlockColumn(cpuCol2BlockId, numBlocks, useBlockId2Col); break; } case DataType::Double: { auto matrix = GetWritableMatrix(); matrix->AdjustSparseBlockColumn(cpuCol2BlockId, numBlocks, useBlockId2Col); break; } default: LogicError("NDArrayView::AdjustSparseBlockColumn: Unsupported DataType %s", DataTypeName(m_dataType)); break; } } void NDArrayView::ChangeDevice(const DeviceDescriptor& device) { if (device == m_device) return; switch (m_dataType) { case DataType::Float: { auto matrix = GetMatrix(); matrix->TransferFromDeviceToDevice(matrix->GetDeviceId(), AsCNTKImplDeviceId(device), /*isBeingMoved = */ true, /*emptyTransfer =*/ false, /*updatePreferredDevice =*/ true); matrix->CollapseDataLocation(); break; } case DataType::Double: { auto matrix = GetMatrix(); matrix->TransferFromDeviceToDevice(matrix->GetDeviceId(), AsCNTKImplDeviceId(device), /*isBeingMoved = */ true, /*emptyTransfer =*/ false, /*updatePreferredDevice =*/ true); matrix->CollapseDataLocation(); break; } default: LogicError("NDArrayView::ChangeDevice: Unsupported DataType %s", DataTypeName(m_dataType)); break; } m_device = device; } template /*static*/ NDArrayViewPtr NDArrayView::RandomNormal(const NDShape& shape, double mean, double stdDev, unsigned long seed, const DeviceDescriptor& device /*= DeviceDescriptor::UseDefaultDevice()*/) { auto matrixDims = GetMatrixDimensions(shape); auto randomNormalMatrix = std::make_shared>(Matrix::RandomGaussian(matrixDims.first, matrixDims.second, AsCNTKImplDeviceId(device), (ElementType)mean, (ElementType)stdDev, seed)); auto tensorView = new TensorView(randomNormalMatrix, AsTensorViewShape(shape)); return MakeSharedObject(AsDataType(), device, StorageFormat::Dense, shape, false, tensorView); } template /*static*/ NDArrayViewPtr NDArrayView::RandomUniform(const NDShape& shape, double rangeBegin, double rangeEnd, unsigned long seed, const DeviceDescriptor& device/* = DeviceDescriptor::UseDefaultDevice()*/) { auto matrixDims = GetMatrixDimensions(shape); auto randomUniformMatrix = std::make_shared>(Matrix::RandomUniform(matrixDims.first, matrixDims.second, AsCNTKImplDeviceId(device), (ElementType)rangeBegin, (ElementType)rangeEnd, seed)); auto tensorView = new TensorView(randomUniformMatrix, AsTensorViewShape(shape)); return MakeSharedObject(AsDataType(), device, StorageFormat::Dense, shape, false, tensorView); } template ElementType NDArrayView::AsScalar() const { auto scalarData = this->shared_from_this(); if (scalarData->Shape().TotalSize() != 1) LogicError("NDArrayView::AsScalar: The NDArrayView shaped '%S' is not a scalar.", scalarData->Shape().AsString().c_str()); ElementType scalar = std::numeric_limits::quiet_NaN(); std::shared_ptr cpuData; if (scalarData->Device() == DeviceDescriptor::CPUDevice()) cpuData = scalarData; else { auto tmpCPUData = std::make_shared(scalarData->GetDataType(), scalarData->Shape(), CNTK::DeviceDescriptor::CPUDevice()); tmpCPUData->CopyFrom(*scalarData); cpuData = tmpCPUData; } if (scalarData->GetDataType() == DataType::Float) scalar = *(cpuData->DataBuffer()); else if (scalarData->GetDataType() == DataType::Double) scalar = static_cast(*(cpuData->DataBuffer())); else LogicError("NDArrayView::AsScalar: Unsupported DataType"); return scalar; } std::wstring NDArrayView::AsString() const { wstringstream wss; std::wstring device = DeviceKindName(m_device.Type()); wss << L"NDArrayView(" << m_viewShape.AsString() << L", " << device << L")"; return wss.str(); } // Explicit template instantiations template CNTK_API NDArrayViewPtr NDArrayView::RandomUniform(const NDShape& shape, double rangeBegin, double rangeEnd, unsigned long seed, const DeviceDescriptor& device/* = DeviceDescriptor::UseDefaultDevice()*/); template CNTK_API NDArrayViewPtr NDArrayView::RandomUniform(const NDShape& shape, double rangeBegin, double rangeEnd, unsigned long seed, const DeviceDescriptor& device/* = DeviceDescriptor::UseDefaultDevice()*/); template CNTK_API NDArrayViewPtr NDArrayView::RandomNormal(const NDShape& shape, double mean, double stdDev, unsigned long seed, const DeviceDescriptor& device/* = DeviceDescriptor::UseDefaultDevice()*/); template CNTK_API NDArrayViewPtr NDArrayView::RandomNormal(const NDShape& shape, double mean, double stdDev, unsigned long seed, const DeviceDescriptor& device/* = DeviceDescriptor::UseDefaultDevice()*/); template CNTK_API const float* NDArrayView::DataBuffer() const; template CNTK_API const double* NDArrayView::DataBuffer() const; template CNTK_API const TensorView* NDArrayView::GetTensorView() const; template CNTK_API const TensorView* NDArrayView::GetTensorView() const; template CNTK_API std::tuple NDArrayView::SparseCSCDataBuffers() const; template CNTK_API std::tuple NDArrayView::SparseCSCDataBuffers() const; template CNTK_API std::tuple NDArrayView::SparseBlockColumnDataBuffers() const; template CNTK_API std::tuple NDArrayView::SparseBlockColumnDataBuffers() const; template CNTK_API float* NDArrayView::WritableDataBuffer(); template CNTK_API double* NDArrayView::WritableDataBuffer(); template std::shared_ptr> NDArrayView::GetMatrix(size_t rowColSplitPoint/* = AutoSelectRowColSplitPoint*/) const; template std::shared_ptr> NDArrayView::GetMatrix(size_t rowColSplitPoint/* = AutoSelectRowColSplitPoint*/) const; template std::shared_ptr> NDArrayView::GetWritableMatrix(size_t rowColSplitPoint/* = AutoSelectRowColSplitPoint*/); template std::shared_ptr> NDArrayView::GetWritableMatrix(size_t rowColSplitPoint/* = AutoSelectRowColSplitPoint*/); template TensorView* NDArrayView::GetWritableTensorView(); template TensorView* NDArrayView::GetWritableTensorView(); template CNTK_API NDArrayView::NDArrayView(const NDShape& viewShape, const SparseIndexType* colStarts, const SparseIndexType* rowIndices, const float* nonZeroValues, size_t numNonZeroValues, const DeviceDescriptor& device, bool readOnly/* = false*/); template CNTK_API NDArrayView::NDArrayView(const NDShape& viewShape, const SparseIndexType* colStarts, const SparseIndexType* rowIndices, const double* nonZeroValues, size_t numNonZeroValues, const DeviceDescriptor& device, bool readOnly/* = false*/); template float NDArrayView::AsScalar() const; template double NDArrayView::AsScalar() const; }