From 891346e4e3ff531401725ae87053bfa5f43ec72e Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 21 Aug 2019 15:53:40 +0300 Subject: [PATCH 01/57] Refactored DisposableObject --- src/TensorFlowNET.Core/APIs/c_api.cs | 2 +- src/TensorFlowNET.Core/Binding.Util.cs | 14 +++---- src/TensorFlowNET.Core/Buffers/Buffer.cs | 2 +- src/TensorFlowNET.Core/DisposableObject.cs | 41 +++++++++++-------- .../Eager/ContextOptions.cs | 3 +- src/TensorFlowNET.Core/Graphs/Graph.cs | 4 +- .../Graphs/ImportGraphDefOptions.cs | 2 +- .../Sessions/BaseSession.cs | 2 +- .../Sessions/SessionOptions.cs | 2 +- src/TensorFlowNET.Core/Status/Status.cs | 6 +-- src/TensorFlowNET.Core/Tensors/Tensor.cs | 8 ++-- 11 files changed, 44 insertions(+), 42 deletions(-) diff --git a/src/TensorFlowNET.Core/APIs/c_api.cs b/src/TensorFlowNET.Core/APIs/c_api.cs index 1656edd0..adf0b86f 100644 --- a/src/TensorFlowNET.Core/APIs/c_api.cs +++ b/src/TensorFlowNET.Core/APIs/c_api.cs @@ -59,6 +59,6 @@ namespace Tensorflow } [DllImport(TensorFlowLibName)] - public static unsafe extern IntPtr TF_Version(); + public static extern IntPtr TF_Version(); } } diff --git a/src/TensorFlowNET.Core/Binding.Util.cs b/src/TensorFlowNET.Core/Binding.Util.cs index da2cdf6e..bfbfa4ec 100644 --- a/src/TensorFlowNET.Core/Binding.Util.cs +++ b/src/TensorFlowNET.Core/Binding.Util.cs @@ -308,15 +308,14 @@ namespace Tensorflow public static IEnumerable TupleToEnumerable(object tuple) { Type t = tuple.GetType(); - if(t.IsGenericType && (t.FullName.StartsWith("System.Tuple") || t.FullName.StartsWith("System.ValueTuple"))) + if (t.IsGenericType && (t.FullName.StartsWith("System.Tuple") || t.FullName.StartsWith("System.ValueTuple"))) { var flds = t.GetFields(); - for(int i = 0; i < flds.Length;i++) + for (int i = 0; i < flds.Length; i++) { yield return flds[i].GetValue(tuple); } - } - else + } else { throw new System.Exception("Expected Tuple."); } @@ -329,12 +328,9 @@ namespace Tensorflow public static bool isinstance(object Item1, object tuple) { - var tup = TupleToEnumerable(tuple); - foreach(var t in tup) - { - if(isinstance(Item1, (Type)t)) + foreach (var t in TupleToEnumerable(tuple)) + if (isinstance(Item1, (Type) t)) return true; - } return false; } } diff --git a/src/TensorFlowNET.Core/Buffers/Buffer.cs b/src/TensorFlowNET.Core/Buffers/Buffer.cs index dbe576b8..396fb311 100644 --- a/src/TensorFlowNET.Core/Buffers/Buffer.cs +++ b/src/TensorFlowNET.Core/Buffers/Buffer.cs @@ -66,7 +66,7 @@ namespace Tensorflow return buffer.Data; } - protected override void DisposeUnManagedState(IntPtr handle) + protected override void DisposeUnmanagedResources(IntPtr handle) => c_api.TF_DeleteBuffer(handle); } } diff --git a/src/TensorFlowNET.Core/DisposableObject.cs b/src/TensorFlowNET.Core/DisposableObject.cs index 7e416e6d..8d06f182 100644 --- a/src/TensorFlowNET.Core/DisposableObject.cs +++ b/src/TensorFlowNET.Core/DisposableObject.cs @@ -29,49 +29,54 @@ namespace Tensorflow protected DisposableObject() { } - public DisposableObject(IntPtr handle) - { - _handle = handle; - } - - protected virtual void DisposeManagedState() - { - } - - protected abstract void DisposeUnManagedState(IntPtr handle); + protected DisposableObject(IntPtr handle) + => _handle = handle; - protected virtual void Dispose(bool disposing) + private void internal_dispose(bool disposing) { if (disposing) { + // dispose managed state (managed objects). + DisposeManagedResources(); + // free unmanaged resources (unmanaged objects) and override a finalizer below. if (_handle != IntPtr.Zero) { - // dispose managed state (managed objects). - DisposeManagedState(); - // set large fields to null. - DisposeUnManagedState(_handle); + DisposeUnmanagedResources(_handle); _handle = IntPtr.Zero; } } } + /// + /// Dispose any managed resources. + /// + /// Equivalent to what you would perform inside + protected virtual void DisposeManagedResources() + { + } + + /// + /// Dispose any unmanaged resources related to given . + /// + protected abstract void DisposeUnmanagedResources(IntPtr handle); + // override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. ~DisposableObject() { // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - Dispose(false); + internal_dispose(false); } // This code added to correctly implement the disposable pattern. public void Dispose() { // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - Dispose(true); + internal_dispose(true); // uncomment the following line if the finalizer is overridden above. GC.SuppressFinalize(this); } } -} +} \ No newline at end of file diff --git a/src/TensorFlowNET.Core/Eager/ContextOptions.cs b/src/TensorFlowNET.Core/Eager/ContextOptions.cs index 4bffddf6..4bdf04b3 100644 --- a/src/TensorFlowNET.Core/Eager/ContextOptions.cs +++ b/src/TensorFlowNET.Core/Eager/ContextOptions.cs @@ -1,8 +1,9 @@ using System; +using System.IO; namespace Tensorflow.Eager { - public class ContextOptions : IDisposable + public class ContextOptions : IDisposable //TODO! Eli: Shouldn't this inherieting DisposableObject? { private IntPtr _handle; diff --git a/src/TensorFlowNET.Core/Graphs/Graph.cs b/src/TensorFlowNET.Core/Graphs/Graph.cs index 77926dca..6fcc42a4 100644 --- a/src/TensorFlowNET.Core/Graphs/Graph.cs +++ b/src/TensorFlowNET.Core/Graphs/Graph.cs @@ -439,12 +439,12 @@ namespace Tensorflow _unfetchable_ops.Add(op); } - protected override void DisposeManagedState() + protected override void DisposeManagedResources() { ops.default_graph_stack.remove(this); } - protected override void DisposeUnManagedState(IntPtr handle) + protected override void DisposeUnmanagedResources(IntPtr handle) { c_api.TF_DeleteGraph(handle); } diff --git a/src/TensorFlowNET.Core/Graphs/ImportGraphDefOptions.cs b/src/TensorFlowNET.Core/Graphs/ImportGraphDefOptions.cs index 97720206..bdcaf60c 100644 --- a/src/TensorFlowNET.Core/Graphs/ImportGraphDefOptions.cs +++ b/src/TensorFlowNET.Core/Graphs/ImportGraphDefOptions.cs @@ -37,7 +37,7 @@ namespace Tensorflow c_api.TF_ImportGraphDefOptionsAddReturnOutput(_handle, name, index); } - protected override void DisposeUnManagedState(IntPtr handle) + protected override void DisposeUnmanagedResources(IntPtr handle) => c_api.TF_DeleteImportGraphDefOptions(handle); public static implicit operator IntPtr(ImportGraphDefOptions opts) => opts._handle; diff --git a/src/TensorFlowNET.Core/Sessions/BaseSession.cs b/src/TensorFlowNET.Core/Sessions/BaseSession.cs index efe2afd4..b6db7a65 100644 --- a/src/TensorFlowNET.Core/Sessions/BaseSession.cs +++ b/src/TensorFlowNET.Core/Sessions/BaseSession.cs @@ -396,7 +396,7 @@ namespace Tensorflow Dispose(); } - protected override void DisposeUnManagedState(IntPtr handle) + protected override void DisposeUnmanagedResources(IntPtr handle) { using (var status = new Status()) { diff --git a/src/TensorFlowNET.Core/Sessions/SessionOptions.cs b/src/TensorFlowNET.Core/Sessions/SessionOptions.cs index 8e0a0a74..ed99b7fe 100644 --- a/src/TensorFlowNET.Core/Sessions/SessionOptions.cs +++ b/src/TensorFlowNET.Core/Sessions/SessionOptions.cs @@ -32,7 +32,7 @@ namespace Tensorflow _handle = handle; } - protected override void DisposeUnManagedState(IntPtr handle) + protected override void DisposeUnmanagedResources(IntPtr handle) => c_api.TF_DeleteSessionOptions(handle); public void SetConfig(ConfigProto config) diff --git a/src/TensorFlowNET.Core/Status/Status.cs b/src/TensorFlowNET.Core/Status/Status.cs index 7eb2d7e3..2bdd806a 100644 --- a/src/TensorFlowNET.Core/Status/Status.cs +++ b/src/TensorFlowNET.Core/Status/Status.cs @@ -50,7 +50,7 @@ namespace Tensorflow /// public void Check(bool throwException = false) { - if(Code != TF_Code.TF_OK) + if (Code != TF_Code.TF_OK) { Console.WriteLine(Message); if (throwException) @@ -65,7 +65,7 @@ namespace Tensorflow return status._handle; } - protected override void DisposeUnManagedState(IntPtr handle) + protected override void DisposeUnmanagedResources(IntPtr handle) => c_api.TF_DeleteStatus(handle); } -} +} \ No newline at end of file diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.cs b/src/TensorFlowNET.Core/Tensors/Tensor.cs index d52b9422..66466b22 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.cs @@ -392,11 +392,11 @@ namespace Tensorflow return $"tf.Tensor '{name}' shape=({string.Join(",", shape)}) dtype={dtype}"; } - protected override void DisposeManagedState() - { - } + //protected override void DisposeManagedState() + //{ + //} - protected override void DisposeUnManagedState(IntPtr handle) + protected override void DisposeUnmanagedResources(IntPtr handle) { if(handle != IntPtr.Zero) { From 35295f8f18cfd09db1eda92f4f41e0d392890583 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 21 Aug 2019 15:54:04 +0300 Subject: [PATCH 02/57] Added different build directory for TensorflowNET.Examples.GPU --- .../TensorFlowNET.Examples.GPU.csproj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/TensorFlowNET.Examples/TensorFlowNET.Examples.GPU.csproj b/test/TensorFlowNET.Examples/TensorFlowNET.Examples.GPU.csproj index 1bd3d530..55e9b27d 100644 --- a/test/TensorFlowNET.Examples/TensorFlowNET.Examples.GPU.csproj +++ b/test/TensorFlowNET.Examples/TensorFlowNET.Examples.GPU.csproj @@ -6,6 +6,14 @@ false + + bin\debug-gpu + + + + bin\release-gpu + + From c876be4a793e1b3c520e5fb24c2fb05fae80c707 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 21 Aug 2019 15:54:45 +0300 Subject: [PATCH 03/57] _FetchHandler: Switched to NPTypeCode --- .../Sessions/_FetchHandler.cs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/TensorFlowNET.Core/Sessions/_FetchHandler.cs b/src/TensorFlowNET.Core/Sessions/_FetchHandler.cs index a46decb1..e1a77d90 100644 --- a/src/TensorFlowNET.Core/Sessions/_FetchHandler.cs +++ b/src/TensorFlowNET.Core/Sessions/_FetchHandler.cs @@ -17,6 +17,7 @@ using NumSharp; using System; using System.Collections.Generic; +using NumSharp.Backends; namespace Tensorflow { @@ -71,18 +72,18 @@ namespace Tensorflow { if(tensor_values.Length > 0) { - switch (tensor_values[0].dtype.Name) + switch (tensor_values[0].typecode) { - case "Int32": + case NPTypeCode.Int32: full_values.Add(float.NaN); break; - case "Single": + case NPTypeCode.Single: full_values.Add(float.NaN); break; - case "String": + case NPTypeCode.String: full_values.Add(float.NaN); break; - case "Char": + case NPTypeCode.Char: full_values.Add(float.NaN); break; default: @@ -100,21 +101,21 @@ namespace Tensorflow j += 1; if (value.ndim == 0) { - switch (value.dtype.Name) + switch (value.typecode) { - case "Int16": + case NPTypeCode.Int16: full_values.Add(value.GetValue(0)); break; - case "Int32": + case NPTypeCode.Int32: full_values.Add(value.GetValue(0)); break; - case "Int64": + case NPTypeCode.Int64: full_values.Add(value.GetValue(0)); break; - case "Single": + case NPTypeCode.Single: full_values.Add(value.GetValue(0)); break; - case "Double": + case NPTypeCode.Double: full_values.Add(value.GetValue(0)); break; /*case "String": From 1992bb054abd0cba5d155d85a81ce9302fa5587f Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 21 Aug 2019 15:55:07 +0300 Subject: [PATCH 04/57] gfile.cs, Walk(...): Handle case when directory top doesn't exist. --- src/TensorFlowNET.Core/IO/gfile.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/TensorFlowNET.Core/IO/gfile.cs b/src/TensorFlowNET.Core/IO/gfile.cs index 930dd652..a7303bf6 100644 --- a/src/TensorFlowNET.Core/IO/gfile.cs +++ b/src/TensorFlowNET.Core/IO/gfile.cs @@ -16,6 +16,7 @@ using System.Collections.Generic; using System.IO; +using System.Linq; namespace Tensorflow.IO { @@ -28,6 +29,9 @@ namespace Tensorflow.IO /// Traverse in order if True, post order if False. public IEnumerable<(string, string[], string[])> Walk(string top, bool in_order = true) { + if (!Directory.Exists(top)) + return Enumerable.Empty<(string, string[], string[])>(); + return walk_v2(top, in_order); } From d60c36f557daaa42f5623943cc29b62ffb62508d Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 21 Aug 2019 16:01:08 +0300 Subject: [PATCH 05/57] Tensor.Creation: Perf-opted when creating tensor from NDArray of string --- .../Tensors/Tensor.Creation.cs | 52 +++++++++++++------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs index 63fda866..e80e8157 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs @@ -21,6 +21,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; +using NumSharp.Backends; using NumSharp.Backends.Unmanaged; using static Tensorflow.c_api; @@ -477,7 +478,7 @@ namespace Tensorflow IntPtr tensor = c_api.TF_TensorData(handle); Marshal.WriteInt64(tensor, 0); - fixed (byte* src = &buffer[0]) + fixed (byte* src = buffer) c_api.TF_StringEncode(src, (UIntPtr)buffer.Length, (sbyte*)(tensor + sizeof(Int64)), size, status); _handle = handle; status.Check(true); @@ -486,24 +487,45 @@ namespace Tensorflow public unsafe Tensor(NDArray nd, TF_DataType? tensorDType = null) { // todo: handle nd of type "String" here too - if (tensorDType == TF_DataType.TF_STRING && nd.dtype.Name == "Byte") + if (tensorDType == TF_DataType.TF_STRING && nd.typecode == NPTypeCode.Byte) { - var buffer = nd.ToArray(); - var size = c_api.TF_StringEncodedSize((UIntPtr)buffer.Length); - var handle = TF_AllocateTensor(TF_DataType.TF_STRING, IntPtr.Zero, 0, (UIntPtr)((ulong)size + 8)); - - IntPtr tensor = c_api.TF_TensorData(handle); - Marshal.WriteInt64(tensor, 0); + if (nd.Unsafe.Storage.Shape.IsContiguous) + { + var bytesLength = (UIntPtr)nd.size; + var size = c_api.TF_StringEncodedSize(bytesLength); + var handle = TF_AllocateTensor(TF_DataType.TF_STRING, IntPtr.Zero, 0, (UIntPtr) ((ulong) size + 8)); + + IntPtr tensor = c_api.TF_TensorData(handle); + Marshal.WriteInt64(tensor, 0); + + var status = new Status(); + c_api.TF_StringEncode((byte*) nd.Unsafe.Address, bytesLength, (sbyte*) (tensor + sizeof(Int64)), size, status); + + status.Check(true); + _handle = handle; + IsMemoryOwner = false; + } + else + { + var buffer = nd.ToArray(); + var size = c_api.TF_StringEncodedSize((UIntPtr) buffer.Length); + var handle = TF_AllocateTensor(TF_DataType.TF_STRING, IntPtr.Zero, 0, (UIntPtr) ((ulong) size + 8)); + + IntPtr tensor = c_api.TF_TensorData(handle); + Marshal.WriteInt64(tensor, 0); + + var status = new Status(); + fixed (byte* src = buffer) + c_api.TF_StringEncode(src, (UIntPtr) buffer.Length, (sbyte*) (tensor + sizeof(Int64)), size, status); + + status.Check(true); + _handle = handle; + IsMemoryOwner = false; + } - var status = new Status(); - fixed (byte* src = &buffer[0]) - c_api.TF_StringEncode(src, (UIntPtr)buffer.Length, (sbyte*)(tensor + sizeof(Int64)), size, status); - - status.Check(true); - _handle=handle; - IsMemoryOwner = false; return; } + _handle = CreateTensorFromNDArray(nd, tensorDType); IsMemoryOwner = true; } From 4be99ecdd82bf1fa51667320765a8ed65927ceae Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 21 Aug 2019 16:24:35 +0300 Subject: [PATCH 06/57] Graph.cs: refactor and added docs --- src/TensorFlowNET.Core/Graphs/Graph.cs | 99 +++++++++++++------------- 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/src/TensorFlowNET.Core/Graphs/Graph.cs b/src/TensorFlowNET.Core/Graphs/Graph.cs index 6fcc42a4..07dc117e 100644 --- a/src/TensorFlowNET.Core/Graphs/Graph.cs +++ b/src/TensorFlowNET.Core/Graphs/Graph.cs @@ -23,57 +23,58 @@ using static Tensorflow.Binding; namespace Tensorflow { + /* + A TensorFlow computation, represented as a dataflow graph. + + A `Graph` contains a set of + `tf.Operation` objects, + which represent units of computation; and + `tf.Tensor` objects, which represent + the units of data that flow between operations. + + A default `Graph` is always registered, and accessible by calling + `tf.get_default_graph`. + To add an operation to the default graph, simply call one of the functions + that defines a new `Operation`: + + ```python + c = tf.constant(4.0) + assert c.graph is tf.get_default_graph() + ``` + + Another typical usage involves the + `tf.Graph.as_default` + context manager, which overrides the current default graph for the + lifetime of the context: + + ```python + g = tf.Graph() + with g.as_default(): + # Define operations and tensors in `g`. + c = tf.constant(30.0) + assert c.graph is g + ``` + + Important note: This class *is not* thread-safe for graph construction. All + operations should be created from a single thread, or external + synchronization must be provided. Unless otherwise specified, all methods + are not thread-safe. + + A `Graph` instance supports an arbitrary number of "collections" + that are identified by name. For convenience when building a large + graph, collections can store groups of related objects: for + example, the `tf.Variable` uses a collection (named + `tf.GraphKeys.GLOBAL_VARIABLES`) for + all variables that are created during the construction of a graph. The caller + may define additional collections by specifying a new name. + */ + /// - /// TensorFlow uses a dataflow graph to represent your computation in terms of the dependencies between individual operations. - /// This leads to a low-level programming model in which you first define the dataflow graph, - /// then create a TensorFlow session to run parts of the graph across a set of local and remote devices. - /// https://www.tensorflow.org/guide/graphs + /// TensorFlow uses a dataflow graph to represent your computation in terms of the dependencies between individual operations. + /// This leads to a low-level programming model in which you first define the dataflow graph, + /// then create a TensorFlow session to run parts of the graph across a set of local and remote devices. /// - /* - A TensorFlow computation, represented as a dataflow graph. - - A `Graph` contains a set of - `tf.Operation` objects, - which represent units of computation; and - `tf.Tensor` objects, which represent - the units of data that flow between operations. - - A default `Graph` is always registered, and accessible by calling - `tf.get_default_graph`. - To add an operation to the default graph, simply call one of the functions - that defines a new `Operation`: - - ```python - c = tf.constant(4.0) - assert c.graph is tf.get_default_graph() - ``` - - Another typical usage involves the - `tf.Graph.as_default` - context manager, which overrides the current default graph for the - lifetime of the context: - - ```python - g = tf.Graph() - with g.as_default(): - # Define operations and tensors in `g`. - c = tf.constant(30.0) - assert c.graph is g - ``` - - Important note: This class *is not* thread-safe for graph construction. All - operations should be created from a single thread, or external - synchronization must be provided. Unless otherwise specified, all methods - are not thread-safe. - - A `Graph` instance supports an arbitrary number of "collections" - that are identified by name. For convenience when building a large - graph, collections can store groups of related objects: for - example, the `tf.Variable` uses a collection (named - `tf.GraphKeys.GLOBAL_VARIABLES`) for - all variables that are created during the construction of a graph. The caller - may define additional collections by specifying a new name. - */ + /// https://www.tensorflow.org/guide/graphs

https://www.tensorflow.org/api_docs/python/tf/Graph
public partial class Graph : DisposableObject, IEnumerable { private Dictionary _nodes_by_id; From 533f8fdd6b5f1a6aba7016f43439ba487fdb1ddb Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 21 Aug 2019 16:28:00 +0300 Subject: [PATCH 07/57] Tensor.Creation.cs: perf-ops --- .../Tensors/Tensor.Creation.cs | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs index e80e8157..5fd3dfba 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs @@ -16,6 +16,7 @@ using NumSharp; using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; @@ -463,7 +464,7 @@ namespace Tensorflow *v = value; _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(Complex)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(Complex), deallocator: _hGlobalDeallocator, ref _deallocatorArgs); IsMemoryOwner=true; - } + } #endif /// @@ -532,11 +533,10 @@ namespace Tensorflow private unsafe IntPtr CreateTensorFromNDArray(NDArray nd, TF_DataType? given_dtype) { - if (nd.dtype.Name == "String") - throw new NotImplementedException("Support for NDArray of type string not implemented yet"); + if (nd.dtype.Name == "String") + throw new NotImplementedException("Support for NDArray of type string not implemented yet"); IArraySlice arraySlice; - var shape = nd.Unsafe.Storage.Shape; - if (shape.IsSliced || shape.IsBroadcasted) + if (nd.Unsafe.Storage.Shape.IsContiguous == false) { // the memory is NOT contiguous, so we have to copy the view into a contiguous memory block. arraySlice = nd.CloneData(); @@ -553,40 +553,40 @@ namespace Tensorflow var handle = TF_NewTensor(dtype, dims: nd.shape.Select(i=>(long)i).ToArray(), num_dims: nd.ndim, data: ptr, len: (UIntPtr)num_bytes, deallocator: _nothingDeallocator, ref _deallocatorArgs); IsMemoryOwner = false; return handle; - } - - public unsafe Tensor(byte[][] buffer, long[] shape) - { - int size = 0; - foreach (var b in buffer) - { - size += (int)TF_StringEncodedSize((UIntPtr)b.Length); - } - int totalSize = size + buffer.Length * 8; - ulong offset = 0; - IntPtr handle = TF_AllocateTensor(TF_DataType.TF_STRING, shape, shape.Length, (UIntPtr)totalSize); - - // Clear offset table - IntPtr pOffset = TF_TensorData(handle); - IntPtr dst = pOffset + buffer.Length * 8; - IntPtr dstLimit = pOffset + totalSize; - for (int i = 0; i < buffer.Length; i++) - { - Marshal.WriteInt64(pOffset, (long)offset); - using (var status = new Status()) - { - fixed (byte* src = &buffer[i][0]) - { - var written = TF_StringEncode(src, (UIntPtr)buffer[i].Length, (sbyte*)dst, (UIntPtr)(dstLimit.ToInt64() - dst.ToInt64()), status); - status.Check(true); - pOffset += 8; - dst += (int)written; - offset += written; - } - } - } - - _handle = handle; + } + + public unsafe Tensor(byte[][] buffer, long[] shape) + { + int size = 0; + foreach (var b in buffer) + { + size += (int)TF_StringEncodedSize((UIntPtr)b.Length); + } + int totalSize = size + buffer.Length * 8; + ulong offset = 0; + IntPtr handle = TF_AllocateTensor(TF_DataType.TF_STRING, shape, shape.Length, (UIntPtr)totalSize); + + // Clear offset table + IntPtr pOffset = TF_TensorData(handle); + IntPtr dst = pOffset + buffer.Length * 8; + IntPtr dstLimit = pOffset + totalSize; + for (int i = 0; i < buffer.Length; i++) + { + Marshal.WriteInt64(pOffset, (long)offset); + using (var status = new Status()) + { + fixed (byte* src = &buffer[i][0]) + { + var written = TF_StringEncode(src, (UIntPtr)buffer[i].Length, (sbyte*)dst, (UIntPtr)(dstLimit.ToInt64() - dst.ToInt64()), status); + status.Check(true); + pOffset += 8; + dst += (int)written; + offset += written; + } + } + } + + _handle = handle; } public Tensor(Operation op, int value_index, TF_DataType dtype) @@ -611,11 +611,11 @@ namespace Tensorflow /// specified dimensions. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] + [SuppressMessage("ReSharper", "LocalVariableHidesMember")] protected unsafe IntPtr CreateTensorWithoutCopying(TF_DataType dt, long[] shape, Array data, int element_size) { - if (dt == TF_DataType.TF_STRING && data is byte[]) + if (dt == TF_DataType.TF_STRING && data is byte[] buffer) { - var buffer = (byte[])data; var size = c_api.TF_StringEncodedSize((UIntPtr)buffer.Length); var handle = TF_AllocateTensor(TF_DataType.TF_STRING, IntPtr.Zero, 0, (UIntPtr)((ulong)size + 8)); From 4ba71e24db83d883badd563ad7e72b91ec08cf16 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 21 Aug 2019 16:28:17 +0300 Subject: [PATCH 08/57] Tensor.Explicit.cs: perf-ops --- .../Tensors/Tensor.Explicit.cs | 79 +++++++++++++------ 1 file changed, 57 insertions(+), 22 deletions(-) diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.Explicit.cs b/src/TensorFlowNET.Core/Tensors/Tensor.Explicit.cs index 6db60b4a..a4e9c428 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.Explicit.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.Explicit.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; namespace Tensorflow { @@ -6,70 +7,104 @@ namespace Tensorflow { public static explicit operator bool(Tensor tensor) { - EnsureScalar(tensor); - return tensor.Data()[0]; + unsafe + { + EnsureScalar(tensor); + return *(bool*) tensor.buffer; + } } public static explicit operator sbyte(Tensor tensor) { - EnsureScalar(tensor); - return tensor.Data()[0]; + unsafe + { + EnsureScalar(tensor); + return *(sbyte*) tensor.buffer; + } } public static explicit operator byte(Tensor tensor) { - EnsureScalar(tensor); - return tensor.Data()[0]; + unsafe + { + EnsureScalar(tensor); + return *(byte*) tensor.buffer; + } } public static explicit operator ushort(Tensor tensor) { - EnsureScalar(tensor); - return tensor.Data()[0]; + unsafe + { + EnsureScalar(tensor); + return *(ushort*) tensor.buffer; + } } public static explicit operator short(Tensor tensor) { - EnsureScalar(tensor); - return tensor.Data()[0]; + unsafe + { + EnsureScalar(tensor); + return *(short*) tensor.buffer; + } } public static explicit operator int(Tensor tensor) { - EnsureScalar(tensor); - return tensor.Data()[0]; + unsafe + { + EnsureScalar(tensor); + return *(int*) tensor.buffer; + } } public static explicit operator uint(Tensor tensor) { - EnsureScalar(tensor); - return tensor.Data()[0]; + unsafe + { + EnsureScalar(tensor); + return *(uint*) tensor.buffer; + } } public static explicit operator long(Tensor tensor) { - EnsureScalar(tensor); - return tensor.Data()[0]; + unsafe + { + EnsureScalar(tensor); + return *(long*) tensor.buffer; + } } public static explicit operator ulong(Tensor tensor) { - EnsureScalar(tensor); - return tensor.Data()[0]; + unsafe + { + EnsureScalar(tensor); + return *(ulong*) tensor.buffer; + } } public static explicit operator float(Tensor tensor) { - EnsureScalar(tensor); - return tensor.Data()[0]; + unsafe + { + EnsureScalar(tensor); + return *(float*) tensor.buffer; + } } public static explicit operator double(Tensor tensor) { - EnsureScalar(tensor); - return tensor.Data()[0]; + unsafe + { + EnsureScalar(tensor); + return *(double*) tensor.buffer; + } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void EnsureScalar(Tensor tensor) { if (tensor == null) From cfc3926ed199208146bf11dda1c91267827e8f60 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 21 Aug 2019 21:21:31 +0300 Subject: [PATCH 09/57] Copied globals.regen from NumSharp - Added supported_numericals_TF_DataType --- src/TensorFlowNET.Core/globals.regen | 38 ++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/TensorFlowNET.Core/globals.regen diff --git a/src/TensorFlowNET.Core/globals.regen b/src/TensorFlowNET.Core/globals.regen new file mode 100644 index 00000000..146155b3 --- /dev/null +++ b/src/TensorFlowNET.Core/globals.regen @@ -0,0 +1,38 @@ +%all_dtypes = ["NDArray","Complex","Boolean","Byte","Int16","UInt16","Int32","UInt32","Int64","UInt64","Char","Double","Single","String"] +%all_dtypes_lowercase = ["NDArray","Complex","bool","byte","short","ushort","int","uint","long","ulong","char","double","float","string"] + +%supported_primitives = ["Boolean","Byte","Int16","UInt16","Int32","UInt32","Int64","UInt64","Char","Double","Single","String"] +%supported_primitives_lowercase = ["bool","byte","short","ushort","int","uint","long","ulong","char","double","float","string"] + +%supported_numericals = ["Byte","Int16","UInt16","Int32","UInt32","Int64","UInt64","Char","Double","Single"] +%supported_numericals_lowercase = ["byte","short","ushort","int","uint","long","ulong","char","double","float"] +%supported_numericals_defaultvals = ["0","0","0","0","0u","0L","0UL","'\0'","0d","0f"] +%supported_numericals_onevales = ["1","1","1","1","1u","1L","1UL",1,"1d","1f"] +%supported_numericals_TF_DataType = ["TF_DataType.TF_UINT8","TF_DataType.TF_INT16","TF_DataType.TF_UINT16","TF_DataType.TF_INT32","TF_DataType.TF_UINT32","TF_DataType.TF_INT64","TF_DataType.TF_UINT64","TF_DataType.TF_UINT8","TF_DataType.TF_DOUBLE","TF_DataType.TF_FLOAT"] + +//this is the type we use in summerizing/reducting: +%supported_numericals_accumulatingType = ["UInt32","Int32","UInt32","Int32","UInt32","Int64","UInt64","UInt32","Double","Single"] +%supported_numericals_accumulatingType_defaultvals = ["0","0","0","0","0u","0L","0UL","'\0'","0d","0f"] + +%supported_numericals_signed = ["Int16","Int32","Int64","Double","Single"] +%supported_numericals_signed_lowercase = ["short","int","long","double","float"] +%supported_numericals_signed_defaultvals = ["0","0","0L","0d","0f"] +%supported_numericals_signed_onevales = ["1","1","1L","1d","1f"] + +%supported_numericals_unsigned = ["Byte","UInt16","UInt32","UInt64","Char"] +%supported_numericals_unsigned_lowercase = ["byte","ushort","uint","ulong","char"] +%supported_numericals_unsigned_defaultvals = ["0","0","0U","0UL","'\0'"] +%supported_numericals_unsigned_onevales = ["1","1","1U","1UL","'\1'"] + +%supported_dtypes = ["Boolean","Byte","Int16","UInt16","Int32","UInt32","Int64","UInt64","Char","Double","Single"] +%supported_numericals_TF_DataType = ["TF_DataType.TF_UINT8","TF_DataType.TF_INT16","TF_DataType.TF_UINT16","TF_DataType.TF_INT32","TF_DataType.TF_UINT32","TF_DataType.TF_INT64","TF_DataType.TF_UINT64","TF_DataType.TF_UINT8","TF_DataType.TF_DOUBLE","TF_DataType.TF_FLOAT"] + +%supported_dtypes_lowercase = ["bool","byte","short","ushort","int","uint","long","ulong","char","double","float"] +%supported_dtypes_defaultvals = [false,"0","0","0","0","0u","0L","0UL","'\0'","0d","0f"] +%supported_dtypes_onevales = [true,"1","1","1","1","1u","1L","1UL","'\1'","1d","1f"] +%supported_dtypes_dtype = ["bool","uint8","int16","uint16","int32","uint32","int64","uint64","uint8","float64","float32"] + +//this is the type we use in summerizing/reducting: +%supported_dtypes_accumulatingType = ["Int32","UInt32","Int32","UInt32","Int32","UInt32","Int64","UInt64","UInt32","Double","Single"] +%supported_dtypes_accumulatingType_defaultvals = [false, "0","0","0","0u","0L","0UL","'\0'","0d","0f"] + From 2129dbd675b4a474fd7703d1ddb5db9421206b5e Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 21 Aug 2019 22:13:48 +0300 Subject: [PATCH 10/57] Tensor perf-ops and cleanup, Revamped dtypes.cs, some renames. - Cleanup and docs to all Tensor.cs files - Changed all uses of System.Convert to NumSharp.Utilities.Converts - Added all missing types in dtypes.cs - Renamed tensor.Data to tensor.ToArray, added obsolete message - Renamed tensor.Data() to tensor.BufferToArray(), added obsolete message - Made GraphKeys to use const string instead allocating strings at every use of GraphKeys. --- .../ControlFlows/ControlFlowContext.cs | 2 +- .../Operations/NnOps/rnn.cs | 2 +- .../Operations/array_ops.py.cs | 2 +- src/TensorFlowNET.Core/Operations/nn_ops.cs | 2 +- .../Sessions/BaseSession.cs | 6 +- .../Tensors/Tensor.Creation.cs | 3 +- src/TensorFlowNET.Core/Tensors/Tensor.cs | 313 ++++++++++++------ src/TensorFlowNET.Core/Tensors/dtypes.cs | 99 ++++-- src/TensorFlowNET.Core/Tensors/tensor_util.cs | 15 +- src/TensorFlowNET.Core/ops.GraphKeys.cs | 84 ++++- test/TensorFlowNET.UnitTest/SessionTest.cs | 4 +- test/TensorFlowNET.UnitTest/TensorTest.cs | 2 +- .../nn_test/ZeroFractionTest.cs | 2 +- 13 files changed, 379 insertions(+), 157 deletions(-) diff --git a/src/TensorFlowNET.Core/Operations/ControlFlows/ControlFlowContext.cs b/src/TensorFlowNET.Core/Operations/ControlFlows/ControlFlowContext.cs index 2c05e36a..2a76c52c 100644 --- a/src/TensorFlowNET.Core/Operations/ControlFlows/ControlFlowContext.cs +++ b/src/TensorFlowNET.Core/Operations/ControlFlows/ControlFlowContext.cs @@ -141,7 +141,7 @@ namespace Tensorflow.Operations data, frame_name, is_constant, parallel_iterations, name: name); if (use_input_shape) - result.SetShape(data.TensorShape); + result.set_shape(data.TensorShape); return result; } diff --git a/src/TensorFlowNET.Core/Operations/NnOps/rnn.cs b/src/TensorFlowNET.Core/Operations/NnOps/rnn.cs index 3198942b..1b68d1cd 100644 --- a/src/TensorFlowNET.Core/Operations/NnOps/rnn.cs +++ b/src/TensorFlowNET.Core/Operations/NnOps/rnn.cs @@ -233,7 +233,7 @@ namespace Tensorflow.Operations dims.AddRange(x_static_shape.dims.Skip(2)); var shape = new TensorShape(dims.ToArray()); - x_t.SetShape(shape); + x_t.set_shape(shape); return x_t; } diff --git a/src/TensorFlowNET.Core/Operations/array_ops.py.cs b/src/TensorFlowNET.Core/Operations/array_ops.py.cs index d3213250..92fe2e3c 100644 --- a/src/TensorFlowNET.Core/Operations/array_ops.py.cs +++ b/src/TensorFlowNET.Core/Operations/array_ops.py.cs @@ -351,7 +351,7 @@ namespace Tensorflow var input_shape = tensor_util.to_shape(input_tensor.shape); if (optimize && input_tensor.NDims > -1 && input_shape.is_fully_defined()) { - var nd = np.array(input_tensor.shape).astype(out_type.as_numpy_datatype()); + var nd = np.array(input_tensor.shape).astype(out_type.as_numpy_dtype()); return constant_op.constant(nd, name: name); } } diff --git a/src/TensorFlowNET.Core/Operations/nn_ops.cs b/src/TensorFlowNET.Core/Operations/nn_ops.cs index cbf55861..63e0fca1 100644 --- a/src/TensorFlowNET.Core/Operations/nn_ops.cs +++ b/src/TensorFlowNET.Core/Operations/nn_ops.cs @@ -98,7 +98,7 @@ namespace Tensorflow // float to be selected, hence we use a >= comparison. var keep_mask = random_tensor >= rate; var ret = x * scale * math_ops.cast(keep_mask, x.dtype); - ret.SetShape(x.TensorShape); + ret.set_shape(x.TensorShape); return ret; }); } diff --git a/src/TensorFlowNET.Core/Sessions/BaseSession.cs b/src/TensorFlowNET.Core/Sessions/BaseSession.cs index b6db7a65..42ab1d4b 100644 --- a/src/TensorFlowNET.Core/Sessions/BaseSession.cs +++ b/src/TensorFlowNET.Core/Sessions/BaseSession.cs @@ -273,7 +273,7 @@ namespace Tensorflow { var tensor = new Tensor(output); NDArray nd = null; - Type type = tensor.dtype.as_numpy_datatype(); + Type type = tensor.dtype.as_numpy_dtype(); var ndims = tensor.shape; var offset = c_api.TF_TensorData(output); @@ -285,7 +285,7 @@ namespace Tensorflow nd = NDArray.Scalar(*(bool*)offset); break; case TF_DataType.TF_STRING: - var bytes = tensor.Data(); + var bytes = tensor.BufferToArray(); // wired, don't know why we have to start from offset 9. // length in the begin var str = UTF8Encoding.Default.GetString(bytes, 9, bytes[8]); @@ -324,7 +324,7 @@ namespace Tensorflow nd = np.array(bools).reshape(ndims); break; case TF_DataType.TF_STRING: - var bytes = tensor.Data(); + var bytes = tensor.BufferToArray(); // wired, don't know why we have to start from offset 9. // length in the begin var str = UTF8Encoding.Default.GetString(bytes, 9, bytes[8]); diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs index 5fd3dfba..ea58607b 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs @@ -549,10 +549,11 @@ namespace Tensorflow this.Tag = arraySlice; // keep a reference to the memory block to make sure it is not disposed while TF is using it var ptr = new IntPtr(arraySlice.Address); int num_bytes = (nd.size * nd.dtypesize); - var dtype = given_dtype ?? ToTFDataType(nd.dtype); + var dtype = given_dtype ?? nd.dtype.as_dtype(); var handle = TF_NewTensor(dtype, dims: nd.shape.Select(i=>(long)i).ToArray(), num_dims: nd.ndim, data: ptr, len: (UIntPtr)num_bytes, deallocator: _nothingDeallocator, ref _deallocatorArgs); IsMemoryOwner = false; return handle; + } public unsafe Tensor(byte[][] buffer, long[] shape) diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.cs b/src/TensorFlowNET.Core/Tensors/Tensor.cs index 66466b22..798c27b6 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.cs @@ -17,9 +17,16 @@ using NumSharp; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Linq; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; +using System.Threading.Tasks; +using NumSharp.Backends; +using NumSharp.Backends.Unmanaged; +using NumSharp.Utilities; using Tensorflow.Framework; using static Tensorflow.Binding; @@ -29,42 +36,68 @@ namespace Tensorflow /// A tensor is a generalization of vectors and matrices to potentially higher dimensions. /// Internally, TensorFlow represents tensors as n-dimensional arrays of base datatypes. /// + [SuppressMessage("ReSharper", "ConvertToAutoProperty")] public partial class Tensor : DisposableObject, ITensorOrOperation, _TensorLike { - private int _id; - private Operation _op; + private readonly int _id; + private readonly Operation _op; + private readonly int _value_index; + private TF_Output? _tf_output; + private readonly TF_DataType _dtype; public int Id => _id; + + /// + /// The Graph that contains this tensor. + /// public Graph graph => op?.graph; + + /// + /// The Operation that produces this tensor as an output. + /// public Operation op => _op; + public Tensor[] outputs => op.outputs; /// - /// The string name of this tensor. + /// The string name of this tensor. /// public string name => $"{(op == null ? "" : $"{op.name}:{_value_index}")}"; - private int _value_index; + /// + /// The index of this tensor in the outputs of its Operation. + /// public int value_index => _value_index; - private TF_DataType _dtype = TF_DataType.DtInvalid; + /// + /// The DType of elements in this tensor. + /// public TF_DataType dtype => _handle == IntPtr.Zero ? _dtype : c_api.TF_TensorType(_handle); public ulong bytesize => _handle == IntPtr.Zero ? 0 : c_api.TF_TensorByteSize(_handle); - public ulong itemsize => _handle == IntPtr.Zero ? 0 : c_api.TF_DataTypeSize(dtype); public ulong size => _handle == IntPtr.Zero ? 0 : bytesize / itemsize; - public IntPtr buffer => _handle == IntPtr.Zero ? IntPtr.Zero : c_api.TF_TensorData(_handle); public int num_consumers(TF_Output oper_out) => _handle == IntPtr.Zero ? 0 : c_api.TF_OperationOutputNumConsumers(oper_out); + public int NDims => rank; - private TF_Output? _tf_output; + /// + /// The name of the device on which this tensor will be produced, or null. + /// + public string Device => op.Device; + + public int[] dims => shape; /// - /// used for keep other pointer when do implicit operating + /// Used for keep other pointer when do implicit operating /// public object Tag { get; set; } + + /// + /// Returns the shape of a tensor. + /// + /// https://www.tensorflow.org/api_docs/python/tf/shape public int[] shape { get @@ -76,14 +109,13 @@ namespace Tensorflow var status = new Status(); c_api.TF_GraphGetTensorShape(op.graph, _as_tf_output(), dims, rank, status); status.Check(); - } - else + } else { for (int i = 0; i < rank; i++) dims[i] = c_api.TF_Dim(_handle, i); } - return dims.Select(x => Convert.ToInt32(x)).ToArray(); + return dims.Select(x => ((IConvertible) x).ToInt32(CultureInfo.InvariantCulture)).ToArray(); } set @@ -93,38 +125,52 @@ namespace Tensorflow if (value == null) c_api.TF_GraphSetTensorShape(this.graph, this._as_tf_output(), null, -1, status); else - c_api.TF_GraphSetTensorShape(this.graph, this._as_tf_output(), value.Select(x => Convert.ToInt64(x)).ToArray(), value.Length, status); + c_api.TF_GraphSetTensorShape(this.graph, this._as_tf_output(), value.Select(Convert.ToInt64).ToArray(), value.Length, status); } } public int[] _shape_tuple() { - if (shape == null) return null; - return shape.Select(x => (int)x).ToArray(); + return (int[]) shape.Clone(); } public TensorShape TensorShape => tensor_util.to_shape(shape); - public void SetShape(TensorShape shape) + /// + /// Updates the shape of this tensor. + /// + public void set_shape(TensorShape shape) + { + this.shape = (int[]) shape.dims.Clone(); + } + + /// + /// Updates the shape of this tensor. + /// + [Obsolete("Please use set_shape(TensorShape shape) instead.", false)] + public void SetShape(TensorShape shape) { - this.shape = shape.dims; + this.shape = (int[]) shape.dims.Clone(); } + /// + /// Updates the shape of this tensor. + /// public void set_shape(Tensor shape) { + // ReSharper disable once MergeConditionalExpression this.shape = shape is null ? null : shape.shape; } - public int[] dims => shape; - /// - /// number of dimensions - /// 0 Scalar (magnitude only) - /// 1 Vector (magnitude and direction) - /// 2 Matrix (table of numbers) - /// 3 3-Tensor (cube of numbers) + /// number of dimensions

+ /// 0 Scalar (magnitude only)

+ /// 1 Vector (magnitude and direction)

+ /// 2 Matrix (table of numbers)

+ /// 3 3-Tensor (cube of numbers)

/// n n-Tensor (you get the idea) ///
+ /// https://www.tensorflow.org/api_docs/python/tf/rank public int rank { get @@ -137,17 +183,15 @@ namespace Tensorflow status.Check(); return ndim; } - else - { - return c_api.TF_NumDims(_handle); - } + + return c_api.TF_NumDims(_handle); } } - public int NDims => rank; - - public string Device => op.Device; - + /// + /// Returns a list of Operations that consume this tensor. + /// + /// public Operation[] consumers() { var output = _as_tf_output(); @@ -157,37 +201,136 @@ namespace Tensorflow public TF_Output _as_tf_output() { - if(!_tf_output.HasValue) + if (!_tf_output.HasValue) _tf_output = new TF_Output(op, value_index); return _tf_output.Value; } - public T[] Data() + [Obsolete("Please use ToArray() instead.", false)] + public T[] Data() where T : unmanaged + { + return ToArray(); + } + + /// + /// + /// + /// + /// + /// When is string + public T[] ToArray() where T : unmanaged { - // Column major order - // https://en.wikipedia.org/wiki/File:Row_and_column_major_order.svg - // matrix:[[1, 2, 3], [4, 5, 6]] - // index: 0 2 4 1 3 5 - // result: 1 4 2 5 3 6 - var data = new T[size]; - - for (ulong i = 0; i < size; i++) + //when T is string + if (typeof(T) == typeof(string)) { - data[i] = Marshal.PtrToStructure(buffer + (int)(i * itemsize)); + if (dtype != TF_DataType.TF_STRING) + throw new ArgumentException($"Given <{typeof(T).Name}> can't be converted to string."); + + return (T[]) (object) StringData(); } - return data; + //Are the types matching? + if (typeof(T).as_dtype() == _dtype) + { + //types match, no need to perform cast + var ret = new T[size]; + unsafe + { + var len = (long) size; + fixed (T* dstRet = ret) + { + T* dst = dstRet; //local stack copy + if (typeof(T).IsPrimitive) + { + var src = (T*) buffer; + len *= ((long) itemsize); + System.Buffer.MemoryCopy(src, dst, len, len); + } else + { + var itemsize = (long) this.itemsize; + var buffer = this.buffer.ToInt64(); + Parallel.For(0L, len, i => dst[i] = Marshal.PtrToStructure(new IntPtr(buffer + i * itemsize))); + } + } + } + + return ret; + } else + { + + //types do not match, need to perform cast + var ret = new T[size]; + unsafe + { + var len = (long) size; + fixed (T* dstRet = ret) + { + T* dst = dstRet; //local stack copy + +#if _REGEN + #region Compute + switch (_dtype.as_numpy_datatype().GetTypeCode()) + { + %foreach supported_dtypes,supported_dtypes_lowercase% + case NPTypeCode.#1:new UnmanagedMemoryBlock<#2>((#2*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + % + default: + throw new NotSupportedException(); + } + #endregion +#else + #region Compute + switch (_dtype.as_numpy_dtype().GetTypeCode()) + { + case NPTypeCode.Boolean:new UnmanagedMemoryBlock((bool*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.Byte:new UnmanagedMemoryBlock((byte*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.Int16:new UnmanagedMemoryBlock((short*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.UInt16:new UnmanagedMemoryBlock((ushort*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.Int32:new UnmanagedMemoryBlock((int*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.UInt32:new UnmanagedMemoryBlock((uint*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.Int64:new UnmanagedMemoryBlock((long*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.UInt64:new UnmanagedMemoryBlock((ulong*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.Char:new UnmanagedMemoryBlock((char*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.Double:new UnmanagedMemoryBlock((double*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.Single:new UnmanagedMemoryBlock((float*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + default: + throw new NotSupportedException(); + } + #endregion +#endif + + } + } + + return ret; + } } + + /// + /// Copies the memory of current buffer onto newly allocated array. + /// + /// + [Obsolete("Please use set_shape(TensorShape shape) instead.", false)] public byte[] Data() + { + return BufferToArray(); + } + + /// + /// Copies the memory of current buffer onto newly allocated array. + /// + /// + public byte[] BufferToArray() { var data = new byte[bytesize]; - Marshal.Copy(buffer, data, 0, (int)bytesize); + Marshal.Copy(buffer, data, 0, (int) bytesize); return data; } - public unsafe string[] StringData() + /// Used internally in ToArray<T> + private unsafe string[] StringData() { // // TF_STRING tensors are encoded with a table of 8-byte offsets followed by TF_StringEncode-encoded bytes. @@ -199,19 +342,19 @@ namespace Tensorflow var buffer = new byte[size][]; var src = c_api.TF_TensorData(_handle); - var srcLen = (IntPtr)(src.ToInt64() + (long)bytesize); - src += (int)(size * 8); + var srcLen = (IntPtr) (src.ToInt64() + (long) bytesize); + src += (int) (size * 8); for (int i = 0; i < buffer.Length; i++) { using (var status = new Status()) { IntPtr dst = IntPtr.Zero; UIntPtr dstLen = UIntPtr.Zero; - var read = c_api.TF_StringDecode((byte*)src, (UIntPtr)(srcLen.ToInt64() - src.ToInt64()), (byte**)&dst, &dstLen, status); + var read = c_api.TF_StringDecode((byte*) src, (UIntPtr) (srcLen.ToInt64() - src.ToInt64()), (byte**) &dst, &dstLen, status); status.Check(true); - buffer[i] = new byte[(int)dstLen]; + buffer[i] = new byte[(int) dstLen]; Marshal.Copy(dst, buffer[i], 0, buffer[i].Length); - src += (int)read; + src += (int) read; } } @@ -229,51 +372,29 @@ namespace Tensorflow } /// - /// Evaluates this tensor in a `Session`. + /// Evaluates this tensor in a `Session`. /// /// A dictionary that maps `Tensor` objects to feed values. - /// The `Session` to be used to evaluate this tensor. - /// + /// A array corresponding to the value of this tensor. public NDArray eval(params FeedItem[] feed_dict) { return ops._eval_using_default_session(this, feed_dict, graph); } + /// + /// Evaluates this tensor in a `Session`. + /// + /// A dictionary that maps `Tensor` objects to feed values. + /// The `Session` to be used to evaluate this tensor. + /// A array corresponding to the value of this tensor. public NDArray eval(Session session, FeedItem[] feed_dict = null) { return ops._eval_using_default_session(this, feed_dict, graph, session); } - public TF_DataType ToTFDataType(Type type) - { - switch (type.Name) - { - case "Char": - return TF_DataType.TF_UINT8; - case "Int16": - return TF_DataType.TF_INT16; - case "Int32": - return TF_DataType.TF_INT32; - case "Int64": - return TF_DataType.TF_INT64; - case "Single": - return TF_DataType.TF_FLOAT; - case "Double": - return TF_DataType.TF_DOUBLE; - case "Byte": - return TF_DataType.TF_UINT8; - case "String": - return TF_DataType.TF_STRING; - case "Boolean": - return TF_DataType.TF_BOOL; - default: - throw new NotImplementedException("ToTFDataType error"); - } - } - public Tensor slice(Slice slice) { - var slice_spec = new int[] { slice.Start.Value }; + var slice_spec = new int[] {slice.Start.Value}; var begin = new List(); var end = new List(); var strides = new List(); @@ -289,26 +410,26 @@ namespace Tensorflow if (slice.Stop.HasValue) { end.Add(slice.Stop.Value); - } - else + } else { end.Add(0); end_mask |= (1 << index); } + strides.Add(slice.Step); index += 1; } - return tf_with(ops.name_scope(null, "strided_slice", new { begin, end, strides }), scope => + return tf_with(ops.name_scope(null, "strided_slice", new {begin, end, strides}), scope => { string name = scope; if (begin != null) { var (packed_begin, packed_end, packed_strides) = (array_ops.stack(begin.ToArray()), - array_ops.stack(end.ToArray()), - array_ops.stack(strides.ToArray())); + array_ops.stack(end.ToArray()), + array_ops.stack(strides.ToArray())); return gen_array_ops.strided_slice( this, @@ -320,7 +441,6 @@ namespace Tensorflow shrink_axis_mask: shrink_axis_mask, new_axis_mask: new_axis_mask, ellipsis_mask: ellipsis_mask, - name: name); } @@ -330,7 +450,7 @@ namespace Tensorflow public Tensor slice(int start) { - var slice_spec = new int[] { start }; + var slice_spec = new int[] {start}; var begin = new List(); var end = new List(); var strides = new List(); @@ -349,15 +469,15 @@ namespace Tensorflow index += 1; } - return tf_with(ops.name_scope(null, "strided_slice", new { begin, end, strides }), scope => + return tf_with(ops.name_scope(null, "strided_slice", new {begin, end, strides}), scope => { string name = scope; if (begin != null) { var (packed_begin, packed_end, packed_strides) = (array_ops.stack(begin.ToArray()), - array_ops.stack(end.ToArray()), - array_ops.stack(strides.ToArray())); + array_ops.stack(end.ToArray()), + array_ops.stack(strides.ToArray())); return gen_array_ops.strided_slice( this, @@ -369,7 +489,6 @@ namespace Tensorflow shrink_axis_mask: shrink_axis_mask, new_axis_mask: new_axis_mask, ellipsis_mask: ellipsis_mask, - name: name); } @@ -392,13 +511,9 @@ namespace Tensorflow return $"tf.Tensor '{name}' shape=({string.Join(",", shape)}) dtype={dtype}"; } - //protected override void DisposeManagedState() - //{ - //} - protected override void DisposeUnmanagedResources(IntPtr handle) { - if(handle != IntPtr.Zero) + if (handle != IntPtr.Zero) { c_api.TF_DeleteTensor(handle); } @@ -417,4 +532,4 @@ namespace Tensorflow public int tensor_int_val { get; set; } } -} +} \ No newline at end of file diff --git a/src/TensorFlowNET.Core/Tensors/dtypes.cs b/src/TensorFlowNET.Core/Tensors/dtypes.cs index 807dc6f5..37f1ca61 100644 --- a/src/TensorFlowNET.Core/Tensors/dtypes.cs +++ b/src/TensorFlowNET.Core/Tensors/dtypes.cs @@ -15,6 +15,8 @@ ******************************************************************************/ using System; +using System.Numerics; +using NumSharp.Backends; namespace Tensorflow { @@ -23,35 +25,100 @@ namespace Tensorflow public static TF_DataType int8 = TF_DataType.TF_INT8; public static TF_DataType int32 = TF_DataType.TF_INT32; public static TF_DataType int64 = TF_DataType.TF_INT64; + public static TF_DataType uint8 = TF_DataType.TF_UINT8; + public static TF_DataType uint32 = TF_DataType.TF_UINT32; + public static TF_DataType uint64 = TF_DataType.TF_UINT64; public static TF_DataType float32 = TF_DataType.TF_FLOAT; // is that float32? public static TF_DataType float16 = TF_DataType.TF_HALF; public static TF_DataType float64 = TF_DataType.TF_DOUBLE; - public static Type as_numpy_datatype(this TF_DataType type) + /// + /// + /// + /// + /// equivalent to , if none exists, returns null. + public static Type as_numpy_dtype(this TF_DataType type) { switch (type) { case TF_DataType.TF_BOOL: return typeof(bool); + case TF_DataType.TF_UINT8: + return typeof(byte); case TF_DataType.TF_INT64: return typeof(long); + case TF_DataType.TF_UINT64: + return typeof(ulong); case TF_DataType.TF_INT32: return typeof(int); + case TF_DataType.TF_UINT32: + return typeof(uint); case TF_DataType.TF_INT16: return typeof(short); + case TF_DataType.TF_UINT16: + return typeof(ushort); case TF_DataType.TF_FLOAT: return typeof(float); case TF_DataType.TF_DOUBLE: return typeof(double); case TF_DataType.TF_STRING: return typeof(string); + case TF_DataType.TF_COMPLEX128: + case TF_DataType.TF_COMPLEX64: //64 is also TF_COMPLEX + return typeof(Complex); default: return null; } } - // "sbyte", "byte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double", "Complex" - public static TF_DataType as_dtype(Type type, TF_DataType? dtype = null) + /// + /// + /// + /// + /// + /// When has no equivalent + public static NPTypeCode as_numpy_typecode(this TF_DataType type) + { + switch (type) + { + case TF_DataType.TF_BOOL: + return NPTypeCode.Boolean; + case TF_DataType.TF_UINT8: + return NPTypeCode.Byte; + case TF_DataType.TF_INT64: + return NPTypeCode.Int64; + case TF_DataType.TF_INT32: + return NPTypeCode.Int32; + case TF_DataType.TF_INT16: + return NPTypeCode.Int16; + case TF_DataType.TF_UINT64: + return NPTypeCode.UInt64; + case TF_DataType.TF_UINT32: + return NPTypeCode.UInt32; + case TF_DataType.TF_UINT16: + return NPTypeCode.UInt16; + case TF_DataType.TF_FLOAT: + return NPTypeCode.Single; + case TF_DataType.TF_DOUBLE: + return NPTypeCode.Double; + case TF_DataType.TF_STRING: + return NPTypeCode.String; + case TF_DataType.TF_COMPLEX128: + case TF_DataType.TF_COMPLEX64: //64 is also TF_COMPLEX + return NPTypeCode.Complex; + default: + throw new NotSupportedException($"Unable to convert {type} to a NumSharp typecode."); + } + } + + /// + /// + /// + /// + /// + /// + /// When has no equivalent + public static TF_DataType as_dtype(this Type type, TF_DataType? dtype = null) { switch (type.Name) { @@ -98,7 +165,7 @@ namespace Tensorflow dtype = TF_DataType.TF_BOOL; break; default: - throw new Exception("as_dtype Not Implemented"); + throw new NotSupportedException($"Unable to convert {type} to a NumSharp typecode."); } return dtype.Value; @@ -106,16 +173,7 @@ namespace Tensorflow public static DataType as_datatype_enum(this TF_DataType type) { - DataType dtype = DataType.DtInvalid; - - switch (type) - { - default: - Enum.TryParse(((int)type).ToString(), out dtype); - break; - } - - return dtype; + return Enum.TryParse(((int) type).ToString(), out DataType dtype) ? dtype : DataType.DtInvalid; } public static TF_DataType as_base_dtype(this TF_DataType type) @@ -132,7 +190,7 @@ namespace Tensorflow public static Type as_numpy_dtype(this DataType type) { - return type.as_tf_dtype().as_numpy_datatype(); + return type.as_tf_dtype().as_numpy_dtype(); } public static DataType as_base_dtype(this DataType type) @@ -144,16 +202,7 @@ namespace Tensorflow public static TF_DataType as_tf_dtype(this DataType type) { - TF_DataType dtype = TF_DataType.DtInvalid; - - switch (type) - { - default: - Enum.TryParse(((int)type).ToString(), out dtype); - break; - } - - return dtype; + return Enum.TryParse(((int) type).ToString(), out TF_DataType dtype) ? dtype : TF_DataType.DtInvalid; } public static TF_DataType as_ref(this TF_DataType type) diff --git a/src/TensorFlowNET.Core/Tensors/tensor_util.cs b/src/TensorFlowNET.Core/Tensors/tensor_util.cs index ded105c7..43848da6 100644 --- a/src/TensorFlowNET.Core/Tensors/tensor_util.cs +++ b/src/TensorFlowNET.Core/Tensors/tensor_util.cs @@ -17,6 +17,7 @@ using NumSharp; using System; using System.Linq; +using NumSharp.Utilities; namespace Tensorflow { @@ -109,7 +110,7 @@ namespace Tensorflow // We first convert value to a numpy array or scalar. NDArray nparray = null; - var np_dt = dtype.as_numpy_datatype(); + var np_dt = dtype.as_numpy_dtype(); if (values is NDArray nd) { @@ -188,37 +189,37 @@ namespace Tensorflow if (values.GetType().IsArray) nparray = np.array((int[])values, np_dt); else - nparray = Convert.ToInt32(values); + nparray = Converts.ToInt32(values); break; case "Int64": if (values.GetType().IsArray) nparray = np.array((int[])values, np_dt); else - nparray = Convert.ToInt64(values); + nparray = Converts.ToInt64(values); break; case "Single": if (values.GetType().IsArray) nparray = np.array((float[])values, np_dt); else - nparray = Convert.ToSingle(values); + nparray = Converts.ToSingle(values); break; case "Double": if (values.GetType().IsArray) nparray = np.array((double[])values, np_dt); else - nparray = Convert.ToDouble(values); + nparray = Converts.ToDouble(values); break; case "String": if (values.GetType().IsArray) nparray = np.array((string[])values, np_dt); else - nparray = NDArray.FromString(Convert.ToString(values)); + nparray = NDArray.FromString(Converts.ToString(values)); break; case "Boolean": if (values.GetType().IsArray) nparray = np.array((bool[])values, np_dt); else - nparray = Convert.ToBoolean(values); + nparray = Converts.ToBoolean(values); break; default: throw new NotImplementedException($"make_tensor_proto: Support for type {np_dt.Name} Not Implemented"); diff --git a/src/TensorFlowNET.Core/ops.GraphKeys.cs b/src/TensorFlowNET.Core/ops.GraphKeys.cs index 17b095a4..c5a06433 100644 --- a/src/TensorFlowNET.Core/ops.GraphKeys.cs +++ b/src/TensorFlowNET.Core/ops.GraphKeys.cs @@ -29,55 +29,111 @@ namespace Tensorflow /// public class GraphKeys { + #region const + + + /// + /// the subset of `Variable` objects that will be trained by an optimizer. + /// + public const string TRAINABLE_VARIABLES_ = "trainable_variables"; + + /// + /// Trainable resource-style variables. + /// + public const string TRAINABLE_RESOURCE_VARIABLES_ = "trainable_resource_variables"; + + /// + /// Key for streaming model ports. + /// + public const string _STREAMING_MODEL_PORTS_ = "streaming_model_ports"; + + /// + /// Key to collect losses + /// + public const string LOSSES_ = "losses"; + + /// + /// Key to collect Variable objects that are global (shared across machines). + /// Default collection for all variables, except local ones. + /// + public const string GLOBAL_VARIABLES_ = "variables"; + + public const string TRAIN_OP_ = "train_op"; + + public const string GLOBAL_STEP_ = "global_step"; + + public string[] _VARIABLE_COLLECTIONS_ = new string[] { "variables", "trainable_variables", "model_variables" }; + /// + /// Key to collect BaseSaverBuilder.SaveableObject instances for checkpointing. + /// + public const string SAVEABLE_OBJECTS_ = "saveable_objects"; + /// + /// Key to collect update_ops + /// + public const string UPDATE_OPS_ = "update_ops"; + + // Key to collect summaries. + public const string SUMMARIES_ = "summaries"; + + // Used to store v2 summary names. + public const string _SUMMARY_COLLECTION_ = "_SUMMARY_V2"; + + // Key for control flow context. + public const string COND_CONTEXT_ = "cond_context"; + public const string WHILE_CONTEXT_ = "while_context"; + + #endregion + + /// /// the subset of `Variable` objects that will be trained by an optimizer. /// - public string TRAINABLE_VARIABLES = "trainable_variables"; + public string TRAINABLE_VARIABLES => TRAINABLE_VARIABLES_; /// /// Trainable resource-style variables. /// - public string TRAINABLE_RESOURCE_VARIABLES = "trainable_resource_variables"; + public string TRAINABLE_RESOURCE_VARIABLES => TRAINABLE_RESOURCE_VARIABLES_; /// /// Key for streaming model ports. /// - public string _STREAMING_MODEL_PORTS = "streaming_model_ports"; + public string _STREAMING_MODEL_PORTS => _STREAMING_MODEL_PORTS_; /// /// Key to collect losses /// - public string LOSSES = "losses"; + public string LOSSES => LOSSES_; /// /// Key to collect Variable objects that are global (shared across machines). /// Default collection for all variables, except local ones. /// - public string GLOBAL_VARIABLES = "variables"; + public string GLOBAL_VARIABLES => GLOBAL_VARIABLES_; - public string TRAIN_OP = "train_op"; + public string TRAIN_OP => TRAIN_OP_; - public string GLOBAL_STEP = "global_step"; + public string GLOBAL_STEP => GLOBAL_STEP_; - public string[] _VARIABLE_COLLECTIONS = new string[] { "variables", "trainable_variables", "model_variables" }; + public string[] _VARIABLE_COLLECTIONS => _VARIABLE_COLLECTIONS_; /// /// Key to collect BaseSaverBuilder.SaveableObject instances for checkpointing. /// - public string SAVEABLE_OBJECTS = "saveable_objects"; + public string SAVEABLE_OBJECTS => SAVEABLE_OBJECTS_; /// /// Key to collect update_ops /// - public string UPDATE_OPS = "update_ops"; + public string UPDATE_OPS => UPDATE_OPS_; // Key to collect summaries. - public string SUMMARIES = "summaries"; + public string SUMMARIES => SUMMARIES_; // Used to store v2 summary names. - public string _SUMMARY_COLLECTION = "_SUMMARY_V2"; + public string _SUMMARY_COLLECTION => _SUMMARY_COLLECTION_; // Key for control flow context. - public string COND_CONTEXT = "cond_context"; - public string WHILE_CONTEXT = "while_context"; + public string COND_CONTEXT => COND_CONTEXT_; + public string WHILE_CONTEXT => WHILE_CONTEXT_; } } } diff --git a/test/TensorFlowNET.UnitTest/SessionTest.cs b/test/TensorFlowNET.UnitTest/SessionTest.cs index 9c8485ec..8fd4dc8a 100644 --- a/test/TensorFlowNET.UnitTest/SessionTest.cs +++ b/test/TensorFlowNET.UnitTest/SessionTest.cs @@ -45,7 +45,7 @@ namespace TensorFlowNET.UnitTest EXPECT_EQ(TF_DataType.TF_INT32, outTensor.dtype); EXPECT_EQ(0, outTensor.NDims); ASSERT_EQ((ulong)sizeof(uint), outTensor.bytesize); - var output_contents = outTensor.Data(); + var output_contents = outTensor.ToArray(); EXPECT_EQ(3 + 2, output_contents[0]); // Add another operation to the graph. @@ -66,7 +66,7 @@ namespace TensorFlowNET.UnitTest EXPECT_EQ(TF_DataType.TF_INT32, outTensor.dtype); EXPECT_EQ(0, outTensor.NDims); // scalar ASSERT_EQ((ulong)sizeof(uint), outTensor.bytesize); - output_contents = outTensor.Data(); + output_contents = outTensor.ToArray(); EXPECT_EQ(-(7 + 2), output_contents[0]); // Clean up diff --git a/test/TensorFlowNET.UnitTest/TensorTest.cs b/test/TensorFlowNET.UnitTest/TensorTest.cs index 07da9dca..11557f14 100644 --- a/test/TensorFlowNET.UnitTest/TensorTest.cs +++ b/test/TensorFlowNET.UnitTest/TensorTest.cs @@ -112,7 +112,7 @@ namespace TensorFlowNET.UnitTest var nd = np.array(1f, 2f, 3f, 4f, 5f, 6f).reshape(2, 3); var tensor = new Tensor(nd); - var array = tensor.Data(); + var array = tensor.ToArray(); EXPECT_EQ(tensor.dtype, TF_DataType.TF_FLOAT); EXPECT_EQ(tensor.rank, nd.ndim); diff --git a/test/TensorFlowNET.UnitTest/nn_test/ZeroFractionTest.cs b/test/TensorFlowNET.UnitTest/nn_test/ZeroFractionTest.cs index 1fd7d3aa..3a5515d9 100644 --- a/test/TensorFlowNET.UnitTest/nn_test/ZeroFractionTest.cs +++ b/test/TensorFlowNET.UnitTest/nn_test/ZeroFractionTest.cs @@ -31,7 +31,7 @@ namespace TensorFlowNET.UnitTest.nn_test var y_np = this._ZeroFraction(x_np); var x_tf = constant_op.constant(x_np); - x_tf.SetShape(x_shape); + x_tf.set_shape(x_shape); var y_tf = nn_impl.zero_fraction(x_tf); var y_tf_np = self.evaluate(y_tf); From 766fa6fc5da0843b6d86eb9785c598b1d5926072 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 21 Aug 2019 22:59:55 +0300 Subject: [PATCH 11/57] Tensor: Added guards for explicit casts. --- .../Tensors/Tensor.Explicit.cs | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.Explicit.cs b/src/TensorFlowNET.Core/Tensors/Tensor.Explicit.cs index a4e9c428..cc2c4cb6 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.Explicit.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.Explicit.cs @@ -10,6 +10,7 @@ namespace Tensorflow unsafe { EnsureScalar(tensor); + EnsureDType(tensor, TF_DataType.TF_BOOL); return *(bool*) tensor.buffer; } } @@ -19,6 +20,7 @@ namespace Tensorflow unsafe { EnsureScalar(tensor); + EnsureDType(tensor, TF_DataType.TF_INT8); return *(sbyte*) tensor.buffer; } } @@ -28,6 +30,7 @@ namespace Tensorflow unsafe { EnsureScalar(tensor); + EnsureDType(tensor, TF_DataType.TF_UINT8); return *(byte*) tensor.buffer; } } @@ -37,6 +40,7 @@ namespace Tensorflow unsafe { EnsureScalar(tensor); + EnsureDType(tensor, TF_DataType.TF_UINT16); return *(ushort*) tensor.buffer; } } @@ -46,6 +50,7 @@ namespace Tensorflow unsafe { EnsureScalar(tensor); + EnsureDType(tensor, TF_DataType.TF_INT16); return *(short*) tensor.buffer; } } @@ -55,6 +60,7 @@ namespace Tensorflow unsafe { EnsureScalar(tensor); + EnsureDType(tensor, TF_DataType.TF_INT32); return *(int*) tensor.buffer; } } @@ -64,6 +70,7 @@ namespace Tensorflow unsafe { EnsureScalar(tensor); + EnsureDType(tensor, TF_DataType.TF_UINT32); return *(uint*) tensor.buffer; } } @@ -73,6 +80,7 @@ namespace Tensorflow unsafe { EnsureScalar(tensor); + EnsureDType(tensor, TF_DataType.TF_INT64); return *(long*) tensor.buffer; } } @@ -82,6 +90,7 @@ namespace Tensorflow unsafe { EnsureScalar(tensor); + EnsureDType(tensor, TF_DataType.TF_UINT64); return *(ulong*) tensor.buffer; } } @@ -91,6 +100,7 @@ namespace Tensorflow unsafe { EnsureScalar(tensor); + EnsureDType(tensor, TF_DataType.TF_FLOAT); return *(float*) tensor.buffer; } } @@ -100,27 +110,29 @@ namespace Tensorflow unsafe { EnsureScalar(tensor); + EnsureDType(tensor, TF_DataType.TF_DOUBLE); return *(double*) tensor.buffer; } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void EnsureDType(Tensor tensor, TF_DataType @is) + { + if (tensor._dtype != @is) + throw new InvalidCastException($"Unable to cast scalar tensor {tensor._dtype} to {@is}"); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void EnsureScalar(Tensor tensor) { if (tensor == null) - { throw new ArgumentNullException(nameof(tensor)); - } if (tensor.TensorShape.ndim != 0) - { throw new ArgumentException("Tensor must have 0 dimensions in order to convert to scalar"); - } if (tensor.TensorShape.size != 1) - { throw new ArgumentException("Tensor must have size 1 in order to convert to scalar"); - } } } From 4cf817ea18d79e8da4c59c152206f126a3134662 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 21 Aug 2019 23:00:17 +0300 Subject: [PATCH 12/57] Tensor: Added explicit cast to string --- src/TensorFlowNET.Core/Tensors/Tensor.Explicit.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.Explicit.cs b/src/TensorFlowNET.Core/Tensors/Tensor.Explicit.cs index cc2c4cb6..77603e49 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.Explicit.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.Explicit.cs @@ -115,6 +115,16 @@ namespace Tensorflow } } + public static explicit operator string(Tensor tensor) + { + unsafe + { + EnsureScalar(tensor); + EnsureDType(tensor, TF_DataType.TF_STRING); + return new string((char*) tensor.buffer, 0, (int) tensor.size); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void EnsureDType(Tensor tensor, TF_DataType @is) { From 52955b3d9db288802e81f7e4c1a56837d1671b28 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 21 Aug 2019 23:33:27 +0300 Subject: [PATCH 13/57] Tensor.ToArray(): Added support for cases when tensor is scalar. --- src/TensorFlowNET.Core/Tensors/Tensor.cs | 76 +++++++++++++++++++----- 1 file changed, 62 insertions(+), 14 deletions(-) diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.cs b/src/TensorFlowNET.Core/Tensors/Tensor.cs index 798c27b6..bccc2569 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.cs @@ -233,6 +233,14 @@ namespace Tensorflow //Are the types matching? if (typeof(T).as_dtype() == _dtype) { + if (NDims == 0 && size == 1) //is it a scalar? + { + unsafe + { + return new T[] {*(T*) buffer}; + } + } + //types match, no need to perform cast var ret = new T[size]; unsafe @@ -260,6 +268,46 @@ namespace Tensorflow { //types do not match, need to perform cast + if (NDims == 0 && size == 1) //is it a scalar? + { + unsafe + { +#if _REGEN + #region Compute + switch (_dtype.as_numpy_dtype().GetTypeCode()) + { + %foreach supported_dtypes,supported_dtypes_lowercase% + case NPTypeCode.#1: return new T[] {Converts.ChangeType(*(#2*) buffer, NPTypeCode.#1)}; + % + case NPTypeCode.String: return new T[] {Converts.ChangeType((string)this, NPTypeCode.String)}; + default: + throw new NotSupportedException(); + } + #endregion +#else + #region Compute + switch (_dtype.as_numpy_dtype().GetTypeCode()) + { + case NPTypeCode.Boolean: return new T[] {Converts.ChangeType(*(bool*) buffer, NPTypeCode.Boolean)}; + case NPTypeCode.Byte: return new T[] {Converts.ChangeType(*(byte*) buffer, NPTypeCode.Byte)}; + case NPTypeCode.Int16: return new T[] {Converts.ChangeType(*(short*) buffer, NPTypeCode.Int16)}; + case NPTypeCode.UInt16: return new T[] {Converts.ChangeType(*(ushort*) buffer, NPTypeCode.UInt16)}; + case NPTypeCode.Int32: return new T[] {Converts.ChangeType(*(int*) buffer, NPTypeCode.Int32)}; + case NPTypeCode.UInt32: return new T[] {Converts.ChangeType(*(uint*) buffer, NPTypeCode.UInt32)}; + case NPTypeCode.Int64: return new T[] {Converts.ChangeType(*(long*) buffer, NPTypeCode.Int64)}; + case NPTypeCode.UInt64: return new T[] {Converts.ChangeType(*(ulong*) buffer, NPTypeCode.UInt64)}; + case NPTypeCode.Char: return new T[] {Converts.ChangeType(*(char*) buffer, NPTypeCode.Char)}; + case NPTypeCode.Double: return new T[] {Converts.ChangeType(*(double*) buffer, NPTypeCode.Double)}; + case NPTypeCode.Single: return new T[] {Converts.ChangeType(*(float*) buffer, NPTypeCode.Single)}; + case NPTypeCode.String: return new T[] {Converts.ChangeType((string)this, NPTypeCode.String)}; + default: + throw new NotSupportedException(); + } + #endregion +#endif + } + } + var ret = new T[size]; unsafe { @@ -270,10 +318,10 @@ namespace Tensorflow #if _REGEN #region Compute - switch (_dtype.as_numpy_datatype().GetTypeCode()) + switch (_dtype.as_numpy_dtype().GetTypeCode()) { %foreach supported_dtypes,supported_dtypes_lowercase% - case NPTypeCode.#1:new UnmanagedMemoryBlock<#2>((#2*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.#1: new UnmanagedMemoryBlock<#2>((#2*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; % default: throw new NotSupportedException(); @@ -283,17 +331,18 @@ namespace Tensorflow #region Compute switch (_dtype.as_numpy_dtype().GetTypeCode()) { - case NPTypeCode.Boolean:new UnmanagedMemoryBlock((bool*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; - case NPTypeCode.Byte:new UnmanagedMemoryBlock((byte*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; - case NPTypeCode.Int16:new UnmanagedMemoryBlock((short*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; - case NPTypeCode.UInt16:new UnmanagedMemoryBlock((ushort*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; - case NPTypeCode.Int32:new UnmanagedMemoryBlock((int*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; - case NPTypeCode.UInt32:new UnmanagedMemoryBlock((uint*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; - case NPTypeCode.Int64:new UnmanagedMemoryBlock((long*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; - case NPTypeCode.UInt64:new UnmanagedMemoryBlock((ulong*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; - case NPTypeCode.Char:new UnmanagedMemoryBlock((char*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; - case NPTypeCode.Double:new UnmanagedMemoryBlock((double*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; - case NPTypeCode.Single:new UnmanagedMemoryBlock((float*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.Boolean: new UnmanagedMemoryBlock((bool*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.Byte: new UnmanagedMemoryBlock((byte*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.Int16: new UnmanagedMemoryBlock((short*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.UInt16: new UnmanagedMemoryBlock((ushort*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.Int32: new UnmanagedMemoryBlock((int*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.UInt32: new UnmanagedMemoryBlock((uint*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.Int64: new UnmanagedMemoryBlock((long*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.UInt64: new UnmanagedMemoryBlock((ulong*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.Char: new UnmanagedMemoryBlock((char*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.Double: new UnmanagedMemoryBlock((double*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.Single: new UnmanagedMemoryBlock((float*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; + case NPTypeCode.String: throw new NotSupportedException("Unable to convert from string to other dtypes"); //TODO! this should call Converts.To default: throw new NotSupportedException(); } @@ -307,7 +356,6 @@ namespace Tensorflow } } - /// /// Copies the memory of current buffer onto newly allocated array. /// From f375e1c0bbf5e31742a5f8fbc763cdf4f4fb42db Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 21 Aug 2019 23:33:54 +0300 Subject: [PATCH 14/57] Tensor.BufferToArray(): Fixed to use long instead of int. --- src/TensorFlowNET.Core/Tensors/Tensor.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.cs b/src/TensorFlowNET.Core/Tensors/Tensor.cs index bccc2569..6f4cc21a 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.cs @@ -372,9 +372,16 @@ namespace Tensorflow /// public byte[] BufferToArray() { - var data = new byte[bytesize]; - Marshal.Copy(buffer, data, 0, (int) bytesize); - return data; + unsafe + { + // ReSharper disable once LocalVariableHidesMember + var bytesize = (long) this.bytesize; + var data = new byte[bytesize]; + fixed (byte* dst = data) + System.Buffer.MemoryCopy(buffer.ToPointer(), dst, bytesize, bytesize); + + return data; + } } /// Used internally in ToArray<T> From 290d0dd046919459180040d45e7fe9dfaff1a9ae Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 21 Aug 2019 23:34:11 +0300 Subject: [PATCH 15/57] TensorShape: Revamped and documented. --- src/TensorFlowNET.Core/Tensors/TensorShape.cs | 108 +++++++++++++++--- 1 file changed, 93 insertions(+), 15 deletions(-) diff --git a/src/TensorFlowNET.Core/Tensors/TensorShape.cs b/src/TensorFlowNET.Core/Tensors/TensorShape.cs index 13258f79..382b0002 100644 --- a/src/TensorFlowNET.Core/Tensors/TensorShape.cs +++ b/src/TensorFlowNET.Core/Tensors/TensorShape.cs @@ -1,35 +1,84 @@ using NumSharp; using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Runtime.CompilerServices; namespace Tensorflow { /// - /// Represents the shape of a `Tensor`. + /// Represents the shape of a `Tensor`. /// + /// https://www.tensorflow.org/api_docs/python/tf/TensorShape public class TensorShape { - private Shape shape; + private readonly Shape shape; + + /// + /// Returns a list of Dimensions, or None if the shape is unspecified. + /// public int[] dims => shape.Dimensions; + + /// + /// Returns the rank of this shape. + /// public int ndim => shape.NDim; + + /// + /// Returns the rank of this shape. + /// + public int rank => shape.NDim; + + /// + /// Returns the size this shape represents. + /// public int size => shape.Size; public TensorShape(TensorShapeProto proto) { if (proto.UnknownRank) return; + switch (proto.Dim.Count) + { + case 0: shape = new Shape(new int[0]); break; + case 1: shape = Shape.Vector((int) proto.Dim[0].Size); break; + case 2: shape = Shape.Matrix((int) proto.Dim[0].Size, (int) proto.Dim[1].Size); break; + default: + var protodims = proto.Dim; + var len = protodims.Count; + var dims = new int[len]; + for (int i = 0; i < len; i++) + dims[i] = (int) protodims[i].Size; + - shape.reshape(proto.Dim.Select(x => (int)x.Size).ToArray()); + shape = new Shape(dims); break; + } } public TensorShape(params int[] dims) { - shape = new Shape(dims); + switch (dims.Length) + { + case 0: shape = new Shape(new int[0]); break; + case 1: shape = Shape.Vector((int) dims[0]); break; + case 2: shape = Shape.Matrix(dims[0], dims[1]); break; + default: shape = new Shape(dims); break; + } } + /// + /// + /// + /// + /// + /// When is not an Index. + [SuppressMessage("ReSharper", "PossibleInvalidOperationException")] public TensorShape this[Slice slice] { get { + if (slice.IsIndex == false) + throw new ArgumentException("Slice must be an index."); + return new TensorShape(dims.Skip(slice.Start.Value) .Take(slice.Length.Value) .ToArray()); @@ -37,7 +86,7 @@ namespace Tensorflow } /// - /// Returns True iff `self` is fully defined in every dimension. + /// Returns True iff `self` is fully defined in every dimension. /// /// public bool is_fully_defined() @@ -50,6 +99,7 @@ namespace Tensorflow throw new NotImplementedException("TensorShape is_compatible_with"); } + [SuppressMessage("ReSharper", "ParameterHidesMember")] public TensorShape with_rank_at_least(int rank) { if (rank != ndim) @@ -59,35 +109,63 @@ namespace Tensorflow } /// - /// Returns the concatenation of the dimension in `self` and `other`. + /// Returns the concatenation of the dimension in `self` and `other`. /// /// /// - public TensorShape concatenate(int[] other_) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TensorShape concatenate(int[] other) { - var other = new TensorShape(other_); + return concatenate(new TensorShape(other)); + } - if (ndim < 0 || other.ndim < 0) + /// + /// Returns the concatenation of the dimension in `self` and `other`. + /// + /// + /// + public TensorShape concatenate(TensorShape other) + { + var otherShape = other; + + if (ndim < 0 || otherShape.ndim < 0) return new TensorShape(); else { - var concatenate_dims = new int[ndim + other.ndim]; + var concatenate_dims = new int[ndim + otherShape.ndim]; for (int i = 0; i < ndim; i++) concatenate_dims[i] = dims[i]; - for (int i = 0; i < other.ndim; i++) - concatenate_dims[ndim + i] = other.dims[i]; + for (int i = 0; i < otherShape.ndim; i++) + concatenate_dims[ndim + i] = otherShape.dims[i]; return new TensorShape(concatenate_dims); } } - public static implicit operator TensorShape(Shape shape) => new TensorShape(shape.Dimensions); - public static implicit operator Shape(TensorShape shape) => new Shape(shape.dims); + public static implicit operator TensorShape(Shape shape) => new TensorShape((int[]) shape.Dimensions.Clone()); + public static implicit operator Shape(TensorShape shape) => new Shape((int[]) shape.dims.Clone()); + + public static implicit operator int[](TensorShape shape) => (int[])shape.dims.Clone(); //we clone to avoid any changes public static implicit operator TensorShape(int[] dims) => new TensorShape(dims); - public static implicit operator int[](TensorShape shape) => shape.dims; + + public static explicit operator int(TensorShape shape) => shape.size; + public static explicit operator TensorShape(int dim) => new TensorShape(dim); + + public static explicit operator (int, int)(TensorShape shape) => shape.dims.Length == 2 ? (shape.dims[0], shape.dims[1]) : (0, 0); public static implicit operator TensorShape((int, int) dims) => new TensorShape(dims.Item1, dims.Item2); + + public static explicit operator (int, int, int)(TensorShape shape) => shape.dims.Length == 3 ? (shape.dims[0], shape.dims[1], shape.dims[2]) : (0, 0, 0); public static implicit operator TensorShape((int, int, int) dims) => new TensorShape(dims.Item1, dims.Item2, dims.Item3); + + public static explicit operator (int, int, int, int)(TensorShape shape) => shape.dims.Length == 4 ? (shape.dims[0], shape.dims[1], shape.dims[2], shape.dims[3]) : (0, 0, 0, 0); public static implicit operator TensorShape((int, int, int, int) dims) => new TensorShape(dims.Item1, dims.Item2, dims.Item3, dims.Item4); + + public static explicit operator (int, int, int, int, int)(TensorShape shape) => shape.dims.Length == 5 ? (shape.dims[0], shape.dims[1], shape.dims[2], shape.dims[3], shape.dims[4]) : (0, 0, 0, 0, 0); + public static implicit operator TensorShape((int, int, int, int, int) dims) => new TensorShape(dims.Item1, dims.Item2, dims.Item3, dims.Item4, dims.Item5); + + public static explicit operator (int, int, int, int, int, int)(TensorShape shape) => shape.dims.Length == 6 ? (shape.dims[0], shape.dims[1], shape.dims[2], shape.dims[3], shape.dims[4], shape.dims[5]) : (0, 0, 0, 0, 0, 0); + public static implicit operator TensorShape((int, int, int, int, int, int) dims) => new TensorShape(dims.Item1, dims.Item2, dims.Item3, dims.Item4, dims.Item5, dims.Item6); + } } From 35360e9d5aeed68c7caa5251849a29e0edb3c391 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 21 Aug 2019 23:53:31 +0300 Subject: [PATCH 16/57] BaseSession: Added Session.run(ITensorOrOperation fetche, params FeedItem[] feed_dict) --- src/TensorFlowNET.Core/Sessions/BaseSession.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/TensorFlowNET.Core/Sessions/BaseSession.cs b/src/TensorFlowNET.Core/Sessions/BaseSession.cs index 42ab1d4b..907c5414 100644 --- a/src/TensorFlowNET.Core/Sessions/BaseSession.cs +++ b/src/TensorFlowNET.Core/Sessions/BaseSession.cs @@ -64,6 +64,11 @@ namespace Tensorflow return _run(fetche, feed_dict)[0]; } + public virtual NDArray run(ITensorOrOperation fetche, params FeedItem[] feed_dict) + { + return _run(fetche, feed_dict)[0]; + } + public virtual (NDArray, NDArray, NDArray, NDArray) run((ITensorOrOperation, ITensorOrOperation, ITensorOrOperation, ITensorOrOperation) fetches, params FeedItem[] feed_dict) { var results = _run(new object[] { fetches.Item1, fetches.Item2, fetches.Item3, fetches.Item4 }, feed_dict); From 38f27f16418b55b6cba4937e1cb6dd31a5607c66 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 21 Aug 2019 23:54:50 +0300 Subject: [PATCH 17/57] Tensor: renamed _dtype to _override_dtype - Fixed all locations _dtype is used incorrectly. --- .../Tensors/Tensor.Creation.cs | 2 +- .../Tensors/Tensor.Explicit.cs | 4 ++-- .../Tensors/Tensor.Operators.cs | 9 ++++----- src/TensorFlowNET.Core/Tensors/Tensor.cs | 16 ++++++++-------- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs index ea58607b..73f116ec 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs @@ -594,7 +594,7 @@ namespace Tensorflow { _op = op; _value_index = value_index; - _dtype = dtype; + _override_dtype = dtype; _id = ops.uid(); } diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.Explicit.cs b/src/TensorFlowNET.Core/Tensors/Tensor.Explicit.cs index 77603e49..6d7f20f1 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.Explicit.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.Explicit.cs @@ -128,8 +128,8 @@ namespace Tensorflow [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void EnsureDType(Tensor tensor, TF_DataType @is) { - if (tensor._dtype != @is) - throw new InvalidCastException($"Unable to cast scalar tensor {tensor._dtype} to {@is}"); + if (tensor.dtype != @is) + throw new InvalidCastException($"Unable to cast scalar tensor {tensor.dtype} to {@is}"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.Operators.cs b/src/TensorFlowNET.Core/Tensors/Tensor.Operators.cs index eb912eb9..02d19e56 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.Operators.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.Operators.cs @@ -69,11 +69,12 @@ namespace Tensorflow TF_DataType.TF_QINT8, TF_DataType.TF_QINT16, TF_DataType.TF_QINT32, TF_DataType.TF_UINT8, TF_DataType.TF_UINT16, TF_DataType.TF_UINT32, TF_DataType.TF_UINT64 }; + public static Tensor operator /(double x, Tensor y) => BinaryOpWrapper("truediv", x, y); public static Tensor operator /(float x, Tensor y) => BinaryOpWrapper("truediv", x, y); public static Tensor operator /(int x, Tensor y) => BinaryOpWrapper("floordiv", x, y); public static Tensor operator /(Tensor x, Tensor y) => - _intTfDataTypes.Contains(x._dtype) + _intTfDataTypes.Contains(x.dtype) ? BinaryOpWrapper("floordiv", x, y) : BinaryOpWrapper("truediv", x, y); public static Tensor operator /(Tensor x, int y) => BinaryOpWrapper("floordiv", x, y); @@ -122,8 +123,7 @@ namespace Tensorflow if (y is Tensor tr) dtype = tr.dtype.as_base_dtype(); - var namescope = ops.name_scope(null, name, new { x, y }); - return tf_with(namescope, scope => + using (var scope = ops.name_scope(null, name, new { x, y })) { Tensor result = null; var x1 = ops.convert_to_tensor(x, dtype: dtype, name: "x"); @@ -154,8 +154,7 @@ namespace Tensorflow } return result; - }); - + } } } } diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.cs b/src/TensorFlowNET.Core/Tensors/Tensor.cs index 6f4cc21a..b23b8b98 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.cs @@ -43,7 +43,7 @@ namespace Tensorflow private readonly Operation _op; private readonly int _value_index; private TF_Output? _tf_output; - private readonly TF_DataType _dtype; + private readonly TF_DataType _override_dtype; public int Id => _id; @@ -72,7 +72,7 @@ namespace Tensorflow /// /// The DType of elements in this tensor. /// - public TF_DataType dtype => _handle == IntPtr.Zero ? _dtype : c_api.TF_TensorType(_handle); + public TF_DataType dtype => _handle == IntPtr.Zero ? _override_dtype : c_api.TF_TensorType(_handle); public ulong bytesize => _handle == IntPtr.Zero ? 0 : c_api.TF_TensorByteSize(_handle); public ulong itemsize => _handle == IntPtr.Zero ? 0 : c_api.TF_DataTypeSize(dtype); @@ -231,7 +231,7 @@ namespace Tensorflow } //Are the types matching? - if (typeof(T).as_dtype() == _dtype) + if (typeof(T).as_dtype() == dtype) { if (NDims == 0 && size == 1) //is it a scalar? { @@ -274,7 +274,7 @@ namespace Tensorflow { #if _REGEN #region Compute - switch (_dtype.as_numpy_dtype().GetTypeCode()) + switch (dtype.as_numpy_dtype().GetTypeCode()) { %foreach supported_dtypes,supported_dtypes_lowercase% case NPTypeCode.#1: return new T[] {Converts.ChangeType(*(#2*) buffer, NPTypeCode.#1)}; @@ -286,7 +286,7 @@ namespace Tensorflow #endregion #else #region Compute - switch (_dtype.as_numpy_dtype().GetTypeCode()) + switch (dtype.as_numpy_dtype()?.GetTypeCode()) { case NPTypeCode.Boolean: return new T[] {Converts.ChangeType(*(bool*) buffer, NPTypeCode.Boolean)}; case NPTypeCode.Byte: return new T[] {Converts.ChangeType(*(byte*) buffer, NPTypeCode.Byte)}; @@ -301,7 +301,7 @@ namespace Tensorflow case NPTypeCode.Single: return new T[] {Converts.ChangeType(*(float*) buffer, NPTypeCode.Single)}; case NPTypeCode.String: return new T[] {Converts.ChangeType((string)this, NPTypeCode.String)}; default: - throw new NotSupportedException(); + throw new NotSupportedException(); } #endregion #endif @@ -318,7 +318,7 @@ namespace Tensorflow #if _REGEN #region Compute - switch (_dtype.as_numpy_dtype().GetTypeCode()) + switch (dtype.as_numpy_dtype().GetTypeCode()) { %foreach supported_dtypes,supported_dtypes_lowercase% case NPTypeCode.#1: new UnmanagedMemoryBlock<#2>((#2*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; @@ -329,7 +329,7 @@ namespace Tensorflow #endregion #else #region Compute - switch (_dtype.as_numpy_dtype().GetTypeCode()) + switch (dtype.as_numpy_dtype().GetTypeCode()) { case NPTypeCode.Boolean: new UnmanagedMemoryBlock((bool*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; case NPTypeCode.Byte: new UnmanagedMemoryBlock((byte*) buffer, len).CastTo(new UnmanagedMemoryBlock(dst, len), null, null); break; From 605a05eef5e9b4771ca22f790be44db136a0a543 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 21 Aug 2019 23:55:17 +0300 Subject: [PATCH 18/57] Fixed unit tests --- test/TensorFlowNET.UnitTest/ConstantTest.cs | 34 +-- test/TensorFlowNET.UnitTest/GradientTest.cs | 6 +- test/TensorFlowNET.UnitTest/OperationsTest.cs | 222 +++++++++--------- .../TensorFlowNET.UnitTest/PlaceholderTest.cs | 2 +- test/TensorFlowNET.UnitTest/VariableTest.cs | 13 +- 5 files changed, 139 insertions(+), 138 deletions(-) diff --git a/test/TensorFlowNET.UnitTest/ConstantTest.cs b/test/TensorFlowNET.UnitTest/ConstantTest.cs index c1d4c9e5..b532e558 100644 --- a/test/TensorFlowNET.UnitTest/ConstantTest.cs +++ b/test/TensorFlowNET.UnitTest/ConstantTest.cs @@ -98,9 +98,9 @@ namespace TensorFlowNET.UnitTest { var result = sess.run(tensor); - Assert.AreEqual(result[0].shape[0], 3); - Assert.AreEqual(result[0].shape[1], 2); - Assert.IsTrue(Enumerable.SequenceEqual(new int[] { 0, 0, 0, 0, 0, 0 }, result[0].Data())); + Assert.AreEqual(result.shape[0], 3); + Assert.AreEqual(result.shape[1], 2); + Assert.IsTrue(Enumerable.SequenceEqual(new int[] { 0, 0, 0, 0, 0, 0 }, result.Data())); } // big size @@ -109,13 +109,13 @@ namespace TensorFlowNET.UnitTest { var result = sess.run(tensor); - Assert.AreEqual(result[0].shape[0], 200); - Assert.AreEqual(result[0].shape[1], 100); + Assert.AreEqual(result.shape[0], 200); + Assert.AreEqual(result.shape[1], 100); - var data = result[0].Data(); + var data = result.Data(); Assert.AreEqual(0, data[0]); Assert.AreEqual(0, data[500]); - Assert.AreEqual(0, data[result[0].size - 1]); + Assert.AreEqual(0, data[result.size - 1]); } } @@ -127,9 +127,9 @@ namespace TensorFlowNET.UnitTest { var result = sess.run(ones); - Assert.AreEqual(result[0].shape[0], 3); - Assert.AreEqual(result[0].shape[1], 2); - Assert.IsTrue(new[] { 1, 1, 1, 1, 1, 1 }.SequenceEqual(result[0].Data())); + Assert.AreEqual(result.shape[0], 3); + Assert.AreEqual(result.shape[1], 2); + Assert.IsTrue(new[] { 1, 1, 1, 1, 1, 1 }.SequenceEqual(result.Data())); } } @@ -142,9 +142,9 @@ namespace TensorFlowNET.UnitTest { var result = sess.run(halfes); - Assert.AreEqual(result[0].shape[0], 3); - Assert.AreEqual(result[0].shape[1], 2); - Assert.IsTrue(new[] { .5, .5, .5, .5, .5, .5 }.SequenceEqual(result[0].Data())); + Assert.AreEqual(result.shape[0], 3); + Assert.AreEqual(result.shape[1], 2); + Assert.IsTrue(new[] { .5, .5, .5, .5, .5, .5 }.SequenceEqual(result.Data())); } } @@ -161,10 +161,10 @@ namespace TensorFlowNET.UnitTest using (var sess = tf.Session()) { var result = sess.run(tensor); - var data = result[0].Data(); + var data = result.Data(); - Assert.AreEqual(result[0].shape[0], 2); - Assert.AreEqual(result[0].shape[1], 3); + Assert.AreEqual(result.shape[0], 2); + Assert.AreEqual(result.shape[1], 3); Assert.IsTrue(Enumerable.SequenceEqual(new int[] { 3, 1, 1, 2, 1, 3 }, data)); } } @@ -177,7 +177,7 @@ namespace TensorFlowNET.UnitTest var c = a * b; var sess = tf.Session(); - double result = sess.run(c)[0]; + double result = sess.run(c); sess.close(); Assert.AreEqual(6.0, result); diff --git a/test/TensorFlowNET.UnitTest/GradientTest.cs b/test/TensorFlowNET.UnitTest/GradientTest.cs index b52bc1cf..c8e57ba4 100644 --- a/test/TensorFlowNET.UnitTest/GradientTest.cs +++ b/test/TensorFlowNET.UnitTest/GradientTest.cs @@ -41,7 +41,7 @@ namespace TensorFlowNET.UnitTest var grad = tf.gradients(y, x); Assert.AreEqual(grad[0].name, "gradients/AddN:0"); - float r = sess.run(grad[0])[0]; + float r = sess.run(grad[0]); Assert.AreEqual(r, 1.4f); } } @@ -57,7 +57,7 @@ namespace TensorFlowNET.UnitTest var grad = tf.gradients(y, x); Assert.AreEqual(grad[0].name, "gradients/AddN:0"); - float r = sess.run(grad[0])[0]; + float r = sess.run(grad[0]); Assert.AreEqual(r, 14.700001f); }); } @@ -94,7 +94,7 @@ namespace TensorFlowNET.UnitTest using (var sess = tf.Session(graph)) { - var r = sess.run(slice)[0]; + var r = sess.run(slice); Assert.IsTrue(Enumerable.SequenceEqual(r.shape, new[] { 2, 1, 2 })); Assert.IsTrue(Enumerable.SequenceEqual(r[0].GetData(), new[] { 11, 13 })); diff --git a/test/TensorFlowNET.UnitTest/OperationsTest.cs b/test/TensorFlowNET.UnitTest/OperationsTest.cs index 4c6ae3d0..0caa5259 100644 --- a/test/TensorFlowNET.UnitTest/OperationsTest.cs +++ b/test/TensorFlowNET.UnitTest/OperationsTest.cs @@ -44,7 +44,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, 3.0f), new FeedItem(b, 2.0f)); - Assert.AreEqual((float)o[0], 5.0f); + Assert.AreEqual((float)o, 5.0f); } } @@ -58,7 +58,7 @@ namespace TensorFlowNET.UnitTest using (var sess = tf.Session()) { var o = sess.run(c); - Assert.AreEqual((float)o[0], 9.0f); + Assert.AreEqual((float)o, 9.0f); } } @@ -72,7 +72,7 @@ namespace TensorFlowNET.UnitTest using (var sess = tf.Session()) { var o = sess.run(b); - Assert.IsTrue(o[0].array_equal(check)); + Assert.IsTrue(o.array_equal(check)); } } @@ -86,7 +86,7 @@ namespace TensorFlowNET.UnitTest using (var sess = tf.Session()) { var o = sess.run(b); - Assert.IsTrue(o[0].array_equal(check)); + Assert.IsTrue(o.array_equal(check)); } } @@ -100,7 +100,7 @@ namespace TensorFlowNET.UnitTest using (var sess = tf.Session()) { var o = sess.run(b); - Assert.IsTrue(o[0].array_equal(check)); + Assert.IsTrue(o.array_equal(check)); } b = tf.cumsum(a, exclusive: true); @@ -109,7 +109,7 @@ namespace TensorFlowNET.UnitTest using (var sess = tf.Session()) { var o = sess.run(b); - Assert.IsTrue(o[0].array_equal(check)); + Assert.IsTrue(o.array_equal(check)); } b = tf.cumsum(a, reverse: true); @@ -118,7 +118,7 @@ namespace TensorFlowNET.UnitTest using (var sess = tf.Session()) { var o = sess.run(b); - Assert.IsTrue(o[0].array_equal(check)); + Assert.IsTrue(o.array_equal(check)); } b = tf.cumsum(a, exclusive:true, reverse: true); @@ -127,7 +127,7 @@ namespace TensorFlowNET.UnitTest using (var sess = tf.Session()) { var o = sess.run(b); - Assert.IsTrue(o[0].array_equal(check)); + Assert.IsTrue(o.array_equal(check)); } } @@ -143,7 +143,7 @@ namespace TensorFlowNET.UnitTest using (var sess = tf.Session()) { var o = sess.run(d); - Assert.IsTrue(o[0].array_equal(check)); + Assert.IsTrue(o.array_equal(check)); } d = tf.cast(tf.logical_not(b), tf.int32); @@ -152,7 +152,7 @@ namespace TensorFlowNET.UnitTest using (var sess = tf.Session()) { var o = sess.run(d); - Assert.IsTrue(o[0].array_equal(check)); + Assert.IsTrue(o.array_equal(check)); } d = tf.cast(tf.logical_or(b, c), tf.int32); @@ -161,7 +161,7 @@ namespace TensorFlowNET.UnitTest using (var sess = tf.Session()) { var o = sess.run(d); - Assert.IsTrue(o[0].array_equal(check)); + Assert.IsTrue(o.array_equal(check)); } d = tf.cast(tf.logical_xor(b, c), tf.int32); @@ -170,7 +170,7 @@ namespace TensorFlowNET.UnitTest using (var sess = tf.Session()) { var o = sess.run(d); - Assert.IsTrue(o[0].array_equal(check)); + Assert.IsTrue(o.array_equal(check)); } } @@ -197,7 +197,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator +(Tensor x, Tensor y)` @@ -207,7 +207,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator +(Tensor x, int y)` @@ -216,7 +216,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator +(int x, Tensor y)` @@ -225,7 +225,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } #endregion @@ -246,7 +246,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((float)o[0], floatResult); + Assert.AreEqual((float)o, floatResult); } // Testing `operator +(Tensor x, Tensor y) @@ -256,7 +256,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((float)o[0], floatResult); + Assert.AreEqual((float)o, floatResult); } // Testing `operator +(Tensor x, float y) @@ -265,7 +265,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((float)o[0], floatResult); + Assert.AreEqual((float)o, floatResult); } // Testing `operator +(float x, Tensor y) @@ -274,7 +274,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((float)o[0], floatResult); + Assert.AreEqual((float)o, floatResult); } #endregion @@ -295,7 +295,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((double)o[0], doubleResult); + Assert.AreEqual((double)o, doubleResult); } // Testing `operator +(Tensor x, Tensor y) @@ -305,7 +305,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((double)o[0], doubleResult); + Assert.AreEqual((double)o, doubleResult); } // Testing `operator +(Tensor x, double y) @@ -314,7 +314,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((double)o[0], doubleResult); + Assert.AreEqual((double)o, doubleResult); } // Testing `operator +(double x, Tensor y) @@ -323,7 +323,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((double)o[0], doubleResult); + Assert.AreEqual((double)o, doubleResult); } #endregion } @@ -352,7 +352,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator -(Tensor x, Tensor y) @@ -362,7 +362,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator -(Tensor x, int y) @@ -371,7 +371,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator -(int x, Tensor y) @@ -380,7 +380,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], Math.Abs(intResult)); + Assert.AreEqual((int)o, Math.Abs(intResult)); } // Testing `operator -(Tensor x) @@ -389,7 +389,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResultTwo); + Assert.AreEqual((int)o, intResultTwo); } #endregion @@ -411,7 +411,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((float)o[0], floatResult); + Assert.AreEqual((float)o, floatResult); } // Testing `operator -(Tensor x, Tensor y) @@ -421,7 +421,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((float)o[0], floatResult); + Assert.AreEqual((float)o, floatResult); } // Testing `operator -(Tensor x, float y) @@ -430,7 +430,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((float)o[0], floatResult); + Assert.AreEqual((float)o, floatResult); } // Testing `operator -(float x, Tensor y) @@ -439,7 +439,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((float)o[0], Math.Abs(floatResult)); + Assert.AreEqual((float)o, Math.Abs(floatResult)); } // Testing `operator -(Tensor x) @@ -448,7 +448,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((float)o[0], floatResultTwo); + Assert.AreEqual((float)o, floatResultTwo); } #endregion @@ -470,7 +470,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((double)o[0], doubleResult); + Assert.AreEqual((double)o, doubleResult); } // Testing `operator -(Tensor x, Tensor y) @@ -480,7 +480,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((double)o[0], doubleResult); + Assert.AreEqual((double)o, doubleResult); } // Testing `operator -(Tensor x, double y) @@ -489,7 +489,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((double)o[0], doubleResult); + Assert.AreEqual((double)o, doubleResult); } // Testing `operator -(double x, Tensor y) @@ -498,7 +498,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((double)o[0], Math.Abs(doubleResult)); + Assert.AreEqual((double)o, Math.Abs(doubleResult)); } // Testing `operator -(Tensor x) @@ -507,7 +507,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((double)o[0], doubleResultTwo); + Assert.AreEqual((double)o, doubleResultTwo); } #endregion } @@ -593,7 +593,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator *(Tensor x, Tensor y) @@ -603,7 +603,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator *(Tensor x, int y) @@ -612,7 +612,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator *(int x, Tensor y) @@ -621,7 +621,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(b, new NDArray(secondIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } #endregion @@ -642,7 +642,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((float)o[0], floatResult); + Assert.AreEqual((float)o, floatResult); } // Testing `operator *(Tensor x, Tensor y) @@ -652,7 +652,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((float)o[0], floatResult); + Assert.AreEqual((float)o, floatResult); } // Testing `operator *(Tensor x, float y) @@ -661,7 +661,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((float)o[0], floatResult); + Assert.AreEqual((float)o, floatResult); } // Testing `operator *(float x, Tensor y) @@ -670,7 +670,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(b, new NDArray(secondFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((float)o[0], floatResult); + Assert.AreEqual((float)o, floatResult); } #endregion @@ -691,7 +691,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((double)o[0], doubleResult); + Assert.AreEqual((double)o, doubleResult); } // Testing `operator *(Tensor x, Tensor y) @@ -701,7 +701,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((double)o[0], doubleResult); + Assert.AreEqual((double)o, doubleResult); } // Testing `operator *(Tensor x, double y) @@ -710,7 +710,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((double)o[0], doubleResult); + Assert.AreEqual((double)o, doubleResult); } // Testing `operator *(double x, Tensor y) @@ -719,7 +719,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(b, new NDArray(secondDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((double)o[0], doubleResult); + Assert.AreEqual((double)o, doubleResult); } #endregion } @@ -747,7 +747,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator /(Tensor x, Tensor y) @@ -757,7 +757,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator /(Tensor x, int y) @@ -766,7 +766,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator /(int x, Tensor y) @@ -775,7 +775,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(b, new NDArray(secondIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } #endregion @@ -796,7 +796,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((float)o[0], floatResult); + Assert.AreEqual((float)o, floatResult); } // Testing `operator /(Tensor x, Tensor y) @@ -806,7 +806,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((float)o[0], floatResult); + Assert.AreEqual((float)o, floatResult); } // Testing `operator /(Tensor x, float y) @@ -815,7 +815,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((float)o[0], floatResult); + Assert.AreEqual((float)o, floatResult); } // Testing `operator /(float x, Tensor y) @@ -824,7 +824,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(b, new NDArray(secondFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((float)o[0], floatResult); + Assert.AreEqual((float)o, floatResult); } #endregion @@ -845,7 +845,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((double)o[0], doubleResult); + Assert.AreEqual((double)o, doubleResult); } // Testing `operator /(Tensor x, Tensor y) @@ -855,7 +855,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((double)o[0], doubleResult); + Assert.AreEqual((double)o, doubleResult); } // Testing `operator /(Tensor x, double y) @@ -864,7 +864,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((double)o[0], doubleResult); + Assert.AreEqual((double)o, doubleResult); } // Testing `operator /(double x, Tensor y) @@ -873,7 +873,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(b, new NDArray(secondDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((double)o[0], doubleResult); + Assert.AreEqual((double)o, doubleResult); } #endregion } @@ -901,7 +901,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator >(Tensor x, Tensor y) @@ -911,7 +911,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator >(Tensor x, int y) @@ -920,7 +920,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator >(int x, Tensor y) @@ -929,7 +929,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResultTwo); + Assert.AreEqual((int)o, intResultTwo); } #endregion @@ -950,7 +950,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], floatResult); + Assert.AreEqual((int)o, floatResult); } // Testing `operator >(Tensor x, Tensor y) @@ -960,7 +960,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], floatResult); + Assert.AreEqual((int)o, floatResult); } // Testing `operator >(Tensor x, float y) @@ -969,7 +969,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], floatResult); + Assert.AreEqual((int)o, floatResult); } // Testing `operator >(float x, Tensor y) @@ -978,7 +978,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], floatResultTwo); + Assert.AreEqual((int)o, floatResultTwo); } #endregion @@ -999,7 +999,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], doubleResult); + Assert.AreEqual((int)o, doubleResult); } // Testing `operator >(Tensor x, Tensor y) @@ -1009,7 +1009,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], doubleResult); + Assert.AreEqual((int)o, doubleResult); } // Testing `operator >(Tensor x, double y) @@ -1018,7 +1018,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], doubleResult); + Assert.AreEqual((int)o, doubleResult); } // Testing `operator >(double x, Tensor y) @@ -1027,7 +1027,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], doubleResultTwo); + Assert.AreEqual((int)o, doubleResultTwo); } #endregion } @@ -1055,7 +1055,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator <(Tensor x, Tensor y) @@ -1065,7 +1065,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator <(Tensor x, int y) @@ -1074,7 +1074,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator <(int x, Tensor y) @@ -1083,7 +1083,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResultTwo); + Assert.AreEqual((int)o, intResultTwo); } #endregion @@ -1104,7 +1104,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], floatResult); + Assert.AreEqual((int)o, floatResult); } // Testing `operator <(Tensor x, Tensor y) @@ -1114,7 +1114,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], floatResult); + Assert.AreEqual((int)o, floatResult); } // Testing `operator <(Tensor x, float y) @@ -1123,7 +1123,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], floatResult); + Assert.AreEqual((int)o, floatResult); } // Testing `operator <(float x, Tensor y) @@ -1132,7 +1132,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], floatResultTwo); + Assert.AreEqual((int)o, floatResultTwo); } #endregion @@ -1153,7 +1153,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], doubleResult); + Assert.AreEqual((int)o, doubleResult); } // Testing `operator <(Tensor x, Tensor y) @@ -1163,7 +1163,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], doubleResult); + Assert.AreEqual((int)o, doubleResult); } // Testing `operator <(Tensor x, double y) @@ -1172,7 +1172,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], doubleResult); + Assert.AreEqual((int)o, doubleResult); } // Testing `operator <(double x, Tensor y) @@ -1181,7 +1181,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], doubleResultTwo); + Assert.AreEqual((int)o, doubleResultTwo); } #endregion } @@ -1209,7 +1209,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator >=(Tensor x, Tensor y) @@ -1219,7 +1219,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator >=(Tensor x, int y) @@ -1228,7 +1228,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator >=(int x, Tensor y) @@ -1237,7 +1237,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResultTwo); + Assert.AreEqual((int)o, intResultTwo); } #endregion @@ -1258,7 +1258,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], floatResult); + Assert.AreEqual((int)o, floatResult); } // Testing `operator >=(Tensor x, Tensor y) @@ -1268,7 +1268,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], floatResult); + Assert.AreEqual((int)o, floatResult); } // Testing `operator >=(Tensor x, float y) @@ -1277,7 +1277,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], floatResult); + Assert.AreEqual((int)o, floatResult); } // Testing `operator >=(float x, Tensor y) @@ -1286,7 +1286,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], floatResultTwo); + Assert.AreEqual((int)o, floatResultTwo); } #endregion @@ -1307,7 +1307,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], doubleResult); + Assert.AreEqual((int)o, doubleResult); } // Testing `operator >=(Tensor x, Tensor y) @@ -1317,7 +1317,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], doubleResult); + Assert.AreEqual((int)o, doubleResult); } // Testing `operator >=(Tensor x, double y) @@ -1326,7 +1326,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], doubleResult); + Assert.AreEqual((int)o, doubleResult); } // Testing `operator >=(double x, Tensor y) @@ -1335,7 +1335,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], doubleResultTwo); + Assert.AreEqual((int)o, doubleResultTwo); } #endregion } @@ -1363,7 +1363,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator <=(Tensor x, Tensor y) @@ -1373,7 +1373,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator <=(Tensor x, int y) @@ -1382,7 +1382,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResult); + Assert.AreEqual((int)o, intResult); } // Testing `operator <=(int x, Tensor y) @@ -1391,7 +1391,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstIntFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], intResultTwo); + Assert.AreEqual((int)o, intResultTwo); } #endregion @@ -1412,7 +1412,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], floatResult); + Assert.AreEqual((int)o, floatResult); } // Testing `operator <=(Tensor x, Tensor y) @@ -1422,7 +1422,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], floatResult); + Assert.AreEqual((int)o, floatResult); } // Testing `operator <=(Tensor x, float y) @@ -1431,7 +1431,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], floatResult); + Assert.AreEqual((int)o, floatResult); } // Testing `operator <=(float x, Tensor y) @@ -1440,7 +1440,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstFloatFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], floatResultTwo); + Assert.AreEqual((int)o, floatResultTwo); } #endregion @@ -1461,7 +1461,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], doubleResult); + Assert.AreEqual((int)o, doubleResult); } // Testing `operator <=(Tensor x, Tensor y) @@ -1471,7 +1471,7 @@ namespace TensorFlowNET.UnitTest var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols))), new FeedItem(b, new NDArray(secondDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], doubleResult); + Assert.AreEqual((int)o, doubleResult); } // Testing `operator <=(Tensor x, double y) @@ -1480,7 +1480,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], doubleResult); + Assert.AreEqual((int)o, doubleResult); } // Testing `operator <=(double x, Tensor y) @@ -1489,7 +1489,7 @@ namespace TensorFlowNET.UnitTest { var o = sess.run(c, new FeedItem(a, new NDArray(firstDoubleFeed, new Shape(rows, cols)))); - Assert.AreEqual((int)o[0], doubleResultTwo); + Assert.AreEqual((int)o, doubleResultTwo); } #endregion } diff --git a/test/TensorFlowNET.UnitTest/PlaceholderTest.cs b/test/TensorFlowNET.UnitTest/PlaceholderTest.cs index 14b16c23..5135bd25 100644 --- a/test/TensorFlowNET.UnitTest/PlaceholderTest.cs +++ b/test/TensorFlowNET.UnitTest/PlaceholderTest.cs @@ -17,7 +17,7 @@ namespace TensorFlowNET.UnitTest { var result = sess.run(y, new FeedItem(x, 2)); - Assert.AreEqual((int)result[0], 6); + Assert.AreEqual((int)result, 6); } } } diff --git a/test/TensorFlowNET.UnitTest/VariableTest.cs b/test/TensorFlowNET.UnitTest/VariableTest.cs index 4c5ddd7a..7673cac8 100644 --- a/test/TensorFlowNET.UnitTest/VariableTest.cs +++ b/test/TensorFlowNET.UnitTest/VariableTest.cs @@ -1,4 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using NumSharp; using Tensorflow; using static Tensorflow.Binding; @@ -16,7 +17,7 @@ namespace TensorFlowNET.UnitTest { session.run(x.initializer); var result = session.run(x); - Assert.AreEqual(10, (int)result[0]); + Assert.AreEqual(10, (int)result); } } @@ -81,7 +82,7 @@ namespace TensorFlowNET.UnitTest using (var session = tf.Session()) { session.run(model); - int result = session.run(y)[0]; + int result = session.run(y); Assert.AreEqual(result, 4); } } @@ -97,12 +98,12 @@ namespace TensorFlowNET.UnitTest var sess = tf.Session(graph); sess.run(init); - var result = sess.run(variable); - Assert.IsTrue((int)result[0] == 31); + NDArray result = sess.run(variable); + Assert.IsTrue((int)result == 31); var assign = variable.assign(12); result = sess.run(assign); - Assert.IsTrue((int)result[0] == 12); + Assert.IsTrue((int)result == 12); } [TestMethod] @@ -139,7 +140,7 @@ namespace TensorFlowNET.UnitTest for(int i = 0; i < 5; i++) { x = x + 1; - result = session.run(x)[0]; + result = session.run(x); print(result); } } From 667a56661f50a143ec8bf1ef5c6a49512e49c98b Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Thu, 22 Aug 2019 00:14:41 +0300 Subject: [PATCH 19/57] Tensor.Operations: Reverted commit --- src/TensorFlowNET.Core/Tensors/Tensor.Operators.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.Operators.cs b/src/TensorFlowNET.Core/Tensors/Tensor.Operators.cs index 02d19e56..4b15864f 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.Operators.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.Operators.cs @@ -123,7 +123,7 @@ namespace Tensorflow if (y is Tensor tr) dtype = tr.dtype.as_base_dtype(); - using (var scope = ops.name_scope(null, name, new { x, y })) + return tf_with(ops.name_scope(null, name, new { x, y }), scope => { Tensor result = null; var x1 = ops.convert_to_tensor(x, dtype: dtype, name: "x"); @@ -154,7 +154,7 @@ namespace Tensorflow } return result; - } + }); } } } From a5bdd9939552f9d8e30852860ed08ad6a41213ce Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Thu, 22 Aug 2019 00:33:14 +0300 Subject: [PATCH 20/57] DisposableObject: sorted internal_dispose to properly handle Dispose() calls --- src/TensorFlowNET.Core/DisposableObject.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/TensorFlowNET.Core/DisposableObject.cs b/src/TensorFlowNET.Core/DisposableObject.cs index 8d06f182..6359b826 100644 --- a/src/TensorFlowNET.Core/DisposableObject.cs +++ b/src/TensorFlowNET.Core/DisposableObject.cs @@ -34,19 +34,19 @@ namespace Tensorflow private void internal_dispose(bool disposing) { + // free unmanaged resources (unmanaged objects). + if (_handle != IntPtr.Zero) + { + // set large fields to null. + DisposeUnmanagedResources(_handle); + + _handle = IntPtr.Zero; + } + if (disposing) { // dispose managed state (managed objects). DisposeManagedResources(); - - // free unmanaged resources (unmanaged objects) and override a finalizer below. - if (_handle != IntPtr.Zero) - { - // set large fields to null. - DisposeUnmanagedResources(_handle); - - _handle = IntPtr.Zero; - } } } From 2c0115364a9c3352e0ad9d9acc50f4908f79514c Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Thu, 22 Aug 2019 00:54:02 +0300 Subject: [PATCH 21/57] Tensor.DisposeUnmanagedResources: Nullify _handle after delete. --- src/TensorFlowNET.Core/Tensors/Tensor.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.cs b/src/TensorFlowNET.Core/Tensors/Tensor.cs index b23b8b98..8ac6c73e 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.cs @@ -571,6 +571,7 @@ namespace Tensorflow if (handle != IntPtr.Zero) { c_api.TF_DeleteTensor(handle); + _handle = IntPtr.Zero; } } From 44adad6d92740f788a7b2be44c47d7bd74e8638a Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Thu, 22 Aug 2019 01:31:08 +0300 Subject: [PATCH 22/57] TensorShape.this[...]: fixed guard check. --- src/TensorFlowNET.Core/Tensors/TensorShape.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TensorFlowNET.Core/Tensors/TensorShape.cs b/src/TensorFlowNET.Core/Tensors/TensorShape.cs index 382b0002..cf62ce04 100644 --- a/src/TensorFlowNET.Core/Tensors/TensorShape.cs +++ b/src/TensorFlowNET.Core/Tensors/TensorShape.cs @@ -76,8 +76,8 @@ namespace Tensorflow { get { - if (slice.IsIndex == false) - throw new ArgumentException("Slice must be an index."); + if (slice.Start.HasValue == false || slice.Length.HasValue == false) + throw new ArgumentException("Slice must has Start and Length."); return new TensorShape(dims.Skip(slice.Start.Value) .Take(slice.Length.Value) From 672c9230066e1d3047464d19b4475a9b19b5f602 Mon Sep 17 00:00:00 2001 From: Oceania2018 Date: Wed, 21 Aug 2019 22:44:24 -0500 Subject: [PATCH 23/57] DisposableObject #362 --- src/TensorFlowNET.Core/DisposableObject.cs | 20 +++++++++---------- .../Sessions/BaseSession.cs | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/TensorFlowNET.Core/DisposableObject.cs b/src/TensorFlowNET.Core/DisposableObject.cs index 6359b826..688ac92c 100644 --- a/src/TensorFlowNET.Core/DisposableObject.cs +++ b/src/TensorFlowNET.Core/DisposableObject.cs @@ -34,19 +34,19 @@ namespace Tensorflow private void internal_dispose(bool disposing) { - // free unmanaged resources (unmanaged objects). - if (_handle != IntPtr.Zero) + if (disposing) { - // set large fields to null. - DisposeUnmanagedResources(_handle); + // free unmanaged resources (unmanaged objects) and override a finalizer below. + if (_handle != IntPtr.Zero) + { + // dispose managed state (managed objects). + DisposeManagedResources(); - _handle = IntPtr.Zero; - } + // set large fields to null. + DisposeUnmanagedResources(_handle); - if (disposing) - { - // dispose managed state (managed objects). - DisposeManagedResources(); + _handle = IntPtr.Zero; + } } } diff --git a/src/TensorFlowNET.Core/Sessions/BaseSession.cs b/src/TensorFlowNET.Core/Sessions/BaseSession.cs index 907c5414..4c5f2be3 100644 --- a/src/TensorFlowNET.Core/Sessions/BaseSession.cs +++ b/src/TensorFlowNET.Core/Sessions/BaseSession.cs @@ -49,7 +49,7 @@ namespace Tensorflow // dispose newOpts if (opts == null) - c_api.TF_DeleteSessionOptions(newOpts); + newOpts.Dispose(); status.Check(true); } From 93d7e9a669d71fa4802dda076ae7a2a4578f8e43 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Thu, 22 Aug 2019 19:45:54 +0300 Subject: [PATCH 24/57] DisposableObject: Revamped based on VS unmanaged resource pattern. --- src/TensorFlowNET.Core/DisposableObject.cs | 39 +++++++++++----------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/TensorFlowNET.Core/DisposableObject.cs b/src/TensorFlowNET.Core/DisposableObject.cs index 688ac92c..12bb8c3a 100644 --- a/src/TensorFlowNET.Core/DisposableObject.cs +++ b/src/TensorFlowNET.Core/DisposableObject.cs @@ -16,6 +16,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Text; namespace Tensorflow @@ -26,27 +27,32 @@ namespace Tensorflow public abstract class DisposableObject : IDisposable { protected IntPtr _handle; + protected bool _disposed; - protected DisposableObject() { } + [SuppressMessage("ReSharper", "UnusedMember.Global")] + protected DisposableObject() + { } - protected DisposableObject(IntPtr handle) + protected DisposableObject(IntPtr handle) => _handle = handle; private void internal_dispose(bool disposing) { + if (_disposed) + return; + + _disposed = true; + + //first handle managed, they might use the unmanaged resources. if (disposing) - { - // free unmanaged resources (unmanaged objects) and override a finalizer below. - if (_handle != IntPtr.Zero) - { - // dispose managed state (managed objects). - DisposeManagedResources(); + // dispose managed state (managed objects). + DisposeManagedResources(); - // set large fields to null. - DisposeUnmanagedResources(_handle); + if (_handle != IntPtr.Zero) + { + DisposeUnmanagedResources(_handle); - _handle = IntPtr.Zero; - } + _handle = IntPtr.Zero; } } @@ -55,27 +61,22 @@ namespace Tensorflow /// /// Equivalent to what you would perform inside protected virtual void DisposeManagedResources() - { - } + { } /// /// Dispose any unmanaged resources related to given . /// protected abstract void DisposeUnmanagedResources(IntPtr handle); - // override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. ~DisposableObject() { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. internal_dispose(false); } - // This code added to correctly implement the disposable pattern. public void Dispose() { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. internal_dispose(true); - // uncomment the following line if the finalizer is overridden above. + GC.SuppressFinalize(this); } } From 21199b0163a7451fb4af5b7702ef0061d7271ca2 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Fri, 23 Aug 2019 00:27:16 +0300 Subject: [PATCH 25/57] Added TensorflowException as base class to all exceptions. --- src/TensorFlowNET.Core/Exceptions/KeyError.cs | 2 +- .../Exceptions/RuntimeError.cs | 2 +- .../Exceptions/TensorflowException.cs | 36 +++++++++++++++++++ .../Exceptions/TypeError.cs | 2 +- .../Exceptions/ValueError.cs | 2 +- 5 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 src/TensorFlowNET.Core/Exceptions/TensorflowException.cs diff --git a/src/TensorFlowNET.Core/Exceptions/KeyError.cs b/src/TensorFlowNET.Core/Exceptions/KeyError.cs index 8cecae76..949fd309 100644 --- a/src/TensorFlowNET.Core/Exceptions/KeyError.cs +++ b/src/TensorFlowNET.Core/Exceptions/KeyError.cs @@ -2,7 +2,7 @@ namespace Tensorflow { - public class KeyError : Exception + public class KeyError : TensorflowException { public KeyError() : base() { diff --git a/src/TensorFlowNET.Core/Exceptions/RuntimeError.cs b/src/TensorFlowNET.Core/Exceptions/RuntimeError.cs index 09a02a4a..6f7e4f48 100644 --- a/src/TensorFlowNET.Core/Exceptions/RuntimeError.cs +++ b/src/TensorFlowNET.Core/Exceptions/RuntimeError.cs @@ -2,7 +2,7 @@ namespace Tensorflow { - public class RuntimeError : Exception + public class RuntimeError : TensorflowException { public RuntimeError() : base() { diff --git a/src/TensorFlowNET.Core/Exceptions/TensorflowException.cs b/src/TensorFlowNET.Core/Exceptions/TensorflowException.cs new file mode 100644 index 00000000..ee9eca69 --- /dev/null +++ b/src/TensorFlowNET.Core/Exceptions/TensorflowException.cs @@ -0,0 +1,36 @@ +using System; +using System.Runtime.Serialization; + +namespace Tensorflow +{ + + /// + /// Serves as a base class to all exceptions of Tensorflow.NET. + /// + [Serializable] + public class TensorflowException : Exception + { + /// Initializes a new instance of the class. + public TensorflowException() + { } + + /// Initializes a new instance of the class with serialized data. + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + /// The info parameter is null. + /// The class name is null or is zero (0). + protected TensorflowException(SerializationInfo info, StreamingContext context) : base(info, context) + { } + + /// Initializes a new instance of the class with a specified error message. + /// The message that describes the error. + public TensorflowException(string message) : base(message) + { } + + /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + public TensorflowException(string message, Exception innerException) : base(message, innerException) + { } + } +} \ No newline at end of file diff --git a/src/TensorFlowNET.Core/Exceptions/TypeError.cs b/src/TensorFlowNET.Core/Exceptions/TypeError.cs index a4c37988..42c8e3a0 100644 --- a/src/TensorFlowNET.Core/Exceptions/TypeError.cs +++ b/src/TensorFlowNET.Core/Exceptions/TypeError.cs @@ -2,7 +2,7 @@ namespace Tensorflow { - public class TypeError : Exception + public class TypeError : TensorflowException { public TypeError() : base() { diff --git a/src/TensorFlowNET.Core/Exceptions/ValueError.cs b/src/TensorFlowNET.Core/Exceptions/ValueError.cs index 825d27a1..0d6fb4e3 100644 --- a/src/TensorFlowNET.Core/Exceptions/ValueError.cs +++ b/src/TensorFlowNET.Core/Exceptions/ValueError.cs @@ -2,7 +2,7 @@ namespace Tensorflow { - public class ValueError : Exception + public class ValueError : TensorflowException { public ValueError() : base() { From 71c592ceaf1718d2bdbc4b43de87b89371021cce Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Fri, 23 Aug 2019 00:31:29 +0300 Subject: [PATCH 26/57] DisposableObject: Added CheckDisposed() --- src/TensorFlowNET.Core/DisposableObject.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/TensorFlowNET.Core/DisposableObject.cs b/src/TensorFlowNET.Core/DisposableObject.cs index 12bb8c3a..ddd32173 100644 --- a/src/TensorFlowNET.Core/DisposableObject.cs +++ b/src/TensorFlowNET.Core/DisposableObject.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using System.Text; namespace Tensorflow @@ -36,6 +37,7 @@ namespace Tensorflow protected DisposableObject(IntPtr handle) => _handle = handle; + [SuppressMessage("ReSharper", "InvertIf")] private void internal_dispose(bool disposing) { if (_disposed) @@ -48,6 +50,7 @@ namespace Tensorflow // dispose managed state (managed objects). DisposeManagedResources(); + //free unmanaged memory if (_handle != IntPtr.Zero) { DisposeUnmanagedResources(_handle); @@ -79,5 +82,16 @@ namespace Tensorflow GC.SuppressFinalize(this); } + + /// + /// If is then throws + /// + /// When is + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected void EnsureNotDisposed() + { + if (_disposed) + throw new ObjectDisposedException($"Unable to access disposed object, Type: {GetType().Name}"); + } } } \ No newline at end of file From 07453f6fbb956941c126e3dcbff5397d9b7d26f1 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Fri, 23 Aug 2019 00:32:45 +0300 Subject: [PATCH 27/57] ScopedTFImportGraphDefOptions: removed unnecessary deconstructor --- .../Framework/Models/ScopedTFImportGraphDefOptions.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/TensorFlowNET.Core/Framework/Models/ScopedTFImportGraphDefOptions.cs b/src/TensorFlowNET.Core/Framework/Models/ScopedTFImportGraphDefOptions.cs index dc3955b2..145a3058 100644 --- a/src/TensorFlowNET.Core/Framework/Models/ScopedTFImportGraphDefOptions.cs +++ b/src/TensorFlowNET.Core/Framework/Models/ScopedTFImportGraphDefOptions.cs @@ -6,10 +6,5 @@ { } - - ~ScopedTFImportGraphDefOptions() - { - base.Dispose(); - } } } From 76a97295791afc481bdc53f0374725fd35e8af6d Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Fri, 23 Aug 2019 00:36:20 +0300 Subject: [PATCH 28/57] DefaultGraphStack: Revamped --- .../Graphs/DefaultGraphStack.cs | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/src/TensorFlowNET.Core/Graphs/DefaultGraphStack.cs b/src/TensorFlowNET.Core/Graphs/DefaultGraphStack.cs index 6c9f6b18..42612d4e 100644 --- a/src/TensorFlowNET.Core/Graphs/DefaultGraphStack.cs +++ b/src/TensorFlowNET.Core/Graphs/DefaultGraphStack.cs @@ -14,49 +14,61 @@ limitations under the License. ******************************************************************************/ +using System; using System.Collections.Generic; using System.Linq; using static Tensorflow.Binding; namespace Tensorflow { + /// + /// Serves as a stack for determining current default graph. + /// public class DefaultGraphStack { - List stack = new List(); + private readonly List _stack = new List(); public void set_controller(Graph @default) { - if (!stack.Exists(x => x.Graph == @default)) - stack.Add(new StackModel { Graph = @default, IsDefault = true }); + if (!_stack.Exists(x => x.Graph == @default)) + _stack.Add(new StackModel {Graph = @default, IsDefault = true}); - foreach (var s in stack) + foreach (var s in _stack) s.IsDefault = s.Graph == @default; } public Graph get_controller() { - if (stack.Count(x => x.IsDefault) == 0) - stack.Add(new StackModel { Graph = tf.Graph(), IsDefault = true }); + if (_stack.Count(x => x.IsDefault) == 0) + _stack.Add(new StackModel {Graph = tf.Graph(), IsDefault = true}); + for (var i = _stack.Count - 1; i >= 0; i--) + { + var x = _stack[i]; + if (x.IsDefault) + return x.Graph; + } - return stack.Last(x => x.IsDefault).Graph; + throw new TensorflowException("Unable to find a default graph"); } public bool remove(Graph g) { - var sm = stack.FirstOrDefault(x => x.Graph == g); - if (sm == null) return false; - return stack.Remove(sm); + if (_stack.Count == 0) + return false; + + var sm = _stack.Find(model => model.Graph == g); + return sm != null && _stack.Remove(sm); } public void reset() { - stack.Clear(); + _stack.Clear(); } - } - public class StackModel - { - public Graph Graph { get; set; } - public bool IsDefault { get; set; } + private class StackModel + { + public Graph Graph { get; set; } + public bool IsDefault { get; set; } + } } -} +} \ No newline at end of file From d208f31db7daf46cff59ca580bd2bcf08b8244f6 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Fri, 23 Aug 2019 00:36:38 +0300 Subject: [PATCH 29/57] tensor_util.cs: minor perf-op --- src/TensorFlowNET.Core/Tensors/tensor_util.cs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/TensorFlowNET.Core/Tensors/tensor_util.cs b/src/TensorFlowNET.Core/Tensors/tensor_util.cs index 43848da6..59c107fc 100644 --- a/src/TensorFlowNET.Core/Tensors/tensor_util.cs +++ b/src/TensorFlowNET.Core/Tensors/tensor_util.cs @@ -83,6 +83,12 @@ namespace Tensorflow throw new NotImplementedException("MakeNdarray"); } + private static readonly TF_DataType[] quantized_types = new TF_DataType[] + { + TF_DataType.TF_QINT8, TF_DataType.TF_QUINT8, TF_DataType.TF_QINT16, TF_DataType.TF_QUINT16, + TF_DataType.TF_QINT32 + }; + /// /// Create a TensorProto. /// @@ -99,15 +105,6 @@ namespace Tensorflow if (values is TensorProto tp) return tp; - if (dtype != TF_DataType.DtInvalid) - ; - - bool is_quantized = new TF_DataType[] - { - TF_DataType.TF_QINT8, TF_DataType.TF_QUINT8, TF_DataType.TF_QINT16, TF_DataType.TF_QUINT16, - TF_DataType.TF_QINT32 - }.Contains(dtype); - // We first convert value to a numpy array or scalar. NDArray nparray = null; var np_dt = dtype.as_numpy_dtype(); @@ -227,13 +224,13 @@ namespace Tensorflow } } - var numpy_dtype = dtypes.as_dtype(nparray.dtype, dtype: dtype); + var numpy_dtype = nparray.dtype.as_dtype(dtype: dtype); if (numpy_dtype == TF_DataType.DtInvalid) throw new TypeError($"Unrecognized data type: {nparray.dtype}"); // If dtype was specified and is a quantized type, we convert // numpy_dtype back into the quantized version. - if (is_quantized) + if (quantized_types.Contains(dtype)) numpy_dtype = dtype; bool is_same_size = false; From cbf8945caefb21d108d9c1c077fa8b2af12c0eaa Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Fri, 23 Aug 2019 00:37:02 +0300 Subject: [PATCH 30/57] Tensor.Creation: uncommented `Marshal.FreeHGlobal(dataPtr);` --- src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs index 73f116ec..68d07312 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs @@ -667,8 +667,9 @@ namespace Tensorflow { if (args.deallocator_called) return; + // NumSharp will dispose - // Marshal.FreeHGlobal(dataPtr); + Marshal.FreeHGlobal(dataPtr); args.deallocator_called = true; } From 6772e19e7c20aaa6ec8204a0b66d0259f889acce Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Fri, 23 Aug 2019 01:42:54 +0300 Subject: [PATCH 31/57] globals.regen: fixed and added more arrays --- src/TensorFlowNET.Core/globals.regen | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/TensorFlowNET.Core/globals.regen b/src/TensorFlowNET.Core/globals.regen index 146155b3..86cbee67 100644 --- a/src/TensorFlowNET.Core/globals.regen +++ b/src/TensorFlowNET.Core/globals.regen @@ -8,7 +8,8 @@ %supported_numericals_lowercase = ["byte","short","ushort","int","uint","long","ulong","char","double","float"] %supported_numericals_defaultvals = ["0","0","0","0","0u","0L","0UL","'\0'","0d","0f"] %supported_numericals_onevales = ["1","1","1","1","1u","1L","1UL",1,"1d","1f"] -%supported_numericals_TF_DataType = ["TF_DataType.TF_UINT8","TF_DataType.TF_INT16","TF_DataType.TF_UINT16","TF_DataType.TF_INT32","TF_DataType.TF_UINT32","TF_DataType.TF_INT64","TF_DataType.TF_UINT64","TF_DataType.TF_UINT8","TF_DataType.TF_DOUBLE","TF_DataType.TF_FLOAT"] +%supported_numericals_TF_DataType = ["TF_UINT8","TF_INT16","TF_UINT16","TF_INT32","TF_UINT32","TF_INT64","TF_UINT64","TF_STRING","TF_DOUBLE","TF_FLOAT"] +%supported_numericals_TF_DataType_full = ["TF_DataType.TF_UINT8","TF_DataType.TF_INT16","TF_DataType.TF_UINT16","TF_DataType.TF_INT32","TF_DataType.TF_UINT32","TF_DataType.TF_INT64","TF_DataType.TF_UINT64","TF_DataType.TF_STRING","TF_DataType.TF_DOUBLE","TF_DataType.TF_FLOAT"] //this is the type we use in summerizing/reducting: %supported_numericals_accumulatingType = ["UInt32","Int32","UInt32","Int32","UInt32","Int64","UInt64","UInt32","Double","Single"] @@ -25,7 +26,8 @@ %supported_numericals_unsigned_onevales = ["1","1","1U","1UL","'\1'"] %supported_dtypes = ["Boolean","Byte","Int16","UInt16","Int32","UInt32","Int64","UInt64","Char","Double","Single"] -%supported_numericals_TF_DataType = ["TF_DataType.TF_UINT8","TF_DataType.TF_INT16","TF_DataType.TF_UINT16","TF_DataType.TF_INT32","TF_DataType.TF_UINT32","TF_DataType.TF_INT64","TF_DataType.TF_UINT64","TF_DataType.TF_UINT8","TF_DataType.TF_DOUBLE","TF_DataType.TF_FLOAT"] +%supported_dtypes_TF_DataType = ["TF_BOOL","TF_UINT8","TF_INT16","TF_UINT16","TF_INT32","TF_UINT32","TF_INT64","TF_UINT64","TF_STRING","TF_DOUBLE","TF_FLOAT"] +%supported_dtypes_TF_DataType_full = ["TF_DataType.TF_BOOL","TF_DataType.TF_UINT8","TF_DataType.TF_INT16","TF_DataType.TF_UINT16","TF_DataType.TF_INT32","TF_DataType.TF_UINT32","TF_DataType.TF_INT64","TF_DataType.TF_UINT64","TF_DataType.TF_STRING","TF_DataType.TF_DOUBLE","TF_DataType.TF_FLOAT"] %supported_dtypes_lowercase = ["bool","byte","short","ushort","int","uint","long","ulong","char","double","float"] %supported_dtypes_defaultvals = [false,"0","0","0","0","0u","0L","0UL","'\0'","0d","0f"] From a7dd34dca6f16179a7b1bb8868973c7318ee435f Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Fri, 23 Aug 2019 01:47:27 +0300 Subject: [PATCH 32/57] Revamped Context and ContextOptions --- src/TensorFlowNET.Core/Eager/Context.cs | 29 ++++++++----------- .../Eager/ContextOptions.cs | 25 +++++++--------- 2 files changed, 23 insertions(+), 31 deletions(-) diff --git a/src/TensorFlowNET.Core/Eager/Context.cs b/src/TensorFlowNET.Core/Eager/Context.cs index 4ee43d35..700e1236 100644 --- a/src/TensorFlowNET.Core/Eager/Context.cs +++ b/src/TensorFlowNET.Core/Eager/Context.cs @@ -2,12 +2,10 @@ namespace Tensorflow.Eager { - public class Context : IDisposable + public class Context : DisposableObject { - private IntPtr _handle; - - public static int GRAPH_MODE = 0; - public static int EAGER_MODE = 1; + public const int GRAPH_MODE = 0; + public const int EAGER_MODE = 1; public int default_execution_mode; @@ -17,19 +15,16 @@ namespace Tensorflow.Eager status.Check(true); } - public void Dispose() - { - c_api.TFE_DeleteContext(_handle); - } + /// + /// Dispose any unmanaged resources related to given . + /// + protected sealed override void DisposeUnmanagedResources(IntPtr handle) + => c_api.TFE_DeleteContext(_handle); - public bool executing_eagerly() - { - return false; - } - public static implicit operator IntPtr(Context ctx) - { - return ctx._handle; - } + public bool executing_eagerly() => false; + + public static implicit operator IntPtr(Context ctx) + => ctx._handle; } } diff --git a/src/TensorFlowNET.Core/Eager/ContextOptions.cs b/src/TensorFlowNET.Core/Eager/ContextOptions.cs index 4bdf04b3..12c4cdfc 100644 --- a/src/TensorFlowNET.Core/Eager/ContextOptions.cs +++ b/src/TensorFlowNET.Core/Eager/ContextOptions.cs @@ -3,23 +3,20 @@ using System.IO; namespace Tensorflow.Eager { - public class ContextOptions : IDisposable //TODO! Eli: Shouldn't this inherieting DisposableObject? + public class ContextOptions : DisposableObject { - private IntPtr _handle; + public ContextOptions() : base(c_api.TFE_NewContextOptions()) + { } - public ContextOptions() - { - _handle = c_api.TFE_NewContextOptions(); - } + /// + /// Dispose any unmanaged resources related to given . + /// + protected sealed override void DisposeUnmanagedResources(IntPtr handle) + => c_api.TFE_DeleteContextOptions(_handle); - public void Dispose() - { - c_api.TFE_DeleteContextOptions(_handle); - } - public static implicit operator IntPtr(ContextOptions opts) - { - return opts._handle; - } + public static implicit operator IntPtr(ContextOptions opts) + => opts._handle; } + } From 916ebc53b2db15efcbbd83245b8541d2f23c94eb Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Fri, 23 Aug 2019 02:06:41 +0300 Subject: [PATCH 33/57] BaseSession: Heavy perf-ops in sess.run logic-flow. --- src/TensorFlowNET.Core/Graphs/Graph.cs | 8 +- .../Graphs/ImportGraphDefOptions.cs | 3 +- .../Sessions/BaseSession.cs | 874 +++++++++--------- src/TensorFlowNET.Core/Status/Status.cs | 2 + 4 files changed, 468 insertions(+), 419 deletions(-) diff --git a/src/TensorFlowNET.Core/Graphs/Graph.cs b/src/TensorFlowNET.Core/Graphs/Graph.cs index 07dc117e..38c35385 100644 --- a/src/TensorFlowNET.Core/Graphs/Graph.cs +++ b/src/TensorFlowNET.Core/Graphs/Graph.cs @@ -497,11 +497,9 @@ namespace Tensorflow IEnumerator IEnumerable.GetEnumerator() => GetEnumerable().GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() - { - throw new NotImplementedException(); - } - + IEnumerator IEnumerable.GetEnumerator() + => throw new NotImplementedException(); + public static implicit operator IntPtr(Graph graph) { return graph._handle; diff --git a/src/TensorFlowNET.Core/Graphs/ImportGraphDefOptions.cs b/src/TensorFlowNET.Core/Graphs/ImportGraphDefOptions.cs index bdcaf60c..70802597 100644 --- a/src/TensorFlowNET.Core/Graphs/ImportGraphDefOptions.cs +++ b/src/TensorFlowNET.Core/Graphs/ImportGraphDefOptions.cs @@ -20,7 +20,8 @@ namespace Tensorflow { public class ImportGraphDefOptions : DisposableObject { - public int NumReturnOutputs => c_api.TF_ImportGraphDefOptionsNumReturnOutputs(_handle); + public int NumReturnOutputs + => c_api.TF_ImportGraphDefOptionsNumReturnOutputs(_handle); public ImportGraphDefOptions() { diff --git a/src/TensorFlowNET.Core/Sessions/BaseSession.cs b/src/TensorFlowNET.Core/Sessions/BaseSession.cs index 4c5f2be3..96c8a657 100644 --- a/src/TensorFlowNET.Core/Sessions/BaseSession.cs +++ b/src/TensorFlowNET.Core/Sessions/BaseSession.cs @@ -1,413 +1,461 @@ -/***************************************************************************** - Copyright 2018 The TensorFlow.NET Authors. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -******************************************************************************/ - -using NumSharp; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Text; - -namespace Tensorflow -{ - public class BaseSession : DisposableObject - { - protected Graph _graph; - protected bool _opened; - protected bool _closed; - protected int _current_version; - protected byte[] _target; - public Graph graph => _graph; - - public BaseSession(string target = "", Graph g = null, SessionOptions opts = null) - { - _graph = g is null ? ops.get_default_graph() : g; - _graph.as_default(); - _target = UTF8Encoding.UTF8.GetBytes(target); - - SessionOptions newOpts = null; - if (opts == null) - newOpts = new SessionOptions(); - - var status = new Status(); - - _handle = c_api.TF_NewSession(_graph, opts ?? newOpts, status); - - // dispose newOpts - if (opts == null) - newOpts.Dispose(); - - status.Check(true); - } - - public virtual void run(Operation op, params FeedItem[] feed_dict) - { - _run(op, feed_dict); - } - - public virtual NDArray run(Tensor fetche, params FeedItem[] feed_dict) - { - return _run(fetche, feed_dict)[0]; - } - - public virtual NDArray run(ITensorOrOperation fetche, params FeedItem[] feed_dict) - { - return _run(fetche, feed_dict)[0]; - } - - public virtual (NDArray, NDArray, NDArray, NDArray) run((ITensorOrOperation, ITensorOrOperation, ITensorOrOperation, ITensorOrOperation) fetches, params FeedItem[] feed_dict) - { - var results = _run(new object[] { fetches.Item1, fetches.Item2, fetches.Item3, fetches.Item4 }, feed_dict); - return (results[0], results[1], results[2], results[3]); - } - - public virtual (NDArray, NDArray, NDArray) run((ITensorOrOperation, ITensorOrOperation, ITensorOrOperation) fetches, params FeedItem[] feed_dict) - { - var results = _run(new object[] { fetches.Item1, fetches.Item2, fetches.Item3 }, feed_dict); - return (results[0], results[1], results[2]); - } - - public virtual (NDArray, NDArray) run((ITensorOrOperation, ITensorOrOperation) fetches, params FeedItem[] feed_dict) - { - var results = _run(new object[] { fetches.Item1, fetches.Item2 }, feed_dict); - return (results[0], results[1]); - } - - public virtual NDArray[] run(object fetches, params FeedItem[] feed_dict) - { - return _run(fetches, feed_dict); - } - - public virtual NDArray[] run(object fetches, Hashtable feed_dict = null) - { - var feed_items = feed_dict == null ? new FeedItem[0] : - feed_dict.Keys.OfType().Select(key => new FeedItem(key, feed_dict[key])).ToArray(); - return _run(fetches, feed_items); - } - - private NDArray[] _run(object fetches, FeedItem[] feed_dict = null) - { - var feed_dict_tensor = new Dictionary(); - var feed_map = new Dictionary(); - - Func> feed_fn = (item) => - { - return new (object, object)[] { (item.Key, item.Value) }; - }; - - // Validate and process feed_dict. - if (feed_dict != null) - { - foreach (var feed in feed_dict) - { - foreach (var (subfeed, subfeed_val) in feed_fn(feed)) - { - var subfeed_t = _graph.as_graph_element(subfeed, allow_tensor: true, allow_operation: false); - //var subfeed_dtype = subfeed_t.dtype.as_numpy_datatype(); // subfeed_dtype was never used - feed_dict_tensor[subfeed_t] = subfeed_val; - feed_map[subfeed_t.name] = (subfeed_t, subfeed_val); - } - } - } - - // Create a fetch handler to take care of the structure of fetches. - var fetch_handler = new _FetchHandler(_graph, fetches, feed_dict_tensor); - - // Run request and get response. - // We need to keep the returned movers alive for the following _do_run(). - // These movers are no longer needed when _do_run() completes, and - // are deleted when `movers` goes out of scope when this _run() ends. - var _ = _update_with_movers(); - var final_fetches = fetch_handler.fetches(); - var final_targets = fetch_handler.targets(); - - // We only want to really perform the run if fetches or targets are provided, - // or if the call is a partial run that specifies feeds. - var results = _do_run(final_targets.Select(x => (Operation)x).ToList(), final_fetches, feed_dict_tensor); - - return fetch_handler.build_results(this, results); - } - - /// - /// Runs a step based on the given fetches and feeds. - /// - /// - /// A list of operations to be run, but not fetched. - /// - /// - /// - /// A list of numpy ndarrays, corresponding to the elements of - /// `fetch_list`. If the ith element of `fetch_list` contains the - /// name of an operation, the first Tensor output of that operation - /// will be returned for that element. - /// - private NDArray[] _do_run(List target_list, List fetch_list, Dictionary feed_dict) - { - var feeds = feed_dict.Select(x => - { - if (x.Key is Tensor tensor) - { - switch (x.Value) - { -#if _REGEN - %types=["sbyte", "byte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double", "Complex"] - %foreach types% - case #1 v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case #1[] v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - % -#else - case sbyte v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case sbyte[] v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case byte v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case byte[] v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case short v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case short[] v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case ushort v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case ushort[] v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case int v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case int[] v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case uint v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case uint[] v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case long v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case long[] v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case ulong v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case ulong[] v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case float v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case float[] v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case double v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case double[] v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case Complex v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case Complex[] v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); -#endif - case bool v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor((byte)(v?1:0), TF_DataType.TF_BOOL)); - case string v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case IntPtr v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); - case Tensor v: - return new KeyValuePair(tensor._as_tf_output(), v); - case NDArray v: - return new KeyValuePair(tensor._as_tf_output(), new Tensor(v, tensor.dtype)); - default: - throw new NotImplementedException($"feed_dict data type {(x.Value?.GetType().Name ?? "")}"); - } - } - throw new NotImplementedException("_do_run.feed_dict"); - }).ToArray(); - var fetches = fetch_list.Select(x => x._as_tf_output()).ToArray(); - var targets = target_list; - - return _call_tf_sessionrun(feeds, fetches, target_list); - } - - private unsafe NDArray[] _call_tf_sessionrun(KeyValuePair[] feed_dict, TF_Output[] fetch_list, List target_list) - { - // Ensure any changes to the graph are reflected in the runtime. - _extend_graph(); - - var status = new Status(); - - var output_values = fetch_list.Select(x => IntPtr.Zero).ToArray(); - - c_api.TF_SessionRun(_handle, - run_options: null, - inputs: feed_dict.Select(f => f.Key).ToArray(), - input_values: feed_dict.Select(f => (IntPtr)f.Value).ToArray(), - ninputs: feed_dict.Length, - outputs: fetch_list, - output_values: output_values, - noutputs: fetch_list.Length, - target_opers: target_list.Select(f => (IntPtr)f).ToArray(), - ntargets: target_list.Count, - run_metadata: IntPtr.Zero, - status: status); - - status.Check(true); - - var result = new NDArray[fetch_list.Length]; - - for (int i = 0; i < fetch_list.Length; i++) - result[i] = fetchValue(output_values[i]); - - for (int i = 0; i < feed_dict.Length; i++) - feed_dict[i].Value.Dispose(); - - return result; - } - - private unsafe NDArray fetchValue(IntPtr output) - { - var tensor = new Tensor(output); - NDArray nd = null; - Type type = tensor.dtype.as_numpy_dtype(); - var ndims = tensor.shape; - var offset = c_api.TF_TensorData(output); - - if(ndims.Length == 0) - { - switch (tensor.dtype) - { - case TF_DataType.TF_BOOL: - nd = NDArray.Scalar(*(bool*)offset); - break; - case TF_DataType.TF_STRING: - var bytes = tensor.BufferToArray(); - // wired, don't know why we have to start from offset 9. - // length in the begin - var str = UTF8Encoding.Default.GetString(bytes, 9, bytes[8]); - nd = NDArray.FromString(str); - break; - case TF_DataType.TF_UINT8: - nd = NDArray.Scalar(*(byte*)offset); - break; - case TF_DataType.TF_INT16: - nd = NDArray.Scalar(*(short*)offset); - break; - case TF_DataType.TF_INT32: - nd = NDArray.Scalar(*(int*)offset); - break; - case TF_DataType.TF_INT64: - nd = NDArray.Scalar(*(long*)offset); - break; - case TF_DataType.TF_FLOAT: - nd = NDArray.Scalar(*(float*)offset); - break; - case TF_DataType.TF_DOUBLE: - nd = NDArray.Scalar(*(double*)offset); - break; - default: - throw new NotImplementedException("can't fetch output"); - } - } - else - { - switch (tensor.dtype) - { - case TF_DataType.TF_BOOL: - var bools = new bool[tensor.size]; - for (ulong i = 0; i < tensor.size; i++) - bools[i] = *(bool*)(offset + (int)(tensor.itemsize * i)); - nd = np.array(bools).reshape(ndims); - break; - case TF_DataType.TF_STRING: - var bytes = tensor.BufferToArray(); - // wired, don't know why we have to start from offset 9. - // length in the begin - var str = UTF8Encoding.Default.GetString(bytes, 9, bytes[8]); - nd = np.array(str); - break; - case TF_DataType.TF_UINT8: - var _bytes = new byte[tensor.size]; - for (ulong i = 0; i < tensor.size; i++) - _bytes[i] = *(byte*)(offset + (int)(tensor.itemsize * i)); - nd = np.array(_bytes).reshape(ndims); - break; - case TF_DataType.TF_INT16: - var shorts = new short[tensor.size]; - for (ulong i = 0; i < tensor.size; i++) - shorts[i] = *(short*)(offset + (int)(tensor.itemsize * i)); - nd = np.array(shorts).reshape(ndims); - break; - case TF_DataType.TF_INT32: - var ints = new int[tensor.size]; - for (ulong i = 0; i < tensor.size; i++) - ints[i] = *(int*)(offset + (int)(tensor.itemsize * i)); - nd = np.array(ints).reshape(ndims); - break; - case TF_DataType.TF_INT64: - var longs = new long[tensor.size]; - for (ulong i = 0; i < tensor.size; i++) - longs[i] = *(long*)(offset + (int)(tensor.itemsize * i)); - nd = np.array(longs).reshape(ndims); - break; - case TF_DataType.TF_FLOAT: - var floats = new float[tensor.size]; - for (ulong i = 0; i < tensor.size; i++) - floats[i] = *(float*)(offset + (int)(tensor.itemsize * i)); - nd = np.array(floats).reshape(ndims); - break; - case TF_DataType.TF_DOUBLE: - var doubles = new double[tensor.size]; - for (ulong i = 0; i < tensor.size; i++) - doubles[i] = *(double*)(offset + (int)(tensor.itemsize * i)); - nd = np.array(doubles).reshape(ndims); - break; - default: - throw new NotImplementedException("can't fetch output"); - } - } - - tensor.Dispose(); - - return nd; - } - - /// - /// If a tensor handle that is fed to a device incompatible placeholder, - /// we move the tensor to the right device, generate a new tensor handle, - /// and update feed_dict to use the new handle. - /// - private List _update_with_movers() - { - return new List { }; - } - - private void _extend_graph() - { - - } - - public void close() - { - Dispose(); - } - - protected override void DisposeUnmanagedResources(IntPtr handle) - { - using (var status = new Status()) - { - c_api.TF_DeleteSession(handle, status); - status.Check(true); - } - } - } -} +/***************************************************************************** + Copyright 2018 The TensorFlow.NET Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +******************************************************************************/ + +using NumSharp; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; +using NumSharp.Backends; +using NumSharp.Backends.Unmanaged; + +namespace Tensorflow +{ + public class BaseSession : DisposableObject + { + protected Graph _graph; + protected SessionOptions _options; + protected bool _opened; + protected bool _closed; + protected int _current_version; + protected byte[] _target; + public Graph graph => _graph; + + public BaseSession(string target = "", Graph g = null, SessionOptions opts = null) + { + _graph = g ?? ops.get_default_graph(); + _graph.as_default(); + _target = Encoding.UTF8.GetBytes(target); + _options = opts = opts ?? new SessionOptions(); + var status = new Status(); + + _handle = c_api.TF_NewSession(_graph, opts, status); + + status.Check(true); + } + + public virtual void run(Operation op, params FeedItem[] feed_dict) + { + _run(op, feed_dict); + } + + public virtual NDArray run(Tensor fetche, params FeedItem[] feed_dict) + { + return _run(fetche, feed_dict)[0]; + } + + public virtual NDArray run(ITensorOrOperation fetche, params FeedItem[] feed_dict) + { + return _run(fetche, feed_dict)[0]; + } + + public virtual (NDArray, NDArray, NDArray, NDArray) run((ITensorOrOperation, ITensorOrOperation, ITensorOrOperation, ITensorOrOperation) fetches, params FeedItem[] feed_dict) + { + var results = _run(new object[] {fetches.Item1, fetches.Item2, fetches.Item3, fetches.Item4}, feed_dict); + return (results[0], results[1], results[2], results[3]); + } + + public virtual (NDArray, NDArray, NDArray) run((ITensorOrOperation, ITensorOrOperation, ITensorOrOperation) fetches, params FeedItem[] feed_dict) + { + var results = _run(new object[] {fetches.Item1, fetches.Item2, fetches.Item3}, feed_dict); + return (results[0], results[1], results[2]); + } + + public virtual (NDArray, NDArray) run((ITensorOrOperation, ITensorOrOperation) fetches, params FeedItem[] feed_dict) + { + var results = _run(new object[] {fetches.Item1, fetches.Item2}, feed_dict); + return (results[0], results[1]); + } + + public virtual NDArray[] run(object fetches, params FeedItem[] feed_dict) + { + return _run(fetches, feed_dict); + } + + public virtual NDArray[] run(object fetches, Hashtable feed_dict = null) + { + var feed_items = feed_dict == null ? new FeedItem[0] : feed_dict.Keys.OfType().Select(key => new FeedItem(key, feed_dict[key])).ToArray(); + return _run(fetches, feed_items); + } + + private NDArray[] _run(object fetches, FeedItem[] feed_dict = null) + { + var feed_dict_tensor = new Dictionary(); + var feed_map = new Dictionary(); + + Func> feed_fn = (item) => { return new (object, object)[] {(item.Key, item.Value)}; }; + + // Validate and process feed_dict. + if (feed_dict != null) + { + foreach (var feed in feed_dict) + { + foreach (var (subfeed, subfeed_val) in feed_fn(feed)) + { + var subfeed_t = _graph.as_graph_element(subfeed, allow_tensor: true, allow_operation: false); + //var subfeed_dtype = subfeed_t.dtype.as_numpy_datatype(); // subfeed_dtype was never used + feed_dict_tensor[subfeed_t] = subfeed_val; + feed_map[subfeed_t.name] = (subfeed_t, subfeed_val); + } + } + } + + // Create a fetch handler to take care of the structure of fetches. + var fetch_handler = new _FetchHandler(_graph, fetches, feed_dict_tensor); + + // Run request and get response. + // We need to keep the returned movers alive for the following _do_run(). + // These movers are no longer needed when _do_run() completes, and + // are deleted when `movers` goes out of scope when this _run() ends. + var _ = _update_with_movers(); + var final_fetches = fetch_handler.fetches(); + var final_targets = fetch_handler.targets(); + + // We only want to really perform the run if fetches or targets are provided, + // or if the call is a partial run that specifies feeds. + var results = _do_run(final_targets.Select(x => (Operation) x).ToList(), final_fetches, feed_dict_tensor); + + return fetch_handler.build_results(this, results); + } + + /// + /// Runs a step based on the given fetches and feeds. + /// + /// + /// A list of operations to be run, but not fetched. + /// + /// + /// + /// A list of numpy ndarrays, corresponding to the elements of + /// `fetch_list`. If the ith element of `fetch_list` contains the + /// name of an operation, the first Tensor output of that operation + /// will be returned for that element. + /// + private NDArray[] _do_run(List target_list, List fetch_list, Dictionary feed_dict) + { + var feeds = new KeyValuePair[feed_dict.Count]; + int i = 0; + foreach (var x in feed_dict) + { + var tensor = (Tensor) x.Key; + switch (x.Value) + { +#if _REGEN + %types = ["sbyte", "byte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double", "Complex"] + %foreach types% + case #1 v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case #1[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + % +#else + case sbyte v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case sbyte[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case byte v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case byte[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case short v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case short[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case ushort v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case ushort[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case int v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case int[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case uint v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case uint[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case long v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case long[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case ulong v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case ulong[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case float v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case float[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case double v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case double[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case Complex v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case Complex[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; +#endif + case bool v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor((byte) (v ? 1 : 0), TF_DataType.TF_BOOL)); break; + case string v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case IntPtr v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case Tensor v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), v); break; + case NDArray v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v, tensor.dtype)); break; + default: + throw new NotImplementedException($"feed_dict data type {x.Value?.GetType().Name ?? ""}"); + } + } + + var fetches = new TF_Output[fetch_list.Count]; + for (i = 0; i < fetch_list.Count; i++) + fetches[i] = fetch_list[i]._as_tf_output(); + + //var targets = target_list; + + return _call_tf_sessionrun(feeds, fetches, target_list); + } + + private unsafe NDArray[] _call_tf_sessionrun(KeyValuePair[] feed_dict, TF_Output[] fetch_list, List target_list) + { + // Ensure any changes to the graph are reflected in the runtime. + _extend_graph(); + + var status = new Status(); + var fetch_len = fetch_list.Length; + var output_values = new IntPtr[fetch_len]; + + c_api.TF_SessionRun(_handle, + run_options: null, + inputs: feed_dict.Select(f => f.Key).ToArray(), + input_values: feed_dict.Select(f => (IntPtr) f.Value).ToArray(), + ninputs: feed_dict.Length, + outputs: fetch_list, + output_values: output_values, + noutputs: fetch_len, + target_opers: target_list.Select(f => (IntPtr) f).ToArray(), + ntargets: target_list.Count, + run_metadata: IntPtr.Zero, + status: status); + + status.Check(true); + + var result = new NDArray[fetch_len]; + + for (int i = 0; i < fetch_len; i++) + result[i] = fetchValue(output_values[i]); + + for (int i = 0; i < feed_dict.Length; i++) + feed_dict[i].Value.Dispose(); + + return result; + } + + private unsafe NDArray fetchValue(IntPtr output) + { + NDArray ret; + using (var tensor = new Tensor(output)) + { + var ndims = tensor.shape; + var srcAddress = c_api.TF_TensorData(output).ToInt64(); + + if (ndims.Length == 0) + { + switch (tensor.dtype) + { + case TF_DataType.TF_BOOL: + ret = NDArray.Scalar(*(bool*) srcAddress); + break; + case TF_DataType.TF_STRING: + var bytes = tensor.BufferToArray(); + // offset has to start from 9/ + var str = Encoding.Default.GetString(bytes, 9, bytes[8]); + ret = NDArray.FromString(str); + break; + case TF_DataType.TF_UINT8: + ret = NDArray.Scalar(*(byte*) srcAddress); + break; + case TF_DataType.TF_INT16: + ret = NDArray.Scalar(*(short*) srcAddress); + break; + case TF_DataType.TF_INT32: + ret = NDArray.Scalar(*(int*) srcAddress); + break; + case TF_DataType.TF_INT64: + ret = NDArray.Scalar(*(long*) srcAddress); + break; + case TF_DataType.TF_UINT16: + ret = NDArray.Scalar(*(ushort*) srcAddress); + break; + case TF_DataType.TF_UINT32: + ret = NDArray.Scalar(*(uint*) srcAddress); + break; + case TF_DataType.TF_UINT64: + ret = NDArray.Scalar(*(ulong*) srcAddress); + break; + case TF_DataType.TF_FLOAT: + ret = NDArray.Scalar(*(float*) srcAddress); + break; + case TF_DataType.TF_DOUBLE: + ret = NDArray.Scalar(*(double*) srcAddress); + break; + default: + throw new NotImplementedException("can't fetch output"); + } + } else + { + //var size = (long) tensor.size; + //var itemsize = (long) tensor.itemsize; + var bytesize = (long) tensor.bytesize; + var src = (void*) srcAddress; + +#if _REGEN + #region Compute + switch (tensor.dtype) + { + %foreach except(supported_dtypes, "Char"),except(supported_dtypes_lowercase, "char"),except(supported_dtypes_TF_DataType,"TF_STRING")% + case TF_DataType.#3: + { + ret = new NDArray(NPTypeCode.#1, ndims, false); + System.Buffer.MemoryCopy(src, #(#3=="TF_STRING"|"(byte*)ret.Unsafe.Address + 8"|"ret.Unsafe.Address"), bytesize, bytesize); + break; + } + % + case TF_DataType.TF_STRING: + { + ret = new NDArray(NPTypeCode.Char, Shape.Vector((int) size), false); //TODO! Eli: when numsharp supports long size, remove (int) cast. + //var bytes = tensor.BufferToArray(); + //// wired, don't know why we have to start from offset 9. + //// length in the begin + //var str = Encoding.Default.GetString(bytes, 9, bytes[8]); + //ret = np.array(str); + + //TODO! Eli: this has to be unit-tested. + var len = sizeof(char) * size; + var dst = ret.Unsafe.Address; + System.Buffer.MemoryCopy((byte*) src + 8, dst, len, len); + break; + } + default: + throw new NotSupportedException(); + } + #endregion +#else + + #region Compute + + switch (tensor.dtype) + { + case TF_DataType.TF_BOOL: + { + ret = new NDArray(NPTypeCode.Boolean, ndims, false); + System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize); + break; + } + + case TF_DataType.TF_UINT8: + { + ret = new NDArray(NPTypeCode.Byte, ndims, false); + System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize); + break; + } + + case TF_DataType.TF_INT16: + { + ret = new NDArray(NPTypeCode.Int16, ndims, false); + System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize); + break; + } + + case TF_DataType.TF_UINT16: + { + ret = new NDArray(NPTypeCode.UInt16, ndims, false); + System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize); + break; + } + + case TF_DataType.TF_INT32: + { + ret = new NDArray(NPTypeCode.Int32, ndims, false); + System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize); + break; + } + + case TF_DataType.TF_UINT32: + { + ret = new NDArray(NPTypeCode.UInt32, ndims, false); + System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize); + break; + } + + case TF_DataType.TF_INT64: + { + ret = new NDArray(NPTypeCode.Int64, ndims, false); + System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize); + break; + } + + case TF_DataType.TF_UINT64: + { + ret = new NDArray(NPTypeCode.UInt64, ndims, false); + System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize); + break; + } + + case TF_DataType.TF_DOUBLE: + { + ret = new NDArray(NPTypeCode.Double, ndims, false); + System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize); + break; + } + + case TF_DataType.TF_FLOAT: + { + ret = new NDArray(NPTypeCode.Single, ndims, false); + System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize); + break; + } + + case TF_DataType.TF_STRING: + { + ret = new NDArray(NPTypeCode.Char, Shape.Vector((int) (bytesize - 8) / sizeof(char)), false); //TODO! Eli: when numsharp supports long size, remove (int) cast. + + //TODO! Eli: this has to be unit-tested. + var len = bytesize - 8; + var dst = ret.Unsafe.Address; + System.Buffer.MemoryCopy((byte*) src + 8, dst, len, len); + break; + } + + default: + throw new NotSupportedException(); + } + + #endregion + +#endif + } + } + + return ret; + } + + /// + /// If a tensor handle that is fed to a device incompatible placeholder, + /// we move the tensor to the right device, generate a new tensor handle, + /// and update feed_dict to use the new handle. + /// + private List _update_with_movers() + { + return new List { }; + } + + private void _extend_graph() + { } + + public void close() + { + Dispose(); + } + + protected override void DisposeUnmanagedResources(IntPtr handle) + { + using (var status = new Status()) + { + c_api.TF_DeleteSession(handle, status); + status.Check(true); + } + + _options.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/TensorFlowNET.Core/Status/Status.cs b/src/TensorFlowNET.Core/Status/Status.cs index 2bdd806a..7d029e79 100644 --- a/src/TensorFlowNET.Core/Status/Status.cs +++ b/src/TensorFlowNET.Core/Status/Status.cs @@ -15,6 +15,7 @@ ******************************************************************************/ using System; +using System.Runtime.CompilerServices; namespace Tensorflow { @@ -48,6 +49,7 @@ namespace Tensorflow /// Check status /// Throw exception with error message if code != TF_OK /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Check(bool throwException = false) { if (Code != TF_Code.TF_OK) From f433c32c447eab417ce106cf903912a6b8df8701 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Sun, 25 Aug 2019 20:24:12 +0300 Subject: [PATCH 34/57] DefaultGraphStack: Perf-ops --- .../Graphs/DefaultGraphStack.cs | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/TensorFlowNET.Core/Graphs/DefaultGraphStack.cs b/src/TensorFlowNET.Core/Graphs/DefaultGraphStack.cs index 42612d4e..33a3aaa3 100644 --- a/src/TensorFlowNET.Core/Graphs/DefaultGraphStack.cs +++ b/src/TensorFlowNET.Core/Graphs/DefaultGraphStack.cs @@ -21,29 +21,30 @@ using static Tensorflow.Binding; namespace Tensorflow { + /// /// Serves as a stack for determining current default graph. /// - public class DefaultGraphStack + public class DefaultGraphStack { - private readonly List _stack = new List(); + private readonly List stack = new List(); public void set_controller(Graph @default) { - if (!_stack.Exists(x => x.Graph == @default)) - _stack.Add(new StackModel {Graph = @default, IsDefault = true}); + if (!stack.Exists(x => x.Graph == @default)) + stack.Add(new StackModel {Graph = @default, IsDefault = true}); - foreach (var s in _stack) + foreach (var s in stack) s.IsDefault = s.Graph == @default; } public Graph get_controller() { - if (_stack.Count(x => x.IsDefault) == 0) - _stack.Add(new StackModel {Graph = tf.Graph(), IsDefault = true}); - for (var i = _stack.Count - 1; i >= 0; i--) + if (stack.Count(x => x.IsDefault) == 0) + stack.Add(new StackModel {Graph = tf.Graph(), IsDefault = true}); + for (var i = stack.Count - 1; i >= 0; i--) { - var x = _stack[i]; + var x = stack[i]; if (x.IsDefault) return x.Graph; } @@ -53,16 +54,16 @@ namespace Tensorflow public bool remove(Graph g) { - if (_stack.Count == 0) + if (stack.Count == 0) return false; - var sm = _stack.Find(model => model.Graph == g); - return sm != null && _stack.Remove(sm); + var sm = stack.Find(model => model.Graph == g); + return sm != null && stack.Remove(sm); } public void reset() { - _stack.Clear(); + stack.Clear(); } private class StackModel From 729d8e92d6ee8b953fb81904604668ce69f70bc2 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Sun, 25 Aug 2019 22:26:10 +0300 Subject: [PATCH 35/57] Added benchmark for struct casting --- .../TensorFlowBenchmark.csproj | 1 + .../Unmanaged/StructCastBenchmark.cs | 76 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 src/TensorFlowNet.Benchmarks/Unmanaged/StructCastBenchmark.cs diff --git a/src/TensorFlowNet.Benchmarks/TensorFlowBenchmark.csproj b/src/TensorFlowNet.Benchmarks/TensorFlowBenchmark.csproj index bc2a0ff3..4618f06b 100644 --- a/src/TensorFlowNet.Benchmarks/TensorFlowBenchmark.csproj +++ b/src/TensorFlowNet.Benchmarks/TensorFlowBenchmark.csproj @@ -6,6 +6,7 @@ true TensorFlowBenchmark TensorFlowBenchmark + 7.3 diff --git a/src/TensorFlowNet.Benchmarks/Unmanaged/StructCastBenchmark.cs b/src/TensorFlowNet.Benchmarks/Unmanaged/StructCastBenchmark.cs new file mode 100644 index 00000000..5b3a0cd3 --- /dev/null +++ b/src/TensorFlowNet.Benchmarks/Unmanaged/StructCastBenchmark.cs @@ -0,0 +1,76 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using BenchmarkDotNet.Attributes; +using Google.Protobuf.WellKnownTypes; +using NumSharp; +using Tensorflow; +using static Tensorflow.Binding; + +namespace TensorFlowBenchmark.Unmanaged +{ + public struct UnmanagedStruct + { + public int a; + public long b; + public UnmanagedStruct(int _) + { + a = 2; + b = 3; + } + } + + [SimpleJob(launchCount: 1, warmupCount: 2, targetCount: 10)] + [MinColumn, MaxColumn, MeanColumn, MedianColumn] + public unsafe class StructCastBenchmark + { + private static void EnsureIsUnmanaged(T _) where T : unmanaged + { } + + static StructCastBenchmark() //if UnmanagedStruct is not unmanaged struct then this will fail to compile. + => EnsureIsUnmanaged(new UnmanagedStruct()); + + private IntPtr data; + private void* dataptr; + + [GlobalSetup] + public void Setup() + { + data = Marshal.AllocHGlobal(Marshal.SizeOf()); + dataptr = data.ToPointer(); + } + + [Benchmark, MethodImpl(MethodImplOptions.NoOptimization)] + public void Marshal_PtrToStructure() + { + UnmanagedStruct _; + for (int i = 0; i < 10000; i++) + { + _ = Marshal.PtrToStructure(data); + } + } + + [Benchmark, MethodImpl(MethodImplOptions.NoOptimization)] + public void PointerCast() + { + var dptr = dataptr; + UnmanagedStruct _; + for (int i = 0; i < 10000; i++) + { + _ = *(UnmanagedStruct*) dptr; + } + } + + [Benchmark, MethodImpl(MethodImplOptions.NoOptimization)] + public void Unsafe_Read() + { + var dptr = dataptr; + UnmanagedStruct _; + for (int i = 0; i < 10000; i++) + { + _ = Unsafe.Read(dptr); + } + } + + } +} \ No newline at end of file From 5cf45432051f5351e6f9654ab0f4434f866fc540 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Sun, 25 Aug 2019 22:26:58 +0300 Subject: [PATCH 36/57] TensorflowNET.UnitTest: Signed and added InternalVisibleTo in Core project. --- src/TensorFlowNET.Core/Assembly/Properties.cs | 4 ++++ test/TensorFlowNET.UnitTest/Open.snk | Bin 0 -> 596 bytes .../TensorFlowNET.UnitTest.csproj | 6 ++++++ 3 files changed, 10 insertions(+) create mode 100644 src/TensorFlowNET.Core/Assembly/Properties.cs create mode 100644 test/TensorFlowNET.UnitTest/Open.snk diff --git a/src/TensorFlowNET.Core/Assembly/Properties.cs b/src/TensorFlowNET.Core/Assembly/Properties.cs new file mode 100644 index 00000000..28aee65e --- /dev/null +++ b/src/TensorFlowNET.Core/Assembly/Properties.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; +#if DEBUG +[assembly: InternalsVisibleTo("TensorFlowNET.UnitTest, PublicKey=00240000048000009400000006020000002400005253413100040000010001004b86c4cb78549b34bab61a3b1800e23bfeb5b3ec390074041536a7e3cbd97f5f04cf0f857155a8928eaa29ebfd11cfbbad3ba70efea7bda3226c6a8d370a4cd303f714486b6ebc225985a638471e6ef571cc92a4613c00b8fa65d61ccee0cbe5f36330c9a01f4183559f1bef24cc2917c6d913e3a541333a1d05d9bed22b38cb")] +#endif diff --git a/test/TensorFlowNET.UnitTest/Open.snk b/test/TensorFlowNET.UnitTest/Open.snk new file mode 100644 index 0000000000000000000000000000000000000000..22a3cbd253a7f1091078ac81eb16eb2ffd32880c GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50096?hQ!NwRGT!qwi-Ja0OC9TwX^Iw0CWTu zHmBpu*?(UI&ku!hRj87VswwOJ5zo7=JEso*r@f;hY-)`+3QW@j_Y_ELZoDE{g{C-1 z9&Yt<%#x&GJOH@*W!4%#X z>O=LYCI9oV9(*3OvX<8KOfn^kauKMK)Q40Ck2iNzf(#`9!BGFmecB2~ccY%9L3QBBe zG%-a6fU%je)>Y8T3}#BFs84D=?*xidlht=JZr@^(#S^o(LP3+g}r|MD4BH`p~&~6 zt~;;|=bt(1I3jtmbU;6+`22n#0nqha;uK51JVJW88+KQpHUsg+5ZCZ{4D~-qSw{)8 ixNldK-pEwZArM>aiB7nKifAJOwbkQ2Qk0=;PvjC{`6;si literal 0 HcmV?d00001 diff --git a/test/TensorFlowNET.UnitTest/TensorFlowNET.UnitTest.csproj b/test/TensorFlowNET.UnitTest/TensorFlowNET.UnitTest.csproj index 848512f0..661d85ea 100644 --- a/test/TensorFlowNET.UnitTest/TensorFlowNET.UnitTest.csproj +++ b/test/TensorFlowNET.UnitTest/TensorFlowNET.UnitTest.csproj @@ -4,6 +4,12 @@ netcoreapp2.2 false + + true + + false + + Open.snk From 821ac131382c0c217c481043caec1e656f75d72d Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Sun, 25 Aug 2019 22:51:38 +0300 Subject: [PATCH 37/57] TensorflowNET.Benchmarks: Removed InProcessToolchain from benchmarking. --- src/TensorFlowNet.Benchmarks/Program.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/TensorFlowNet.Benchmarks/Program.cs b/src/TensorFlowNet.Benchmarks/Program.cs index e17a1d68..ea7c2bde 100644 --- a/src/TensorFlowNet.Benchmarks/Program.cs +++ b/src/TensorFlowNet.Benchmarks/Program.cs @@ -9,24 +9,18 @@ namespace TensorFlowBenchmark { static void Main(string[] args) { -#if DEBUG - IConfig config = new DebugInProcessConfig(); -#else - IConfig config = null; -#endif - if (args?.Length > 0) { for (int i = 0; i < args.Length; i++) { string name = $"TensorFlowBenchmark.{args[i]}"; var type = Type.GetType(name); - BenchmarkRunner.Run(type, config); + BenchmarkRunner.Run(type); } } else { - BenchmarkSwitcher.FromAssembly(Assembly.GetExecutingAssembly()).Run(args, config); + BenchmarkSwitcher.FromAssembly(Assembly.GetExecutingAssembly()).Run(args, ManualConfig.Create(DefaultConfig.Instance).With(ConfigOptions.DisableOptimizationsValidator)); } Console.ReadLine(); From b5d1021f077fc0252232bfa4f7418f0f01b2c5ae Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Tue, 27 Aug 2019 23:01:25 +0300 Subject: [PATCH 38/57] BaseSession.run: revamped validate and process feed_dict. --- src/TensorFlowNET.Core/Sessions/BaseSession.cs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/TensorFlowNET.Core/Sessions/BaseSession.cs b/src/TensorFlowNET.Core/Sessions/BaseSession.cs index 96c8a657..f81e1bf9 100644 --- a/src/TensorFlowNET.Core/Sessions/BaseSession.cs +++ b/src/TensorFlowNET.Core/Sessions/BaseSession.cs @@ -99,20 +99,15 @@ namespace Tensorflow var feed_dict_tensor = new Dictionary(); var feed_map = new Dictionary(); - Func> feed_fn = (item) => { return new (object, object)[] {(item.Key, item.Value)}; }; - // Validate and process feed_dict. - if (feed_dict != null) + if (feed_dict != null && feed_dict.Length > 0) { - foreach (var feed in feed_dict) + foreach (var subfeed in feed_dict) { - foreach (var (subfeed, subfeed_val) in feed_fn(feed)) - { - var subfeed_t = _graph.as_graph_element(subfeed, allow_tensor: true, allow_operation: false); - //var subfeed_dtype = subfeed_t.dtype.as_numpy_datatype(); // subfeed_dtype was never used - feed_dict_tensor[subfeed_t] = subfeed_val; - feed_map[subfeed_t.name] = (subfeed_t, subfeed_val); - } + var subfeed_t = _graph.as_graph_element(subfeed.Key, allow_tensor: true, allow_operation: false); + //var subfeed_dtype = subfeed_t.dtype.as_numpy_datatype(); // subfeed_dtype was never used + feed_dict_tensor[subfeed_t] = subfeed.Value; + feed_map[subfeed_t.name] = (subfeed_t, subfeed.Value); } } From fb7f4d42eb03cc3ebca7b0ddb9d506d0179b7566 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Tue, 27 Aug 2019 23:01:34 +0300 Subject: [PATCH 39/57] FeedItem: Added deconstructor --- src/TensorFlowNET.Core/Sessions/FeedItem.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/TensorFlowNET.Core/Sessions/FeedItem.cs b/src/TensorFlowNET.Core/Sessions/FeedItem.cs index f87457e7..c3a3dc67 100644 --- a/src/TensorFlowNET.Core/Sessions/FeedItem.cs +++ b/src/TensorFlowNET.Core/Sessions/FeedItem.cs @@ -16,5 +16,11 @@ public static implicit operator FeedItem((object, object) feed) => new FeedItem(feed.Item1, feed.Item2); + + public void Deconstruct(out object key, out object value) + { + key = Key; + value = Value; + } } } From 47e80cddd07a17679e105f1ad7e08921e71b8b8d Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Tue, 27 Aug 2019 23:02:55 +0300 Subject: [PATCH 40/57] Status: Changed exception of Check(bool) to TensorflowException --- src/TensorFlowNET.Core/Status/Status.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/TensorFlowNET.Core/Status/Status.cs b/src/TensorFlowNET.Core/Status/Status.cs index 7d029e79..ce561f75 100644 --- a/src/TensorFlowNET.Core/Status/Status.cs +++ b/src/TensorFlowNET.Core/Status/Status.cs @@ -16,6 +16,7 @@ using System; using System.Runtime.CompilerServices; +using static Tensorflow.c_api; namespace Tensorflow { @@ -28,27 +29,28 @@ namespace Tensorflow /// /// Error message /// - public string Message => c_api.StringPiece(c_api.TF_Message(_handle)); + public string Message => c_api.StringPiece(TF_Message(_handle)); /// /// Error code /// - public TF_Code Code => c_api.TF_GetCode(_handle); + public TF_Code Code => TF_GetCode(_handle); public Status() { - _handle = c_api.TF_NewStatus(); + _handle = TF_NewStatus(); } public void SetStatus(TF_Code code, string msg) { - c_api.TF_SetStatus(_handle, code, msg); + TF_SetStatus(_handle, code, msg); } /// /// Check status /// Throw exception with error message if code != TF_OK /// + /// When the returned check is not TF_Code.TF_OK [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Check(bool throwException = false) { @@ -56,9 +58,7 @@ namespace Tensorflow { Console.WriteLine(Message); if (throwException) - { - throw new Exception(Message); - } + throw new TensorflowException(Message); } } @@ -68,6 +68,6 @@ namespace Tensorflow } protected override void DisposeUnmanagedResources(IntPtr handle) - => c_api.TF_DeleteStatus(handle); + => TF_DeleteStatus(handle); } } \ No newline at end of file From a53d6ccda6303f57dcc62bf7de9573b28c311354 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Tue, 27 Aug 2019 23:03:46 +0300 Subject: [PATCH 41/57] Tensor.Creation: changed deallocatiors to readonly private --- src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs index 68d07312..61cfbef3 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs @@ -52,9 +52,9 @@ namespace Tensorflow private DeallocatorArgs _deallocatorArgs = new DeallocatorArgs() { gc_handle = IntPtr.Zero }; // note: they must be assigned to a static variable in order to work as unmanaged callbacks - static Deallocator _hGlobalDeallocator = FreeHGlobalMemory; - static Deallocator _gcHandleDeallocator = FreeGCHandle; - private static Deallocator _nothingDeallocator = FreeNothing; + private static readonly Deallocator _hGlobalDeallocator = FreeHGlobalMemory; + private static readonly Deallocator _gcHandleDeallocator = FreeGCHandle; + private static readonly Deallocator _nothingDeallocator = FreeNothing; /// /// Create a Tensor object from an existing TF handle @@ -624,7 +624,7 @@ namespace Tensorflow Marshal.WriteInt64(tensor, 0); var status = new Status(); - fixed (byte* src = &buffer[0]) + fixed (byte* src = buffer) c_api.TF_StringEncode(src, (UIntPtr)buffer.Length, (sbyte*)(tensor + sizeof(Int64)), size, status); status.Check(true); From 1e791f6750b951ba3abdc0acf2500831eaf3cfd9 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Tue, 27 Aug 2019 23:04:21 +0300 Subject: [PATCH 42/57] Tensor: Changed eval(Session session, FeedItem[] feed_dict = null) to eval(Session session, params FeedItem[] feed_dict) --- src/TensorFlowNET.Core/Tensors/Tensor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.cs b/src/TensorFlowNET.Core/Tensors/Tensor.cs index 8ac6c73e..91c8e984 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.cs @@ -442,7 +442,7 @@ namespace Tensorflow /// A dictionary that maps `Tensor` objects to feed values. /// The `Session` to be used to evaluate this tensor. /// A array corresponding to the value of this tensor. - public NDArray eval(Session session, FeedItem[] feed_dict = null) + public NDArray eval(Session session, params FeedItem[] feed_dict) { return ops._eval_using_default_session(this, feed_dict, graph, session); } From 3fecaf02b84d1a04c3024876ffdebd8ea6eb6866 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Tue, 27 Aug 2019 23:04:43 +0300 Subject: [PATCH 43/57] Tensor: Fixed redundant check and assignment in DisposeUnmanagedResources --- src/TensorFlowNET.Core/Tensors/Tensor.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.cs b/src/TensorFlowNET.Core/Tensors/Tensor.cs index 91c8e984..29492da6 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.cs @@ -568,11 +568,7 @@ namespace Tensorflow protected override void DisposeUnmanagedResources(IntPtr handle) { - if (handle != IntPtr.Zero) - { - c_api.TF_DeleteTensor(handle); - _handle = IntPtr.Zero; - } + c_api.TF_DeleteTensor(handle); } public bool IsDisposed From 4e7308d5483451067a31c31268c3380d5c2871da Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Tue, 27 Aug 2019 23:04:56 +0300 Subject: [PATCH 44/57] Tensor: revamp of IsDisposed --- src/TensorFlowNET.Core/Tensors/Tensor.cs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.cs b/src/TensorFlowNET.Core/Tensors/Tensor.cs index 29492da6..e9f90478 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.cs @@ -571,16 +571,7 @@ namespace Tensorflow c_api.TF_DeleteTensor(handle); } - public bool IsDisposed - { - get - { - lock (this) - { - return _handle == IntPtr.Zero; - } - } - } + public bool IsDisposed => _disposed; public int tensor_int_val { get; set; } } From a9798598aa6ca395d67ebb748ed6308cfebb8246 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Tue, 27 Aug 2019 23:09:47 +0300 Subject: [PATCH 45/57] Create UnmanagedExtensions.cs --- .../Util/UnmanagedExtensions.cs | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/TensorFlowNET.Core/Util/UnmanagedExtensions.cs diff --git a/src/TensorFlowNET.Core/Util/UnmanagedExtensions.cs b/src/TensorFlowNET.Core/Util/UnmanagedExtensions.cs new file mode 100644 index 00000000..02b8bb73 --- /dev/null +++ b/src/TensorFlowNET.Core/Util/UnmanagedExtensions.cs @@ -0,0 +1,94 @@ +using System; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using NumSharp.Backends.Unmanaged; + +namespace Tensorflow.Util +{ + public static class UnmanagedExtensions + { + //internally UnmanagedMemoryStream can't construct with null address. + private static readonly unsafe byte* _empty = (byte*) Marshal.AllocHGlobal(1); + + /// + /// Creates a memory stream based on given . + /// + /// The block to stream. Can be default/null. + /// There is no need to dispose the returned + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UnmanagedMemoryStream Stream(this UnmanagedMemoryBlock block) + { + unsafe + { + if (block.Address == null) + return new UnmanagedMemoryStream(_empty, 0); + return new UnmanagedMemoryStream(block.Address, block.BytesCount); + } + } + + /// + /// Creates a memory stream based on given . + /// + /// The block to stream. Can be default/null. + /// Offset from the start of the block. + /// There is no need to dispose the returned + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UnmanagedMemoryStream Stream(this UnmanagedMemoryBlock block, long offset) + { + if (block.BytesCount - offset <= 0) + throw new ArgumentOutOfRangeException(nameof(offset)); + + unsafe + { + if (block.Address == null) + return new UnmanagedMemoryStream(_empty, 0); + return new UnmanagedMemoryStream(block.Address + offset, block.BytesCount - offset); + } + } + + /// + /// Creates a memory stream based on given . + /// + /// The block to stream. Can be IntPtr.Zero. + /// The length of the block in bytes. + /// There is no need to dispose the returned + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UnmanagedMemoryStream Stream(this IntPtr address, long length) + { + if (length <= 0) + throw new ArgumentOutOfRangeException(nameof(length)); + + unsafe + { + if (address == IntPtr.Zero) + return new UnmanagedMemoryStream(_empty, 0); + + // ReSharper disable once AssignNullToNotNullAttribute + return new UnmanagedMemoryStream((byte*) address, length); + } + } + + /// + /// Creates a memory stream based on given . + /// + /// The block to stream. Can be IntPtr.Zero. + /// Offset from the start of the block. + /// The length of the block in bytes. + /// There is no need to dispose the returned + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UnmanagedMemoryStream Stream(this IntPtr address, long offset, long length) + { + if (length <= 0) + throw new ArgumentOutOfRangeException(nameof(length)); + + unsafe + { + if (address == IntPtr.Zero) + return new UnmanagedMemoryStream(_empty, 0); + + return new UnmanagedMemoryStream((byte*) address + offset, length); + } + } + } +} \ No newline at end of file From 4a9a2c6c952e1a5d5662d552219e7ff0fd0c342b Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Tue, 27 Aug 2019 23:32:49 +0300 Subject: [PATCH 46/57] Tensor.ToArray: Minor change --- src/TensorFlowNET.Core/Tensors/Tensor.cs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.cs b/src/TensorFlowNET.Core/Tensors/Tensor.cs index e9f90478..aaf8ddad 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.cs @@ -246,20 +246,12 @@ namespace Tensorflow unsafe { var len = (long) size; - fixed (T* dstRet = ret) + fixed (T* dst = ret) { - T* dst = dstRet; //local stack copy - if (typeof(T).IsPrimitive) - { - var src = (T*) buffer; - len *= ((long) itemsize); - System.Buffer.MemoryCopy(src, dst, len, len); - } else - { - var itemsize = (long) this.itemsize; - var buffer = this.buffer.ToInt64(); - Parallel.For(0L, len, i => dst[i] = Marshal.PtrToStructure(new IntPtr(buffer + i * itemsize))); - } + //T can only be unmanaged, I believe it is safe to say that MemoryCopy is valid for all cases this method can be called. + var src = (T*) buffer; + len *= ((long) itemsize); + System.Buffer.MemoryCopy(src, dst, len, len); } } From bd18f5db17c7586d1be4d7eef18a214eeaf866ff Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Tue, 27 Aug 2019 23:36:10 +0300 Subject: [PATCH 47/57] Removed all use-cases of Marshal.PtrToStructure in favor of unsafe ptrs. --- src/TensorFlowNET.Core/DisposableObject.cs | 1 - src/TensorFlowNET.Core/Graphs/Graph.Import.cs | 9 ++++----- src/TensorFlowNET.Core/Graphs/Graph.Operation.cs | 10 +++++++--- src/TensorFlowNET.Core/Graphs/Graph.cs | 12 ++++++------ .../Operations/Operation.Output.cs | 8 +++----- .../Sessions/c_api.tf_session_helper.cs | 12 ++++++++---- 6 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/TensorFlowNET.Core/DisposableObject.cs b/src/TensorFlowNET.Core/DisposableObject.cs index c9bf8875..53a15abc 100644 --- a/src/TensorFlowNET.Core/DisposableObject.cs +++ b/src/TensorFlowNET.Core/DisposableObject.cs @@ -54,7 +54,6 @@ namespace Tensorflow if (_handle != IntPtr.Zero) { DisposeUnmanagedResources(_handle); - _handle = IntPtr.Zero; } } diff --git a/src/TensorFlowNET.Core/Graphs/Graph.Import.cs b/src/TensorFlowNET.Core/Graphs/Graph.Import.cs index 82695527..0b2dc0e6 100644 --- a/src/TensorFlowNET.Core/Graphs/Graph.Import.cs +++ b/src/TensorFlowNET.Core/Graphs/Graph.Import.cs @@ -30,11 +30,10 @@ namespace Tensorflow var return_output_handle = Marshal.AllocHGlobal(size * num_return_outputs); c_api.TF_GraphImportGraphDefWithReturnOutputs(_handle, graph_def, opts, return_output_handle, num_return_outputs, s); - for (int i = 0; i < num_return_outputs; i++) - { - var handle = return_output_handle + i * size; - return_outputs[i] = Marshal.PtrToStructure(handle); - } + + var tf_output_ptr = (TF_Output*) return_output_handle; + for (int i = 0; i < num_return_outputs; i++) + return_outputs[i] = *(tf_output_ptr + i); Marshal.FreeHGlobal(return_output_handle); diff --git a/src/TensorFlowNET.Core/Graphs/Graph.Operation.cs b/src/TensorFlowNET.Core/Graphs/Graph.Operation.cs index 436afcc9..ded3ca9c 100644 --- a/src/TensorFlowNET.Core/Graphs/Graph.Operation.cs +++ b/src/TensorFlowNET.Core/Graphs/Graph.Operation.cs @@ -39,16 +39,20 @@ namespace Tensorflow return c_api.TF_NewOperation(_handle, opType, opName); } - public unsafe Operation[] ReturnOperations(IntPtr results) + public Operation[] ReturnOperations(IntPtr results) { TF_Operation return_oper_handle = new TF_Operation(); int num_return_opers = 0; c_api.TF_ImportGraphDefResultsReturnOperations(results, ref num_return_opers, ref return_oper_handle); Operation[] return_opers = new Operation[num_return_opers]; + var tf_op_size = Marshal.SizeOf(); for (int i = 0; i < num_return_opers; i++) { - var handle = return_oper_handle.node + Marshal.SizeOf() * i; - return_opers[i] = new Operation(*(IntPtr*)handle); + unsafe + { + var handle = return_oper_handle.node + tf_op_size * i; + return_opers[i] = new Operation(*(IntPtr*)handle); + } } return return_opers; diff --git a/src/TensorFlowNET.Core/Graphs/Graph.cs b/src/TensorFlowNET.Core/Graphs/Graph.cs index 38c35385..0dfb68db 100644 --- a/src/TensorFlowNET.Core/Graphs/Graph.cs +++ b/src/TensorFlowNET.Core/Graphs/Graph.cs @@ -369,7 +369,7 @@ namespace Tensorflow var name_key = name.ToLower(); int i = 0; if (_names_in_use.ContainsKey(name_key)) - i = _names_in_use[name_key]; + i = _names_in_use[name_key]; // Increment the number for "name_key". if (mark_as_used) _names_in_use[name_key] = i + 1; @@ -399,13 +399,13 @@ namespace Tensorflow int num_return_outputs = 0; c_api.TF_ImportGraphDefResultsReturnOutputs(results, ref num_return_outputs, ref return_output_handle); TF_Output[] return_outputs = new TF_Output[num_return_outputs]; - for (int i = 0; i < num_return_outputs; i++) + unsafe { - var handle = return_output_handle + (Marshal.SizeOf() * i); - return_outputs[i] = Marshal.PtrToStructure(handle); + var tf_output_ptr = (TF_Output*) return_output_handle; + for (int i = 0; i < num_return_outputs; i++) + return_outputs[i] = *(tf_output_ptr + i); + return return_outputs; } - - return return_outputs; } public string[] get_all_collection_keys() diff --git a/src/TensorFlowNET.Core/Operations/Operation.Output.cs b/src/TensorFlowNET.Core/Operations/Operation.Output.cs index 24348322..62c8f378 100644 --- a/src/TensorFlowNET.Core/Operations/Operation.Output.cs +++ b/src/TensorFlowNET.Core/Operations/Operation.Output.cs @@ -50,14 +50,12 @@ namespace Tensorflow public unsafe TF_Input[] OutputConsumers(int index, int max_consumers) { - int size = Marshal.SizeOf(); - var handle = Marshal.AllocHGlobal(size); + var handle = Marshal.AllocHGlobal(Marshal.SizeOf()); int num = c_api.TF_OperationOutputConsumers(new TF_Output(_handle, index), handle, max_consumers); var consumers = new TF_Input[num]; + var inputptr = (TF_Input*) handle; for (int i = 0; i < num; i++) - { - consumers[i] = Marshal.PtrToStructure(handle + i * size); - } + consumers[i] = *(inputptr + i); return consumers; } diff --git a/src/TensorFlowNET.Core/Sessions/c_api.tf_session_helper.cs b/src/TensorFlowNET.Core/Sessions/c_api.tf_session_helper.cs index c40b2a00..6cbf4eec 100644 --- a/src/TensorFlowNET.Core/Sessions/c_api.tf_session_helper.cs +++ b/src/TensorFlowNET.Core/Sessions/c_api.tf_session_helper.cs @@ -27,13 +27,17 @@ namespace Tensorflow var handle = Marshal.AllocHGlobal(size * num_consumers); int num = TF_OperationOutputConsumers(oper_out, handle, num_consumers); var consumers = new string[num_consumers]; - for (int i = 0; i < num; i++) + unsafe { - TF_Input input = Marshal.PtrToStructure(handle + i * size); - consumers[i] = Marshal.PtrToStringAnsi(TF_OperationName(input.oper)); + var inputptr = (TF_Input*) handle; + for (int i = 0; i < num; i++) + { + var oper = (inputptr + i)->oper; + consumers[i] = Marshal.PtrToStringAnsi(TF_OperationName(oper)); + } } return consumers; } } -} +} \ No newline at end of file From 7e46d4fa937b2e5db7e8027b18df061ea19afbf8 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Tue, 27 Aug 2019 23:37:04 +0300 Subject: [PATCH 48/57] CSession: Revmoed Dispose of Input and Ouput values in DeleteInputValues, ResetOutputValues They are managed by GC. --- test/TensorFlowNET.UnitTest/CSession.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/test/TensorFlowNET.UnitTest/CSession.cs b/test/TensorFlowNET.UnitTest/CSession.cs index 33e88286..ae57b075 100644 --- a/test/TensorFlowNET.UnitTest/CSession.cs +++ b/test/TensorFlowNET.UnitTest/CSession.cs @@ -40,10 +40,7 @@ namespace TensorFlowNET.UnitTest private void DeleteInputValues() { - for (var i = 0; i < input_values_.Count; ++i) - { - input_values_[i].Dispose(); - } + //clearing is enough as they will be disposed by the GC unless they are referenced else-where. input_values_.Clear(); } @@ -60,11 +57,7 @@ namespace TensorFlowNET.UnitTest private void ResetOutputValues() { - for (var i = 0; i < output_values_.Count; ++i) - { - if (output_values_[i] != IntPtr.Zero) - output_values_[i].Dispose(); - } + //clearing is enough as they will be disposed by the GC unless they are referenced else-where. output_values_.Clear(); } From 9d6525ef9f85da8685ab2fbb6c0c091f6596d699 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Tue, 27 Aug 2019 23:59:40 +0300 Subject: [PATCH 49/57] Buffer: Revamped and all perf-optted all use-cases. - Fixed all test cases to use using(Buffer) - Fixed all test cases to explicitly specify session --- src/TensorFlowNET.Core/Buffers/Buffer.cs | 102 ++++++++++++++---- .../Framework/op_def_registry.py.cs | 14 +-- .../Graphs/Graph.Control.cs | 4 +- src/TensorFlowNET.Core/Graphs/Graph.Export.cs | 23 ++-- .../Graphs/Graph.Operation.cs | 7 +- .../Operations/Operation.cs | 19 ++-- .../Sessions/SessionOptions.cs | 4 +- src/TensorFlowNET.Core/ops.cs | 4 +- src/TensorFlowNET.Core/tensorflow.cs | 3 +- .../BasicModels/LogisticRegression.cs | 6 +- .../Basics/AssignTests.cs | 26 ++--- .../CApiGradientsTest.cs | 22 ++-- test/TensorFlowNET.UnitTest/GraphTest.cs | 5 - test/TensorFlowNET.UnitTest/NameScopeTest.cs | 37 ++++++- test/TensorFlowNET.UnitTest/OperationsTest.cs | 3 +- test/TensorFlowNET.UnitTest/PythonTest.cs | 2 +- test/TensorFlowNET.UnitTest/SessionTest.cs | 4 +- test/TensorFlowNET.UnitTest/VariableTest.cs | 2 +- test/TensorFlowNET.UnitTest/c_test_util.cs | 17 +-- .../ops_test/CreateOpFromTfOperationTest.cs | 21 ++-- 20 files changed, 216 insertions(+), 109 deletions(-) diff --git a/src/TensorFlowNET.Core/Buffers/Buffer.cs b/src/TensorFlowNET.Core/Buffers/Buffer.cs index 396fb311..c08d3175 100644 --- a/src/TensorFlowNET.Core/Buffers/Buffer.cs +++ b/src/TensorFlowNET.Core/Buffers/Buffer.cs @@ -15,58 +15,116 @@ ******************************************************************************/ using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using NumSharp.Backends.Unmanaged; +using static Tensorflow.c_api; namespace Tensorflow { + /// + /// Represents a TF_Buffer that can be passed to Tensorflow. + /// public class Buffer : DisposableObject { - private TF_Buffer buffer => Marshal.PtrToStructure(_handle); + private unsafe TF_Buffer buffer + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => *bufferptr; + } + + private unsafe TF_Buffer* bufferptr + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (TF_Buffer*) _handle; + } - public byte[] Data + /// + /// The memory block representing this buffer. + /// + /// The deallocator is set to null. + public UnmanagedMemoryBlock MemoryBlock { - get + get { - var data = new byte[buffer.length]; - if (data.Length > 0) - Marshal.Copy(buffer.data, data, 0, data.Length); - return data; + unsafe + { + EnsureNotDisposed(); + var buff = (TF_Buffer*) _handle; + return new UnmanagedMemoryBlock((byte*) buff->data.ToPointer(), (long) buff->length); + } } } - public int Length => (int)buffer.length; - - public Buffer() + /// + /// The bytes length of this buffer. + /// + public ulong Length { - _handle = c_api.TF_NewBuffer(); + get + { + EnsureNotDisposed(); + return buffer.length; + } } - public Buffer(IntPtr handle) + public Buffer() => _handle = TF_NewBuffer(); + + internal Buffer(IntPtr handle) { + if (handle == IntPtr.Zero) + throw new ArgumentException("Handle (IntPtr) can't be zero.", nameof(handle)); + _handle = handle; } - public Buffer(byte[] data) - { - var dst = Marshal.AllocHGlobal(data.Length); - Marshal.Copy(data, 0, dst, data.Length); + public Buffer(byte[] data) : this(_toBuffer(data)) + { } - _handle = c_api.TF_NewBufferFromString(dst, (ulong)data.Length); + private static IntPtr _toBuffer(byte[] data) + { + if (data == null) + throw new ArgumentNullException(nameof(data)); - Marshal.FreeHGlobal(dst); + unsafe + { + fixed (byte* src = data) + return TF_NewBufferFromString(new IntPtr(src), (ulong) data.LongLength); + } } public static implicit operator IntPtr(Buffer buffer) { + buffer.EnsureNotDisposed(); return buffer._handle; } - public static implicit operator byte[](Buffer buffer) + public static explicit operator byte[](Buffer buffer) => buffer.ToArray(); //has to be explicit, developer will assume it doesn't cost. + + /// + /// Copies this buffer's contents onto a array. + /// + public byte[] ToArray() { - return buffer.Data; + EnsureNotDisposed(); + + unsafe + { + var len = buffer.length; + if (len == 0) + return Array.Empty(); + + byte[] data = new byte[len]; + fixed (byte* dst = data) + System.Buffer.MemoryCopy((void*) bufferptr->data, dst, len, len); + + return data; + } } protected override void DisposeUnmanagedResources(IntPtr handle) - => c_api.TF_DeleteBuffer(handle); + { + TF_DeleteBuffer(handle); + } } -} +} \ No newline at end of file diff --git a/src/TensorFlowNET.Core/Framework/op_def_registry.py.cs b/src/TensorFlowNET.Core/Framework/op_def_registry.py.cs index 9f9b4ad7..8a2bc5c3 100644 --- a/src/TensorFlowNET.Core/Framework/op_def_registry.py.cs +++ b/src/TensorFlowNET.Core/Framework/op_def_registry.py.cs @@ -15,6 +15,8 @@ ******************************************************************************/ using System.Collections.Generic; +using System.IO; +using Tensorflow.Util; namespace Tensorflow { @@ -27,12 +29,12 @@ namespace Tensorflow if(_registered_ops == null) { _registered_ops = new Dictionary(); - var handle = c_api.TF_GetAllOpList(); - var buffer = new Buffer(handle); - var op_list = OpList.Parser.ParseFrom(buffer); - - foreach (var op_def in op_list.Op) - _registered_ops[op_def.Name] = op_def; + using (var buffer = new Buffer(c_api.TF_GetAllOpList())) + { + var op_list = OpList.Parser.ParseFrom(buffer.MemoryBlock.Stream()); + foreach (var op_def in op_list.Op) + _registered_ops[op_def.Name] = op_def; + } } return _registered_ops; diff --git a/src/TensorFlowNET.Core/Graphs/Graph.Control.cs b/src/TensorFlowNET.Core/Graphs/Graph.Control.cs index 4a3ac793..c97e1b6f 100644 --- a/src/TensorFlowNET.Core/Graphs/Graph.Control.cs +++ b/src/TensorFlowNET.Core/Graphs/Graph.Control.cs @@ -15,6 +15,7 @@ ******************************************************************************/ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Tensorflow.Operations; @@ -66,8 +67,9 @@ namespace Tensorflow /// within the context should have control dependencies on /// `control_inputs`. /// + [SuppressMessage("ReSharper", "CoVariantArrayConversion")] public _ControlDependenciesController control_dependencies(ITensorOrOperation[] control_inputs) - => control_dependencies(control_inputs == null ? null : control_inputs.OfType().ToArray()); + => control_dependencies((object[])control_inputs); /// /// Returns a context manager that specifies control dependencies. diff --git a/src/TensorFlowNET.Core/Graphs/Graph.Export.cs b/src/TensorFlowNET.Core/Graphs/Graph.Export.cs index 17828c73..4a7e0ed8 100644 --- a/src/TensorFlowNET.Core/Graphs/Graph.Export.cs +++ b/src/TensorFlowNET.Core/Graphs/Graph.Export.cs @@ -14,6 +14,9 @@ limitations under the License. ******************************************************************************/ +using System.IO; +using Tensorflow.Util; + namespace Tensorflow { public partial class Graph @@ -23,21 +26,19 @@ namespace Tensorflow var buffer = new Buffer(); c_api.TF_GraphToGraphDef(_handle, buffer, s); s.Check(true); - // var def = GraphDef.Parser.ParseFrom(buffer); - // buffer.Dispose(); return buffer; } private GraphDef _as_graph_def(bool add_shapes = false) { - var status = new Status(); - var buffer = ToGraphDef(status); - status.Check(true); - status.Dispose(); - - var def = GraphDef.Parser.ParseFrom(buffer); - buffer.Dispose(); + GraphDef def; + using (var status = new Status()) + using (var buffer = ToGraphDef(status)) + { + status.Check(true); + def = GraphDef.Parser.ParseFrom(buffer.MemoryBlock.Stream()); + } // Strip the experimental library field iff it's empty. // if(def.Library.Function.Count == 0) @@ -45,7 +46,7 @@ namespace Tensorflow return def; } - public GraphDef as_graph_def(bool add_shapes = false) + public GraphDef as_graph_def(bool add_shapes = false) => _as_graph_def(add_shapes); } -} +} \ No newline at end of file diff --git a/src/TensorFlowNET.Core/Graphs/Graph.Operation.cs b/src/TensorFlowNET.Core/Graphs/Graph.Operation.cs index ded3ca9c..0e28dd9a 100644 --- a/src/TensorFlowNET.Core/Graphs/Graph.Operation.cs +++ b/src/TensorFlowNET.Core/Graphs/Graph.Operation.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; +using Tensorflow.Util; using static Tensorflow.Binding; namespace Tensorflow @@ -30,7 +31,7 @@ namespace Tensorflow using (var status = new Status()) { c_api.TF_GraphGetOpDef(_handle, type, buffer, status); - return OpDef.Parser.ParseFrom(buffer.Data); + return OpDef.Parser.ParseFrom(buffer.MemoryBlock.Stream()); } } @@ -71,7 +72,7 @@ namespace Tensorflow public ITensorOrOperation[] get_operations() { - return _nodes_by_name.Values.Select(x => x).ToArray(); + return _nodes_by_name.Values.ToArray(); } /// @@ -85,7 +86,7 @@ namespace Tensorflow public ITensorOrOperation _get_operation_by_name_unsafe(string name) { - return _nodes_by_name.ContainsKey(name) ? _nodes_by_name[name] : null; + return _nodes_by_name.TryGetValue(name, out var val) ? val : null; } public ITensorOrOperation _get_operation_by_tf_operation(IntPtr tf_oper) diff --git a/src/TensorFlowNET.Core/Operations/Operation.cs b/src/TensorFlowNET.Core/Operations/Operation.cs index 059290f4..5fff9ade 100644 --- a/src/TensorFlowNET.Core/Operations/Operation.cs +++ b/src/TensorFlowNET.Core/Operations/Operation.cs @@ -17,7 +17,9 @@ using Google.Protobuf.Collections; using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using Tensorflow.Util; namespace Tensorflow { @@ -226,9 +228,12 @@ namespace Tensorflow using (var status = new Status()) using (var buf = new Buffer()) { - c_api.TF_OperationGetAttrValueProto(_handle, name, buf, status); - status.Check(true); - x = AttrValue.Parser.ParseFrom(buf); + unsafe + { + c_api.TF_OperationGetAttrValueProto(_handle, name, buf, status); + status.Check(true); + x = AttrValue.Parser.ParseFrom(buf.MemoryBlock.Stream()); + } } string oneof_value = x.ValueCase.ToString(); @@ -259,7 +264,7 @@ namespace Tensorflow { c_api.TF_OperationToNodeDef(_handle, buffer, s); s.Check(); - return NodeDef.Parser.ParseFrom(buffer); + return NodeDef.Parser.ParseFrom(buffer.MemoryBlock.Stream()); } } @@ -299,8 +304,7 @@ namespace Tensorflow /// public TF_Output _tf_output(int output_idx) { - var tf_output = new TF_Output(op, output_idx); - return tf_output; + return new TF_Output(op, output_idx); } /// @@ -308,8 +312,7 @@ namespace Tensorflow /// public TF_Input _tf_input(int input_idx) { - var tf_input = new TF_Input(op, input_idx); - return tf_input; + return new TF_Input(op, input_idx); } } } diff --git a/src/TensorFlowNET.Core/Sessions/SessionOptions.cs b/src/TensorFlowNET.Core/Sessions/SessionOptions.cs index ed99b7fe..112543fe 100644 --- a/src/TensorFlowNET.Core/Sessions/SessionOptions.cs +++ b/src/TensorFlowNET.Core/Sessions/SessionOptions.cs @@ -37,8 +37,8 @@ namespace Tensorflow public void SetConfig(ConfigProto config) { - var bytes = config.ToByteArray(); - var proto = Marshal.AllocHGlobal(bytes.Length); + var bytes = config.ToByteArray(); //TODO! we can use WriteTo + var proto = Marshal.AllocHGlobal(bytes.Length); //TODO! potential memory leak Marshal.Copy(bytes, 0, proto, bytes.Length); using (var status = new Status()) diff --git a/src/TensorFlowNET.Core/ops.cs b/src/TensorFlowNET.Core/ops.cs index e485ba6f..1dc8eb56 100644 --- a/src/TensorFlowNET.Core/ops.cs +++ b/src/TensorFlowNET.Core/ops.cs @@ -230,8 +230,8 @@ namespace Tensorflow // Add attrs foreach (var attr in node_def.Attr) { - var bytes = attr.Value.ToByteArray(); - var proto = Marshal.AllocHGlobal(bytes.Length); + var bytes = attr.Value.ToByteArray(); //TODO: we can use attr.Value.WriteTo with a memory stream. + var proto = Marshal.AllocHGlobal(bytes.Length); //TODO: potential memory leak Marshal.Copy(bytes, 0, proto, bytes.Length); uint len = (uint)bytes.Length; c_api.TF_SetAttrValueProto(op_desc, attr.Key, proto, proto_len: len, status: status); diff --git a/src/TensorFlowNET.Core/tensorflow.cs b/src/TensorFlowNET.Core/tensorflow.cs index da873722..ac6c38dc 100644 --- a/src/TensorFlowNET.Core/tensorflow.cs +++ b/src/TensorFlowNET.Core/tensorflow.cs @@ -64,8 +64,7 @@ namespace Tensorflow public Session Session() { - defaultSession = new Session(); - return defaultSession; + return new Session(); } public Session Session(Graph graph) diff --git a/test/TensorFlowNET.Examples/BasicModels/LogisticRegression.cs b/test/TensorFlowNET.Examples/BasicModels/LogisticRegression.cs index 73d40d28..3116e6f4 100644 --- a/test/TensorFlowNET.Examples/BasicModels/LogisticRegression.cs +++ b/test/TensorFlowNET.Examples/BasicModels/LogisticRegression.cs @@ -102,7 +102,7 @@ namespace TensorFlowNET.Examples // Display logs per epoch step if ((epoch + 1) % display_step == 0) - print($"Epoch: {(epoch + 1).ToString("D4")} Cost: {avg_cost.ToString("G9")} Elapse: {sw.ElapsedMilliseconds}ms"); + print($"Epoch: {(epoch + 1):D4} Cost: {avg_cost:G9} Elapse: {sw.ElapsedMilliseconds}ms"); sw.Reset(); } @@ -114,8 +114,8 @@ namespace TensorFlowNET.Examples var correct_prediction = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1)); // Calculate accuracy var accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)); - float acc = accuracy.eval((x, mnist.Test.Data), (y, mnist.Test.Labels)); - print($"Accuracy: {acc.ToString("F4")}"); + float acc = accuracy.eval(sess, (x, mnist.Test.Data), (y, mnist.Test.Labels)); + print($"Accuracy: {acc:F4}"); return acc > 0.9; } diff --git a/test/TensorFlowNET.UnitTest/Basics/AssignTests.cs b/test/TensorFlowNET.UnitTest/Basics/AssignTests.cs index 15d9b819..6c593929 100644 --- a/test/TensorFlowNET.UnitTest/Basics/AssignTests.cs +++ b/test/TensorFlowNET.UnitTest/Basics/AssignTests.cs @@ -1,4 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using Tensorflow; using static Tensorflow.Binding; namespace TensorFlowNET.UnitTest.Basics @@ -14,21 +15,22 @@ namespace TensorFlowNET.UnitTest.Basics var expected = new[] { false, true, false, false, true, false, true }; var spike = tf.Variable(false); - - spike.initializer.run(); - foreach (var i in range(1, 2)) + using (var sess = new Session()) { - if (raw_data[i] - raw_data[i - 1] > 5d) - { - var updater = tf.assign(spike, tf.constant(true)); - updater.eval(); - } - else + spike.initializer.run(session: sess); + foreach (var i in range(1, 2)) { - tf.assign(spike, tf.constant(true)).eval(); - } + if (raw_data[i] - raw_data[i - 1] > 5d) + { + var updater = tf.assign(spike, tf.constant(true)); + updater.eval(sess); + } else + { + tf.assign(spike, tf.constant(true)).eval(sess); + } - Assert.AreEqual((bool)spike.eval(), expected[i - 1]); + Assert.AreEqual((bool) spike.eval(), expected[i - 1]); + } } } } diff --git a/test/TensorFlowNET.UnitTest/CApiGradientsTest.cs b/test/TensorFlowNET.UnitTest/CApiGradientsTest.cs index 33e38870..58609c17 100644 --- a/test/TensorFlowNET.UnitTest/CApiGradientsTest.cs +++ b/test/TensorFlowNET.UnitTest/CApiGradientsTest.cs @@ -2,6 +2,7 @@ using NumSharp; using System; using Tensorflow; +using Tensorflow.Util; using Buffer = Tensorflow.Buffer; namespace TensorFlowNET.UnitTest @@ -45,15 +46,18 @@ namespace TensorFlowNET.UnitTest private bool GetGraphDef(Graph graph, out GraphDef graph_def) { graph_def = null; - var s = new Status(); - var buffer = new Buffer(); - c_api.TF_GraphToGraphDef(graph, buffer, s); - bool ret = TF_GetCode(s) == TF_OK; - EXPECT_EQ(TF_OK, TF_GetCode(s)); - if (ret) graph_def = GraphDef.Parser.ParseFrom(buffer.Data); - buffer.Dispose(); - s.Dispose(); - return ret; + using (var s = new Status()) + { + using (var buffer = new Buffer()) + { + c_api.TF_GraphToGraphDef(graph, buffer, s); + bool ret = TF_GetCode(s) == TF_OK; + EXPECT_EQ(TF_OK, TF_GetCode(s)); + if (ret) + graph_def = GraphDef.Parser.ParseFrom(buffer.MemoryBlock.Stream()); + return ret; + } + } } private void RunGraphsAndCompareOutputs(TF_Output[] grad_outputs, TF_Output[] expected_grad_outputs) diff --git a/test/TensorFlowNET.UnitTest/GraphTest.cs b/test/TensorFlowNET.UnitTest/GraphTest.cs index f5431e01..94da6d97 100644 --- a/test/TensorFlowNET.UnitTest/GraphTest.cs +++ b/test/TensorFlowNET.UnitTest/GraphTest.cs @@ -322,7 +322,6 @@ namespace TensorFlowNET.UnitTest EXPECT_EQ(feed2, control_inputs[1]); // Export to a graph def so we can import a graph with control dependencies - graph_def.Dispose(); graph_def = new Buffer(); c_api.TF_GraphToGraphDef(graph, graph_def, s); EXPECT_EQ(TF_Code.TF_OK, s.Code); @@ -346,14 +345,10 @@ namespace TensorFlowNET.UnitTest EXPECT_EQ(feed4, control_inputs[1]); c_api.TF_DeleteImportGraphDefOptions(opts); - c_api.TF_DeleteBuffer(graph_def); // Can add nodes to the imported graph without trouble. c_test_util.Add(feed, scalar, graph, s); ASSERT_EQ(TF_Code.TF_OK, s.Code); - - graph.Dispose(); - s.Dispose(); } /// diff --git a/test/TensorFlowNET.UnitTest/NameScopeTest.cs b/test/TensorFlowNET.UnitTest/NameScopeTest.cs index 4ff50deb..3d763b38 100644 --- a/test/TensorFlowNET.UnitTest/NameScopeTest.cs +++ b/test/TensorFlowNET.UnitTest/NameScopeTest.cs @@ -1,4 +1,5 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Tensorflow; using static Tensorflow.Binding; @@ -42,5 +43,39 @@ namespace TensorFlowNET.UnitTest Assert.AreEqual("", g._name_stack); } + + [TestMethod] + public void NestedNameScope_Using() + { + Graph g = tf.Graph().as_default(); + + using (var name = new ops.NameScope("scope1")) + { + Assert.AreEqual("scope1", g._name_stack); + Assert.AreEqual("scope1/", name); + + var const1 = tf.constant(1.0); + Assert.AreEqual("scope1/Const:0", const1.name); + + using (var name2 = new ops.NameScope("scope2")) + { + Assert.AreEqual("scope1/scope2", g._name_stack); + Assert.AreEqual("scope1/scope2/", name); + + var const2 = tf.constant(2.0); + Assert.AreEqual("scope1/scope2/Const:0", const2.name); + } + + Assert.AreEqual("scope1", g._name_stack); + var const3 = tf.constant(2.0); + Assert.AreEqual("scope1/Const_1:0", const3.name); + } + + ; + + g.Dispose(); + + Assert.AreEqual("", g._name_stack); + } } } diff --git a/test/TensorFlowNET.UnitTest/OperationsTest.cs b/test/TensorFlowNET.UnitTest/OperationsTest.cs index 0caa5259..226a4839 100644 --- a/test/TensorFlowNET.UnitTest/OperationsTest.cs +++ b/test/TensorFlowNET.UnitTest/OperationsTest.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using NumSharp; using Tensorflow; +using Tensorflow.Util; using Buffer = Tensorflow.Buffer; using static Tensorflow.Binding; @@ -21,7 +22,7 @@ namespace TensorFlowNET.UnitTest { var handle = c_api.TF_GetAllOpList(); var buffer = new Buffer(handle); - var op_list = OpList.Parser.ParseFrom(buffer); + var op_list = OpList.Parser.ParseFrom(buffer.MemoryBlock.Stream()); var _registered_ops = new Dictionary(); foreach (var op_def in op_list.Op) diff --git a/test/TensorFlowNET.UnitTest/PythonTest.cs b/test/TensorFlowNET.UnitTest/PythonTest.cs index 701b4b4b..d2ae36d7 100644 --- a/test/TensorFlowNET.UnitTest/PythonTest.cs +++ b/test/TensorFlowNET.UnitTest/PythonTest.cs @@ -165,7 +165,7 @@ namespace TensorFlowNET.UnitTest { using (var sess = tf.Session()) { - var ndarray=tensor.eval(); + var ndarray=tensor.eval(sess); if (typeof(T) == typeof(double)) { double x = ndarray; diff --git a/test/TensorFlowNET.UnitTest/SessionTest.cs b/test/TensorFlowNET.UnitTest/SessionTest.cs index 8fd4dc8a..62d7c63d 100644 --- a/test/TensorFlowNET.UnitTest/SessionTest.cs +++ b/test/TensorFlowNET.UnitTest/SessionTest.cs @@ -72,8 +72,6 @@ namespace TensorFlowNET.UnitTest // Clean up csession.CloseAndDelete(s); ASSERT_EQ(TF_Code.TF_OK, s.Code); - graph.Dispose(); - s.Dispose(); } [TestMethod] @@ -84,7 +82,7 @@ namespace TensorFlowNET.UnitTest var c = math_ops.matmul(a, b, name: "matmul"); using (var sess = tf.Session()) { - var result = c.eval(); + var result = c.eval(sess); Assert.AreEqual(6, result.Data()[0]); } } diff --git a/test/TensorFlowNET.UnitTest/VariableTest.cs b/test/TensorFlowNET.UnitTest/VariableTest.cs index 211d7b65..4d9d1059 100644 --- a/test/TensorFlowNET.UnitTest/VariableTest.cs +++ b/test/TensorFlowNET.UnitTest/VariableTest.cs @@ -119,7 +119,7 @@ namespace TensorFlowNET.UnitTest { sess.run(init_op); // o some work with the model. - inc_v1.op.run(); + inc_v1.op.run(session: sess); } } diff --git a/test/TensorFlowNET.UnitTest/c_test_util.cs b/test/TensorFlowNET.UnitTest/c_test_util.cs index 1b6909e7..627d7c2f 100644 --- a/test/TensorFlowNET.UnitTest/c_test_util.cs +++ b/test/TensorFlowNET.UnitTest/c_test_util.cs @@ -1,4 +1,6 @@ -using Tensorflow; +using System.Diagnostics.CodeAnalysis; +using Tensorflow; +using Tensorflow.Util; using Buffer = Tensorflow.Buffer; namespace TensorFlowNET.UnitTest @@ -26,12 +28,15 @@ namespace TensorFlowNET.UnitTest return op; } + [SuppressMessage("ReSharper", "RedundantAssignment")] public static bool GetAttrValue(Operation oper, string attr_name, ref AttrValue attr_value, Status s) { - var buffer = new Buffer(); - c_api.TF_OperationGetAttrValueProto(oper, attr_name, buffer, s); - attr_value = AttrValue.Parser.ParseFrom(buffer); - buffer.Dispose(); + using (var buffer = new Buffer()) + { + c_api.TF_OperationGetAttrValueProto(oper, attr_name, buffer, s); + attr_value = AttrValue.Parser.ParseFrom(buffer.MemoryBlock.Stream()); + } + return s.Code == TF_Code.TF_OK; } @@ -42,7 +47,7 @@ namespace TensorFlowNET.UnitTest { c_api.TF_GraphToGraphDef(graph, buffer, s); s.Check(); - return GraphDef.Parser.ParseFrom(buffer); + return GraphDef.Parser.ParseFrom(buffer.MemoryBlock.Stream()); } } diff --git a/test/TensorFlowNET.UnitTest/ops_test/CreateOpFromTfOperationTest.cs b/test/TensorFlowNET.UnitTest/ops_test/CreateOpFromTfOperationTest.cs index cdbd5f14..310ac634 100644 --- a/test/TensorFlowNET.UnitTest/ops_test/CreateOpFromTfOperationTest.cs +++ b/test/TensorFlowNET.UnitTest/ops_test/CreateOpFromTfOperationTest.cs @@ -24,16 +24,17 @@ namespace TensorFlowNET.UnitTest.ops_test [TestMethod] public void TestShape() { - var g = tf.Graph().as_default(); - - var x = constant_op.constant(new[,] { { 1, 2, 3 }, { 4, 5, 6 } }); - var (c_op, op_desc) = ops._create_c_op(g, ops._NodeDef("Identity", "myop"), new[] { x }, new Operation[0]); - var op = g._create_op_from_tf_operation(c_op); - - Assert.AreEqual("myop", op.name); - Assert.AreEqual("Identity", op.type); - Assert.AreEqual(1, len(op.outputs)); - assertItemsEqual(new[] { 2, 3 }, op.outputs[0].shape); + using (var g = tf.Graph().as_default()) + { + var x = constant_op.constant(new[,] {{1, 2, 3}, {4, 5, 6}}); + var (c_op, op_desc) = ops._create_c_op(g, ops._NodeDef("Identity", "myop"), new[] {x}, new Operation[0]); + var op = g._create_op_from_tf_operation(c_op); + + Assert.AreEqual("myop", op.name); + Assert.AreEqual("Identity", op.type); + Assert.AreEqual(1, len(op.outputs)); + assertItemsEqual(new[] {2, 3}, op.outputs[0].shape); + } } [TestMethod] From 3a42d80acafdc63c8944d97a7a4847868c3545bf Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 28 Aug 2019 00:00:31 +0300 Subject: [PATCH 50/57] Tensor.StringData: Changed to public. --- src/TensorFlowNET.Core/Tensors/Tensor.cs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.cs b/src/TensorFlowNET.Core/Tensors/Tensor.cs index aaf8ddad..75cba69e 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.cs @@ -221,15 +221,6 @@ namespace Tensorflow /// When is string public T[] ToArray() where T : unmanaged { - //when T is string - if (typeof(T) == typeof(string)) - { - if (dtype != TF_DataType.TF_STRING) - throw new ArgumentException($"Given <{typeof(T).Name}> can't be converted to string."); - - return (T[]) (object) StringData(); - } - //Are the types matching? if (typeof(T).as_dtype() == dtype) { @@ -376,9 +367,15 @@ namespace Tensorflow } } - /// Used internally in ToArray<T> - private unsafe string[] StringData() + /// + /// Extracts string array from current Tensor. + /// + /// When != TF_DataType.TF_STRING + public unsafe string[] StringData() { + if (dtype != TF_DataType.TF_STRING) + throw new InvalidOperationException($"Unable to call StringData when dtype != TF_DataType.TF_STRING (dtype is {dtype})"); + // // TF_STRING tensors are encoded with a table of 8-byte offsets followed by TF_StringEncode-encoded bytes. // [offset1, offset2,...,offsetn, s1size, s1bytes, s2size, s2bytes,...,snsize,snbytes] From f1bc50ecb75b2ef65a54d2c8124cc518f84dc805 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 28 Aug 2019 10:29:14 +0300 Subject: [PATCH 51/57] Reverted: DefaultGraphStack: Perf-ops --- .../Graphs/DefaultGraphStack.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/TensorFlowNET.Core/Graphs/DefaultGraphStack.cs b/src/TensorFlowNET.Core/Graphs/DefaultGraphStack.cs index 33a3aaa3..66419b3e 100644 --- a/src/TensorFlowNET.Core/Graphs/DefaultGraphStack.cs +++ b/src/TensorFlowNET.Core/Graphs/DefaultGraphStack.cs @@ -27,24 +27,24 @@ namespace Tensorflow /// public class DefaultGraphStack { - private readonly List stack = new List(); + private readonly List _stack = new List(); public void set_controller(Graph @default) { - if (!stack.Exists(x => x.Graph == @default)) - stack.Add(new StackModel {Graph = @default, IsDefault = true}); + if (!_stack.Exists(x => x.Graph == @default)) + _stack.Add(new StackModel {Graph = @default, IsDefault = true}); - foreach (var s in stack) + foreach (var s in _stack) s.IsDefault = s.Graph == @default; } public Graph get_controller() { - if (stack.Count(x => x.IsDefault) == 0) - stack.Add(new StackModel {Graph = tf.Graph(), IsDefault = true}); - for (var i = stack.Count - 1; i >= 0; i--) + if (_stack.Count(x => x.IsDefault) == 0) + _stack.Add(new StackModel {Graph = tf.Graph(), IsDefault = true}); + for (var i = _stack.Count - 1; i >= 0; i--) { - var x = stack[i]; + var x = _stack[i]; if (x.IsDefault) return x.Graph; } @@ -54,16 +54,16 @@ namespace Tensorflow public bool remove(Graph g) { - if (stack.Count == 0) + if (_stack.Count == 0) return false; - var sm = stack.Find(model => model.Graph == g); - return sm != null && stack.Remove(sm); + var sm = _stack.Find(model => model.Graph == g); + return sm != null && _stack.Remove(sm); } public void reset() { - stack.Clear(); + _stack.Clear(); } private class StackModel From 33286b99e96ae5b243f65a2c5183629f15bbf8e9 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 28 Aug 2019 16:33:54 +0300 Subject: [PATCH 52/57] RetainImageClassifier.cs: Fixed download url --- .../ImageProcessing/RetrainImageClassifier.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/TensorFlowNET.Examples/ImageProcessing/RetrainImageClassifier.cs b/test/TensorFlowNET.Examples/ImageProcessing/RetrainImageClassifier.cs index 79cc548f..4bb31ea3 100644 --- a/test/TensorFlowNET.Examples/ImageProcessing/RetrainImageClassifier.cs +++ b/test/TensorFlowNET.Examples/ImageProcessing/RetrainImageClassifier.cs @@ -508,7 +508,7 @@ namespace TensorFlowNET.Examples { // get a set of images to teach the network about the new classes string fileName = "flower_photos.tgz"; - string url = $"http://download.tf.org/example_images/{fileName}"; + string url = $"http://download.tensorflow.org/example_images/{fileName}"; Web.Download(url, data_dir, fileName); Compress.ExtractTGZ(Path.Join(data_dir, fileName), data_dir); From 8347f61f51c906601ad1f8ad9c38d163efe9a9b8 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 28 Aug 2019 16:47:21 +0300 Subject: [PATCH 53/57] RetrainImageClassifier.cs: Changed cache_bottlenecks to use Parallel.For --- .../ImageProcessing/RetrainImageClassifier.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/TensorFlowNET.Examples/ImageProcessing/RetrainImageClassifier.cs b/test/TensorFlowNET.Examples/ImageProcessing/RetrainImageClassifier.cs index 4bb31ea3..7f2d81f4 100644 --- a/test/TensorFlowNET.Examples/ImageProcessing/RetrainImageClassifier.cs +++ b/test/TensorFlowNET.Examples/ImageProcessing/RetrainImageClassifier.cs @@ -21,6 +21,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; +using System.Threading.Tasks; using Tensorflow; using TensorFlowNET.Examples.Utility; using static Tensorflow.Binding; @@ -381,10 +382,15 @@ namespace TensorFlowNET.Examples Tensor resized_input_tensor, Tensor bottleneck_tensor, string module_name) { int how_many_bottlenecks = 0; - foreach (var (label_name, label_lists) in image_lists) + var kvs = image_lists.ToArray(); + var categories = new string[] {"training", "testing", "validation"}; + Parallel.For(0, kvs.Length, i => { - foreach (var category in new string[] { "training", "testing", "validation" }) + var (label_name, label_lists) = kvs[i]; + + Parallel.For(0, categories.Length, j => { + var category = categories[j]; var category_list = label_lists[category]; foreach (var (index, unused_base_name) in enumerate(category_list)) { @@ -395,8 +401,8 @@ namespace TensorFlowNET.Examples if (how_many_bottlenecks % 300 == 0) print($"{how_many_bottlenecks} bottleneck files created."); } - } - } + }); + }); } private float[] get_or_create_bottleneck(Session sess, Dictionary> image_lists, From 70aafa720322d23277d8439fbb466ea200006ea0 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 28 Aug 2019 19:34:29 +0300 Subject: [PATCH 54/57] ObjectDetection: Fixed download url --- test/TensorFlowNET.Examples/ImageProcessing/ObjectDetection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/TensorFlowNET.Examples/ImageProcessing/ObjectDetection.cs b/test/TensorFlowNET.Examples/ImageProcessing/ObjectDetection.cs index 50093f3c..d0c06704 100644 --- a/test/TensorFlowNET.Examples/ImageProcessing/ObjectDetection.cs +++ b/test/TensorFlowNET.Examples/ImageProcessing/ObjectDetection.cs @@ -84,7 +84,7 @@ namespace TensorFlowNET.Examples public void PrepareData() { // get model file - string url = "http://download.tf.org/models/object_detection/ssd_mobilenet_v1_coco_2018_01_28.tar.gz"; + string url = "http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v1_coco_2018_01_28.tar.gz"; Web.Download(url, modelDir, "ssd_mobilenet_v1_coco.tar.gz"); Compress.ExtractTGZ(Path.Join(modelDir, "ssd_mobilenet_v1_coco.tar.gz"), "./"); From c0969be2b0acbdb463cc59fbdc4364cd1d6d005a Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 28 Aug 2019 21:43:43 +0300 Subject: [PATCH 55/57] Tensor.Creation: Minor bug fix --- src/TensorFlowNET.Core/Status/c_api.status.cs | 2 +- src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/TensorFlowNET.Core/Status/c_api.status.cs b/src/TensorFlowNET.Core/Status/c_api.status.cs index cfac49d1..ee17e447 100644 --- a/src/TensorFlowNET.Core/Status/c_api.status.cs +++ b/src/TensorFlowNET.Core/Status/c_api.status.cs @@ -51,7 +51,7 @@ namespace Tensorflow /// /// [DllImport(TensorFlowLibName)] - public static unsafe extern IntPtr TF_NewStatus(); + public static extern IntPtr TF_NewStatus(); /// /// Record in *s. Any previous information is lost. diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs index 61cfbef3..625b424a 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs @@ -528,7 +528,6 @@ namespace Tensorflow } _handle = CreateTensorFromNDArray(nd, tensorDType); - IsMemoryOwner = true; } private unsafe IntPtr CreateTensorFromNDArray(NDArray nd, TF_DataType? given_dtype) From 6b2fa402c40c14c629c9748bf40aff55abd2cab1 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 28 Aug 2019 21:45:07 +0300 Subject: [PATCH 56/57] BaseSession: Reverted all changes --- .../Sessions/BaseSession.cs | 465 ++++++++---------- 1 file changed, 211 insertions(+), 254 deletions(-) diff --git a/src/TensorFlowNET.Core/Sessions/BaseSession.cs b/src/TensorFlowNET.Core/Sessions/BaseSession.cs index f81e1bf9..58177df2 100644 --- a/src/TensorFlowNET.Core/Sessions/BaseSession.cs +++ b/src/TensorFlowNET.Core/Sessions/BaseSession.cs @@ -21,16 +21,12 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Text; -using System.Threading.Tasks; -using NumSharp.Backends; -using NumSharp.Backends.Unmanaged; namespace Tensorflow { public class BaseSession : DisposableObject { protected Graph _graph; - protected SessionOptions _options; protected bool _opened; protected bool _closed; protected int _current_version; @@ -39,13 +35,21 @@ namespace Tensorflow public BaseSession(string target = "", Graph g = null, SessionOptions opts = null) { - _graph = g ?? ops.get_default_graph(); + _graph = g is null ? ops.get_default_graph() : g; _graph.as_default(); - _target = Encoding.UTF8.GetBytes(target); - _options = opts = opts ?? new SessionOptions(); + _target = UTF8Encoding.UTF8.GetBytes(target); + + SessionOptions newOpts = null; + if (opts == null) + newOpts = new SessionOptions(); + var status = new Status(); - _handle = c_api.TF_NewSession(_graph, opts, status); + _handle = c_api.TF_NewSession(_graph, opts ?? newOpts, status); + + // dispose newOpts + if (opts == null) + newOpts.Dispose(); status.Check(true); } @@ -67,19 +71,19 @@ namespace Tensorflow public virtual (NDArray, NDArray, NDArray, NDArray) run((ITensorOrOperation, ITensorOrOperation, ITensorOrOperation, ITensorOrOperation) fetches, params FeedItem[] feed_dict) { - var results = _run(new object[] {fetches.Item1, fetches.Item2, fetches.Item3, fetches.Item4}, feed_dict); + var results = _run(new object[] { fetches.Item1, fetches.Item2, fetches.Item3, fetches.Item4 }, feed_dict); return (results[0], results[1], results[2], results[3]); } public virtual (NDArray, NDArray, NDArray) run((ITensorOrOperation, ITensorOrOperation, ITensorOrOperation) fetches, params FeedItem[] feed_dict) { - var results = _run(new object[] {fetches.Item1, fetches.Item2, fetches.Item3}, feed_dict); + var results = _run(new object[] { fetches.Item1, fetches.Item2, fetches.Item3 }, feed_dict); return (results[0], results[1], results[2]); } public virtual (NDArray, NDArray) run((ITensorOrOperation, ITensorOrOperation) fetches, params FeedItem[] feed_dict) { - var results = _run(new object[] {fetches.Item1, fetches.Item2}, feed_dict); + var results = _run(new object[] { fetches.Item1, fetches.Item2 }, feed_dict); return (results[0], results[1]); } @@ -90,7 +94,8 @@ namespace Tensorflow public virtual NDArray[] run(object fetches, Hashtable feed_dict = null) { - var feed_items = feed_dict == null ? new FeedItem[0] : feed_dict.Keys.OfType().Select(key => new FeedItem(key, feed_dict[key])).ToArray(); + var feed_items = feed_dict == null ? new FeedItem[0] : + feed_dict.Keys.OfType().Select(key => new FeedItem(key, feed_dict[key])).ToArray(); return _run(fetches, feed_items); } @@ -99,15 +104,23 @@ namespace Tensorflow var feed_dict_tensor = new Dictionary(); var feed_map = new Dictionary(); + Func> feed_fn = (item) => + { + return new (object, object)[] { (item.Key, item.Value) }; + }; + // Validate and process feed_dict. - if (feed_dict != null && feed_dict.Length > 0) + if (feed_dict != null) { - foreach (var subfeed in feed_dict) + foreach (var feed in feed_dict) { - var subfeed_t = _graph.as_graph_element(subfeed.Key, allow_tensor: true, allow_operation: false); - //var subfeed_dtype = subfeed_t.dtype.as_numpy_datatype(); // subfeed_dtype was never used - feed_dict_tensor[subfeed_t] = subfeed.Value; - feed_map[subfeed_t.name] = (subfeed_t, subfeed.Value); + foreach (var (subfeed, subfeed_val) in feed_fn(feed)) + { + var subfeed_t = _graph.as_graph_element(subfeed, allow_tensor: true, allow_operation: false); + //var subfeed_dtype = subfeed_t.dtype.as_numpy_datatype(); // subfeed_dtype was never used + feed_dict_tensor[subfeed_t] = subfeed_val; + feed_map[subfeed_t.name] = (subfeed_t, subfeed_val); + } } } @@ -124,7 +137,7 @@ namespace Tensorflow // We only want to really perform the run if fetches or targets are provided, // or if the call is a partial run that specifies feeds. - var results = _do_run(final_targets.Select(x => (Operation) x).ToList(), final_fetches, feed_dict_tensor); + var results = _do_run(final_targets.Select(x => (Operation)x).ToList(), final_fetches, feed_dict_tensor); return fetch_handler.build_results(this, results); } @@ -144,58 +157,84 @@ namespace Tensorflow /// private NDArray[] _do_run(List target_list, List fetch_list, Dictionary feed_dict) { - var feeds = new KeyValuePair[feed_dict.Count]; - int i = 0; - foreach (var x in feed_dict) + var feeds = feed_dict.Select(x => { - var tensor = (Tensor) x.Key; - switch (x.Value) + if (x.Key is Tensor tensor) { + switch (x.Value) + { #if _REGEN - %types = ["sbyte", "byte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double", "Complex"] - %foreach types% - case #1 v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case #1[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - % + %types=["sbyte", "byte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double", "Complex"] + %foreach types% + case #1 v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case #1[] v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + % #else - case sbyte v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case sbyte[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case byte v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case byte[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case short v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case short[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case ushort v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case ushort[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case int v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case int[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case uint v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case uint[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case long v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case long[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case ulong v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case ulong[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case float v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case float[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case double v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case double[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case Complex v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case Complex[] v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; + case sbyte v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case sbyte[] v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case byte v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case byte[] v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case short v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case short[] v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case ushort v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case ushort[] v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case int v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case int[] v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case uint v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case uint[] v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case long v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case long[] v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case ulong v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case ulong[] v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case float v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case float[] v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case double v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case double[] v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case Complex v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case Complex[] v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); #endif - case bool v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor((byte) (v ? 1 : 0), TF_DataType.TF_BOOL)); break; - case string v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case IntPtr v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break; - case Tensor v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), v); break; - case NDArray v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v, tensor.dtype)); break; - default: - throw new NotImplementedException($"feed_dict data type {x.Value?.GetType().Name ?? ""}"); + case bool v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor((byte)(v?1:0), TF_DataType.TF_BOOL)); + case string v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case IntPtr v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); + case Tensor v: + return new KeyValuePair(tensor._as_tf_output(), v); + case NDArray v: + return new KeyValuePair(tensor._as_tf_output(), new Tensor(v, tensor.dtype)); + default: + throw new NotImplementedException($"feed_dict data type {(x.Value?.GetType().Name ?? "")}"); + } } - } - - var fetches = new TF_Output[fetch_list.Count]; - for (i = 0; i < fetch_list.Count; i++) - fetches[i] = fetch_list[i]._as_tf_output(); - - //var targets = target_list; + throw new NotImplementedException("_do_run.feed_dict"); + }).ToArray(); + var fetches = fetch_list.Select(x => x._as_tf_output()).ToArray(); + var targets = target_list; return _call_tf_sessionrun(feeds, fetches, target_list); } @@ -206,27 +245,27 @@ namespace Tensorflow _extend_graph(); var status = new Status(); - var fetch_len = fetch_list.Length; - var output_values = new IntPtr[fetch_len]; + + var output_values = fetch_list.Select(x => IntPtr.Zero).ToArray(); c_api.TF_SessionRun(_handle, run_options: null, inputs: feed_dict.Select(f => f.Key).ToArray(), - input_values: feed_dict.Select(f => (IntPtr) f.Value).ToArray(), + input_values: feed_dict.Select(f => (IntPtr)f.Value).ToArray(), ninputs: feed_dict.Length, outputs: fetch_list, output_values: output_values, - noutputs: fetch_len, - target_opers: target_list.Select(f => (IntPtr) f).ToArray(), + noutputs: fetch_list.Length, + target_opers: target_list.Select(f => (IntPtr)f).ToArray(), ntargets: target_list.Count, run_metadata: IntPtr.Zero, status: status); status.Check(true); - var result = new NDArray[fetch_len]; + var result = new NDArray[fetch_list.Length]; - for (int i = 0; i < fetch_len; i++) + for (int i = 0; i < fetch_list.Length; i++) result[i] = fetchValue(output_values[i]); for (int i = 0; i < feed_dict.Length; i++) @@ -237,191 +276,109 @@ namespace Tensorflow private unsafe NDArray fetchValue(IntPtr output) { - NDArray ret; - using (var tensor = new Tensor(output)) - { - var ndims = tensor.shape; - var srcAddress = c_api.TF_TensorData(output).ToInt64(); + var tensor = new Tensor(output); + NDArray nd = null; + Type type = tensor.dtype.as_numpy_dtype(); + var ndims = tensor.shape; + var offset = c_api.TF_TensorData(output); - if (ndims.Length == 0) + if(ndims.Length == 0) + { + switch (tensor.dtype) { - switch (tensor.dtype) - { - case TF_DataType.TF_BOOL: - ret = NDArray.Scalar(*(bool*) srcAddress); - break; - case TF_DataType.TF_STRING: - var bytes = tensor.BufferToArray(); - // offset has to start from 9/ - var str = Encoding.Default.GetString(bytes, 9, bytes[8]); - ret = NDArray.FromString(str); - break; - case TF_DataType.TF_UINT8: - ret = NDArray.Scalar(*(byte*) srcAddress); - break; - case TF_DataType.TF_INT16: - ret = NDArray.Scalar(*(short*) srcAddress); - break; - case TF_DataType.TF_INT32: - ret = NDArray.Scalar(*(int*) srcAddress); - break; - case TF_DataType.TF_INT64: - ret = NDArray.Scalar(*(long*) srcAddress); - break; - case TF_DataType.TF_UINT16: - ret = NDArray.Scalar(*(ushort*) srcAddress); - break; - case TF_DataType.TF_UINT32: - ret = NDArray.Scalar(*(uint*) srcAddress); - break; - case TF_DataType.TF_UINT64: - ret = NDArray.Scalar(*(ulong*) srcAddress); - break; - case TF_DataType.TF_FLOAT: - ret = NDArray.Scalar(*(float*) srcAddress); - break; - case TF_DataType.TF_DOUBLE: - ret = NDArray.Scalar(*(double*) srcAddress); - break; - default: - throw new NotImplementedException("can't fetch output"); - } - } else + case TF_DataType.TF_BOOL: + nd = NDArray.Scalar(*(bool*)offset); + break; + case TF_DataType.TF_STRING: + var bytes = tensor.BufferToArray(); + // wired, don't know why we have to start from offset 9. + // length in the begin + var str = UTF8Encoding.Default.GetString(bytes, 9, bytes[8]); + nd = NDArray.FromString(str); + break; + case TF_DataType.TF_UINT8: + nd = NDArray.Scalar(*(byte*)offset); + break; + case TF_DataType.TF_INT16: + nd = NDArray.Scalar(*(short*)offset); + break; + case TF_DataType.TF_INT32: + nd = NDArray.Scalar(*(int*)offset); + break; + case TF_DataType.TF_INT64: + nd = NDArray.Scalar(*(long*)offset); + break; + case TF_DataType.TF_FLOAT: + nd = NDArray.Scalar(*(float*)offset); + break; + case TF_DataType.TF_DOUBLE: + nd = NDArray.Scalar(*(double*)offset); + break; + default: + throw new NotImplementedException("can't fetch output"); + } + } + else + { + switch (tensor.dtype) { - //var size = (long) tensor.size; - //var itemsize = (long) tensor.itemsize; - var bytesize = (long) tensor.bytesize; - var src = (void*) srcAddress; - -#if _REGEN - #region Compute - switch (tensor.dtype) - { - %foreach except(supported_dtypes, "Char"),except(supported_dtypes_lowercase, "char"),except(supported_dtypes_TF_DataType,"TF_STRING")% - case TF_DataType.#3: - { - ret = new NDArray(NPTypeCode.#1, ndims, false); - System.Buffer.MemoryCopy(src, #(#3=="TF_STRING"|"(byte*)ret.Unsafe.Address + 8"|"ret.Unsafe.Address"), bytesize, bytesize); - break; - } - % - case TF_DataType.TF_STRING: - { - ret = new NDArray(NPTypeCode.Char, Shape.Vector((int) size), false); //TODO! Eli: when numsharp supports long size, remove (int) cast. - //var bytes = tensor.BufferToArray(); - //// wired, don't know why we have to start from offset 9. - //// length in the begin - //var str = Encoding.Default.GetString(bytes, 9, bytes[8]); - //ret = np.array(str); - - //TODO! Eli: this has to be unit-tested. - var len = sizeof(char) * size; - var dst = ret.Unsafe.Address; - System.Buffer.MemoryCopy((byte*) src + 8, dst, len, len); - break; - } - default: - throw new NotSupportedException(); - } - #endregion -#else - - #region Compute - - switch (tensor.dtype) - { - case TF_DataType.TF_BOOL: - { - ret = new NDArray(NPTypeCode.Boolean, ndims, false); - System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize); - break; - } - - case TF_DataType.TF_UINT8: - { - ret = new NDArray(NPTypeCode.Byte, ndims, false); - System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize); - break; - } - - case TF_DataType.TF_INT16: - { - ret = new NDArray(NPTypeCode.Int16, ndims, false); - System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize); - break; - } - - case TF_DataType.TF_UINT16: - { - ret = new NDArray(NPTypeCode.UInt16, ndims, false); - System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize); - break; - } - - case TF_DataType.TF_INT32: - { - ret = new NDArray(NPTypeCode.Int32, ndims, false); - System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize); - break; - } - - case TF_DataType.TF_UINT32: - { - ret = new NDArray(NPTypeCode.UInt32, ndims, false); - System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize); - break; - } - - case TF_DataType.TF_INT64: - { - ret = new NDArray(NPTypeCode.Int64, ndims, false); - System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize); - break; - } - - case TF_DataType.TF_UINT64: - { - ret = new NDArray(NPTypeCode.UInt64, ndims, false); - System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize); - break; - } - - case TF_DataType.TF_DOUBLE: - { - ret = new NDArray(NPTypeCode.Double, ndims, false); - System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize); - break; - } - - case TF_DataType.TF_FLOAT: - { - ret = new NDArray(NPTypeCode.Single, ndims, false); - System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize); - break; - } - - case TF_DataType.TF_STRING: - { - ret = new NDArray(NPTypeCode.Char, Shape.Vector((int) (bytesize - 8) / sizeof(char)), false); //TODO! Eli: when numsharp supports long size, remove (int) cast. - - //TODO! Eli: this has to be unit-tested. - var len = bytesize - 8; - var dst = ret.Unsafe.Address; - System.Buffer.MemoryCopy((byte*) src + 8, dst, len, len); - break; - } - - default: - throw new NotSupportedException(); - } - - #endregion - -#endif + case TF_DataType.TF_BOOL: + var bools = new bool[tensor.size]; + for (ulong i = 0; i < tensor.size; i++) + bools[i] = *(bool*)(offset + (int)(tensor.itemsize * i)); + nd = np.array(bools).reshape(ndims); + break; + case TF_DataType.TF_STRING: + var bytes = tensor.BufferToArray(); + // wired, don't know why we have to start from offset 9. + // length in the begin + var str = UTF8Encoding.Default.GetString(bytes, 9, bytes[8]); + nd = np.array(str); + break; + case TF_DataType.TF_UINT8: + var _bytes = new byte[tensor.size]; + for (ulong i = 0; i < tensor.size; i++) + _bytes[i] = *(byte*)(offset + (int)(tensor.itemsize * i)); + nd = np.array(_bytes).reshape(ndims); + break; + case TF_DataType.TF_INT16: + var shorts = new short[tensor.size]; + for (ulong i = 0; i < tensor.size; i++) + shorts[i] = *(short*)(offset + (int)(tensor.itemsize * i)); + nd = np.array(shorts).reshape(ndims); + break; + case TF_DataType.TF_INT32: + var ints = new int[tensor.size]; + for (ulong i = 0; i < tensor.size; i++) + ints[i] = *(int*)(offset + (int)(tensor.itemsize * i)); + nd = np.array(ints).reshape(ndims); + break; + case TF_DataType.TF_INT64: + var longs = new long[tensor.size]; + for (ulong i = 0; i < tensor.size; i++) + longs[i] = *(long*)(offset + (int)(tensor.itemsize * i)); + nd = np.array(longs).reshape(ndims); + break; + case TF_DataType.TF_FLOAT: + var floats = new float[tensor.size]; + for (ulong i = 0; i < tensor.size; i++) + floats[i] = *(float*)(offset + (int)(tensor.itemsize * i)); + nd = np.array(floats).reshape(ndims); + break; + case TF_DataType.TF_DOUBLE: + var doubles = new double[tensor.size]; + for (ulong i = 0; i < tensor.size; i++) + doubles[i] = *(double*)(offset + (int)(tensor.itemsize * i)); + nd = np.array(doubles).reshape(ndims); + break; + default: + throw new NotImplementedException("can't fetch output"); } } + + tensor.Dispose(); - return ret; + return nd; } /// @@ -435,7 +392,9 @@ namespace Tensorflow } private void _extend_graph() - { } + { + + } public void close() { @@ -449,8 +408,6 @@ namespace Tensorflow c_api.TF_DeleteSession(handle, status); status.Check(true); } - - _options.Dispose(); } } } \ No newline at end of file From 854c2e52512a04ab9df99eea40b32ac31772c927 Mon Sep 17 00:00:00 2001 From: Eli Belash Date: Wed, 28 Aug 2019 21:45:25 +0300 Subject: [PATCH 57/57] Binding.Util: Minor perf-op --- src/TensorFlowNET.Core/Binding.Util.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/TensorFlowNET.Core/Binding.Util.cs b/src/TensorFlowNET.Core/Binding.Util.cs index bfbfa4ec..d723283f 100644 --- a/src/TensorFlowNET.Core/Binding.Util.cs +++ b/src/TensorFlowNET.Core/Binding.Util.cs @@ -178,13 +178,18 @@ namespace Tensorflow public static IEnumerable<(TKey, TValue)> enumerate(KeyValuePair[] values) { - foreach (var item in values) + var len = values.Length; + for (var i = 0; i < len; i++) + { + var item = values[i]; yield return (item.Key, item.Value); + } } public static IEnumerable<(int, T)> enumerate(IList values) { - for (int i = 0; i < values.Count; i++) + var len = values.Count; + for (int i = 0; i < len; i++) yield return (i, values[i]); }