diff --git a/src/TensorFlowNET.Core/Attributes/c_api.ops.cs b/src/TensorFlowNET.Core/Attributes/c_api.ops.cs
new file mode 100644
index 00000000..9dc36fa8
--- /dev/null
+++ b/src/TensorFlowNET.Core/Attributes/c_api.ops.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Tensorflow
+{
+ public static partial class c_api
+ {
+ ///
+ /// Fills in `value` with the value of the attribute `attr_name`. `value` must
+ /// point to an array of length at least `max_length` (ideally set to
+ /// TF_AttrMetadata.total_size from TF_OperationGetAttrMetadata(oper,
+ /// attr_name)).
+ ///
+ /// TF_Operation*
+ /// const char*
+ /// TF_Status*
+ ///
+ [DllImport(TensorFlowLibName)]
+ public static extern IntPtr TF_OperationGetAttrMetadata(IntPtr oper, string attr_name, IntPtr status);
+
+ ///
+ /// Fills in `value` with the value of the attribute `attr_name`. `value` must
+ /// point to an array of length at least `max_length` (ideally set to
+ /// TF_AttrMetadata.total_size from TF_OperationGetAttrMetadata(oper,
+ /// attr_name)).
+ ///
+ /// TF_Operation*
+ /// const char*
+ /// void*
+ /// size_t
+ /// TF_Status*
+ [DllImport(TensorFlowLibName)]
+ public static extern void TF_OperationGetAttrString(IntPtr oper, string attr_name, IntPtr value, uint max_length, IntPtr status);
+
+ ///
+ /// Sets `output_attr_value` to the binary-serialized AttrValue proto
+ /// representation of the value of the `attr_name` attr of `oper`.
+ ///
+ ///
+ ///
+ [DllImport(TensorFlowLibName)]
+ public static extern int TF_OperationGetAttrValueProto(IntPtr oper, string attr_name, IntPtr output_attr_value, IntPtr status);
+
+ [DllImport(TensorFlowLibName)]
+ public static extern void TF_SetAttrBool(IntPtr desc, string attr_name, bool value);
+
+ [DllImport(TensorFlowLibName)]
+ public static extern void TF_SetAttrValueProto(IntPtr desc, string attr_name, IntPtr proto, uint proto_len, IntPtr status);
+
+ ///
+ /// Set `num_dims` to -1 to represent "unknown rank".
+ ///
+ ///
+ ///
+ ///
+ ///
+ [DllImport(TensorFlowLibName)]
+ public static extern void TF_SetAttrShape(IntPtr desc, string attr_name, long[] dims, int num_dims);
+
+ ///
+ /// Call some TF_SetAttr*() function for every attr that is not
+ /// inferred from an input and doesn't have a default value you wish to
+ /// keep.
+ ///
+ /// `value` must point to a string of length `length` bytes.
+ ///
+ /// TF_OperationDescription*
+ /// const char*
+ /// const void*
+ /// size_t
+ [DllImport(TensorFlowLibName)]
+ public static extern void TF_SetAttrString(IntPtr desc, string attr_name, IntPtr value, uint length);
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [DllImport(TensorFlowLibName)]
+ public static extern void TF_SetAttrStringList(IntPtr desc, string attr_name, IntPtr[] values, uint[] lengths, int num_values);
+
+ [DllImport(TensorFlowLibName)]
+ public static extern void TF_SetAttrTensor(IntPtr desc, string attr_name, IntPtr value, IntPtr status);
+
+ [DllImport(TensorFlowLibName)]
+ public static extern void TF_SetAttrType(IntPtr desc, string attr_name, TF_DataType value);
+ }
+}
diff --git a/src/TensorFlowNET.Core/Operations/c_api.ops.cs b/src/TensorFlowNET.Core/Operations/c_api.ops.cs
index 96be50d2..cb3ae75e 100644
--- a/src/TensorFlowNET.Core/Operations/c_api.ops.cs
+++ b/src/TensorFlowNET.Core/Operations/c_api.ops.cs
@@ -52,42 +52,6 @@ namespace Tensorflow
[DllImport(TensorFlowLibName)]
public static extern IntPtr TF_OperationDevice(IntPtr oper);
- ///
- /// Fills in `value` with the value of the attribute `attr_name`. `value` must
- /// point to an array of length at least `max_length` (ideally set to
- /// TF_AttrMetadata.total_size from TF_OperationGetAttrMetadata(oper,
- /// attr_name)).
- ///
- /// TF_Operation*
- /// const char*
- /// TF_Status*
- ///
- [DllImport(TensorFlowLibName)]
- public static extern IntPtr TF_OperationGetAttrMetadata(IntPtr oper, string attr_name, IntPtr status);
-
- ///
- /// Fills in `value` with the value of the attribute `attr_name`. `value` must
- /// point to an array of length at least `max_length` (ideally set to
- /// TF_AttrMetadata.total_size from TF_OperationGetAttrMetadata(oper,
- /// attr_name)).
- ///
- /// TF_Operation*
- /// const char*
- /// void*
- /// size_t
- /// TF_Status*
- [DllImport(TensorFlowLibName)]
- public static extern void TF_OperationGetAttrString(IntPtr oper, string attr_name, IntPtr value, uint max_length, IntPtr status);
-
- ///
- /// Sets `output_attr_value` to the binary-serialized AttrValue proto
- /// representation of the value of the `attr_name` attr of `oper`.
- ///
- ///
- ///
- [DllImport(TensorFlowLibName)]
- public static extern int TF_OperationGetAttrValueProto(IntPtr oper, string attr_name, IntPtr output_attr_value, IntPtr status);
-
///
/// Get list of all control inputs to an operation. `control_inputs` must
/// point to an array of length `max_control_inputs` (ideally set to
@@ -195,49 +159,5 @@ namespace Tensorflow
[DllImport(TensorFlowLibName)]
public static extern int TF_OperationOutputListLength(IntPtr oper, string arg_name, IntPtr status);
-
- [DllImport(TensorFlowLibName)]
- public static extern void TF_SetAttrValueProto(IntPtr desc, string attr_name, IntPtr proto, uint proto_len, IntPtr status);
-
- ///
- /// Set `num_dims` to -1 to represent "unknown rank".
- ///
- ///
- ///
- ///
- ///
- [DllImport(TensorFlowLibName)]
- public static extern void TF_SetAttrShape(IntPtr desc, string attr_name, long[] dims, int num_dims);
-
- ///
- /// Call some TF_SetAttr*() function for every attr that is not
- /// inferred from an input and doesn't have a default value you wish to
- /// keep.
- ///
- /// `value` must point to a string of length `length` bytes.
- ///
- /// TF_OperationDescription*
- /// const char*
- /// const void*
- /// size_t
- [DllImport(TensorFlowLibName)]
- public static extern void TF_SetAttrString(IntPtr desc, string attr_name, IntPtr value, uint length);
-
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- [DllImport(TensorFlowLibName)]
- public static extern void TF_SetAttrStringList(IntPtr desc, string attr_name, IntPtr[] values, uint[] lengths, int num_values);
-
- [DllImport(TensorFlowLibName)]
- public static extern void TF_SetAttrTensor(IntPtr desc, string attr_name, IntPtr value, IntPtr status);
-
- [DllImport(TensorFlowLibName)]
- public static extern void TF_SetAttrType(IntPtr desc, string attr_name, TF_DataType value);
}
}
diff --git a/test/TensorFlowNET.UnitTest/CApiGradientsTest.cs b/test/TensorFlowNET.UnitTest/CApiGradientsTest.cs
new file mode 100644
index 00000000..5a7eeba3
--- /dev/null
+++ b/test/TensorFlowNET.UnitTest/CApiGradientsTest.cs
@@ -0,0 +1,116 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using NumSharp.Core;
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+using Tensorflow;
+
+namespace TensorFlowNET.UnitTest
+{
+ ///
+ /// tensorflow\c\c_api_test.cc
+ /// `class CApiGradientsTest`
+ ///
+ [TestClass]
+ public class CApiGradientsTest : CApiTest, IDisposable
+ {
+ private Graph graph_ = new Graph();
+ private Graph expected_graph_ = new Graph();
+ private Status s_ = new Status();
+
+ private void TestGradientsSuccess(bool grad_inputs_provided)
+ {
+ var inputs = new TF_Output[2];
+ var outputs = new TF_Output[1];
+ var grad_outputs = new TF_Output[2];
+ var expected_grad_outputs = new TF_Output[2];
+
+ BuildSuccessGraph(inputs, outputs);
+ }
+
+ private void BuildSuccessGraph(TF_Output[] inputs, TF_Output[] outputs)
+ {
+ // Construct the following graph:
+ // |
+ // z|
+ // |
+ // MatMul
+ // / \
+ // ^ ^
+ // | |
+ // x| y|
+ // | |
+ // | |
+ // Const_0 Const_1
+ //
+ var const0_val = new float[] { 1.0f, 2.0f, 3.0f, 4.0f };
+ var const1_val = new float[] { 1.0f, 0.0f, 0.0f, 1.0f };
+ var const0 = FloatConst2x2(graph_, s_, const0_val, "Const_0");
+ var const1 = FloatConst2x2(graph_, s_, const1_val, "Const_1");
+ var matmul = MatMul(graph_, s_, const0, const1, "MatMul");
+ inputs[0] = new TF_Output(const0, 0);
+ inputs[1] = new TF_Output(const1, 0);
+ outputs[0] = new TF_Output(matmul, 0);
+ EXPECT_EQ(TF_OK, TF_GetCode(s_));
+ }
+
+ private Operation FloatConst2x2(Graph graph, Status s, float[] values, string name)
+ {
+ var tensor = FloatTensor2x2(values);
+ var desc = TF_NewOperation(graph, "Const", name);
+ TF_SetAttrTensor(desc, "value", tensor, s);
+ if (TF_GetCode(s) != TF_OK) return IntPtr.Zero;
+ TF_SetAttrType(desc, "dtype", TF_FLOAT);
+ var op = TF_FinishOperation(desc, s);
+ EXPECT_EQ(TF_OK, TF_GetCode(s));
+ return op;
+ }
+
+ private Tensor FloatTensor2x2(float[] values)
+ {
+ long[] dims = { 2, 2 };
+ Tensor t = c_api.TF_AllocateTensor(TF_FLOAT, dims, 2, sizeof(float) * 4);
+ Marshal.Copy(values, 0, t, 4);
+ return t;
+ }
+
+ private Operation MatMul(Graph graph, Status s, Operation l, Operation r, string name,
+ bool transpose_a = false, bool transpose_b = false)
+ {
+ var desc = TF_NewOperation(graph, "MatMul", name);
+ if (transpose_a)
+ {
+ TF_SetAttrBool(desc, "transpose_a", true);
+ }
+ if (transpose_b)
+ {
+ TF_SetAttrBool(desc, "transpose_b", true);
+ }
+ TF_AddInput(desc, new TF_Output(l, 0));
+ TF_AddInput(desc, new TF_Output(r, 0));
+ var op = TF_FinishOperation(desc, s);
+ EXPECT_EQ(TF_OK, TF_GetCode(s));
+ return op;
+ }
+
+ [TestMethod]
+ public void Gradients_GradInputs()
+ {
+ TestGradientsSuccess(true);
+ }
+
+ [TestMethod]
+ public void Gradients_NoGradInputs()
+ {
+ TestGradientsSuccess(false);
+ }
+
+ public void Dispose()
+ {
+ graph_.Dispose();
+ expected_graph_.Dispose();
+ s_.Dispose();
+ }
+ }
+}
diff --git a/test/TensorFlowNET.UnitTest/CApiTest.cs b/test/TensorFlowNET.UnitTest/CApiTest.cs
index fea2c6e4..bfecd5c1 100644
--- a/test/TensorFlowNET.UnitTest/CApiTest.cs
+++ b/test/TensorFlowNET.UnitTest/CApiTest.cs
@@ -2,24 +2,63 @@
using System;
using System.Collections.Generic;
using System.Text;
+using Tensorflow;
namespace TensorFlowNET.UnitTest
{
public class CApiTest
{
- public void EXPECT_EQ(object expected, object actual)
+ protected TF_Code TF_OK = TF_Code.TF_OK;
+ protected TF_DataType TF_FLOAT = TF_DataType.TF_FLOAT;
+
+ protected void EXPECT_EQ(object expected, object actual)
{
Assert.AreEqual(expected, actual);
}
- public void ASSERT_EQ(object expected, object actual)
+ protected void ASSERT_EQ(object expected, object actual)
{
Assert.AreEqual(expected, actual);
}
- public void ASSERT_TRUE(bool condition)
+ protected void ASSERT_TRUE(bool condition)
{
Assert.IsTrue(condition);
}
+
+ protected OperationDescription TF_NewOperation(Graph graph, string opType, string opName)
+ {
+ return c_api.TF_NewOperation(graph, opType, opName);
+ }
+
+ protected void TF_AddInput(OperationDescription desc, TF_Output input)
+ {
+ c_api.TF_AddInput(desc, input);
+ }
+
+ protected Operation TF_FinishOperation(OperationDescription desc, Status s)
+ {
+ return c_api.TF_FinishOperation(desc, s);
+ }
+
+ protected void TF_SetAttrTensor(OperationDescription desc, string attrName, Tensor value, Status s)
+ {
+ c_api.TF_SetAttrTensor(desc, attrName, value, s);
+ }
+
+ protected void TF_SetAttrType(OperationDescription desc, string attrName, TF_DataType dtype)
+ {
+ c_api.TF_SetAttrType(desc, attrName, dtype);
+ }
+
+ protected void TF_SetAttrBool(OperationDescription desc, string attrName, bool value)
+ {
+ c_api.TF_SetAttrBool(desc, attrName, value);
+ }
+
+ protected TF_Code TF_GetCode(Status s)
+ {
+ return s.Code;
+ }
}
}