From 91befe9163d62ec20eb6b32ee012559746a7dfe6 Mon Sep 17 00:00:00 2001 From: Oceania2018 Date: Mon, 24 Dec 2018 15:46:55 -0600 Subject: [PATCH] Finished Tensor class. --- src/TensorFlowNET.Core/Tensor/Tensor.cs | 66 --------- .../{Tensor => Tensors}/TF_DataType.cs | 1 + src/TensorFlowNET.Core/Tensors/Tensor.cs | 139 ++++++++++++++++++ .../{Tensor => Tensors}/TensorBuffer.cs | 0 src/TensorFlowNET.Core/Tensors/Variable.cs | 10 ++ .../{Tensor => Tensors}/tensor_util.cs | 0 src/TensorFlowNET.Core/c_api.cs | 6 +- src/TensorFlowNET.Core/tf.cs | 2 - test/TensorFlowNET.UnitTest/TensorTest.cs | 43 +----- 9 files changed, 160 insertions(+), 107 deletions(-) delete mode 100644 src/TensorFlowNET.Core/Tensor/Tensor.cs rename src/TensorFlowNET.Core/{Tensor => Tensors}/TF_DataType.cs (97%) create mode 100644 src/TensorFlowNET.Core/Tensors/Tensor.cs rename src/TensorFlowNET.Core/{Tensor => Tensors}/TensorBuffer.cs (100%) create mode 100644 src/TensorFlowNET.Core/Tensors/Variable.cs rename src/TensorFlowNET.Core/{Tensor => Tensors}/tensor_util.cs (100%) diff --git a/src/TensorFlowNET.Core/Tensor/Tensor.cs b/src/TensorFlowNET.Core/Tensor/Tensor.cs deleted file mode 100644 index 142671bb..00000000 --- a/src/TensorFlowNET.Core/Tensor/Tensor.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Text; - -namespace Tensorflow -{ - public class Tensor - { - public Operation op { get; } - public int value_index { get; } - public TF_DataType dtype { get; } - - public Graph graph => op.graph; - - public string name; - public IntPtr handle { get; } - public int ndim { get; } - public ulong bytesize { get; } - public ulong dataTypeSize { get;} - public ulong size => bytesize / dataTypeSize; - public IntPtr buffer { get; } - - public Tensor(IntPtr handle) - { - this.handle = handle; - dtype = c_api.TF_TensorType(handle); - ndim = c_api.TF_NumDims(handle); - bytesize = c_api.TF_TensorByteSize(handle); - buffer = c_api.TF_TensorData(handle); - dataTypeSize = c_api.TF_DataTypeSize(dtype); - } - - public Tensor(Operation op, int value_index, TF_DataType dtype) - { - this.op = op; - this.value_index = value_index; - this.dtype = dtype; - } - - public TF_Output _as_tf_output() - { - return c_api_util.tf_output(op._c_op, value_index); - } - - public T[] Data() - { - var data = new T[size]; - - for (ulong i = 0; i < size; i++) - { - data[i] = Marshal.PtrToStructure(buffer + (int)(i * dataTypeSize)); - } - - return data; - } - - public byte[] Data() - { - var data = new byte[bytesize]; - Marshal.Copy(buffer, data, 0, (int)bytesize); - - return data; - } - } -} diff --git a/src/TensorFlowNET.Core/Tensor/TF_DataType.cs b/src/TensorFlowNET.Core/Tensors/TF_DataType.cs similarity index 97% rename from src/TensorFlowNET.Core/Tensor/TF_DataType.cs rename to src/TensorFlowNET.Core/Tensors/TF_DataType.cs index 61fbfee2..543fb4e4 100644 --- a/src/TensorFlowNET.Core/Tensor/TF_DataType.cs +++ b/src/TensorFlowNET.Core/Tensors/TF_DataType.cs @@ -6,6 +6,7 @@ namespace Tensorflow { public enum TF_DataType { + DtInvalid = 0, TF_FLOAT = 1, TF_DOUBLE = 2, TF_INT32 = 3, // Int32 tensors are always in 'host' memory. diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.cs b/src/TensorFlowNET.Core/Tensors/Tensor.cs new file mode 100644 index 00000000..74ed8e5f --- /dev/null +++ b/src/TensorFlowNET.Core/Tensors/Tensor.cs @@ -0,0 +1,139 @@ +using NumSharp.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +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. + /// + public class Tensor + { + public Operation op { get; } + public int value_index { get; } + + public Graph graph => op.graph; + + public string name; + + public TF_DataType dtype { get; } + public IntPtr handle { get; } + public ulong bytesize { get; } + public ulong dataTypeSize { get;} + public ulong size => bytesize / dataTypeSize; + public IntPtr buffer { get; } + public long[] shape { get; } + + /// + /// 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) + /// + public int rank; + + /// + /// if original buffer is free. + /// + private bool deallocator_called; + + public Tensor(IntPtr handle) + { + this.handle = handle; + dtype = c_api.TF_TensorType(handle); + rank = c_api.TF_NumDims(handle); + bytesize = c_api.TF_TensorByteSize(handle); + buffer = c_api.TF_TensorData(handle); + dataTypeSize = c_api.TF_DataTypeSize(dtype); + + shape = new long[rank]; + for (int i = 0; i < rank; i++) + shape[i] = c_api.TF_Dim(handle, i); + } + + public Tensor(NDArray nd) + { + var data = Marshal.AllocHGlobal(sizeof(float) * nd.size); + Marshal.Copy(nd.Data(), 0, data, nd.size); + var dataType = ToTFDataType(nd.dtype); + + var handle = c_api.TF_NewTensor(dataType, + nd.shape.Select(x => (long)x).ToArray(), // shape + nd.ndim, + data, + (UIntPtr)(nd.size * sizeof(float)), + (IntPtr values, IntPtr len, ref bool closure) => + { + // Free the original buffer and set flag + Marshal.FreeHGlobal(data); + closure = true; + }, + ref deallocator_called); + + this.handle = handle; + dtype = c_api.TF_TensorType(handle); + rank = c_api.TF_NumDims(handle); + bytesize = c_api.TF_TensorByteSize(handle); + buffer = c_api.TF_TensorData(handle); + dataTypeSize = c_api.TF_DataTypeSize(dtype); + + shape = new long[rank]; + for (int i = 0; i < rank; i++) + shape[i] = c_api.TF_Dim(handle, i); + } + + public Tensor(Operation op, int value_index, TF_DataType dtype) + { + this.op = op; + this.value_index = value_index; + this.dtype = dtype; + } + + public TF_Output _as_tf_output() + { + return c_api_util.tf_output(op._c_op, value_index); + } + + public T[] Data() + { + // 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++) + { + data[i] = Marshal.PtrToStructure(buffer + (int)(i * dataTypeSize)); + } + + return data; + } + + public byte[] Data() + { + var data = new byte[bytesize]; + Marshal.Copy(buffer, data, 0, (int)bytesize); + + return data; + } + + public TF_DataType ToTFDataType(Type type) + { + switch (type.Name) + { + case "Single": + return TF_DataType.TF_FLOAT; + } + + return TF_DataType.DtInvalid; + } + } +} diff --git a/src/TensorFlowNET.Core/Tensor/TensorBuffer.cs b/src/TensorFlowNET.Core/Tensors/TensorBuffer.cs similarity index 100% rename from src/TensorFlowNET.Core/Tensor/TensorBuffer.cs rename to src/TensorFlowNET.Core/Tensors/TensorBuffer.cs diff --git a/src/TensorFlowNET.Core/Tensors/Variable.cs b/src/TensorFlowNET.Core/Tensors/Variable.cs new file mode 100644 index 00000000..883c9a44 --- /dev/null +++ b/src/TensorFlowNET.Core/Tensors/Variable.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Tensorflow +{ + public class Variable + { + } +} diff --git a/src/TensorFlowNET.Core/Tensor/tensor_util.cs b/src/TensorFlowNET.Core/Tensors/tensor_util.cs similarity index 100% rename from src/TensorFlowNET.Core/Tensor/tensor_util.cs rename to src/TensorFlowNET.Core/Tensors/tensor_util.cs diff --git a/src/TensorFlowNET.Core/c_api.cs b/src/TensorFlowNET.Core/c_api.cs index f89ded8a..0da39d24 100644 --- a/src/TensorFlowNET.Core/c_api.cs +++ b/src/TensorFlowNET.Core/c_api.cs @@ -73,8 +73,10 @@ namespace Tensorflow /// /// /// - [DllImport(TensorFlowLibName, CallingConvention = CallingConvention.StdCall)] - public static extern unsafe IntPtr TF_NewTensor(TF_DataType dataType, long[] dims, int num_dims, IntPtr data, UIntPtr len, tf.Deallocator deallocator, IntPtr deallocator_arg); + [DllImport(TensorFlowLibName)] + public static extern unsafe IntPtr TF_NewTensor(TF_DataType dataType, long[] dims, int num_dims, IntPtr data, UIntPtr len, Deallocator deallocator, ref bool deallocator_arg); + + public delegate void Deallocator(IntPtr data, IntPtr size, ref bool deallocatorData); /// /// Return the number of dimensions that the tensor has. diff --git a/src/TensorFlowNET.Core/tf.cs b/src/TensorFlowNET.Core/tf.cs index 0d8449e1..95850312 100644 --- a/src/TensorFlowNET.Core/tf.cs +++ b/src/TensorFlowNET.Core/tf.cs @@ -16,8 +16,6 @@ namespace Tensorflow public static Graph g = new Graph(c_api.TF_NewGraph()); - public delegate void Deallocator(IntPtr data, IntPtr size, IntPtr deallocatorData); - public static unsafe Tensor add(Tensor a, Tensor b) { return gen_math_ops.add(a, b); diff --git a/test/TensorFlowNET.UnitTest/TensorTest.cs b/test/TensorFlowNET.UnitTest/TensorTest.cs index 519b7c86..f111e07c 100644 --- a/test/TensorFlowNET.UnitTest/TensorTest.cs +++ b/test/TensorFlowNET.UnitTest/TensorTest.cs @@ -13,50 +13,19 @@ namespace TensorFlowNET.UnitTest public class TensorTest { [TestMethod] - public unsafe void TF_NewTensor() + public unsafe void NewTensor() { var nd = np.array(1f, 2f, 3f, 4f, 5f, 6f).reshape(2, 3); - var data = Marshal.AllocHGlobal(sizeof(float) * nd.size); - Marshal.Copy(nd.Data(), 0, data, nd.size); - - var deallocator_called = Marshal.AllocHGlobal(sizeof(bool)); - Assert.AreEqual(*(bool*)deallocator_called, false); - - var handle = c_api.TF_NewTensor(TF_DataType.TF_FLOAT, - nd.shape.Select(x => (long)x).ToArray(), // shape - nd.ndim, - data, - (UIntPtr)(nd.size * sizeof(float)), - (IntPtr values, IntPtr len, IntPtr closure) => - { - // Free the original buffer and set flag - Marshal.FreeHGlobal(data); - *(bool*)closure = true; - }, - deallocator_called); - - Assert.AreNotEqual(handle, IntPtr.Zero); - - var tensor = new Tensor(handle); + var tensor = new Tensor(nd); + var array = tensor.Data(); Assert.AreEqual(tensor.dtype, TF_DataType.TF_FLOAT); - Assert.AreEqual(tensor.ndim, nd.ndim); - Assert.AreEqual(nd.shape[0], c_api.TF_Dim(handle, 0)); - Assert.AreEqual(nd.shape[1], c_api.TF_Dim(handle, 1)); + Assert.AreEqual(tensor.rank, nd.ndim); + Assert.AreEqual(tensor.shape[0], nd.shape[0]); + Assert.AreEqual(tensor.shape[1], nd.shape[1]); Assert.AreEqual(tensor.bytesize, (uint)nd.size * sizeof(float)); - Assert.AreEqual(*(bool*)deallocator_called, true); - - // 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 array = tensor.Data(); Assert.IsTrue(Enumerable.SequenceEqual(nd.Data(), array)); - - c_api.TF_DeleteTensor(handle); - Assert.AreEqual(*(bool *)deallocator_called, true); } } }