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.0 f; 2.0 f; 3.0 f])
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.0 f; 2.0 f; 3.0 f])
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.0 f; 2.0 f; 3.0 f])
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.0 f; 2.0 f; 3.0 f])
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.0 f; 2.0 f; 3.0 f])
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.0 f; 2.0 f; 3.0 f])
92+ let t2 = combo.tensor([ 1.0 f; 2.0 f; 3.0 f])
93+ let t3 = combo.tensor([ 1.0 f; 2.0 f; 4.0 f])
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.0 f])
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.0 f; 2.0 f; 3.0 f; 4.0 f; 5.0 f])
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.0 f]])
134+ let t2x2 = combo.tensor([[ 1.0 f; 2.0 f]; [ 3.0 f; 4.0 f]])
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.0 f])
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.0 f]]])
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.0 f; 2.0 f; 3.0 f; 4.0 f])
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.0 f; 2.0 f; 3.0 f; 4.0 f])
197+ let sum1d = t1d.sum()
198+ Assert.True( abs( sum1d.toScalar() .toSingle() - 10.0 f) < 0.001 f)
199+
200+ let t2d = combo.tensor([[ 1.0 f; 2.0 f]; [ 3.0 f; 4.0 f]])
201+ let sum2d = t2d.sum()
202+ Assert.True( abs( sum2d.toScalar() .toSingle() - 10.0 f) < 0.001 f)
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.0 f; 2.0 f; 3.0 f])
216+ let t2 = comboBF16.tensor([ 2.0 f; 3.0 f; 4.0 f])
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.0 f; - 1.0 f; 0.0 f; 1.0 f; 2.0 f])
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.0 f; 2.0 f; 3.0 f])
258+ let negative = combo.tensor([- 1.0 f; - 2.0 f; - 3.0 f])
259+ let mixed = combo.tensor([- 1.0 f; 0.0 f; 1.0 f])
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