diff --git a/src/TensorFlowNET.Console/MemoryLeakTesting.cs b/src/TensorFlowNET.Console/MemoryLeakTesting.cs deleted file mode 100644 index 6b1e07f2..00000000 --- a/src/TensorFlowNET.Console/MemoryLeakTesting.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using static Tensorflow.Binding; - -namespace Tensorflow -{ - class MemoryLeakTesting - { - public void WarmUp() - { - print(tf.VERSION); - } - - /// - /// - /// - public void TensorCreation() - { - int total = 1 * 1000 * 1000; - for (int i = 0; i < total; i++) - { - /*var const1 = new Tensor(new float[,] - { - { 3.0f, 1.0f }, - { 1.0f, 2.0f } - }); - const1.Dispose();*/ - - var tensor = new EagerTensorV2(new float[,] - { - { 3.0f, 1.0f }, - { 1.0f, 2.0f } - }); - - tensor.Dispose(); - } - - GC.Collect(); - } - } -} diff --git a/src/TensorFlowNET.Console/MemoryMonitor.cs b/src/TensorFlowNET.Console/MemoryMonitor.cs new file mode 100644 index 00000000..86130583 --- /dev/null +++ b/src/TensorFlowNET.Console/MemoryMonitor.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using static Tensorflow.Binding; + +namespace Tensorflow +{ + public class MemoryMonitor + { + public void WarmUp() + { + print(tf.VERSION); + } + + public void Execute(int epoch, int iterate, Action process) + { + /*GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect();*/ + + print($"{process.Method.Name} started..."); + for (int i = 0; i < epoch; i++) + { + var initialMemory = Process.GetCurrentProcess().PrivateMemorySize64;// GC.GetTotalMemory(true); + process(iterate); + var finalMemory = Process.GetCurrentProcess().PrivateMemorySize64; //GC.GetTotalMemory(true); + print($"Epoch {i}: {Format(finalMemory - initialMemory)}."); + } + + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + + print($"Total {process.Method.Name} usage {Format(Process.GetCurrentProcess().PrivateMemorySize64)}"); + } + + private string Format(long usage) + { + if (usage < 0) + return $"-{Format(0 - usage)}"; + + if (usage <= 1024 && usage >= 0) + return $"{usage} Bytes"; + else if (usage > 1024 && usage <= 1024 * 1024) + return $"{usage / 1024} KB"; + else + return $"{usage / 1024 / 1024} MB"; + } + } +} diff --git a/src/TensorFlowNET.Console/MemoryTestingCases.cs b/src/TensorFlowNET.Console/MemoryTestingCases.cs new file mode 100644 index 00000000..f9356955 --- /dev/null +++ b/src/TensorFlowNET.Console/MemoryTestingCases.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Text; +using static Tensorflow.Binding; + +namespace Tensorflow +{ + class MemoryTestingCases + { + /// + /// + /// + public Action Constant + => (iterate) => + { + for (int i = 0; i < iterate; i++) + { + var tensor = tf.constant(3112.0f); + } + }; + public Action Variable + => (iterate) => + { + for (int i = 0; i < iterate; i++) + { + var tensor = tf.Variable(3112.0f); + } + }; + + public Action MathAdd + => (iterate) => + { + var x = tf.constant(3112.0f); + var y = tf.constant(3112.0f); + + for (int i = 0; i < iterate; i++) + { + var z = x + y; + } + }; + + public Action Gradient + => (iterate) => + { + for(int i = 0; i< iterate; i++) + { + var w = tf.constant(3112.0f); + using var tape = tf.GradientTape(); + tape.watch(w); + var loss = w * w; + var grad = tape.gradient(loss, w); + } + }; + } +} diff --git a/src/TensorFlowNET.Console/Program.cs b/src/TensorFlowNET.Console/Program.cs index 86ed503b..8cfd9200 100644 --- a/src/TensorFlowNET.Console/Program.cs +++ b/src/TensorFlowNET.Console/Program.cs @@ -7,11 +7,24 @@ namespace Tensorflow static void Main(string[] args) { // boot .net core 10.5M. - var memoryTest = new MemoryLeakTesting(); + var mm = new MemoryMonitor(); // warm up tensorflow.net 28.5M. - memoryTest.WarmUp(); - // 1 million float tensor 34.5M. - memoryTest.TensorCreation(); + mm.WarmUp(); + var cases = new MemoryTestingCases(); + + int batchSize = 1000; + + // 1 million float tensor 58.5M. + // mm.Execute(10, 100 * batchSize, cases.Constant); + + // 100K float variable 80.5M. + //mm.Execute(10, 10 * batchSize, cases.Variable); + + // 1 million math add 36.5M. + // mm.Execute(10, 100 * batchSize, cases.MathAdd); + + // 100K gradient 211M. + mm.Execute(100, 1 * batchSize, cases.Gradient); Console.WriteLine("Finished."); Console.ReadLine(); diff --git a/src/TensorFlowNET.Core/APIs/c_api.cs b/src/TensorFlowNET.Core/APIs/c_api.cs index d3dc15ed..c1575fb4 100644 --- a/src/TensorFlowNET.Core/APIs/c_api.cs +++ b/src/TensorFlowNET.Core/APIs/c_api.cs @@ -43,7 +43,7 @@ namespace Tensorflow /// public partial class c_api { - public const string TensorFlowLibName = "tensorflow"; + public const string TensorFlowLibName = @"D:\SciSharp\tensorflow-google\bazel-bin\tensorflow\tensorflow.dll"; public static string StringPiece(IntPtr handle) { diff --git a/src/TensorFlowNET.Core/Eager/EagerOperation.cs b/src/TensorFlowNET.Core/Eager/EagerOperation.cs index dfc5df78..39038608 100644 --- a/src/TensorFlowNET.Core/Eager/EagerOperation.cs +++ b/src/TensorFlowNET.Core/Eager/EagerOperation.cs @@ -7,8 +7,10 @@ namespace Tensorflow.Eager public class EagerOperation : Operation { public int NumInputs; + public IntPtr[] InputHandles { get; set; } public Tensor[] Inputs { get; set; } public int NumOutputs; + public IntPtr[] OutputHandles { get; set; } public Tensor[] Outputs { get; set; } public int[] SkipInputIndices { get; set; } diff --git a/src/TensorFlowNET.Core/Eager/EagerTensor.Creation.cs b/src/TensorFlowNET.Core/Eager/EagerTensor.Creation.cs index fb63e6d8..20edaf3a 100644 --- a/src/TensorFlowNET.Core/Eager/EagerTensor.Creation.cs +++ b/src/TensorFlowNET.Core/Eager/EagerTensor.Creation.cs @@ -2,47 +2,66 @@ using System; using System.Collections.Generic; using System.Text; -using static Tensorflow.Binding; +using Tensorflow.Gradients; namespace Tensorflow.Eager { public partial class EagerTensor : Tensor { - public EagerTensor(IntPtr handle) : base(handle) + public EagerTensor() : base(IntPtr.Zero) { - EagerTensorHandle = handle; - tfe_tensor_handle = c_api.EagerTensor_Handle(handle); - _handle = c_api.TFE_TensorHandleResolve(tfe_tensor_handle, status); - } - - /*public EagerTensor(int value, string device_name) : base(value) - { - tfe_tensor_handle = c_api.TFE_NewTensorHandle(_handle, status); - EagerTensorHandle = c_api.TFE_EagerTensorFromHandle(tf.context, tfe_tensor_handle); + EagerTensorHandle = c_api.TFE_NewEagerTensor(); + // _id = c_api.TFE_EagerTensorId(EagerTensorHandle); + // print($"new EagerTensorHandle {EagerTensorHandle.ToString("x16")} {Id}"); } - public EagerTensor(long value, string device_name) : base(value) + public EagerTensor(IntPtr handle) : base(IntPtr.Zero) { - tfe_tensor_handle = c_api.TFE_NewTensorHandle(_handle, status); - EagerTensorHandle = c_api.TFE_EagerTensorFromHandle(tf.context, tfe_tensor_handle); + EagerTensorHandle = handle; + Resolve(); + // print($"new EagerTensorHandle {EagerTensorHandle.ToString("x16")} {Id}"); } - public EagerTensor(float value, string device_name) : base(value) - { - tfe_tensor_handle = c_api.TFE_NewTensorHandle(_handle, status); - EagerTensorHandle = c_api.TFE_EagerTensorFromHandle(tf.context, tfe_tensor_handle); - }*/ - public EagerTensor(string value, string device_name) : base(value) { + EagerTensorHandle = c_api.TFE_NewEagerTensor(); tfe_tensor_handle = c_api.TFE_NewTensorHandle(_handle, status); - EagerTensorHandle = c_api.TFE_EagerTensorFromHandle(tf.context, tfe_tensor_handle); + c_api.TFE_SetEagerTensorHandle(EagerTensorHandle, tfe_tensor_handle); + Resolve(); + // print($"new EagerTensorHandle {EagerTensorHandle.ToString("x16")} {Id}"); } public EagerTensor(NDArray value, string device_name) : base(value) { + EagerTensorHandle = c_api.TFE_NewEagerTensor(); tfe_tensor_handle = c_api.TFE_NewTensorHandle(_handle, status); - EagerTensorHandle = c_api.TFE_EagerTensorFromHandle(tf.context, tfe_tensor_handle); + c_api.TFE_SetEagerTensorHandle(EagerTensorHandle, tfe_tensor_handle); + Resolve(); + // print($"new EagerTensorHandle {EagerTensorHandle.ToString("x16")} {Id}"); + } + + public EagerTensor Resolve() + { + if (tfe_tensor_handle == IntPtr.Zero) + tfe_tensor_handle = c_api.TFE_EagerTensorHandle(EagerTensorHandle); + + if (_handle == IntPtr.Zero) + _handle = c_api.TFE_TensorHandleResolve(tfe_tensor_handle, status); + + _id = c_api.TFE_EagerTensorId(EagerTensorHandle); + + GarbageCollector.Increase(_handle, GCItemType.TensorHandle); + GarbageCollector.Increase(tfe_tensor_handle, GCItemType.LocalTensorHandle); + GarbageCollector.Increase(EagerTensorHandle, GCItemType.EagerTensorHandle); + + return this; + } + + protected override void DisposeUnmanagedResources(IntPtr handle) + { + GarbageCollector.Decrease(_handle); + GarbageCollector.Decrease(tfe_tensor_handle); + GarbageCollector.Decrease(EagerTensorHandle); } } } diff --git a/src/TensorFlowNET.Core/Eager/EagerTensor.cs b/src/TensorFlowNET.Core/Eager/EagerTensor.cs index 13d89f73..7a88a602 100644 --- a/src/TensorFlowNET.Core/Eager/EagerTensor.cs +++ b/src/TensorFlowNET.Core/Eager/EagerTensor.cs @@ -13,6 +13,25 @@ namespace Tensorflow.Eager public IntPtr EagerTensorHandle { get; set; } public override string Device => c_api.StringPiece(c_api.TFE_TensorHandleDeviceName(tfe_tensor_handle, status)); + public override int rank => c_api.TFE_TensorHandleNumDims(tfe_tensor_handle, status); + + public static int GetRank(IntPtr handle) + { + var tfe_tensor_handle = c_api.TFE_EagerTensorHandle(handle); + using var status = new Status(); + return c_api.TFE_TensorHandleNumDims(tfe_tensor_handle, status); + } + + public static int[] GetDims(IntPtr handle) + { + var tfe_tensor_handle = c_api.TFE_EagerTensorHandle(handle); + using var status = new Status(); + var dims = new int[c_api.TFE_TensorHandleNumDims(tfe_tensor_handle, status)]; + for (int i = 0; i < dims.Length; i++) + dims[i] = c_api.TFE_TensorHandleDim(tfe_tensor_handle, i, status); + return dims; + } + public override string ToString() { switch (rank) diff --git a/src/TensorFlowNET.Core/Eager/Execute.cs b/src/TensorFlowNET.Core/Eager/Execute.cs index 7cb8ebbb..91db9ca6 100644 --- a/src/TensorFlowNET.Core/Eager/Execute.cs +++ b/src/TensorFlowNET.Core/Eager/Execute.cs @@ -33,18 +33,17 @@ namespace Tensorflow.Eager { ctx.ensure_initialized(); - using var status = new Status(); - - BindingArray results = c_api.TFE_QuickExecute(ctx, + var results = Enumerable.Range(0, num_outputs).Select(x => new EagerTensor()).ToArray(); + Status status = c_api.TFE_QuickExecute(ctx, ctx.device_name, op_name, inputs.Select(x => x.EagerTensorHandle).ToArray(), inputs.Length, op => wrap_tfe_src.SetOpAttrs(op, attrs), - status); + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return results.Data().Select(x => new EagerTensor(x)).ToArray(); + return results.Select(x => x.Resolve()).ToArray(); } public (TF_DataType, EagerTensor[]) args_to_matching_eager(Context ctx, TF_DataType default_dtype = TF_DataType.DtInvalid, object[] args = null) diff --git a/src/TensorFlowNET.Core/Eager/c_api.eager.cs b/src/TensorFlowNET.Core/Eager/c_api.eager.cs index 6b33dfa0..b175e3e2 100644 --- a/src/TensorFlowNET.Core/Eager/c_api.eager.cs +++ b/src/TensorFlowNET.Core/Eager/c_api.eager.cs @@ -11,18 +11,28 @@ namespace Tensorflow public static extern void TFE_RegisterGradientFunction(gradient_function_callback gradientFunctionCallback, delete_backward_function_callback deleteBackwardFunctionCallback); + /// + /// + /// + /// + /// + /// + /// + /// previous node ouput + /// + /// [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate IntPtr gradient_function_callback(string op_name, - BindingArray op_inputs, - BindingArray op_outputs, + IntPtr op_inputs, + IntPtr op_outputs, int num_attrs, - BindingArray output_grads, - BindingArray skip_input_indices); + IntPtr output_grads, + IntPtr skip_input_indices); [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate void delete_backward_function_callback(string op_name, - BindingArray op_inputs, - BindingArray op_outputs); + IntPtr op_inputs, + IntPtr op_outputs); [DllImport(TensorFlowLibName)] public static extern IntPtr TFE_WrapGradientResult(IntPtr[] gradients, int num_gradients); @@ -32,7 +42,7 @@ namespace Tensorflow [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate IntPtr VSpace_callback_Ones(long[] shape, int dims, TF_DataType dtype); [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate IntPtr VSpace_callback_AggregateGrads(BindingArray gradients); + public delegate IntPtr VSpace_callback_AggregateGrads(TF_BindingArray gradients); [DllImport(TensorFlowLibName)] public static extern void TFE_RegisterVSpace(IntPtr vspace); @@ -217,10 +227,16 @@ namespace Tensorflow public static extern TFE_TensorHandle TFE_NewTensorHandle(IntPtr t, IntPtr status); [DllImport(TensorFlowLibName)] - public static extern IntPtr EagerTensor_Handle(IntPtr t); + public static extern IntPtr TFE_EagerTensorHandle(IntPtr t); [DllImport(TensorFlowLibName)] - public static extern IntPtr TFE_EagerTensorFromHandle(IntPtr ctx, IntPtr h); + public static extern int TFE_EagerTensorId(IntPtr t); + + [DllImport(TensorFlowLibName)] + public static extern IntPtr TFE_NewEagerTensor(); + + [DllImport(TensorFlowLibName)] + public static extern void TFE_SetEagerTensorHandle(IntPtr tensor, IntPtr handle); /// /// Sets the default execution mode (sync/async). Note that this can be @@ -260,6 +276,9 @@ namespace Tensorflow [DllImport(TensorFlowLibName)] public static extern int TFE_TensorHandleNumDims(IntPtr h, IntPtr status); + [DllImport(TensorFlowLibName)] + public static extern int TFE_TensorHandleDim(IntPtr h, int dim, IntPtr status); + /// /// Returns the device of the operation that produced `h`. If `h` was produced by /// a copy, returns the destination device of the copy. Note that the returned @@ -304,7 +323,13 @@ namespace Tensorflow /// TFE_TensorHandle* [DllImport(TensorFlowLibName)] public static extern void TFE_DeleteEagerTensor(IntPtr h); - + + [DllImport(TensorFlowLibName)] + public static extern void TF_DeleteBindingArray(IntPtr h); + + [DllImport(TensorFlowLibName)] + public static extern void TFE_DeleteBindingTensorArray(IntPtr h); + /// /// Creates a new eager Executor. Nodes in one executor are guaranteed to be /// executed in sequence. Assigning nodes to different executors allows executing @@ -372,10 +397,11 @@ namespace Tensorflow string device_name, string op_name, string name, - IntPtr[] args, + IntPtr[] inputs, int input_size, TFE_FastPathExecute_SetOpAttrs set_op_attrs, - IntPtr status); + IntPtr[] outputs, + int output_size); [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate void TFE_FastPathExecute_SetOpAttrs(IntPtr op); @@ -386,13 +412,8 @@ namespace Tensorflow IntPtr[] inputs, int input_size, TFE_FastPathExecute_SetOpAttrs set_op_attrs, - IntPtr status); - - [DllImport(TensorFlowLibName)] - public static extern IntPtr TFE_QuickExecute1( - string op_name, - int input_size, - IntPtr status); + IntPtr[] outputs, + int output_size); [DllImport(TensorFlowLibName)] public static extern IntPtr TFE_TapeSetNew(bool persistent, bool watch_accessed_variables); @@ -415,7 +436,7 @@ namespace Tensorflow [DllImport(TensorFlowLibName)] public static extern IntPtr TFE_TapeGradient(IntPtr tape, IntPtr[] target, int target_size, - IntPtr[] sources, int source_size, - IntPtr status); + IntPtr[] sources, int source_size, + IntPtr[] outputs, int output_size); } } diff --git a/src/TensorFlowNET.Core/Gradients/GradientTape.cs b/src/TensorFlowNET.Core/Gradients/GradientTape.cs index c4cf0cce..ef1ea9fa 100644 --- a/src/TensorFlowNET.Core/Gradients/GradientTape.cs +++ b/src/TensorFlowNET.Core/Gradients/GradientTape.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using Tensorflow.Eager; using static Tensorflow.Binding; @@ -21,7 +22,8 @@ namespace Tensorflow.Gradients /// public class GradientTape : IDisposable { - bool _recording; + static bool _recording; + public static bool Recording => _recording; bool _persistent; bool _watch_accessed_variables; ResourceVariable[] _watched_variables; @@ -76,13 +78,13 @@ namespace Tensorflow.Gradients _pop_tape(); } - using var status = new Status(); - var et = c_api.TFE_TapeGradient(_tape, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_TapeGradient(_tape, new [] { (target as EagerTensor).EagerTensorHandle }, 1, new [] { (source as EagerTensor).EagerTensorHandle }, 1, - status); + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(et); + return results[0]; } public unsafe (Tensor, Tensor) gradient(Tensor target, (ResourceVariable, ResourceVariable) sources) @@ -93,8 +95,8 @@ namespace Tensorflow.Gradients _pop_tape(); } - using var status = new Status(); - BindingArray result_handle = c_api.TFE_TapeGradient(_tape, + var results = new[] { new EagerTensor(), new EagerTensor() }; + Status status = c_api.TFE_TapeGradient(_tape, new IntPtr[] { target as EagerTensor @@ -104,12 +106,9 @@ namespace Tensorflow.Gradients (sources.Item1.Handle as EagerTensor).EagerTensorHandle, (sources.Item2.Handle as EagerTensor).EagerTensorHandle }, 2, - status); + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - var results = result_handle.Data().Select(x => new EagerTensor(x)).ToArray(); - - if (!_persistent) { // Keep track of watched variables before setting tape to None diff --git a/src/TensorFlowNET.Core/Gradients/RegisterGradientEager.cs b/src/TensorFlowNET.Core/Gradients/RegisterGradientEager.cs new file mode 100644 index 00000000..0c621750 --- /dev/null +++ b/src/TensorFlowNET.Core/Gradients/RegisterGradientEager.cs @@ -0,0 +1,30 @@ +/***************************************************************************** + 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 System; + +namespace Tensorflow.Gradients +{ + public class RegisterGradientEager : Attribute + { + public string Name { get; set; } + + public RegisterGradientEager(string name) + { + Name = name; + } + } +} diff --git a/src/TensorFlowNET.Core/Gradients/Tape.cs b/src/TensorFlowNET.Core/Gradients/Tape.cs index de5548e8..9a52d743 100644 --- a/src/TensorFlowNET.Core/Gradients/Tape.cs +++ b/src/TensorFlowNET.Core/Gradients/Tape.cs @@ -34,7 +34,7 @@ namespace Tensorflow.Gradients public unsafe ResourceVariable[] watched_variables() { BindingArray result = c_api.TFE_TapeWatchedVariables(_handle); - var variables = result.Data().Select(x => + var variables = result.Data.Select(x => { var tensor = c_api.ResourceVariable_Handle(x); return new ResourceVariable(x, tensor); diff --git a/src/TensorFlowNET.Core/Gradients/math_grad_eager.cs b/src/TensorFlowNET.Core/Gradients/math_grad_eager.cs new file mode 100644 index 00000000..2e5b65a9 --- /dev/null +++ b/src/TensorFlowNET.Core/Gradients/math_grad_eager.cs @@ -0,0 +1,74 @@ +/***************************************************************************** + 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.Linq; +using Tensorflow.Eager; +using Tensorflow.Operations; +using static Tensorflow.Binding; + +namespace Tensorflow.Gradients +{ + /// + /// Gradients for operators defined in math_ops.py. + /// + [RegisterGradientEager("math_grad")] + public class math_grad_eager + { + [RegisterGradientEager("Mul")] + public static EagerTensor[] _MulGrad(EagerOperation op, IntPtr[] grads) + { + var x = op.InputHandles[0]; + var y = op.InputHandles[1]; + var grad = grads[0]; + + if (op.SkipInputIndices.Contains(1) && + EagerTensor.GetRank(grad) == 0) + { + return new EagerTensor[] + { + null,//gen_math_ops.mul(grad, math_ops.conj(y)), + null + }; + } + + if (_ShapesFullySpecifiedAndEqual(x, y, grad)) + { + return new EagerTensor[] + { + gen_math_ops.mul(grad, y), + gen_math_ops.mul(grad, x) + }; + } + + throw new NotImplementedException(""); + } + + public static bool _ShapesFullySpecifiedAndEqual(IntPtr x, IntPtr y, IntPtr grad) + { + var x_shape = EagerTensor.GetDims(x); + var y_shape = EagerTensor.GetDims(y); + + var grad_shape = EagerTensor.GetDims(grad); + return x_shape != null && + y_shape != null && + Enumerable.SequenceEqual(x_shape, y_shape) && + Enumerable.SequenceEqual(y_shape, grad_shape) && + !x_shape.Contains(-1); + } + } +} diff --git a/src/TensorFlowNET.Core/Gradients/ops.gradient_function_mapping_eager.cs b/src/TensorFlowNET.Core/Gradients/ops.gradient_function_mapping_eager.cs new file mode 100644 index 00000000..432113e0 --- /dev/null +++ b/src/TensorFlowNET.Core/Gradients/ops.gradient_function_mapping_eager.cs @@ -0,0 +1,101 @@ +/***************************************************************************** + 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 System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Tensorflow.Eager; +using Tensorflow.Gradients; + +namespace Tensorflow +{ + public partial class ops + { + public static Dictionary> gradientFunctionsEager = null; + + public static void RegisterFromAssemblyEager() + { + if (gradientFunctionsEager == null) + { + gradientFunctionsEager = new Dictionary>(); + + var gradGroups = Assembly.GetExecutingAssembly() + .GetTypes() + .Where(x => x.GetCustomAttribute() != null) + .ToArray(); + + foreach (var g in gradGroups) + { + var methods = g.GetMethods() + .Where(x => x.GetCustomAttribute() != null) + .ToArray(); + + foreach (var m in methods) + { + RegisterGradientFunctionEager(m.GetCustomAttribute().Name, + (oper, out_grads) => + g.InvokeMember(m.Name, + BindingFlags.InvokeMethod, + null, + null, + args: new object[] { oper, out_grads }) as EagerTensor[] + ); + } + + // REGISTER_NO_GRADIENT_OP + methods = g.GetMethods() + .Where(x => x.GetCustomAttribute() != null) + .ToArray(); + + foreach (var m in methods) + RegisterNoGradientFunctionEager(m.GetCustomAttribute().Name); + } + } + } + + /// + /// Regiter new gradient function + /// + /// operation type + /// function delegate + public static void RegisterGradientFunctionEager(string name, Func func) + { + RegisterFromAssemblyEager(); + + gradientFunctionsEager[name] = func; + } + + public static void RegisterNoGradientFunctionEager(string name) + { + RegisterFromAssemblyEager(); + + gradientFunctionsEager[name] = null; + } + + public static Func get_gradient_function_eager(EagerOperation op) + { + if (op.inputs == null) return null; + + RegisterFromAssemblyEager(); + + if (!gradientFunctionsEager.ContainsKey(op.type)) + throw new LookupError($"can't get graident function through get_gradient_function {op.type}"); + + return gradientFunctionsEager[op.type]; + } + } +} diff --git a/src/TensorFlowNET.Core/Operations/NnOps/gen_nn_ops.cs b/src/TensorFlowNET.Core/Operations/NnOps/gen_nn_ops.cs index 9cada111..b7c3b392 100644 --- a/src/TensorFlowNET.Core/Operations/NnOps/gen_nn_ops.cs +++ b/src/TensorFlowNET.Core/Operations/NnOps/gen_nn_ops.cs @@ -15,6 +15,7 @@ ******************************************************************************/ using System; +using System.Linq; using Tensorflow.Eager; using static Tensorflow.Binding; @@ -467,14 +468,15 @@ namespace Tensorflow.Operations { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Relu", name, new IntPtr[] { features as EagerTensor, - }, 1, null, status); + }, 1, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Relu", name: name, args: new { features }); @@ -485,14 +487,15 @@ namespace Tensorflow.Operations { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Tanh", name, new IntPtr[] { x as EagerTensor, - }, 1, null, status); + }, 1, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Tanh", name: name, args: new { x }); diff --git a/src/TensorFlowNET.Core/Operations/gen_array_ops.cs b/src/TensorFlowNET.Core/Operations/gen_array_ops.cs index e85c743a..8fcc31e6 100644 --- a/src/TensorFlowNET.Core/Operations/gen_array_ops.cs +++ b/src/TensorFlowNET.Core/Operations/gen_array_ops.cs @@ -54,15 +54,16 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "ConcatV2", name, new IntPtr[] { values as EagerTensor, axis as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("ConcatV2", name: name, args: new { values, axis }); @@ -161,14 +162,14 @@ namespace Tensorflow { if(tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Pack", name, values.Select(x => (x as EagerTensor).EagerTensorHandle).ToArray(), values.Length, op => wrap_tfe_src.SetOpAttrs(op, "axis", axis), - status); + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Pack", name: name, args: new { values, axis }); @@ -229,14 +230,15 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Identity", name, new IntPtr[] { input as EagerTensor - }, 1, null, status); + }, 1, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Identity", name, new { input }); @@ -276,15 +278,16 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Fill", name, new IntPtr[] { dims as EagerTensor, value as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Fill", name, new { dims, value }); @@ -302,15 +305,16 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor(), new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "BroadcastGradientArgs", name, new IntPtr[] { s0 as EagerTensor, s1 as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return (new EagerTensor(results[0]), new EagerTensor(results[1])); + return (results[0], results[1]); } var _op = _op_def_lib._apply_op_helper("BroadcastGradientArgs", name, new { s0, s1 }); @@ -328,15 +332,16 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Reshape", name, new IntPtr[] { tensor as EagerTensor, shape as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Reshape", name, new { tensor, shape }); @@ -416,16 +421,16 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Shape", name, new IntPtr[] { input as EagerTensor, }, 1, op => wrap_tfe_src.SetOpAttrs(op, "out_type", out_type), - status); + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Shape", name, new { input, out_type }); @@ -475,15 +480,16 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Tile", name, new IntPtr[] { input as EagerTensor, multiples as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Tile", name, new { input, multiples }); @@ -519,8 +525,8 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "StridedSlice", name, new IntPtr[] { input as EagerTensor, @@ -533,10 +539,10 @@ namespace Tensorflow "end_mask", end_mask, "ellipsis_mask", ellipsis_mask, "new_axis_mask", new_axis_mask, - "shrink_axis_mask", shrink_axis_mask), - status); + "shrink_axis_mask", shrink_axis_mask), + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("StridedSlice", name, new diff --git a/src/TensorFlowNET.Core/Operations/gen_math_ops.cs b/src/TensorFlowNET.Core/Operations/gen_math_ops.cs index 8f9e6d88..9df1986b 100644 --- a/src/TensorFlowNET.Core/Operations/gen_math_ops.cs +++ b/src/TensorFlowNET.Core/Operations/gen_math_ops.cs @@ -16,12 +16,13 @@ using System; using System.Linq; +using System.Runtime.InteropServices; using Tensorflow.Eager; using static Tensorflow.Binding; namespace Tensorflow { - public static class gen_math_ops + public static partial class gen_math_ops { public static OpDefLibrary _op_def_lib = new OpDefLibrary(); public static Execute _execute = new Execute(); @@ -43,14 +44,14 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "AddN", name, inputs.Select(x => (x as EagerTensor).EagerTensorHandle).ToArray(), inputs.Length, null, - status); + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("AddN", name, args: new { inputs }); @@ -58,6 +59,18 @@ namespace Tensorflow return _op.outputs[0]; } + public static EagerTensor add_n(IntPtr[] inputs, string name = null) + { + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + "AddN", name, + inputs, inputs.Length, + null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); + status.Check(true); + return results[0].Resolve(); + } + /// /// Returns the index with the largest value across dimensions of a tensor. /// @@ -131,8 +144,8 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Mean", name, new IntPtr[] { @@ -140,9 +153,9 @@ namespace Tensorflow axis as EagerTensor }, 2, op => wrap_tfe_src.SetOpAttrs(op, "keep_dims", keep_dims), - status); + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0]; } var _op = _op_def_lib._apply_op_helper("Mean", name, args: new { input, reduction_indices = axis, keep_dims = keep_dims }); @@ -178,17 +191,17 @@ namespace Tensorflow { try { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Prod", name, new IntPtr[] { input as EagerTensor, axis as EagerTensor }, 2, - op => wrap_tfe_src.SetOpAttrs(op, "keep_dims", keep_dims), - status); + op => wrap_tfe_src.SetOpAttrs(op, "keep_dims", keep_dims), + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } catch (Exception) { @@ -228,15 +241,16 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Add", name, new IntPtr[] { x as EagerTensor, y as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Add", name, args: new { x, y }); @@ -248,15 +262,16 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Add", name, new IntPtr[] { x as EagerTensor, y as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Add", name, args: new { x, y }); @@ -269,15 +284,16 @@ namespace Tensorflow // forward_compatible(2019, 6, 25): if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "AddV2", name, new IntPtr[] { x as EagerTensor, y as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("AddV2", name, args: new { x, y }); @@ -303,14 +319,15 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Sin", name, new IntPtr[] { x as EagerTensor, - }, 1, null, status); + }, 1, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Sin", name, args: new { x }); @@ -336,14 +353,15 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Sigmoid", name, new IntPtr[] { x as EagerTensor, - }, 1, null, status); + }, 1, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var op = _op_def_lib._apply_op_helper("Sigmoid", name: name, new { x }); @@ -428,14 +446,15 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Tan", name, new IntPtr[] { x as EagerTensor, - }, 1, null, status); + }, 1, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Tan", name, args: new { x }); @@ -510,15 +529,16 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Less", name, new IntPtr[] { x as EagerTensor, y as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Less", name: name, args: new { x, y }); @@ -586,14 +606,15 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Square", name, new IntPtr[] { x as EagerTensor, - }, 1, null, status); + }, 1, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Square", name, args: new { x }); @@ -651,14 +672,14 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Cast", name, new IntPtr[] { x as EagerTensor }, 1, op => wrap_tfe_src.SetOpAttrs(op, "DstT", DstT, "Truncate", Truncate), - status); + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Cast", name, args: new { x, DstT, Truncate }); @@ -670,14 +691,15 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Neg", name, new IntPtr[] { x as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Neg", name, args: new { x }); @@ -689,14 +711,15 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Sqrt", name, new IntPtr[] { x as EagerTensor, - }, 1, null, status); + }, 1, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Sqrt", name, args: new { x }); @@ -708,15 +731,16 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Sub", name, new IntPtr[] { x as EagerTensor, y as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Sub", name, args: new { x, y }); @@ -728,15 +752,16 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Sub", name, new IntPtr[] { x as EagerTensor, y as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Sub", name, args: new { x, y }); @@ -755,15 +780,16 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Equal", name, new IntPtr[] { x as EagerTensor, y as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Equal", name, args: new { x, y }); @@ -783,15 +809,16 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "NotEqual", name, new IntPtr[] { x as EagerTensor, y as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("NotEqual", name, args: new { x, y }); @@ -803,15 +830,16 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Atan2", name, new IntPtr[] { y as EagerTensor, x as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Atan2", name, args: new { y, x }); @@ -822,15 +850,16 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Mul", name, new IntPtr[] { x as EagerTensor, y as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Mul", name, args: new { x, y }); @@ -842,15 +871,16 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Mul", name, new IntPtr[] { x as EagerTensor, y as EagerTensor, - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Mul", name, args: new { x, y }); @@ -869,15 +899,16 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "RealDiv", name, new IntPtr[] { x as EagerTensor, y as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("RealDiv", name, args: new { x, y }); @@ -896,15 +927,16 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "FloorMod", name, new IntPtr[] { x as EagerTensor, y as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("FloorMod", name, args: new { x, y }); @@ -916,15 +948,16 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "FloorDiv", name, new IntPtr[] { x as EagerTensor, y as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("FloorDiv", name, args: new { x, y }); @@ -945,8 +978,8 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "MatMul", name, new IntPtr[] { @@ -955,10 +988,10 @@ namespace Tensorflow }, 2, op => wrap_tfe_src.SetOpAttrs(op, "transpose_a", transpose_a, - "transpose_b", transpose_b), - status); + "transpose_b", transpose_b), + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("MatMul", name, args: new { a, b, transpose_a, transpose_b }); @@ -1054,15 +1087,16 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Pow", name, new IntPtr[] { x as EagerTensor, y as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Pow", name, args: new { x, y }); @@ -1074,8 +1108,8 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Sum", name, new IntPtr[] { @@ -1083,9 +1117,9 @@ namespace Tensorflow axis as EagerTensor }, 2, op => wrap_tfe_src.SetOpAttrs(op, "keep_dims", keep_dims), - status); + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Sum", name, args: new { input, reduction_indices = axis, keep_dims }); @@ -1128,16 +1162,17 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "Range", name, new IntPtr[] { start as EagerTensor, limit as EagerTensor, delta as EagerTensor - }, 3, null, status); + }, 3, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("Range", name, new { start, limit, delta }); diff --git a/src/TensorFlowNET.Core/Operations/gen_math_ops.eager.cs b/src/TensorFlowNET.Core/Operations/gen_math_ops.eager.cs new file mode 100644 index 00000000..80655943 --- /dev/null +++ b/src/TensorFlowNET.Core/Operations/gen_math_ops.eager.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Tensorflow.Eager; +using static Tensorflow.Binding; + +namespace Tensorflow +{ + public static partial class gen_math_ops + { + public static EagerTensor mul(IntPtr x, IntPtr y, string name = null) + { + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + "Mul", name, new IntPtr[] + { + x, + y, + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); + status.Check(true); + return results[0].Resolve(); + } + } +} diff --git a/src/TensorFlowNET.Core/Operations/gen_random_ops.cs b/src/TensorFlowNET.Core/Operations/gen_random_ops.cs index 65824da3..bf474814 100644 --- a/src/TensorFlowNET.Core/Operations/gen_random_ops.cs +++ b/src/TensorFlowNET.Core/Operations/gen_random_ops.cs @@ -14,6 +14,7 @@ limitations under the License. ******************************************************************************/ using System; +using System.Linq; using Tensorflow.Eager; using static Tensorflow.Binding; @@ -41,8 +42,8 @@ namespace Tensorflow if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "RandomStandardNormal", name, new IntPtr[] { shape as EagerTensor, @@ -50,10 +51,10 @@ namespace Tensorflow op => wrap_tfe_src.SetOpAttrs(op, "seed", seed, "seed2", seed2, - "dtype", dtype), - status); + "dtype", dtype), + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("RandomStandardNormal", diff --git a/src/TensorFlowNET.Core/Operations/gen_resource_variable_ops.cs b/src/TensorFlowNET.Core/Operations/gen_resource_variable_ops.cs index 1307f81f..37e78188 100644 --- a/src/TensorFlowNET.Core/Operations/gen_resource_variable_ops.cs +++ b/src/TensorFlowNET.Core/Operations/gen_resource_variable_ops.cs @@ -29,16 +29,17 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "AssignSubVariableOp", name, new IntPtr[] { resource as EagerTensor, value as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return results[0]; + return results[0].Resolve(); } return null; @@ -55,16 +56,17 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "AssignAddVariableOp", name, new IntPtr[] { resource as EagerTensor, value as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return results[0]; + return results[0].Resolve(); } return null; @@ -74,14 +76,15 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - var results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new EagerTensor[0]; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "AssignVariableOp", name, new IntPtr[] { resource as EagerTensor, value as EagerTensor - }, 2, null, status); + }, 2, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); return null; } @@ -95,13 +98,14 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "VarIsInitializedOp", name, new IntPtr[] { resource as EagerTensor }, - 1, null, status); + 1, null, + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("VarIsInitializedOp", name, new { resource }); @@ -123,17 +127,17 @@ namespace Tensorflow { if(tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "VarHandleOp", name, null, 0, op => wrap_tfe_src.SetOpAttrs(op, "container", container, "shared_name", shared_name, "dtype", dtype, - "shape", shape.dims), - status); + "shape", shape.dims), + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("VarHandleOp", name, new { @@ -157,14 +161,14 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "ReadVariableOp", name, new IntPtr[] { resource as EagerTensor }, 1, op => wrap_tfe_src.SetOpAttrs(op, "dtype", dtype), - status); + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return new EagerTensor(results[0]); + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("ReadVariableOp", name, new diff --git a/src/TensorFlowNET.Core/Status/Status.cs b/src/TensorFlowNET.Core/Status/Status.cs index 90597195..68f86c4e 100644 --- a/src/TensorFlowNET.Core/Status/Status.cs +++ b/src/TensorFlowNET.Core/Status/Status.cs @@ -42,6 +42,11 @@ namespace Tensorflow _handle = TF_NewStatus(); } + public Status(IntPtr handle) + { + _handle = handle; + } + public void SetStatus(TF_Code code, string msg) { TF_SetStatus(_handle, code, msg); @@ -69,6 +74,9 @@ namespace Tensorflow public static implicit operator IntPtr(Status status) => status._handle; + public static implicit operator Status(IntPtr handle) + => new Status(handle); + protected override void DisposeUnmanagedResources(IntPtr handle) => TF_DeleteStatus(handle); diff --git a/src/TensorFlowNET.Core/System/GCItemCounter.cs b/src/TensorFlowNET.Core/System/GCItemCounter.cs new file mode 100644 index 00000000..8eecde03 --- /dev/null +++ b/src/TensorFlowNET.Core/System/GCItemCounter.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Tensorflow +{ + public class GCItemCounter + { + public GCItemType ItemType { get; set; } + public int RefCounter { get; set; } + public DateTime LastUpdateTime { get; set; } + public IntPtr Handle { get; set; } + + public override string ToString() + => $"{ItemType} {RefCounter} {LastUpdateTime}"; + } +} diff --git a/src/TensorFlowNET.Core/System/GCItemType.cs b/src/TensorFlowNET.Core/System/GCItemType.cs new file mode 100644 index 00000000..ed6b0b2a --- /dev/null +++ b/src/TensorFlowNET.Core/System/GCItemType.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Tensorflow +{ + public enum GCItemType + { + TensorHandle = 0, + LocalTensorHandle = 1, + EagerTensorHandle = 2 + } +} diff --git a/src/TensorFlowNET.Core/System/GarbageCollector.cs b/src/TensorFlowNET.Core/System/GarbageCollector.cs new file mode 100644 index 00000000..b7ea74d0 --- /dev/null +++ b/src/TensorFlowNET.Core/System/GarbageCollector.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Timers; + +namespace Tensorflow +{ + public class GarbageCollector + { + static Dictionary container = new Dictionary(); + static Timer timer = null; + static object locker = new object(); + + public static void Increase(IntPtr handle, GCItemType type) + { + if(timer == null) + { + timer = new Timer(300); + // Hook up the Elapsed event for the timer. + timer.Elapsed += OnTimedEvent; + timer.AutoReset = true; + timer.Enabled = true; + } + + if (container.ContainsKey(handle)) + { + container[handle].RefCounter++; + container[handle].LastUpdateTime = DateTime.Now; + } + else + { + lock (locker) + { + container[handle] = new GCItemCounter + { + ItemType = type, + RefCounter = 1, + Handle = handle, + LastUpdateTime = DateTime.Now + }; + } + } + } + + public static void Decrease(IntPtr handle) + { + lock (locker) + { + if (container.ContainsKey(handle)) + container[handle].RefCounter--; + } + } + + private static void OnTimedEvent(object source, ElapsedEventArgs e) + { + timer.Stop(); + + // dispose before 1 sec + lock (locker) + { + var items = container.Values + .Where(x => x.RefCounter <= 0 && (DateTime.Now - x.LastUpdateTime).Milliseconds > 300) + .ToArray(); + + foreach (var item in items) + { + item.RefCounter = 0; + container.Remove(item.Handle); + switch (item.ItemType) + { + case GCItemType.TensorHandle: + c_api.TF_DeleteTensor(item.Handle); + break; + case GCItemType.LocalTensorHandle: + c_api.TFE_DeleteTensorHandle(item.Handle); + break; + case GCItemType.EagerTensorHandle: + c_api.TFE_DeleteEagerTensor(item.Handle); + break; + default: + break; + } + } + } + + timer.Start(); + } + } +} diff --git a/src/TensorFlowNET.Core/TensorFlow.Binding.csproj b/src/TensorFlowNET.Core/TensorFlow.Binding.csproj index ae6548a9..e462fded 100644 --- a/src/TensorFlowNET.Core/TensorFlow.Binding.csproj +++ b/src/TensorFlowNET.Core/TensorFlow.Binding.csproj @@ -62,6 +62,7 @@ Please be patient, we're working hard on missing functions, providing full tenso + True diff --git a/src/TensorFlowNET.Core/Tensors/EagerTensorV2.cs b/src/TensorFlowNET.Core/Tensors/EagerTensorV2.cs index 83d23255..9f1d1929 100644 --- a/src/TensorFlowNET.Core/Tensors/EagerTensorV2.cs +++ b/src/TensorFlowNET.Core/Tensors/EagerTensorV2.cs @@ -21,7 +21,7 @@ namespace Tensorflow public EagerTensorV2(IntPtr handle) { EagerTensorHandle = handle; - tfe_tensor_handle = c_api.EagerTensor_Handle(handle); + tfe_tensor_handle = c_api.TFE_EagerTensorHandle(handle); _handle = c_api.TFE_TensorHandleResolve(tfe_tensor_handle, status); } @@ -43,7 +43,7 @@ namespace Tensorflow }, IntPtr.Zero); tfe_tensor_handle = c_api.TFE_NewTensorHandle(_handle, status); - EagerTensorHandle = c_api.TFE_EagerTensorFromHandle(tf.context, tfe_tensor_handle); + EagerTensorHandle = c_api.TFE_NewEagerTensor(); } /*public unsafe EagerTensorV2(float[,] value) diff --git a/src/TensorFlowNET.Core/Tensors/TF_BindingArray.cs b/src/TensorFlowNET.Core/Tensors/TF_BindingArray.cs new file mode 100644 index 00000000..2999dc86 --- /dev/null +++ b/src/TensorFlowNET.Core/Tensors/TF_BindingArray.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace Tensorflow +{ + [StructLayout(LayoutKind.Sequential)] + public struct TF_BindingArray + { + public IntPtr array; + public int length; + + public static implicit operator TF_BindingArray(IntPtr handle) + => handle == IntPtr.Zero ? default : Marshal.PtrToStructure(handle); + + public unsafe IntPtr this[int index] + => array == IntPtr.Zero ? IntPtr.Zero : *((IntPtr*)array + index); + + public unsafe IntPtr[] Data + { + get + { + var results = new IntPtr[length]; + for (int i = 0; i < length; i++) + results[i] = array == IntPtr.Zero ? IntPtr.Zero : *((IntPtr*)array + i); + return results; + } + } + } +} diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.cs b/src/TensorFlowNET.Core/Tensors/Tensor.cs index c11e2e6c..89fb00d1 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.cs @@ -171,7 +171,7 @@ namespace Tensorflow /// n n-Tensor (you get the idea) /// /// https://www.tensorflow.org/api_docs/python/tf/rank - public int rank + public virtual int rank { get { diff --git a/src/TensorFlowNET.Core/Training/gen_training_ops.py.cs b/src/TensorFlowNET.Core/Training/gen_training_ops.py.cs index 40ba3584..e9d4e854 100644 --- a/src/TensorFlowNET.Core/Training/gen_training_ops.py.cs +++ b/src/TensorFlowNET.Core/Training/gen_training_ops.py.cs @@ -15,6 +15,7 @@ ******************************************************************************/ using System; +using System.Linq; using Tensorflow.Eager; using static Tensorflow.Binding; @@ -64,18 +65,18 @@ namespace Tensorflow { if (tf.context.executing_eagerly()) { - using var status = new Status(); - BindingArray results = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, + var results = new[] { new EagerTensor() }; + Status status = c_api.TFE_FastPathExecute(tf.context, tf.context.device_name, "ResourceApplyGradientDescent", name, new IntPtr[] { var, alpha, delta }, 3, - op => wrap_tfe_src.SetOpAttrs(op, "use_locking", use_locking), - status); + op => wrap_tfe_src.SetOpAttrs(op, "use_locking", use_locking), + results.Select(x => x.EagerTensorHandle).ToArray(), results.Length); status.Check(true); - return results[0]; + return results[0].Resolve(); } var _op = _op_def_lib._apply_op_helper("ResourceApplyGradientDescent", name, new diff --git a/src/TensorFlowNET.Core/Util/BindingArray.cs b/src/TensorFlowNET.Core/Util/BindingArray.cs index 984e6642..788d389d 100644 --- a/src/TensorFlowNET.Core/Util/BindingArray.cs +++ b/src/TensorFlowNET.Core/Util/BindingArray.cs @@ -19,24 +19,32 @@ using System.Runtime.InteropServices; namespace Tensorflow { - [StructLayout(LayoutKind.Sequential)] - public struct BindingArray + public class BindingArray : DisposableObject { - public IntPtr array; - public int length; + TF_BindingArray data; + public IntPtr Address => data.array; + public int Length => data.length; + + public BindingArray(IntPtr handle) : base(handle) + { + if (_handle != IntPtr.Zero) + data = Marshal.PtrToStructure(_handle); + else + data = default; + } public static implicit operator BindingArray(IntPtr handle) - => handle == IntPtr.Zero ? default : Marshal.PtrToStructure(handle); + => new BindingArray(handle); public unsafe IntPtr this[int index] - => array == IntPtr.Zero ? IntPtr.Zero : * ((IntPtr*)array + index); + => data[index]; + + public unsafe IntPtr[] Data + => data.Data; - public unsafe IntPtr[] Data() + protected override void DisposeUnmanagedResources(IntPtr handle) { - var results = new IntPtr[length]; - for (int i = 0; i < length; i++) - results[i] = array == IntPtr.Zero ? IntPtr.Zero : * ((IntPtr*)array + i); - return results; + c_api.TF_DeleteBindingArray(_handle); } } } diff --git a/src/TensorFlowNET.Core/Util/BindingTensorArray.cs b/src/TensorFlowNET.Core/Util/BindingTensorArray.cs new file mode 100644 index 00000000..c3862d97 --- /dev/null +++ b/src/TensorFlowNET.Core/Util/BindingTensorArray.cs @@ -0,0 +1,50 @@ +/***************************************************************************** + 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 System; +using System.Runtime.InteropServices; + +namespace Tensorflow +{ + public class BindingTensorArray : DisposableObject + { + TF_BindingArray data; + public IntPtr Address => data.array; + public int Length => data.length; + + public BindingTensorArray(IntPtr handle) : base(handle) + { + if (_handle != IntPtr.Zero) + data = Marshal.PtrToStructure(_handle); + else + data = default; + } + + public static implicit operator BindingTensorArray(IntPtr handle) + => new BindingTensorArray(handle); + + public unsafe IntPtr this[int index] + => data[index]; + + public unsafe IntPtr[] Data + => data.Data; + + protected override void DisposeUnmanagedResources(IntPtr handle) + { + c_api.TFE_DeleteBindingTensorArray(_handle); + } + } +} diff --git a/src/TensorFlowNET.Core/tensorflow.cs b/src/TensorFlowNET.Core/tensorflow.cs index 942388aa..1acf07ba 100644 --- a/src/TensorFlowNET.Core/tensorflow.cs +++ b/src/TensorFlowNET.Core/tensorflow.cs @@ -14,11 +14,14 @@ limitations under the License. ******************************************************************************/ +using NumSharp.Utilities; using System; +using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Threading; using Tensorflow.Eager; +using static Tensorflow.Binding; namespace Tensorflow { @@ -38,7 +41,6 @@ namespace Tensorflow public Context context = new Context(new ContextOptions(), new Status()); - public tensorflow() { _constructThreadingObjects(); @@ -53,20 +55,20 @@ namespace Tensorflow return ones.EagerTensorHandle; }, (gradients) => { - var input_grads = gradients.Data().Select(x => new EagerTensor(x)).ToArray(); + var input_grads = gradients.Data.Select(x => new EagerTensor(x)).ToArray(); var add_n = gen_math_ops.add_n(input_grads) as EagerTensor; return add_n.EagerTensorHandle; }); ops.RegisterFromAssembly(); + // ops.RegisterFromAssemblyEager(); + c_api.TFE_RegisterGradientFunction((op_name, op_inputs, op_outputs, num_attrs, output_grads, skip_input_indices) => { - var input_tensors = op_inputs.Data().Select(x => new EagerTensor(x)).ToArray(); - var output_tensors = op_outputs.Data().Select(x => new EagerTensor(x)).ToArray(); - var output_grad_tensors = output_grads.Data().Select(x => new EagerTensor(x)).ToArray(); - var skip_input_indices_param = new int[skip_input_indices.length]; - for (int i = 0; i < skip_input_indices.length; i++) - skip_input_indices_param[i] = *((int*)skip_input_indices.array + i); + var input_tensors = new BindingTensorArray(op_inputs).Data.Select(x => new EagerTensor(x)).ToArray(); + var output_tensors = new BindingTensorArray(op_outputs).Data.Select(x => new EagerTensor(x)).ToArray(); + var output_grad_tensors = new BindingTensorArray(output_grads).Data.Select(x => new EagerTensor(x)).ToArray(); + var skip_input_indices_param = new BindingArray(skip_input_indices).Data.Select(x => *(int*)x).ToArray(); var gradients = ops.gradientFunctions[op_name](new EagerOperation { diff --git a/test/TensorFlowNET.UnitTest/NativeAPI/Eager/GradientEagerTest.cs b/test/TensorFlowNET.UnitTest/NativeAPI/Eager/GradientEagerTest.cs index a46ab669..edd1a438 100644 --- a/test/TensorFlowNET.UnitTest/NativeAPI/Eager/GradientEagerTest.cs +++ b/test/TensorFlowNET.UnitTest/NativeAPI/Eager/GradientEagerTest.cs @@ -10,7 +10,6 @@ namespace TensorFlowNET.UnitTest.Gradient [TestClass] public class GradientEagerTest : PythonTest { - [Ignore] [TestMethod] public void ConstantSq() {