diff --git a/Makefile b/Makefile index db5381145..1fe28f38d 100644 --- a/Makefile +++ b/Makefile @@ -574,6 +574,11 @@ $(CNTK): $(BUILDINFO) $(CNTK_OBJ) | $(CNTKMATH_LIB) @echo building output for $(ARCH) with build type $(BUILDTYPE) $(CXX) $(LDFLAGS) $(patsubst %,-L%, $(LIBDIR) $(LIBPATH) $(NVMLPATH)) $(patsubst %,$(RPATH)%, $(ORIGINLIBDIR) $(LIBPATH)) -o $@ $^ $(LIBS) -l$(CNTKMATH) -fopenmp +# deployable resources +ALL += $(CNTK).core.bs +$(CNTK).core.bs: $(SOURCEDIR)/CNTK/BrainScript/CNTKCoreLib/CNTK.core.bs + cp -f $^ $@ + ######################################## # General compile and dependency rules ######################################## diff --git a/Source/ActionsLib/TrainActions.cpp b/Source/ActionsLib/TrainActions.cpp index 5a7773091..0fe334b31 100644 --- a/Source/ActionsLib/TrainActions.cpp +++ b/Source/ActionsLib/TrainActions.cpp @@ -49,9 +49,9 @@ using namespace Microsoft::MSR::CNTK; // =========================================================================== // TODO: decide where these should go. Also, do we need three variables? -extern wstring standardFunctions; -extern wstring commonMacros; -extern wstring computationNodes; +//extern wstring standardFunctions; +//extern wstring commonMacros; +//extern wstring computationNodes; // helper that returns 'float' or 'double' depending on ElemType template static const wchar_t* ElemTypeName(); @@ -133,9 +133,15 @@ void DoTrain(const ConfigRecordType& config) // We prepend a few standard definitions, and also definition of deviceId and precision, which all objects will pull out again when they are being constructed. // BUGBUG: We are not getting TextLocations right in this way! Do we need to inject location markers into the source? Moot once we fully switch to BS wstring sourceCode = config.Exists(L"BrainScriptNetworkBuilder") ? config(L"BrainScriptNetworkBuilder") : config(L"ExperimentalNetworkBuilder"); - let expr = BS::ParseConfigDictFromString(standardFunctions + computationNodes + commonMacros + msra::strfun::wstrprintf(L"deviceId = %d ; precision = '%ls' ; network = new ComputationNetwork ", (int) deviceId, ElemTypeName()) // TODO: check if typeid needs postprocessing - + sourceCode, - vector()); // source code has the form [ ... ] + //FILE * f = fopen("c:/me/CNTK.core.bs", "wb"); + //fprintf(f, "%ls", (standardFunctions + computationNodes + commonMacros).c_str()); + //fclose(f); + //wstring boilerplate = standardFunctions + computationNodes + commonMacros; + auto configDirs = ConfigParameters::GetBrainScriptNetworkBuilderIncludePaths(); + let expr = BS::ParseConfigDictFromString(L"include \'CNTK.core.bs\'" + + msra::strfun::wstrprintf(L"deviceId = %d ; precision = '%ls' ; network = new ComputationNetwork ", (int)deviceId, ElemTypeName()) + + sourceCode, // source code has the form [ ... ] with brackets in the string + move(configDirs)); // set include paths to all paths that configs were read from; no additional configurable include paths are supported by BrainScriptNetworkBuilder createNetworkFn = [expr](DEVICEID_TYPE /*deviceId*/) { // evaluate the parse tree--specifically the top-level field 'network'--which will create the network diff --git a/Source/CNTK/BrainScript/BrainScriptParser.cpp b/Source/CNTK/BrainScript/BrainScriptParser.cpp index 846e386a4..834fe0433 100644 --- a/Source/CNTK/BrainScript/BrainScriptParser.cpp +++ b/Source/CNTK/BrainScript/BrainScriptParser.cpp @@ -3,6 +3,7 @@ #define _CRT_SECURE_NO_WARNINGS // "secure" CRT not available on all platforms --add this at the top of all CPP files that give "function or variable may be unsafe" warnings #include "BrainScriptParser.h" +#include "File.h" #include #include #include @@ -32,11 +33,9 @@ SourceFile::SourceFile(wstring location, wstring text) : path(location), lines(split(text, L"\r\n")) { } // from string, e.g. command line -SourceFile::SourceFile(wstring path, const vector& includePaths) +SourceFile::SourceFile(wstring path) : path(path) // from file { - // ... scan paths - includePaths; File(path, fileOptionsRead | fileOptionsText).GetLines(lines); } @@ -380,6 +379,30 @@ private: throw LexerException(msg, where.beginLocation); } + // find a file either at given location or traverse include paths + static wstring FindSourceFile(const wstring& path, const vector& includePaths) + { + if (File::Exists(path)) + return path; + // non-existent path: scan include paths + // TODO: This is a little weird. Rather, this should be done by the call site. + let fileName = File::FileNameOf(path); + for (let& dir : includePaths) + { + // TODO: We should use the separator that matches the include path. + let newPath = dir + L"/" + fileName; + if (File::Exists(newPath)) + return newPath; + } + // not in include path: try EXE directory + let dir = File::DirectoryPathOf(File::GetExecutablePath()); + let newPath = dir + L"/" + fileName; + if (File::Exists(newPath)) + return newPath; + // not found: return unmodified, let caller fail + return path; + } + Token currentToken; // consume input characters to form a next token // - this function mutates the cursor, but does not set currentToken @@ -405,6 +428,7 @@ private: { if (IsInInclude()) { + includePaths.erase(includePaths.begin()); // pop dir of current include file PopSourceFile(); t = NextToken(); // tail call--the current 't' gets dropped/ignored t.isLineInitial = true; // eof is a line end @@ -443,8 +467,9 @@ private: let nameTok = NextToken(); // must be followed by a string literal if (nameTok.kind != stringliteral) Fail(L"'include' must be followed by a quoted string", nameTok); - let path = nameTok.symbol; // TODO: some massaging of the path - PushSourceFile(SourceFile(path, includePaths)); // current cursor is right after the pathname; that's where we will pick up later + let path = FindSourceFile(nameTok.symbol, includePaths); + PushSourceFile(SourceFile(path)); // current cursor is right after the pathname; that's where we will pick up later + includePaths.insert(includePaths.begin(), File::DirectoryPathOf(path)); return NextToken(); } } @@ -940,11 +965,12 @@ ExpressionPtr ParseConfigDictFromString(wstring text, vector&& includeP { return Parse(SourceFile(L"(command line)", text), move(includePaths)); } -ExpressionPtr ParseConfigDictFromFile(wstring path, vector&& includePaths) -{ - auto sourceFile = SourceFile(path, includePaths); - return Parse(move(sourceFile), move(includePaths)); -} +//ExpressionPtr ParseConfigDictFromFile(wstring path, vector includePaths) +//{ +// auto sourceFile = SourceFile(path); // note: no resolution against include paths done here +// includePaths.insert(includePaths.begin(), File::DirectoryPathOf(path)); // must include our own path for nested include statements +// return Parse(move(sourceFile), move(includePaths)); +//} ExpressionPtr ParseConfigExpression(const wstring& sourceText, vector&& includePaths) { auto parser = Parser(SourceFile(L"(command line)", sourceText), move(includePaths)); @@ -952,4 +978,5 @@ ExpressionPtr ParseConfigExpression(const wstring& sourceText, vector&& parser.VerifyAtEnd(); return expr; } -} } } // namespaces + +}}} diff --git a/Source/CNTK/BrainScript/BrainScriptParser.h b/Source/CNTK/BrainScript/BrainScriptParser.h index 6475d350c..8e307ecca 100644 --- a/Source/CNTK/BrainScript/BrainScriptParser.h +++ b/Source/CNTK/BrainScript/BrainScriptParser.h @@ -20,10 +20,10 @@ using namespace std; struct SourceFile // content of one source file (only in this header because TextLocation's private member uses it) { - /*const*/ wstring path; // where it came from - /*const*/ vector lines; // source code lines - SourceFile(wstring location, wstring text); // from string, e.g. command line - SourceFile(wstring path, const vector& includePaths); // from file + /*const*/ wstring path; // where it came from + /*const*/ vector lines; // source code lines + SourceFile(wstring location, wstring text); // from string, e.g. command line + SourceFile(wstring path); // from file }; struct TextLocation // position in the text. Lightweight value struct that we can copy around, even into dictionaries etc., for error messages @@ -135,6 +135,7 @@ typedef Expression::ExpressionPtr ExpressionPtr; // circumvent some circular def // access the parser through one of these functions ExpressionPtr ParseConfigDictFromString(wstring text, vector&& includePaths); // parses a list of dictionary members, returns a dictionary expression -ExpressionPtr ParseConfigDictFromFile(wstring path, vector&& includePaths); // likewise, but from a file path +//ExpressionPtr ParseConfigDictFromFile(wstring path, vector includePaths); // likewise, but from a file path ExpressionPtr ParseConfigExpression(const wstring& sourceText, vector&& includePaths); // parses a single expression from sourceText, which is meant to contain an include statement, hence includePaths -} } } // namespaces + +}}} diff --git a/Source/CNTK/BrainScript/CNTKCoreLib/CNTK.core.bs b/Source/CNTK/BrainScript/CNTKCoreLib/CNTK.core.bs new file mode 100644 index 000000000..b5f62350e --- /dev/null +++ b/Source/CNTK/BrainScript/CNTKCoreLib/CNTK.core.bs @@ -0,0 +1,108 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE.md file in the project root for full license information. +// +// CNTK.core.bs -- core BrainScript library including both general and CNTK-specific definitions +// + +############################################################################## +# standard functions +############################################################################## + +Print(value, format='') = new PrintAction [ what = value /*; how = format*/ ] +Debug(value, say = '', enabled = true) = new Debug [ /*macro arg values*/ ] +Format(value, format) = new StringFunction [ what = 'Format' ; arg = value ; how = format ] +Replace(s, from, to) = new StringFunction [ what = 'Replace' ; arg = s ; replacewhat = from ; withwhat = to ] +Substr(s, begin, num) = new StringFunction [ what = 'Substr' ; arg = s ; pos = begin ; chars = num ] +Chr(c) = new StringFunction [ what = 'Chr' ; arg = c ] +Floor(x) = new NumericFunction [ what = 'Floor' ; arg = x ] +Length(x) = new NumericFunction [ what = 'Length' ; arg = x ] +Ceil(x) = -Floor(-x) +Round(x) = Floor(x+0.5) +Abs(x) = if x >= 0 then x else -x +Sign(x) = if x > 0 then 1 else if x < 0 then -1 else 0 +Min(a,b) = if a < b then a else b +Max(a,b) = if a > b then a else b +Fac(n) = if n > 1 then Fac(n-1) * n else 1 + +############################################################################## +# ComputationNodes +############################################################################## + +LearnableParameter(rows, cols, learningRateMultiplier = 1.0, init = 'uniform'/*|fixedValue|gaussian|fromFile*/, initValueScale = 1, value = 0, initFromFilePath = '', initOnCPUOnly=true, randomSeed=-1, tag='') = new ComputationNode [ operation = 'LearnableParameter' ; shape = new TensorShape [ dims = (rows : cols) ] /*plus the function args*/ ] +Parameter = LearnableParameter // deprecated +ParameterTensor(dims, learningRateMultiplier = 1.0, init = 'uniform'/*|fixedValue|gaussian|fromFile*/, initValueScale = 1, value = 0, initFromFilePath = '', initOnCPUOnly=true, randomSeed=-1, tag='') = new ComputationNode [ operation = 'LearnableParameter' ; shape = new TensorShape [ /*dims*/ ] /*plus the function args*/ ] +Input(dims, tag='feature') = new ComputationNode [ operation = 'InputValue' ; shape = new TensorShape [ /*dims*/ ] ; isImage = false /*plus the function args*/ ] +SparseInput(dims, tag='feature') = new ComputationNode [ operation = 'SparseInputValue' ; shape = new TensorShape [ /*dims*/ ] ; isImage = false /*plus the function args*/ ] +ImageInput(imageWidth, imageHeight, imageChannels, imageLayout='CHW', tag='feature') = new ComputationNode [ operation = 'InputValue' ; isImage = true /*plus the function args*/ ] +SparseImageInput(imageWidth, imageHeight, imageChannels, imageLayout='CHW', tag='feature') = new ComputationNode [ operation = 'SparseInputValue' ; isImage = true /*plus the function args*/ ] +Constant(val, rows = 1, cols = 1, tag='') = Parameter(rows, cols, learningRateMultiplier = 0, init = 'fixedValue', value = val) +PastValue(dims, input, timeStep = 1, defaultHiddenActivation = 0.1, tag='') = new ComputationNode [ operation = 'PastValue' ; inputs = input ; shape = new TensorShape [ /*dims*/ ] /*plus the function args*/ ] +FutureValue(dims, input, timeStep = 1, defaultHiddenActivation = 0.1, tag='') = new ComputationNode [ operation = 'FutureValue' ; inputs = input ; shape = new TensorShape [ /*dims*/ ] /*plus the function args*/ ] +Shift(input, fromOffset, boundaryValue, boundaryMode=-1/*context*/, dim=-1, tag='') = new ComputationNode [ operation = 'Shift' ; inputs = (input : boundaryValue) /*plus the function args*/ ] +RowSlice(startIndex, numRows, input, tag='') = new ComputationNode [ operation = 'RowSlice' ; inputs = input /*plus the function args*/ ] +RowRepeat(input, numRepeats, tag='') = new ComputationNode [ operation = 'RowRepeat' ; inputs = input /*plus the function args*/ ] +RowStack(inputs, tag='') = new ComputationNode [ operation = 'RowStack' /*plus the function args*/ ] +Reshape(input, numRows, imageWidth = 0, imageHeight = 0, imageChannels = 0, tag='') = new ComputationNode [ operation = 'LegacyReshape' ; inputs = input /*plus the function args*/ ] +NewReshape(input, dims, beginDim=0, endDim=0, tag='') = new ComputationNode [ operation = 'Reshape' ; inputs = input ; shape = new TensorShape [ /*dims*/ ] /*plus the function args*/ ] +ReshapeDimension(x, dim, tensorShape) = NewReshape(x, tensorShape, beginDim=dim, endDim=dim + 1) +FlattenDimensions(x, dim, num) = NewReshape(x, 0, beginDim=dim, endDim=dim + num) +SplitDimension(x, dim, N) = ReshapeDimension(x, dim, 0:N) +TransposeDimensions(input, dim1, dim2, tag='') = new ComputationNode [ operation = 'TransposeDimensions' ; inputs = input /*plus the function args*/ ] +Transpose(x) = TransposeDimensions(x, 1, 2) +Times(A, B, outputRank=1, tag='') = new ComputationNode [ operation = 'Times' ; inputs = ( A : B ) /*plus the function args*/ ] +Logistic(label, probability, tag='') = new ComputationNode [ operation = 'Logistic' ; inputs = (label : probability) /*plus the function args*/ ] +WeightedLogistic(label, probability, instanceWeight, tag='') = new ComputationNode [ operation = 'Logistic' ; inputs = (label : probability : instanceWeight) /*plus the function args*/ ] +ReconcileMBLayout(dataInput, layoutInput, tag='') = new ComputationNode [ operation = 'ReconcileMBLayout' ; inputs = (dataInput : layoutInput) /*plus the function args*/ ] +Convolution(weightNode, inputValueNode, kernelWidth, kernelHeight, outputChannels, horizontalSubsample, verticalSubsample, zeroPadding = false, maxTempMemSizeInSamples = 0, imageLayout='CHW', tag='') = new ComputationNode [ operation = 'Convolution' ; inputs = (weightNode : inputValueNode) /*plus the function args*/ ] +MaxPooling(input, windowWidth, windowHeight, horizontalSubsample, verticalSubsample, imageLayout='CHW', tag='') = new ComputationNode [ operation = 'MaxPooling' ; inputs = input /*plus the function args*/ ] +AveragePooling(input, windowWidth, windowHeight, horizontalSubsample, verticalSubsample, imageLayout='CHW', tag='') = new ComputationNode [ operation = 'AveragePooling' ; inputs = input /*plus the function args*/ ] +ColumnwiseCrossProduct = KhatriRaoProduct // deprecated +ClassificationError = ErrorPrediction +Delay = PastValue +BatchNormalization(input, scale, bias, runMean, runInvStdDev, eval, spatial, normalizationTimeConstant = 0, epsilon = 0.00001, useCntkEngine = true, imageLayout='CHW', tag='') = new ComputationNode [ operation = 'BatchNormalization' ; inputs = (input : scale : bias : runMean : runInvStdDev) /*plus the function args*/ ] +ClassBasedCrossEntropyWithSoftmax(labelClassDescriptorVectorSequence, mainInputInfo, mainWeight, classLogProbsBeforeSoftmax, tag='') = new ComputationNode [ operation = 'ClassBasedCrossEntropyWithSoftmax' ; inputs = (labelClassDescriptorVectorSequence : mainInputInfo : mainWeight : classLogProbsBeforeSoftmax) /*plus the function args*/ ] +ColumnElementTimes(aVectorSequence, anotherVectorSequence, tag='') = new ComputationNode [ operation = 'ColumnElementTimes' ; inputs = (aVectorSequence : anotherVectorSequence) /*plus the function args*/ ] +CosDistance(aVectorSequence, anotherVectorSequence, tag='') = new ComputationNode [ operation = 'CosDistance' ; inputs = (aVectorSequence : anotherVectorSequence) /*plus the function args*/ ] +CosDistanceWithNegativeSamples(aVectorSequence, anotherVectorSequence, numShifts, numNegSamples, tag='') = new ComputationNode [ operation = 'CosDistanceWithNegativeSamples' ; inputs = (aVectorSequence : anotherVectorSequence : numShifts : numNegSamples) /*plus the function args*/ ] +Cosine(x, tag='') = new ComputationNode [ operation = 'Cosine' ; inputs = x /*plus the function args*/ ] +CrossEntropy(refProbVectorSequence, outProbVectorSequence, tag='') = new ComputationNode [ operation = 'CrossEntropy' ; inputs = (refProbVectorSequence : outProbVectorSequence) /*plus the function args*/ ] +CrossEntropyWithSoftmax(labelVectorSequence, outProbVectorSequence, tag='') = new ComputationNode [ operation = 'CrossEntropyWithSoftmax' ; inputs = (labelVectorSequence : outProbVectorSequence) /*plus the function args*/ ] +DiagTimes(diagonalMatrixAsColumnVector, matrix, tag='') = new ComputationNode [ operation = 'DiagTimes' ; inputs = (diagonalMatrixAsColumnVector : matrix) /*plus the function args*/ ] +Dropout(activationVectorSequence, tag='') = new ComputationNode [ operation = 'Dropout' ; inputs = activationVectorSequence /*plus the function args*/ ] +ElementTimes(aMatrix, anotherMatrix, tag='') = new ComputationNode [ operation = 'ElementTimes' ; inputs = (aMatrix : anotherMatrix) /*plus the function args*/ ] +ErrorPrediction(labelVectorSequence, outVectorSequence, tag='') = new ComputationNode [ operation = 'ErrorPrediction' ; inputs = (labelVectorSequence : outVectorSequence) /*plus the function args*/ ] +Exp(x, tag='') = new ComputationNode [ operation = 'Exp' ; inputs = x /*plus the function args*/ ] +GMMLogLikelihood(unnormalizedPriorVector, meansAsRows, logStdDevAsRows, dataVectorSequence, tag='') = new ComputationNode [ operation = 'GMMLogLikelihood' ; inputs = (unnormalizedPriorVector : meansAsRows : logStdDevAsRows : dataVectorSequence) /*plus the function args*/ ] +InvStdDev(dataVectorSequence, tag='') = new ComputationNode [ operation = 'InvStdDev' ; inputs = dataVectorSequence /*plus the function args*/ ] +KhatriRaoProduct(leftMatrix, rightMatrix, tag='') = new ComputationNode [ operation = 'KhatriRaoProduct' ; inputs = (leftMatrix : rightMatrix) /*plus the function args*/ ] +Log(x, tag='') = new ComputationNode [ operation = 'Log' ; inputs = x /*plus the function args*/ ] +LogSoftmax(z, tag='') = new ComputationNode [ operation = 'LogSoftmax' ; inputs = z /*plus the function args*/ ] +MatrixL1Reg(matrix, tag='') = new ComputationNode [ operation = 'MatrixL1Reg' ; inputs = matrix /*plus the function args*/ ] +MatrixL2Reg(matrix, tag='') = new ComputationNode [ operation = 'MatrixL2Reg' ; inputs = matrix /*plus the function args*/ ] +Mean(dataVectorSequence, tag='') = new ComputationNode [ operation = 'Mean' ; inputs = dataVectorSequence /*plus the function args*/ ] +Minus(leftMatrix, rightMatrix, tag='') = new ComputationNode [ operation = 'Minus' ; inputs = (leftMatrix : rightMatrix) /*plus the function args*/ ] +Negate(input, tag='') = new ComputationNode [ operation = 'Negate' ; inputs = input /*plus the function args*/ ] +PerDimMeanVarDeNormalization(dataVectorSequence, meanVector, invStdDevVector, tag='') = new ComputationNode [ operation = 'PerDimMeanVarDeNormalization' ; inputs = (dataVectorSequence : meanVector : invStdDevVector) /*plus the function args*/ ] +PerDimMeanVarNormalization(dataVectorSequence, meanVector, invStdDevVector, tag='') = new ComputationNode [ operation = 'PerDimMeanVarNormalization' ; inputs = (dataVectorSequence : meanVector : invStdDevVector) /*plus the function args*/ ] +Plus(leftMatrix, rightMatrix, tag='') = new ComputationNode [ operation = 'Plus' ; inputs = (leftMatrix : rightMatrix) /*plus the function args*/ ] +RectifiedLinear(z, tag='') = new ComputationNode [ operation = 'RectifiedLinear' ; inputs = z /*plus the function args*/ ] +Scale(scalarScalingFactor, matrix, tag='') = new ComputationNode [ operation = 'Scale' ; inputs = (scalarScalingFactor : matrix) /*plus the function args*/ ] +Sigmoid(z, tag='') = new ComputationNode [ operation = 'Sigmoid' ; inputs = z /*plus the function args*/ ] +Softmax(z, tag='') = new ComputationNode [ operation = 'Softmax' ; inputs = z /*plus the function args*/ ] +Hardmax(z, tag='') = new ComputationNode [ operation = 'Hardmax' ; inputs = z /*plus the function args*/ ] +SquareError(aMatrix, anotherMatrix, tag='') = new ComputationNode [ operation = 'SquareError' ; inputs = (aMatrix : anotherMatrix) /*plus the function args*/ ] +SumColumnElements(z, tag='') = new ComputationNode [ operation = 'SumColumnElements' ; inputs = z /*plus the function args*/ ] +SumElements(matrix, tag='') = new ComputationNode [ operation = 'SumElements' ; inputs = matrix /*plus the function args*/ ] +Tanh(z, tag='') = new ComputationNode [ operation = 'Tanh' ; inputs = z /*plus the function args*/ ] +TimeReverse(vectorSequence, tag='') = new ComputationNode [ operation = 'TimeReverse' ; inputs = vectorSequence /*plus the function args*/ ] +TransposeTimes(leftMatrix, rightMatrix, tag='') = new ComputationNode [ operation = 'TransposeTimes' ; inputs = (leftMatrix : rightMatrix) /*plus the function args*/ ] + +############################################################################## +# common macros +############################################################################## + +BFF(in, rows, cols) = [ B = Parameter(rows, 1, init = 'fixedValue', value = 0) ; W = Parameter(rows, cols) ; z = W*in+B ] +SBFF(in, rows, cols) = [ Eh = Sigmoid(BFF(in, rows, cols).z) ] +MeanVarNorm(feat) = PerDimMeanVarNormalization(feat, Mean(feat), InvStdDev(feat)) +LogPrior(labels) = Log(Mean(labels)) diff --git a/Source/CNTK/BrainScript/ExperimentalNetworkBuilder.cpp b/Source/CNTK/BrainScript/ExperimentalNetworkBuilder.cpp index 0868af87b..da7362366 100644 --- a/Source/CNTK/BrainScript/ExperimentalNetworkBuilder.cpp +++ b/Source/CNTK/BrainScript/ExperimentalNetworkBuilder.cpp @@ -1,3 +1,4 @@ +#if 0 // this entire file can be removed once CNTK.core.bs works // ExperimentalNetworkBuilder.cpp -- interface to new version of NDL (and config) parser --fseide #define _CRT_NONSTDC_NO_DEPRECATE // make VS accept POSIX functions without _ @@ -130,3 +131,4 @@ L"ParameterTensor(dims, learningRateMultiplier = 1.0, init = 'uniform'/*|fixedVa //BinaryStandardNode(ParallelNode) //BinaryStandardNode(StrideTimesNode) ; +#endif diff --git a/Source/CNTK/CNTK.cpp b/Source/CNTK/CNTK.cpp index c6c96cbce..03d7949ed 100644 --- a/Source/CNTK/CNTK.cpp +++ b/Source/CNTK/CNTK.cpp @@ -391,9 +391,9 @@ static wstring PathToBSStringLiteral(const wstring& path) // quote a pathname fo } // TODO: decide where these should go. Also, do we need three variables? -extern wstring standardFunctions; -extern wstring commonMacros; -extern wstring computationNodes; +//extern wstring standardFunctions; +//extern wstring commonMacros; +//extern wstring computationNodes; int wmainWithBS(int argc, wchar_t* argv[]) // called from wmain which is a wrapper that catches & reports Win32 exceptions { @@ -435,7 +435,8 @@ int wmainWithBS(int argc, wchar_t* argv[]) // called from wmain which is a wrapp // compile the BrainScript wstring bs = L"[\n"; - bs += standardFunctions + computationNodes + commonMacros + L"\n"; // start with standard macros + bs += L"include \'CNTK.core.bs'"; // start with including the standard macros + //bs += standardFunctions + computationNodes + commonMacros + L"\n"; for (const auto& sourceFile : sourceFiles) bs += L"include " + PathToBSStringLiteral(sourceFile) + L"\n"; bs += L"\n]\n"; diff --git a/Source/CNTK/CNTK.vcxproj b/Source/CNTK/CNTK.vcxproj index ad8f40f45..5790403c6 100644 --- a/Source/CNTK/CNTK.vcxproj +++ b/Source/CNTK/CNTK.vcxproj @@ -122,11 +122,19 @@ Math.dll; msmpi.dll; + + Copying dependencies + xcopy /I /D /Y $(ProjectDir)BrainScript\CNTKCoreLib\CNTK.core.bs $(TargetDir) + + + Copying dependencies + xcopy /I /D /Y $(ProjectDir)BrainScript\CNTKCoreLib\CNTK.core.bs $(TargetDir) + - if exist "%ProgramW6432%\NVIDIA Corporation\NVSMI" xcopy /I /D /Y "%ProgramW6432%\NVIDIA Corporation\NVSMI\nvml*.dll" $(TargetDir) - Copying NVidia GDK extension DLL to target folder + xcopy /I /D /Y $(ProjectDir)BrainScript\CNTKCoreLib\CNTK.core.bs $(TargetDir) && if exist "%ProgramW6432%\NVIDIA Corporation\NVSMI" xcopy /I /D /Y "%ProgramW6432%\NVIDIA Corporation\NVSMI\nvml*.dll" $(TargetDir) + Copying dependencies @@ -211,6 +219,17 @@ + + Document + true + true + true + true + false + false + false + false + diff --git a/Source/CNTK/CNTK.vcxproj.filters b/Source/CNTK/CNTK.vcxproj.filters index 069a8547d..1f2ba5240 100644 --- a/Source/CNTK/CNTK.vcxproj.filters +++ b/Source/CNTK/CNTK.vcxproj.filters @@ -257,6 +257,9 @@ {23e7cd74-fd60-4fb4-a925-c3dea584f176} + + {899f31fa-5906-4485-8875-14ad2c43ed8f} + @@ -266,4 +269,9 @@ BrainScript\Doc + + + BrainScript\CNTKCoreLib + + \ No newline at end of file diff --git a/Source/Common/Config.cpp b/Source/Common/Config.cpp index 95a3b39a3..37bd97127 100644 --- a/Source/Common/Config.cpp +++ b/Source/Common/Config.cpp @@ -62,6 +62,8 @@ std::string ConfigParameters::ParseCommandLine(int argc, wchar_t* argv[], Config // if haven't already read this file, read it resolvedConfigFiles.push_back(filePath); configString += config.ReadConfigFile(filePath); + // remember all config directories, for use as include paths by BrainScriptNetworkBuilder + GetBrainScriptNetworkBuilderIncludePaths().push_back(File::DirectoryPathOf(msra::strfun::utf16(filePath))); } else RuntimeError("Cannot specify same config file multiple times at the command line."); @@ -305,4 +307,5 @@ void TrimQuotes(std::string& str) if (str.front() == '"' && str.back() == '"') str = str.substr(1, str.size() - 2); } -} } } + +}}} diff --git a/Source/Common/File.cpp b/Source/Common/File.cpp index 5674a195a..1a72e806f 100644 --- a/Source/Common/File.cpp +++ b/Source/Common/File.cpp @@ -17,6 +17,8 @@ #ifdef _WIN32 #define NOMINMAX #include "Windows.h" +#include +#pragma comment(lib, "Pathcch.lib") #endif #ifdef __unix__ #include @@ -140,6 +142,63 @@ void File::Init(const wchar_t* filename, int fileOptions) }); } +// determine the directory for a given pathname +// (wstring only for now; feel free to make this a template if needed) +/*static*/ wstring File::DirectoryPathOf(wstring path) +{ +#if WIN32 + auto hr = PathCchRemoveFileSpec(&path[0], path.size()); + if (hr == S_OK) // done + path.resize(wcslen(&path[0])); + else if (hr == S_FALSE) // nothing to remove: use . + path = L"."; +#else + auto pos = path.find_last_of(L"/"); + if (pos != path.npos) + path.erase(pos - 1); + else // if no directory path at all, use current directory + return L"."; +#endif + return path; +} + +// determine the file name for a given pathname +// (wstring only for now; feel free to make this a template if needed) +/*static*/ wstring File::FileNameOf(wstring path) +{ +#if WIN32 + static const wstring delim = L"\\:/"; +#else + static const wstring delim = L"/"; +#endif + auto pos = path.find_last_of(delim); + if (pos != path.npos) + return path.substr(pos + 1); + else // no directory path + return path; +} + +// get path of current executable +/*static*/ wstring File::GetExecutablePath() +{ +#if WIN32 + wchar_t path[33000]; + if (GetModuleFileNameW(NULL, path, _countof(path)) == 0) + LogicError("GetExecutablePath: GetModuleFileNameW() unexpectedly failed."); + return path; +#else + // from http://stackoverflow.com/questions/4025370/can-an-executable-discover-its-own-path-linux + pid_t pid = getpid(); + char path[PATH_MAX]; + sprintf(path, "/proc/%d/exe", pid); + char dest[PATH_MAX]; + if (readlink(path, dest, PATH_MAX) == -1) + RuntimeError("GetExecutableDirectory: readlink() call failed."); + else + return msra::strfun::utf16(dest); +#endif +} + // skip to given delimiter character void File::SkipToDelimiter(int delim) { diff --git a/Source/Common/Include/Config.h b/Source/Common/Include/Config.h index 56bb6f1a0..3eeb33a13 100644 --- a/Source/Common/Include/Config.h +++ b/Source/Common/Include/Config.h @@ -1198,6 +1198,13 @@ public: static std::string ParseCommandLine(int argc, wchar_t* argv[], ConfigParameters& config); + // support for BrainScriptNetworkBuilder: It needs the config directories in order to know where to include files from. + static vector& GetBrainScriptNetworkBuilderIncludePaths() + { + static vector allConfigDirs; + return allConfigDirs; + } + // dump for debugging purposes void dump() const { @@ -1547,4 +1554,5 @@ public: typedef argvector intargvector; typedef argvector floatargvector; typedef argvector stringargvector; -} } } + +}}} diff --git a/Source/Common/Include/File.h b/Source/Common/Include/File.h index a350819b2..41be51a69 100644 --- a/Source/Common/Include/File.h +++ b/Source/Common/Include/File.h @@ -122,13 +122,6 @@ public: bool IsWhiteSpace(bool skip = false); int EndOfLineOrEOF(bool skip = false); - // test whether a file exists - template - static bool Exists(const String& filename); - - // make intermediate directories - template - static void MakeIntermediateDirs(const String& filename); // TryGetText - for text value, try and get a particular type // returns - true if value returned, otherwise false, can't parse @@ -143,6 +136,22 @@ public: void GetLines(std::vector& lines); void GetLines(std::vector& lines); + // static helpers + // test whether a file exists + template + static bool Exists(const String& filename); + + // make intermediate directories + template + static void MakeIntermediateDirs(const String& filename); + + // determine the directory and naked file name for a given pathname + static std::wstring DirectoryPathOf(std::wstring path); + static std::wstring FileNameOf(std::wstring path); + + // get path of current executable + static std::wstring File::GetExecutablePath(); + // put operator for basic types template File& operator<<(T val) diff --git a/Source/ComputationNetworkLib/PreComputeNodes.h b/Source/ComputationNetworkLib/PreComputeNodes.h index c7634fa6b..8f6229da3 100644 --- a/Source/ComputationNetworkLib/PreComputeNodes.h +++ b/Source/ComputationNetworkLib/PreComputeNodes.h @@ -40,7 +40,7 @@ public: // interface through which this node is operated on are these two functions // check whether node has already undergone precomputation - virtual bool /*IPreComputeNode::*/ HasComputed() const override { m_hasComputed; } + virtual bool /*IPreComputeNode::*/ HasComputed() const override { return m_hasComputed; } // call this with 'false' at start and with 'true' at end // This is used for resetting and updating from accumulators. @@ -89,10 +89,12 @@ public: InvalidArgument("%ls %ls operation requires its input to come in minibatches of samples.", NodeName().c_str(), OperationName().c_str()); m_pMBLayout = nullptr; // this node does not hold mini-batch data - if (!m_hasComputed) // this node retains state, and state gets destroyed by Resize(), so we must be careful - SetDims(Input(0)->GetSampleLayout(), false); - else if (!GetSampleLayout().IsElementwiseCompatibleWith(Input(0)->GetSampleLayout())) - InvalidArgument("%ls %ls operation: Precomputed parameter does not match input dimensions.", NodeName().c_str(), OperationName().c_str()); + //if (!m_hasComputed) // this node retains state, and state gets destroyed by Resize(), so we must be careful + SetDims(Input(0)->GetSampleLayout(), false); + //else if (!GetSampleLayout().IsElementwiseCompatibleWith(Input(0)->GetSampleLayout())) + // InvalidArgument("%ls %ls operation: Precomputed parameter does not match input dimensions.", NodeName().c_str(), OperationName().c_str()); + // BUGBUG: Above is a workaround, which may be OK since m_hasComputed getting set requires Validate() to have passed. + // This workaround won't guard agains corrupt files. } virtual void CopyTo(ComputationNodeBasePtr nodeP, const std::wstring& newName, const CopyNodeFlags flags) const override diff --git a/Source/Readers/ImageReader/ImageReader.vcxproj b/Source/Readers/ImageReader/ImageReader.vcxproj index 69a31e94f..0a0533895 100644 --- a/Source/Readers/ImageReader/ImageReader.vcxproj +++ b/Source/Readers/ImageReader/ImageReader.vcxproj @@ -80,10 +80,10 @@ ReaderLib.lib;Math.lib;$(OpenCVLib);$(ZipLibs);%(AdditionalDependencies) - if "$(HasOpenCv)" == "true" xcopy /Y "$(OPENCV_PATH)\x64\vc12\bin\opencv_world300.dll" $(TargetDir) -if "$(UseZip)" == "true" copy /Y "$(ZLIB_PATH)\bin\zip.dll" "$(TargetDir)" -if "$(UseZip)" == "true" if exist "$(ZLIB_PATH)\bin\zlib1.dll" (copy /Y "$(ZLIB_PATH)\bin\zlib1.dll" "$(TargetDir)") else (copy /Y "$(ZLIB_PATH)\bin\zlib.dll" "$(TargetDir)\zlib1.dll") - + if "$(HasOpenCv)" == "true" xcopy /I /D /Y "$(OPENCV_PATH)\x64\vc12\bin\opencv_world300.dll" $(TargetDir) +if "$(UseZip)" == "true" xcopy /I /D /Y "$(ZLIB_PATH)\bin\zip.dll" "$(TargetDir)" +if "$(UseZip)" == "true" if exist "$(ZLIB_PATH)\bin\zlib1.dll" (xcopy /I /D /Y "$(ZLIB_PATH)\bin\zlib1.dll" "$(TargetDir)") else (xcopy /I /D /Y "$(ZLIB_PATH)\bin\zlib.dll" "$(TargetDir)\zlib1.dll") + Copying dependencies