Skip to content

Commit 5684d3f

Browse files
authored
Merge branch 'dev' into perf/optimize-diagonal-function
2 parents 8e8c466 + 965c60d commit 5684d3f

2 files changed

Lines changed: 276 additions & 0 deletions

File tree

tests/Furnace.Tests/Furnace.Tests.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
<Compile Include="TestTorchExtensions.fs" />
3737

3838
<Compile Include="TestReferenceBackend.fs" />
39+
<Compile Include="TestBranchCoverage.fs" />
3940
<Compile Include="TestPlotHelpers.fs" />
4041
<Compile Include="TestPyplot.fs" />
4142
<Compile Include="TestData.fs" />
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
// Copyright (c) 2016- University of Oxford (Atılım Güneş Baydin <gunes@robots.ox.ac.uk>)
2+
// and other contributors, see LICENSE in root of repository.
3+
//
4+
// BSD 2-Clause License. See LICENSE in root of repository.
5+
6+
namespace Tests
7+
8+
open System
9+
open NUnit.Framework
10+
open Furnace
11+
12+
[<TestFixture>]
13+
type TestBranchCoverage() =
14+
15+
[<Test>]
16+
member _.TestTensorCastingBranches() =
17+
let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
18+
let t = combo.tensor([1.0f; 2.0f; 3.0f])
19+
20+
// Test cast to same type (should return same tensor)
21+
let sameCast = t.cast(Dtype.Float32)
22+
Assert.AreSame(t, sameCast)
23+
24+
// Test different type casts
25+
let intCast = t.cast(Dtype.Int32)
26+
Assert.AreEqual(Dtype.Int32, intCast.dtype)
27+
28+
let doubleCast = t.cast(Dtype.Float64)
29+
Assert.AreEqual(Dtype.Float64, doubleCast.dtype)
30+
31+
[<Test>]
32+
member _.TestTensorBackendMoveBranches() =
33+
let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
34+
let t = combo.tensor([1.0f; 2.0f; 3.0f])
35+
36+
// Test move to same backend (should return same tensor)
37+
let sameBackend = t.move(Backend.Reference)
38+
Assert.AreSame(t, sameBackend)
39+
40+
[<Test>]
41+
member _.TestTensorDeviceMoveBranches() =
42+
let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
43+
let t = combo.tensor([1.0f; 2.0f; 3.0f])
44+
45+
// Test move to same device (should return same tensor)
46+
let sameDevice = t.move(Device.CPU)
47+
Assert.AreSame(t, sameDevice)
48+
49+
[<Test>]
50+
member _.TestGenericCastingBranches() =
51+
let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
52+
let t = combo.tensor([1.0f; 2.0f; 3.0f])
53+
54+
// Test different generic cast types
55+
let float32Cast = t.cast<float32>()
56+
Assert.AreEqual(Dtype.Float32, float32Cast.dtype)
57+
58+
let float64Cast = t.cast<double>()
59+
Assert.AreEqual(Dtype.Float64, float64Cast.dtype)
60+
61+
let int32Cast = t.cast<int32>()
62+
Assert.AreEqual(Dtype.Int32, int32Cast.dtype)
63+
64+
let int64Cast = t.cast<int64>()
65+
Assert.AreEqual(Dtype.Int64, int64Cast.dtype)
66+
67+
let int16Cast = t.cast<int16>()
68+
Assert.AreEqual(Dtype.Int16, int16Cast.dtype)
69+
70+
let int8Cast = t.cast<int8>()
71+
Assert.AreEqual(Dtype.Int8, int8Cast.dtype)
72+
73+
let bytecast = t.cast<byte>()
74+
Assert.AreEqual(Dtype.Byte, bytecast.dtype)
75+
76+
let boolCast = t.cast<bool>()
77+
Assert.AreEqual(Dtype.Bool, boolCast.dtype)
78+
79+
[<Test>]
80+
member _.TestInvalidGenericCastBranch() =
81+
let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
82+
let t = combo.tensor([1.0f; 2.0f; 3.0f])
83+
84+
// Test invalid cast type should throw
85+
Assert.Throws<System.Exception>(fun () ->
86+
t.cast<string>() |> ignore) |> ignore
87+
88+
[<Test>]
89+
member _.TestTensorComparisons() =
90+
let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
91+
let t1 = combo.tensor([1.0f; 2.0f; 3.0f])
92+
let t2 = combo.tensor([1.0f; 2.0f; 3.0f])
93+
let t3 = combo.tensor([1.0f; 2.0f; 4.0f])
94+
95+
// Test equality branches
96+
Assert.True(t1.Equals(t2))
97+
Assert.False(t1.Equals(t3))
98+
Assert.False(t1.Equals(null))
99+
100+
[<Test>]
101+
member _.TestTensorShapeValidation() =
102+
let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
103+
104+
// Test zero-sized tensors
105+
let empty = FurnaceImage.zeros([0], dtype=combo.dtype, backend=combo.backend, device=combo.device)
106+
Assert.AreEqual(0, empty.nelement)
107+
108+
// Test single element tensors
109+
let single = combo.tensor([42.0f])
110+
Assert.AreEqual(1, single.nelement)
111+
112+
[<Test>]
113+
member _.TestTensorIndexingBoundaries() =
114+
let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
115+
let t = combo.tensor([1.0f; 2.0f; 3.0f; 4.0f; 5.0f])
116+
117+
// Test valid indexing
118+
Assert.DoesNotThrow(fun () -> t[0] |> ignore)
119+
Assert.DoesNotThrow(fun () -> t[4] |> ignore)
120+
121+
// Test boundary conditions for slicing
122+
let slice1 = t[0..2]
123+
Assert.AreEqual([|3|], slice1.shape)
124+
125+
let slice2 = t[1..4]
126+
Assert.AreEqual([|4|], slice2.shape)
127+
128+
[<Test>]
129+
member _.TestTensorOperationEdgeCases() =
130+
let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
131+
132+
// Test operations with different sized tensors
133+
let t1x1 = combo.tensor([[1.0f]])
134+
let t2x2 = combo.tensor([[1.0f; 2.0f]; [3.0f; 4.0f]])
135+
136+
// Test broadcasting operations
137+
let broadcast1 = t1x1 + t2x2
138+
Assert.AreEqual([|2; 2|], broadcast1.shape)
139+
140+
let broadcast2 = t2x2 * combo.tensor([2.0f])
141+
Assert.AreEqual([|2; 2|], broadcast2.shape)
142+
143+
[<Test>]
144+
member _.TestBoolTensorOperations() =
145+
let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Bool)
146+
147+
// Test boolean tensor creation and operations
148+
let b1 = combo.tensor([true; false; true])
149+
let b2 = combo.tensor([false; true; true])
150+
151+
// Test boolean operations that may have specific branches
152+
Assert.AreEqual(Dtype.Bool, b1.dtype)
153+
Assert.AreEqual(3, b1.nelement)
154+
155+
// Test boolean comparisons
156+
let eq = b1.eq(b2)
157+
Assert.AreEqual(Dtype.Bool, eq.dtype)
158+
159+
[<Test>]
160+
member _.TestTensorCreationEdgeCases() =
161+
let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
162+
163+
// Test creating tensors from different input types
164+
let fromInt = combo.tensor([1; 2; 3])
165+
Assert.AreEqual(Dtype.Float32, fromInt.dtype) // Should be cast to combo dtype
166+
167+
let fromFloat = combo.tensor([1.0; 2.0; 3.0])
168+
Assert.AreEqual(Dtype.Float32, fromFloat.dtype)
169+
170+
// Test nested arrays
171+
let nested = combo.tensor([[[1.0f]]])
172+
Assert.AreEqual([|1; 1; 1|], nested.shape)
173+
Assert.AreEqual(3, nested.dim)
174+
175+
[<Test>]
176+
member _.TestTensorMemoryLayout() =
177+
let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
178+
179+
// Test different memory layouts and shapes
180+
let t1d = combo.tensor([1.0f; 2.0f; 3.0f; 4.0f])
181+
let t2d = t1d.view([2; 2])
182+
183+
Assert.AreEqual([|4|], t1d.shape)
184+
Assert.AreEqual([|2; 2|], t2d.shape)
185+
Assert.AreEqual(t1d.nelement, t2d.nelement)
186+
187+
// Test transpose operations
188+
let transposed = t2d.transpose()
189+
Assert.AreEqual([|2; 2|], transposed.shape)
190+
191+
[<Test>]
192+
member _.TestTensorReductionEdgeCases() =
193+
let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
194+
195+
// Test reductions on different tensor shapes
196+
let t1d = combo.tensor([1.0f; 2.0f; 3.0f; 4.0f])
197+
let sum1d = t1d.sum()
198+
Assert.True(abs(sum1d.toScalar().toSingle() - 10.0f) < 0.001f)
199+
200+
let t2d = combo.tensor([[1.0f; 2.0f]; [3.0f; 4.0f]])
201+
let sum2d = t2d.sum()
202+
Assert.True(abs(sum2d.toScalar().toSingle() - 10.0f) < 0.001f)
203+
204+
// Test specific dimension reductions
205+
let sumDim0 = t2d.sum(0)
206+
Assert.AreEqual([|2|], sumDim0.shape)
207+
208+
let sumDim1 = t2d.sum(1)
209+
Assert.AreEqual([|2|], sumDim1.shape)
210+
211+
[<Test>]
212+
member _.TestLowPrecisionTypeOperations() =
213+
// Test BFloat16 specific operations for branch coverage
214+
let comboBF16 = ComboInfo(Backend.Reference, Device.CPU, Dtype.BFloat16)
215+
let t1 = comboBF16.tensor([1.0f; 2.0f; 3.0f])
216+
let t2 = comboBF16.tensor([2.0f; 3.0f; 4.0f])
217+
218+
// Test various comparison operations
219+
let eq = t1.eq(t2)
220+
let ne = t1.ne(t2)
221+
let lt = t1.lt(t2)
222+
let le = t1.le(t2)
223+
let gt = t1.gt(t2)
224+
let ge = t1.ge(t2)
225+
226+
Assert.AreEqual(Dtype.Bool, eq.dtype)
227+
Assert.AreEqual(Dtype.Bool, ne.dtype)
228+
Assert.AreEqual(Dtype.Bool, lt.dtype)
229+
Assert.AreEqual(Dtype.Bool, le.dtype)
230+
Assert.AreEqual(Dtype.Bool, gt.dtype)
231+
Assert.AreEqual(Dtype.Bool, ge.dtype)
232+
233+
[<Test>]
234+
member _.TestActivationFunctionBranches() =
235+
let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
236+
let t = combo.tensor([-2.0f; -1.0f; 0.0f; 1.0f; 2.0f])
237+
238+
// Test various activation functions that might have different code paths
239+
let sigmoid = t.sigmoid()
240+
Assert.AreEqual(5, sigmoid.nelement)
241+
Assert.AreEqual(Dtype.Float32, sigmoid.dtype)
242+
243+
let tanh = t.tanh()
244+
Assert.AreEqual(5, tanh.nelement)
245+
246+
let relu = t.relu()
247+
Assert.AreEqual(5, relu.nelement)
248+
249+
let softplus = t.softplus()
250+
Assert.AreEqual(5, softplus.nelement)
251+
252+
[<Test>]
253+
member _.TestMathematicalFunctionEdgeCases() =
254+
let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
255+
256+
// Test with various special values that might trigger different branches
257+
let positive = combo.tensor([1.0f; 2.0f; 3.0f])
258+
let negative = combo.tensor([-1.0f; -2.0f; -3.0f])
259+
let mixed = combo.tensor([-1.0f; 0.0f; 1.0f])
260+
261+
// Test exp on different value ranges
262+
let expPos = positive.exp()
263+
let expMixed = mixed.exp()
264+
Assert.AreEqual(3, expPos.nelement)
265+
Assert.AreEqual(3, expMixed.nelement)
266+
267+
// Test log on positive values (negative would be invalid)
268+
let logPos = positive.log()
269+
Assert.AreEqual(3, logPos.nelement)
270+
271+
// Test absolute value
272+
let absNeg = negative.abs()
273+
let absMixed = mixed.abs()
274+
Assert.AreEqual(3, absNeg.nelement)
275+
Assert.AreEqual(3, absMixed.nelement)

0 commit comments

Comments
 (0)