diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs index 81c8006b..b90c4e69 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs @@ -1,5 +1,7 @@ /***************************************************************************** Copyright 2018 The TensorFlow.NET Authors. All Rights Reserved. + + Portions of this file have been adapted from TensorFlowSharp, authored by Miguel de Icaza (miguel@microsoft.com) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,6 +20,8 @@ using NumSharp; using System; using System.Collections.Generic; using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using static Tensorflow.c_api; @@ -36,39 +40,246 @@ namespace Tensorflow _handle = handle; } - public Tensor(NDArray nd, TF_DataType? tensorDType = null) +#if _REGEN + %types=["sbyte", "byte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double", "Complex"] + %foreach types% + + /// + /// Create a 1d Tensor from the given linear array and shape + /// + public Tensor(#1[] data) { - _handle = Allocate(nd, tensorDType: tensorDType); + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(#1)), new long[]{data.Length}, data, Marshal.SizeOf<#1>()); + } + + /// + /// Create a N-dimensional Tensor from the given array + /// + public Tensor(#1[] data, long[] shape) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(#1)), shape, data, Marshal.SizeOf<#1>()); + } + + % +#else + + + /// + /// Create a 1d Tensor from the given linear array and shape + /// + public Tensor(sbyte[] data) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(sbyte)), new long[]{data.Length}, data, Marshal.SizeOf()); + } + + /// + /// Create a N-dimensional Tensor from the given array + /// + public Tensor(sbyte[] data, long[] shape) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(sbyte)), shape, data, Marshal.SizeOf()); + } + + + /// + /// Create a 1d Tensor from the given linear array and shape + /// + public Tensor(byte[] data) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(byte)), new long[]{data.Length}, data, Marshal.SizeOf()); + } + + /// + /// Create a N-dimensional Tensor from the given array + /// + public Tensor(byte[] data, long[] shape) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(byte)), shape, data, Marshal.SizeOf()); + } + + + /// + /// Create a 1d Tensor from the given linear array and shape + /// + public Tensor(short[] data) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(short)), new long[]{data.Length}, data, Marshal.SizeOf()); + } + + /// + /// Create a N-dimensional Tensor from the given array + /// + public Tensor(short[] data, long[] shape) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(short)), shape, data, Marshal.SizeOf()); + } + + + /// + /// Create a 1d Tensor from the given linear array and shape + /// + public Tensor(ushort[] data) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(ushort)), new long[]{data.Length}, data, Marshal.SizeOf()); + } + + /// + /// Create a N-dimensional Tensor from the given array + /// + public Tensor(ushort[] data, long[] shape) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(ushort)), shape, data, Marshal.SizeOf()); + } + + + /// + /// Create a 1d Tensor from the given linear array and shape + /// + public Tensor(int[] data) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(int)), new long[]{data.Length}, data, Marshal.SizeOf()); + } + + /// + /// Create a N-dimensional Tensor from the given array + /// + public Tensor(int[] data, long[] shape) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(int)), shape, data, Marshal.SizeOf()); + } + + + /// + /// Create a 1d Tensor from the given linear array and shape + /// + public Tensor(uint[] data) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(uint)), new long[]{data.Length}, data, Marshal.SizeOf()); + } + + /// + /// Create a N-dimensional Tensor from the given array + /// + public Tensor(uint[] data, long[] shape) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(uint)), shape, data, Marshal.SizeOf()); } - public unsafe Tensor(byte[] buffer) + + /// + /// Create a 1d Tensor from the given linear array and shape + /// + public Tensor(long[] data) { - var size = c_api.TF_StringEncodedSize((UIntPtr)buffer.Length); - _handle = TF_AllocateTensor(TF_DataType.TF_STRING, IntPtr.Zero, 0, (UIntPtr)((ulong)size + 8)); + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(long)), new long[]{data.Length}, data, Marshal.SizeOf()); + } - IntPtr tensor = c_api.TF_TensorData(_handle); - Marshal.WriteInt64(tensor, 0); - fixed (byte* src = &buffer[0]) - c_api.TF_StringEncode(src, (UIntPtr)buffer.Length, (sbyte*)(tensor + sizeof(Int64)), size, status); + /// + /// Create a N-dimensional Tensor from the given array + /// + public Tensor(long[] data, long[] shape) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(long)), shape, data, Marshal.SizeOf()); + } + + + /// + /// Create a 1d Tensor from the given linear array and shape + /// + public Tensor(ulong[] data) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(ulong)), new long[]{data.Length}, data, Marshal.SizeOf()); + } - status.Check(true); + /// + /// Create a N-dimensional Tensor from the given array + /// + public Tensor(ulong[] data, long[] shape) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(ulong)), shape, data, Marshal.SizeOf()); } - private IntPtr Allocate(NDArray nd, TF_DataType? tensorDType = null) + + /// + /// Create a 1d Tensor from the given linear array and shape + /// + public Tensor(float[] data) { - if (tensorDType == TF_DataType.TF_STRING && - nd.dtype.Name == "Byte") + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(float)), new long[]{data.Length}, data, Marshal.SizeOf()); + } + + /// + /// Create a N-dimensional Tensor from the given array + /// + public Tensor(float[] data, long[] shape) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(float)), shape, data, Marshal.SizeOf()); + } + + + /// + /// Create a 1d Tensor from the given linear array and shape + /// + public Tensor(double[] data) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(double)), new long[]{data.Length}, data, Marshal.SizeOf()); + } + + /// + /// Create a N-dimensional Tensor from the given array + /// + public Tensor(double[] data, long[] shape) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(double)), shape, data, Marshal.SizeOf()); + } + + + /// + /// Create a 1d Tensor from the given linear array and shape + /// + public Tensor(Complex[] data) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(Complex)), new long[]{data.Length}, data, Marshal.SizeOf()); + } + + /// + /// Create a N-dimensional Tensor from the given array + /// + public Tensor(Complex[] data, long[] shape) + { + _handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(Complex)), shape, data, Marshal.SizeOf()); + } +#endif + + public Tensor(NDArray nd, TF_DataType? tensorDType = null) + { + _handle = Allocate(nd, tensorDType: tensorDType); + } + + private unsafe IntPtr Allocate(NDArray nd, TF_DataType? tensorDType = null) + { + if (tensorDType == TF_DataType.TF_STRING && nd.dtype.Name == "Byte") { - return new Tensor(nd.Data()); + var buffer=nd.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)); + + IntPtr tensor = c_api.TF_TensorData(handle); + Marshal.WriteInt64(tensor, 0); + fixed (byte* src = &buffer[0]) + c_api.TF_StringEncode(src, (UIntPtr)buffer.Length, (sbyte*)(tensor + sizeof(Int64)), size, status); + + status.Check(true); + return handle; } IntPtr dotHandle = IntPtr.Zero; - ulong size = 0; + int buffersize = 0; if (nd.dtype.Name != "String") { - dotHandle = Marshal.AllocHGlobal(nd.dtypesize * nd.size); - size = (ulong)(nd.size * nd.dtypesize); + buffersize = (nd.size * nd.dtypesize); + dotHandle = Marshal.AllocHGlobal(buffersize); } var dataType = ToTFDataType(nd.dtype); @@ -116,7 +327,7 @@ namespace Tensorflow dims, dims.Length, dotHandle, - (UIntPtr)size, + (UIntPtr)buffersize, deallocator, ref deallocator_called); @@ -130,5 +341,59 @@ namespace Tensorflow _dtype = dtype; _id = ops.uid(); } + + /// + /// Creates a new tensor from the given array without copying memory. The array is pinned down and the pointer passed on. + /// + /// Represents the tensor shape. + /// The linear array of data, the data must fit in the tensor with the specified dimensions. + /// The number of bytes in memory of a single array element + /// + /// Use the FromBuffer method to create a tensor that has the specified dimensions + /// and is initialized with data from the data array. The data is copied starting + /// at the start offset, for count bytes and is laid out into the tensor following the + /// specified dimensions. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected IntPtr CreateTensorWithoutCopying(TF_DataType dt, long[] shape, Array data, int element_size) + { + return CreateTensorWithoutCopying(dt, shape, data, 0, data.Length, element_size); + } + + /// + /// Creates a new tensor from a subsection of the given array without copying memory. The array is pinned down and the pointer passed on. + /// + /// Represents the tensor shape. + /// The linear array of data, the data must fit in the tensor with the specified dimensions. + /// The offset into the provided data array where the data resides. + /// The number of elements to copy from data. + /// The number of bytes in memory of a single array element + /// + /// Use the FromBuffer method to create a tensor that has the specified dimensions + /// and is initialized with data from the data array. The data is copied starting + /// at the start offset, for count bytes and is laid out into the tensor following the + /// specified dimensions. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected IntPtr CreateTensorWithoutCopying(TF_DataType dt, long[] shape, Array data, int start, int count, int element_size) + { + if (start < 0 || start > data.Length - count) + throw new ArgumentException("start + count > Array size"); + + // get a handle to the pinned array which we will pass on to the tensor computation engine to use + var dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned); + + // Free the original buffer and set flag + Deallocator deallocator = (IntPtr values, IntPtr len, ref bool closure) => + { + dataHandle.Free(); + closure = true; + }; + + if (shape == null) + return TF_NewTensor(dt, null, 0, dataHandle.AddrOfPinnedObject() + start * element_size, (UIntPtr)(count * element_size), deallocator, ref deallocator_called); + else + return TF_NewTensor(dt, shape, shape.Length, dataHandle.AddrOfPinnedObject() + start * element_size, (UIntPtr)(count * element_size), deallocator, ref deallocator_called); + } } } diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.cs b/src/TensorFlowNET.Core/Tensors/Tensor.cs index 786daa5c..cc54c667 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.cs @@ -31,7 +31,7 @@ namespace Tensorflow /// public partial class Tensor : IDisposable, ITensorOrOperation { - private readonly IntPtr _handle; + private IntPtr _handle; private int _id; private Operation _op; @@ -351,6 +351,7 @@ namespace Tensorflow public void Dispose() { c_api.TF_DeleteTensor(_handle); + _handle = IntPtr.Zero; status.Dispose(); } diff --git a/src/TensorFlowNET.Core/Tensors/dtypes.cs b/src/TensorFlowNET.Core/Tensors/dtypes.cs index ebe6e283..5000401a 100644 --- a/src/TensorFlowNET.Core/Tensors/dtypes.cs +++ b/src/TensorFlowNET.Core/Tensors/dtypes.cs @@ -52,32 +52,51 @@ namespace Tensorflow } } + // "sbyte", "byte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double", "Complex" public static TF_DataType as_dtype(Type type) { TF_DataType dtype = TF_DataType.DtInvalid; - + switch (type.Name) { - case "Boolean": - dtype = TF_DataType.TF_BOOL; + case "SByte": + dtype = TF_DataType.TF_INT8; + break; + case "Byte": + dtype = TF_DataType.TF_UINT8; + break; + case "Int16": + dtype = TF_DataType.TF_INT16; + break; + case "UInt16": + dtype = TF_DataType.TF_UINT16; break; case "Int32": dtype = TF_DataType.TF_INT32; break; + case "UInt32": + dtype = TF_DataType.TF_UINT32; + break; case "Int64": dtype = TF_DataType.TF_INT64; break; + case "UInt64": + dtype = TF_DataType.TF_UINT64; + break; case "Single": dtype = TF_DataType.TF_FLOAT; break; case "Double": dtype = TF_DataType.TF_DOUBLE; break; + case "Complex": + dtype = TF_DataType.TF_COMPLEX128; + break; case "String": dtype = TF_DataType.TF_STRING; break; - case "Byte": - dtype = TF_DataType.TF_STRING; + case "Boolean": + dtype = TF_DataType.TF_BOOL; break; default: throw new Exception("as_dtype Not Implemented");