Skip to content

Commit 42c2e76

Browse files
cesarsouzamigueldeicaza
authored andcommitted
migueldeicazaGH-180: Missing ReduceProd. (migueldeicaza#181)
1 parent 8d9fb12 commit 42c2e76

6 files changed

Lines changed: 141 additions & 0 deletions

File tree

TensorFlowSharp/OperationsExtras.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,28 @@ public TFOutput ReduceSum (TFOutput input, TFOutput? axis = null, bool? keep_dim
6363
return Sum (input, this.ReduceDims (input, axis), keep_dims, operName);
6464
}
6565

66+
/// <summary>
67+
/// Computes the product of elements across dimensions of a tensor.
68+
/// </summary>
69+
/// <returns>The reduced tensor.</returns>
70+
/// <param name="input">The tensor to reduce. Should have numeric type.</param>
71+
/// <param name="axis">The dimensions to reduce. If not se (the default), reduces all dimensions.</param>
72+
/// <param name="keep_dims">If set to <c>true</c> retains reduced dimensions with length 1.</param>
73+
/// <param name="operName">A name for the operation, optional.</param>
74+
/// <remarks>
75+
/// Reduces input_tensor along the dimensions given in axis.
76+
/// Unless keep_dims is true, the rank of the tensor is reduced by 1 for each
77+
/// entry in axis. If keep_dims is true, the reduced dimensions
78+
/// are retained with length 1.
79+
///
80+
/// If axis has no entries, all dimensions are reduced, and a
81+
/// tensor with a single element is returned.
82+
/// </remarks>
83+
public TFOutput ReduceProd (TFOutput input, TFOutput? axis = null, bool? keep_dims = false, string operName = null)
84+
{
85+
return Prod (input, this.ReduceDims (input, axis), keep_dims, operName);
86+
}
87+
6688
/// <summary>
6789
/// Computes the mean of elements across dimensions of a tensor.
6890
/// </summary>
@@ -89,6 +111,7 @@ public TFOutput ReduceMean (TFOutput input, TFOutput? axis = null, bool? keep_di
89111
return this.Mean (input, this.ReduceDims (input, axis), keep_dims, operName);
90112
}
91113

114+
92115
// Helper method to create a variable and track it.
93116
Variable MakeVariable (TFOutput initialValue, bool trainable, string operName)
94117
{

TensorFlowSharp/Tensorflow.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3454,6 +3454,16 @@ public TFTensor AsTensor ()
34543454
Array.Copy (right.dims, 0, full, left.dims.Length, right.dims.Length);
34553455
return new TFShape (full);
34563456
}
3457+
3458+
/// <summary>
3459+
/// Performs an implicit conversion from <see cref="TFShape"/> to <see cref="TFTensor"/>.
3460+
/// </summary>
3461+
/// <param name="shape">The shape.</param>
3462+
/// <returns>The result of the conversion.</returns>
3463+
public static implicit operator TFTensor (TFShape shape)
3464+
{
3465+
return shape.AsTensor ();
3466+
}
34573467
}
34583468

34593469

tests/TensorFlowSharp.Tests.CSharp/MathTests.cs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,5 +101,84 @@ public void Should_SigmoidCrossEntropyWithLogits (double [] labels, double [] lo
101101
}
102102
}
103103

104+
private static IEnumerable<object []> reduceProdData ()
105+
{
106+
// Example from https://www.tensorflow.org/api_docs/python/tf/reduce_mean but adapted to return prod
107+
var x = new double [,] { { 1, 1 },
108+
{ 2, 2 } };
109+
110+
yield return new object [] { x, null, 4.0 };
111+
yield return new object [] { x, 0, new double [] { 2, 2 } };
112+
yield return new object [] { x, 1, new double [] { 1, 4 } };
113+
}
114+
115+
[Theory]
116+
[MemberData (nameof (reduceProdData))]
117+
public void Should_ReduceProd (double [,] input, int? axis, object expected)
118+
{
119+
using (var graph = new TFGraph ())
120+
using (var session = new TFSession (graph)) {
121+
var tinput = graph.Placeholder (TFDataType.Double, new TFShape (2, 2));
122+
123+
TFTensor [] result;
124+
if (axis != null) {
125+
var taxis = graph.Const (axis.Value);
126+
TFOutput y = graph.ReduceProd (tinput, taxis);
127+
result = session.Run (new [] { tinput, taxis }, new TFTensor [] { input, axis }, new [] { y });
128+
129+
double [] actual = (double [])result [0].GetValue ();
130+
TestUtils.MatrixEqual (expected, actual, precision: 8);
131+
} else {
132+
TFOutput y = graph.ReduceProd (tinput, axis: null);
133+
result = session.Run (new [] { tinput }, new TFTensor [] { input }, new [] { y });
134+
135+
double actual = (double)result [0].GetValue ();
136+
TestUtils.MatrixEqual (expected, actual, precision: 8);
137+
}
138+
}
139+
}
140+
141+
private static IEnumerable<object []> reduceProdData2 ()
142+
{
143+
yield return new object [] { null, 170170.0 };
144+
yield return new object [] { -3, new [] { 1.0 } };
145+
yield return new object [] { -2, new [] { 22.0, 65.0, 119.0 } };
146+
yield return new object [] { -1, new [] { 70.0, 2431.0 } };
147+
yield return new object [] { 0, new [] { 22.0, 65.0, 119.0 } };
148+
yield return new object [] { 1, new [] { 70.0, 2431.0 } };
149+
yield return new object [] { 2, new [] { 1.0 } };
150+
yield return new object [] { 3, new [] { 1.0 } };
151+
}
152+
153+
[Theory]
154+
[MemberData (nameof (reduceProdData2))]
155+
public void Should_ReduceProd2 (int? axis, object expected)
156+
{
157+
using (var graph = new TFGraph ())
158+
using (var session = new TFSession (graph)) {
159+
160+
double [,] test = {
161+
{ 2, 5, 7 },
162+
{ 11, 13, 17 },
163+
};
164+
165+
var x = graph.Const (test);
166+
167+
if (axis == null || axis >= -2 && axis < 2) {
168+
TFOutput y = graph.ReduceProd (x, axis: axis == null ? (TFOutput?)null : graph.Const(axis));
169+
170+
TFTensor [] result = session.Run (new TFOutput [] { }, new TFTensor [] { }, new [] { y });
171+
172+
object actual = result [0].GetValue ();
173+
TestUtils.MatrixEqual (expected, actual, precision: 8);
174+
} else {
175+
Assert.Throws<TFException> (() => {
176+
TFOutput y = graph.ReduceProd (x, axis: axis == null ? (TFOutput?)null : graph.Const (axis));
177+
session.Run (new TFOutput [] { }, new TFTensor [] { }, new [] { y });
178+
});
179+
}
180+
}
181+
}
182+
104183
}
105184
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using TensorFlow;
4+
using Xunit;
5+
6+
namespace TensorFlowSharp.Tests.CSharp
7+
{
8+
public class ShapeTests
9+
{
10+
[Fact]
11+
public void Should_ShapeAutomaticallyConvertToTensor ()
12+
{
13+
using (var graph = new TFGraph ())
14+
using (var session = new TFSession (graph)) {
15+
16+
var x = graph.Const (new TFShape(2, 3));
17+
18+
TFTensor [] result = session.Run (new TFOutput [] { }, new TFTensor [] { }, new TFOutput [] { x });
19+
20+
int[] actual = (int[])result [0].GetValue ();
21+
Assert.Equal (new [] { 2, 3 }, actual);
22+
}
23+
}
24+
25+
}
26+
}

tests/TensorFlowSharp.Tests.CSharp/TensorFlowSharp.Tests.CSharp.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
<Compile Include="CondTests.cs" />
6767
<Compile Include="GradientTests.cs" />
6868
<Compile Include="ArrayTests.cs" />
69+
<Compile Include="ShapeTests.cs" />
6970
<Compile Include="TensorTests.cs" />
7071
<Compile Include="ClipTests.cs" />
7172
<Compile Include="BitwiseOperationTests.cs" />

tests/TensorFlowSharp.Tests.CSharp/TestUtils.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ public static void MatrixEqual (object expected, object actual, int precision)
5656
Assert.Equal ((double)expected, (double)actual, precision: precision);
5757
} else if (expectedType == typeof (float)) {
5858
Assert.Equal ((float)expected, (float)actual, precision: precision);
59+
} else if (expectedType == typeof (int)) {
60+
Assert.Equal ((int)expected, (int)actual);
5961
} else {
6062
Assert.True (Object.Equals (expected, actual));
6163
}

0 commit comments

Comments
 (0)