| @@ -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<T>() | |||||
| { | |||||
| var data = new T[size]; | |||||
| for (ulong i = 0; i < size; i++) | |||||
| { | |||||
| data[i] = Marshal.PtrToStructure<T>(buffer + (int)(i * dataTypeSize)); | |||||
| } | |||||
| return data; | |||||
| } | |||||
| public byte[] Data() | |||||
| { | |||||
| var data = new byte[bytesize]; | |||||
| Marshal.Copy(buffer, data, 0, (int)bytesize); | |||||
| return data; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -6,6 +6,7 @@ namespace Tensorflow | |||||
| { | { | ||||
| public enum TF_DataType | public enum TF_DataType | ||||
| { | { | ||||
| DtInvalid = 0, | |||||
| TF_FLOAT = 1, | TF_FLOAT = 1, | ||||
| TF_DOUBLE = 2, | TF_DOUBLE = 2, | ||||
| TF_INT32 = 3, // Int32 tensors are always in 'host' memory. | TF_INT32 = 3, // Int32 tensors are always in 'host' memory. | ||||
| @@ -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 | |||||
| { | |||||
| /// <summary> | |||||
| /// A tensor is a generalization of vectors and matrices to potentially higher dimensions. | |||||
| /// Internally, TensorFlow represents tensors as n-dimensional arrays of base datatypes. | |||||
| /// </summary> | |||||
| 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; } | |||||
| /// <summary> | |||||
| /// 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) | |||||
| /// </summary> | |||||
| public int rank; | |||||
| /// <summary> | |||||
| /// if original buffer is free. | |||||
| /// </summary> | |||||
| 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<float>(), 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<T>() | |||||
| { | |||||
| // 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<T>(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; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,10 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Text; | |||||
| namespace Tensorflow | |||||
| { | |||||
| public class Variable | |||||
| { | |||||
| } | |||||
| } | |||||
| @@ -73,8 +73,10 @@ namespace Tensorflow | |||||
| /// <param name="deallocator"></param> | /// <param name="deallocator"></param> | ||||
| /// <param name="deallocator_arg"></param> | /// <param name="deallocator_arg"></param> | ||||
| /// <returns></returns> | /// <returns></returns> | ||||
| [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); | |||||
| /// <summary> | /// <summary> | ||||
| /// Return the number of dimensions that the tensor has. | /// Return the number of dimensions that the tensor has. | ||||
| @@ -16,8 +16,6 @@ namespace Tensorflow | |||||
| public static Graph g = new Graph(c_api.TF_NewGraph()); | 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) | public static unsafe Tensor add(Tensor a, Tensor b) | ||||
| { | { | ||||
| return gen_math_ops.add(a, b); | return gen_math_ops.add(a, b); | ||||
| @@ -13,50 +13,19 @@ namespace TensorFlowNET.UnitTest | |||||
| public class TensorTest | public class TensorTest | ||||
| { | { | ||||
| [TestMethod] | [TestMethod] | ||||
| public unsafe void TF_NewTensor() | |||||
| public unsafe void NewTensor() | |||||
| { | { | ||||
| var nd = np.array(1f, 2f, 3f, 4f, 5f, 6f).reshape(2, 3); | 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<float>(), 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<float>(); | |||||
| Assert.AreEqual(tensor.dtype, TF_DataType.TF_FLOAT); | 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(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<float>(); | |||||
| Assert.IsTrue(Enumerable.SequenceEqual(nd.Data<float>(), array)); | Assert.IsTrue(Enumerable.SequenceEqual(nd.Data<float>(), array)); | ||||
| c_api.TF_DeleteTensor(handle); | |||||
| Assert.AreEqual(*(bool *)deallocator_called, true); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||