@@ -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 | |||
{ | |||
DtInvalid = 0, | |||
TF_FLOAT = 1, | |||
TF_DOUBLE = 2, | |||
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_arg"></param> | |||
/// <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> | |||
/// 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 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); | |||
@@ -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<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.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<float>(); | |||
Assert.IsTrue(Enumerable.SequenceEqual(nd.Data<float>(), array)); | |||
c_api.TF_DeleteTensor(handle); | |||
Assert.AreEqual(*(bool *)deallocator_called, true); | |||
} | |||
} | |||
} |